📄 src/models/versioning.ts
D-OPEN SOVEREIGN

Documentation & Insights

Tracks the edit history of each object in the in-memory database.
ObjectTypeId => ObjectId => { properties, relations }
1/*
2 * Copyright (c) 2026 DataPond D-Library Pty Ltd. ("The Sanctuary")
3 * Non-Profit Public Library — The World Library
4 * 
5 * D-SAFE Certification issued by POND Enterprise.
6 * Published under the D-Open Code Sovereign Licence v1.0 framework
7 * DataPond D-Library All exclusive Right reserved on modifiying the code - CC Attribution to data pond, Non modifiable, Non Commercial.
8 * https://registry.world.bibliotech.com/licence
9 * Source code donated to DataPond D-Library by it's author: data pond.
10 * 
11 * Technical Guardian ("The Shield"): Pond Enterprise Pty Ltd. (ACN 694 747 987)
12 * All liability claims about D-SAFE certification issues coming from the published D-Safe direction must be addressed with Pond Enterprise Pty Ltd.
13 * Code Modification automatically voids the certified liability protection provided by Pond Enterprise.
14 */
15import {
16    PatchOperation, PatchAction,
17    type AnyPatch,
18    type EditPropertyStringObj, type EditPropertyNumberObj,
19    type EditRelationObj,
20} from "./binary.ts";
21import { Patches } from "./patcher.ts";
22
23/**
24 * Tracks the edit history of each object in the in-memory database.
25 * ObjectTypeId => ObjectId => { properties, relations }
26 */
27type IVersioning = Map<number, {
28    createdBy: string,
29    // propId => values over time
30    properties: Map<number, Array<{ username: string, value: string | number, langId: number }>>,
31    // relationId => ['+' | '-', targetId]
32    relations: Map<number, Array<{ username: string, value: ['+' | '-', number] }>>
33}>
34
35const versioning: Map<number, IVersioning> = new Map()
36
37let currentUsername = "mine"
38
39export const LockUsername = (username: string) => {
40    currentUsername = username
41}
42export const UnlockUsername = () => {
43    currentUsername = "mine"
44}
45
46const collectionFor = (objectType: number, objectId: number) => {
47    if (!versioning.has(objectType)) {
48        versioning.set(objectType, new Map())
49    }
50    const collection = versioning.get(objectType)!
51    if (!collection.has(objectId)) {
52        collection.set(objectId, {
53            createdBy: currentUsername,
54            properties: new Map(),
55            relations: new Map()
56        })
57    }
58    return collection.get(objectId)!
59}
60
61export const track = (patch: AnyPatch) => {
62    if (Patches._replaying) {
63        return
64    }
65    switch (patch.operation) {
66        case PatchOperation.EditPropertyString:
67        case PatchOperation.EditPropertyNumber: {
68            const op = patch as EditPropertyStringObj | EditPropertyNumberObj
69            const entry = collectionFor(op.objectType, op.objectId)
70            if (!entry.properties.has(op.propertyId)) {
71                entry.properties.set(op.propertyId, [])
72            }
73            entry.properties.get(op.propertyId)!.push({
74                username: currentUsername,
75                value: op.value,
76                langId: op.langId
77            })
78            break
79        }
80        case PatchOperation.EditRelation: {
81            const op = patch as EditRelationObj
82            const entry = collectionFor(op.objectType, op.objectId)
83            if (!entry.relations.has(op.relationId)) {
84                entry.relations.set(op.relationId, [])
85            }
86            entry.relations.get(op.relationId)!.push({
87                username: currentUsername,
88                value: [op.action === PatchAction.Add ? '+' : '-', op.targetId]
89            })
90            break
91        }
92        default:
93            // EditInstanceField* and SetAlias are not tracked in the history view
94            break
95    }
96}
97
98export const getHistory = (objectType: number, objectId: number) => {
99    return versioning.get(objectType)?.get(objectId)
100}
101
102export const clearHistory = () => {
103    versioning.clear()
104}
105