@verdant-web/common 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/cjs/EventSubscriber.d.ts +17 -0
- package/dist/cjs/EventSubscriber.js +67 -0
- package/dist/cjs/EventSubscriber.js.map +1 -0
- package/dist/cjs/baseline.d.ts +5 -0
- package/dist/cjs/baseline.js +3 -0
- package/dist/cjs/baseline.js.map +1 -0
- package/dist/cjs/batching.d.ts +41 -0
- package/dist/cjs/batching.js +89 -0
- package/dist/cjs/batching.js.map +1 -0
- package/dist/cjs/files.d.ts +19 -0
- package/dist/cjs/files.js +19 -0
- package/dist/cjs/files.js.map +1 -0
- package/dist/cjs/files.test.d.ts +1 -0
- package/dist/cjs/files.test.js +81 -0
- package/dist/cjs/files.test.js.map +1 -0
- package/dist/cjs/index.d.ts +18 -0
- package/dist/cjs/index.js +36 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/indexes.d.ts +14 -0
- package/dist/cjs/indexes.js +116 -0
- package/dist/cjs/indexes.js.map +1 -0
- package/dist/cjs/indexes.test.d.ts +1 -0
- package/dist/cjs/indexes.test.js +128 -0
- package/dist/cjs/indexes.test.js.map +1 -0
- package/dist/cjs/migration.d.ts +98 -0
- package/dist/cjs/migration.js +128 -0
- package/dist/cjs/migration.js.map +1 -0
- package/dist/cjs/oids.d.ts +87 -0
- package/dist/cjs/oids.js +375 -0
- package/dist/cjs/oids.js.map +1 -0
- package/dist/cjs/oids.test.d.ts +1 -0
- package/dist/cjs/oids.test.js +282 -0
- package/dist/cjs/oids.test.js.map +1 -0
- package/dist/cjs/operation.d.ts +139 -0
- package/dist/cjs/operation.js +533 -0
- package/dist/cjs/operation.js.map +1 -0
- package/dist/cjs/operation.test.d.ts +1 -0
- package/dist/cjs/operation.test.js +827 -0
- package/dist/cjs/operation.test.js.map +1 -0
- package/dist/cjs/patch.d.ts +26 -0
- package/dist/cjs/patch.js +227 -0
- package/dist/cjs/patch.js.map +1 -0
- package/dist/cjs/presence.d.ts +27 -0
- package/dist/cjs/presence.js +3 -0
- package/dist/cjs/presence.js.map +1 -0
- package/dist/cjs/protocol.d.ts +145 -0
- package/dist/cjs/protocol.js +4 -0
- package/dist/cjs/protocol.js.map +1 -0
- package/dist/cjs/refs.d.ts +5 -0
- package/dist/cjs/refs.js +22 -0
- package/dist/cjs/refs.js.map +1 -0
- package/dist/cjs/replica.d.ts +28 -0
- package/dist/cjs/replica.js +29 -0
- package/dist/cjs/replica.js.map +1 -0
- package/dist/cjs/schema/defaults.d.ts +4 -0
- package/dist/cjs/schema/defaults.js +72 -0
- package/dist/cjs/schema/defaults.js.map +1 -0
- package/dist/cjs/schema/defaults.test.d.ts +1 -0
- package/dist/cjs/schema/defaults.test.js +21 -0
- package/dist/cjs/schema/defaults.test.js.map +1 -0
- package/dist/cjs/schema/index.d.ts +8 -0
- package/dist/cjs/schema/index.js +29 -0
- package/dist/cjs/schema/index.js.map +1 -0
- package/dist/cjs/schema/indexFilters.d.ts +6 -0
- package/dist/cjs/schema/indexFilters.js +31 -0
- package/dist/cjs/schema/indexFilters.js.map +1 -0
- package/dist/cjs/schema/types/collection.d.ts +51 -0
- package/dist/cjs/schema/types/collection.js +3 -0
- package/dist/cjs/schema/types/collection.js.map +1 -0
- package/dist/cjs/schema/types/compounds.d.ts +12 -0
- package/dist/cjs/schema/types/compounds.js +3 -0
- package/dist/cjs/schema/types/compounds.js.map +1 -0
- package/dist/cjs/schema/types/fields.d.ts +59 -0
- package/dist/cjs/schema/types/fields.js +3 -0
- package/dist/cjs/schema/types/fields.js.map +1 -0
- package/dist/cjs/schema/types/filters.d.ts +29 -0
- package/dist/cjs/schema/types/filters.js +3 -0
- package/dist/cjs/schema/types/filters.js.map +1 -0
- package/dist/cjs/schema/types/shapes.d.ts +21 -0
- package/dist/cjs/schema/types/shapes.js +3 -0
- package/dist/cjs/schema/types/shapes.js.map +1 -0
- package/dist/cjs/schema/types/synthetics.d.ts +28 -0
- package/dist/cjs/schema/types/synthetics.js +3 -0
- package/dist/cjs/schema/types/synthetics.js.map +1 -0
- package/dist/cjs/schema/types.d.ts +18 -0
- package/dist/cjs/schema/types.js +23 -0
- package/dist/cjs/schema/types.js.map +1 -0
- package/dist/cjs/timestamp.d.ts +48 -0
- package/dist/cjs/timestamp.js +244 -0
- package/dist/cjs/timestamp.js.map +1 -0
- package/dist/cjs/timestamp.test.d.ts +1 -0
- package/dist/cjs/timestamp.test.js +94 -0
- package/dist/cjs/timestamp.test.js.map +1 -0
- package/dist/cjs/undo.d.ts +3 -0
- package/dist/cjs/undo.js +199 -0
- package/dist/cjs/undo.js.map +1 -0
- package/dist/cjs/utils.d.ts +15 -0
- package/dist/cjs/utils.js +128 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/cjs/utils.test.d.ts +1 -0
- package/dist/cjs/utils.test.js +37 -0
- package/dist/cjs/utils.test.js.map +1 -0
- package/dist/esm/EventSubscriber.d.ts +17 -0
- package/dist/esm/EventSubscriber.js +63 -0
- package/dist/esm/EventSubscriber.js.map +1 -0
- package/dist/esm/baseline.d.ts +5 -0
- package/dist/esm/baseline.js +2 -0
- package/dist/esm/baseline.js.map +1 -0
- package/dist/esm/batching.d.ts +41 -0
- package/dist/esm/batching.js +84 -0
- package/dist/esm/batching.js.map +1 -0
- package/dist/esm/files.d.ts +19 -0
- package/dist/esm/files.js +13 -0
- package/dist/esm/files.js.map +1 -0
- package/dist/esm/files.test.d.ts +1 -0
- package/dist/esm/files.test.js +79 -0
- package/dist/esm/files.test.js.map +1 -0
- package/dist/esm/index.d.ts +18 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/indexes.d.ts +14 -0
- package/dist/esm/indexes.js +106 -0
- package/dist/esm/indexes.js.map +1 -0
- package/dist/esm/indexes.test.d.ts +1 -0
- package/dist/esm/indexes.test.js +126 -0
- package/dist/esm/indexes.test.js.map +1 -0
- package/dist/esm/migration.d.ts +98 -0
- package/dist/esm/migration.js +122 -0
- package/dist/esm/migration.js.map +1 -0
- package/dist/esm/oids.d.ts +87 -0
- package/dist/esm/oids.js +348 -0
- package/dist/esm/oids.js.map +1 -0
- package/dist/esm/oids.test.d.ts +1 -0
- package/dist/esm/oids.test.js +280 -0
- package/dist/esm/oids.test.js.map +1 -0
- package/dist/esm/operation.d.ts +139 -0
- package/dist/esm/operation.js +518 -0
- package/dist/esm/operation.js.map +1 -0
- package/dist/esm/operation.test.d.ts +1 -0
- package/dist/esm/operation.test.js +825 -0
- package/dist/esm/operation.test.js.map +1 -0
- package/dist/esm/patch.d.ts +26 -0
- package/dist/esm/patch.js +223 -0
- package/dist/esm/patch.js.map +1 -0
- package/dist/esm/presence.d.ts +27 -0
- package/dist/esm/presence.js +2 -0
- package/dist/esm/presence.js.map +1 -0
- package/dist/esm/protocol.d.ts +145 -0
- package/dist/esm/protocol.js +3 -0
- package/dist/esm/protocol.js.map +1 -0
- package/dist/esm/refs.d.ts +5 -0
- package/dist/esm/refs.js +17 -0
- package/dist/esm/refs.js.map +1 -0
- package/dist/esm/replica.d.ts +28 -0
- package/dist/esm/replica.js +26 -0
- package/dist/esm/replica.js.map +1 -0
- package/dist/esm/schema/defaults.d.ts +4 -0
- package/dist/esm/schema/defaults.js +66 -0
- package/dist/esm/schema/defaults.js.map +1 -0
- package/dist/esm/schema/defaults.test.d.ts +1 -0
- package/dist/esm/schema/defaults.test.js +19 -0
- package/dist/esm/schema/defaults.test.js.map +1 -0
- package/dist/esm/schema/index.d.ts +8 -0
- package/dist/esm/schema/index.js +10 -0
- package/dist/esm/schema/index.js.map +1 -0
- package/dist/esm/schema/indexFilters.d.ts +6 -0
- package/dist/esm/schema/indexFilters.js +23 -0
- package/dist/esm/schema/indexFilters.js.map +1 -0
- package/dist/esm/schema/types/collection.d.ts +51 -0
- package/dist/esm/schema/types/collection.js +2 -0
- package/dist/esm/schema/types/collection.js.map +1 -0
- package/dist/esm/schema/types/compounds.d.ts +12 -0
- package/dist/esm/schema/types/compounds.js +2 -0
- package/dist/esm/schema/types/compounds.js.map +1 -0
- package/dist/esm/schema/types/fields.d.ts +59 -0
- package/dist/esm/schema/types/fields.js +2 -0
- package/dist/esm/schema/types/fields.js.map +1 -0
- package/dist/esm/schema/types/filters.d.ts +29 -0
- package/dist/esm/schema/types/filters.js +2 -0
- package/dist/esm/schema/types/filters.js.map +1 -0
- package/dist/esm/schema/types/shapes.d.ts +21 -0
- package/dist/esm/schema/types/shapes.js +2 -0
- package/dist/esm/schema/types/shapes.js.map +1 -0
- package/dist/esm/schema/types/synthetics.d.ts +28 -0
- package/dist/esm/schema/types/synthetics.js +2 -0
- package/dist/esm/schema/types/synthetics.js.map +1 -0
- package/dist/esm/schema/types.d.ts +18 -0
- package/dist/esm/schema/types.js +7 -0
- package/dist/esm/schema/types.js.map +1 -0
- package/dist/esm/timestamp.d.ts +48 -0
- package/dist/esm/timestamp.js +229 -0
- package/dist/esm/timestamp.js.map +1 -0
- package/dist/esm/timestamp.test.d.ts +1 -0
- package/dist/esm/timestamp.test.js +92 -0
- package/dist/esm/timestamp.test.js.map +1 -0
- package/dist/esm/undo.d.ts +3 -0
- package/dist/esm/undo.js +195 -0
- package/dist/esm/undo.js.map +1 -0
- package/dist/esm/utils.d.ts +15 -0
- package/dist/esm/utils.js +114 -0
- package/dist/esm/utils.js.map +1 -0
- package/dist/esm/utils.test.d.ts +1 -0
- package/dist/esm/utils.test.js +35 -0
- package/dist/esm/utils.test.js.map +1 -0
- package/dist/tsconfig-cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +38 -0
- package/src/EventSubscriber.ts +81 -0
- package/src/baseline.ts +9 -0
- package/src/batching.ts +135 -0
- package/src/files.ts +34 -0
- package/src/index.ts +26 -0
- package/src/indexes.test.ts +158 -0
- package/src/indexes.ts +151 -0
- package/src/migration.ts +391 -0
- package/src/oids.test.ts +395 -0
- package/src/oids.ts +442 -0
- package/src/operation.test.ts +966 -0
- package/src/operation.ts +755 -0
- package/src/patch.ts +277 -0
- package/src/presence.ts +29 -0
- package/src/protocol.ts +184 -0
- package/src/refs.ts +16 -0
- package/src/replica.ts +29 -0
- package/src/schema/defaults.test.ts +19 -0
- package/src/schema/defaults.ts +72 -0
- package/src/schema/index.ts +28 -0
- package/src/schema/indexFilters.ts +49 -0
- package/src/schema/types/collection.ts +133 -0
- package/src/schema/types/compounds.ts +33 -0
- package/src/schema/types/fields.ts +85 -0
- package/src/schema/types/filters.ts +39 -0
- package/src/schema/types/shapes.ts +83 -0
- package/src/schema/types/synthetics.ts +45 -0
- package/src/schema/types.ts +28 -0
- package/src/timestamp.test.ts +113 -0
- package/src/timestamp.ts +284 -0
- package/src/undo.ts +205 -0
- package/src/utils.test.ts +38 -0
- package/src/utils.ts +137 -0
package/src/oids.ts
ADDED
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import { v4 } from 'uuid';
|
|
2
|
+
import { isFileRef } from './files.js';
|
|
3
|
+
import { isObjectRef, ObjectRef } from './operation.js';
|
|
4
|
+
import { isRef } from './refs.js';
|
|
5
|
+
import { isObject, assert } from './utils.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* OIDs
|
|
9
|
+
*
|
|
10
|
+
* OIDs are used to identify objects in the document. They also encode
|
|
11
|
+
* information about the object useful to identifying an object found
|
|
12
|
+
* on its own and associating it back to its parent.
|
|
13
|
+
*
|
|
14
|
+
* An OID is structured as such:
|
|
15
|
+
* <collection>/<root id>[/<key paths>]:<random>
|
|
16
|
+
*
|
|
17
|
+
* OIDs have a few characteristics:
|
|
18
|
+
* - They include the collection name of the parent document
|
|
19
|
+
* - They include the primary key of the parent document
|
|
20
|
+
* - They include the key path of the object within the document
|
|
21
|
+
* - They include a random sequence to identify different objects which
|
|
22
|
+
* exist at the same key path
|
|
23
|
+
*
|
|
24
|
+
* Collection name and document key are used to link any isolated
|
|
25
|
+
* object back to its parent document.
|
|
26
|
+
*
|
|
27
|
+
* The key path is used for authorization - to associate the object
|
|
28
|
+
* (or an operation related to it by OID) with the field it inhabits
|
|
29
|
+
* to utilize authorization rules from that field in the schema.
|
|
30
|
+
*
|
|
31
|
+
* The random sequence allows the application to encode different
|
|
32
|
+
* identities for objects at the same position in a document for
|
|
33
|
+
* conflict resolution purposes
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export type ObjectIdentifier = string;
|
|
37
|
+
export type KeyPath = (string | number)[];
|
|
38
|
+
|
|
39
|
+
export const LEGACY_OID_KEY = '__@@oid_do_not_use';
|
|
40
|
+
export const OID_KEY = '@@id';
|
|
41
|
+
|
|
42
|
+
const KEY_PATH_SEPARATOR = '.';
|
|
43
|
+
const COLLECTION_SEPARATOR = '/';
|
|
44
|
+
const RANDOM_SEPARATOR = ':';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* This is the global, in-memory storage for all OIDs. It is used to
|
|
48
|
+
* associate JS objects with OIDs without modifying the objects themselves.
|
|
49
|
+
*/
|
|
50
|
+
const oidMap = new WeakMap<any, ObjectIdentifier>();
|
|
51
|
+
|
|
52
|
+
export function getOid(obj: any) {
|
|
53
|
+
const oid = maybeGetOid(obj);
|
|
54
|
+
assert(
|
|
55
|
+
!!oid,
|
|
56
|
+
`Object ${JSON.stringify(obj)} does not have an OID assigned to it`,
|
|
57
|
+
);
|
|
58
|
+
return oid;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function maybeGetOid(obj: any): ObjectIdentifier | undefined {
|
|
62
|
+
if (!isObject(obj)) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return oidMap.get(obj) ?? obj[OID_KEY] ?? obj[LEGACY_OID_KEY];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function assignOid(obj: any, oid: ObjectIdentifier) {
|
|
69
|
+
assert(
|
|
70
|
+
isObject(obj),
|
|
71
|
+
`Only objects can be assigned OIDs, received ${JSON.stringify(obj)}`,
|
|
72
|
+
);
|
|
73
|
+
oidMap.set(obj, oid);
|
|
74
|
+
return obj;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function hasOid(obj: any) {
|
|
78
|
+
return !!maybeGetOid(obj);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function removeOid(obj: any) {
|
|
82
|
+
oidMap.delete(obj);
|
|
83
|
+
return obj;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export function isOidKey(key: string) {
|
|
87
|
+
return key === OID_KEY || key === LEGACY_OID_KEY;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* For sub-objects, assign a random sub-OID if no OID
|
|
92
|
+
* is already assigned.
|
|
93
|
+
*/
|
|
94
|
+
export function ensureOid(
|
|
95
|
+
obj: any,
|
|
96
|
+
rootOid: ObjectIdentifier,
|
|
97
|
+
key: string | number,
|
|
98
|
+
createSubId?: () => string,
|
|
99
|
+
) {
|
|
100
|
+
if (!hasOid(obj)) {
|
|
101
|
+
const oid = createSubOid(rootOid, key, createSubId);
|
|
102
|
+
assignOid(obj, oid);
|
|
103
|
+
return oid;
|
|
104
|
+
} else {
|
|
105
|
+
return getOid(obj);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const SANTIIZE_PLACEHOLDERS = {
|
|
110
|
+
'.': '˙',
|
|
111
|
+
'/': '&slash;',
|
|
112
|
+
':': ':',
|
|
113
|
+
};
|
|
114
|
+
function sanitizeFragment(id: string) {
|
|
115
|
+
// replaces separator characters with placeholders
|
|
116
|
+
return id
|
|
117
|
+
.replace(/[/]/g, SANTIIZE_PLACEHOLDERS['/'])
|
|
118
|
+
.replace(/[:]/g, SANTIIZE_PLACEHOLDERS[':'])
|
|
119
|
+
.replace(/[.]/g, SANTIIZE_PLACEHOLDERS['.']);
|
|
120
|
+
}
|
|
121
|
+
function unsanitizeFragment(id: string) {
|
|
122
|
+
// replaces placeholders with separator characters
|
|
123
|
+
return id
|
|
124
|
+
.replace(/&slash;/g, '/')
|
|
125
|
+
.replace(/:/g, ':')
|
|
126
|
+
.replace(/˙/g, '.');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function createOid(
|
|
130
|
+
collection: string,
|
|
131
|
+
documentId: string,
|
|
132
|
+
keyPath: KeyPath = [],
|
|
133
|
+
subId?: string,
|
|
134
|
+
) {
|
|
135
|
+
let oid =
|
|
136
|
+
sanitizeFragment(collection) +
|
|
137
|
+
COLLECTION_SEPARATOR +
|
|
138
|
+
sanitizeFragment(documentId);
|
|
139
|
+
if (subId) {
|
|
140
|
+
const keyPathItems = keyPath.map((k) =>
|
|
141
|
+
typeof k === 'number' ? '#' : sanitizeFragment(k),
|
|
142
|
+
);
|
|
143
|
+
oid += KEY_PATH_SEPARATOR + keyPathItems.join(KEY_PATH_SEPARATOR);
|
|
144
|
+
oid += RANDOM_SEPARATOR + subId;
|
|
145
|
+
}
|
|
146
|
+
return oid;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function createSubOid(
|
|
150
|
+
root: ObjectIdentifier,
|
|
151
|
+
key: string | number,
|
|
152
|
+
createSubId: () => string = createOidSubId,
|
|
153
|
+
) {
|
|
154
|
+
const { collection, id, keyPath } = decomposeOid(root);
|
|
155
|
+
return createOid(collection, id, [...keyPath, key], createSubId());
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function decomposeOid(oid: ObjectIdentifier): {
|
|
159
|
+
collection: string;
|
|
160
|
+
id: string;
|
|
161
|
+
subId?: string;
|
|
162
|
+
keyPath: KeyPath;
|
|
163
|
+
} {
|
|
164
|
+
const [core, random] = oid.split(RANDOM_SEPARATOR);
|
|
165
|
+
const [collection, paths] = core.split('/');
|
|
166
|
+
const [id, ...keyPath] = paths.split(KEY_PATH_SEPARATOR);
|
|
167
|
+
return {
|
|
168
|
+
collection: unsanitizeFragment(collection),
|
|
169
|
+
id: unsanitizeFragment(id),
|
|
170
|
+
subId: random,
|
|
171
|
+
keyPath: keyPath.map(unsanitizeFragment),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function assertAllLevelsHaveOids(obj: any, root?: any) {
|
|
176
|
+
assert(
|
|
177
|
+
getOid(obj),
|
|
178
|
+
`Object ${JSON.stringify(obj)} must have an oid (child of ${JSON.stringify(
|
|
179
|
+
root,
|
|
180
|
+
)})`,
|
|
181
|
+
);
|
|
182
|
+
if (Array.isArray(obj)) {
|
|
183
|
+
for (const item of obj) {
|
|
184
|
+
assertAllLevelsHaveOids(item, root || obj);
|
|
185
|
+
}
|
|
186
|
+
} else if (isObject(obj)) {
|
|
187
|
+
for (const key of Object.keys(obj)) {
|
|
188
|
+
assertAllLevelsHaveOids(obj[key], root || obj);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function assignOidsToAllSubObjects(
|
|
194
|
+
obj: any,
|
|
195
|
+
createSubId?: () => string,
|
|
196
|
+
) {
|
|
197
|
+
const rootOid = getOid(obj);
|
|
198
|
+
if (Array.isArray(obj)) {
|
|
199
|
+
let item;
|
|
200
|
+
for (let i = 0; i < obj.length; i++) {
|
|
201
|
+
item = obj[i];
|
|
202
|
+
if (isObject(item) && !isRef(item)) {
|
|
203
|
+
ensureOid(item, rootOid, i, createSubId);
|
|
204
|
+
assignOidsToAllSubObjects(item, createSubId);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} else if (isObject(obj) && !isRef(obj)) {
|
|
208
|
+
for (const key of Object.keys(obj)) {
|
|
209
|
+
if (isObject(obj[key]) && !isRef(obj[key])) {
|
|
210
|
+
ensureOid(obj[key], rootOid, key, createSubId);
|
|
211
|
+
assignOidsToAllSubObjects(obj[key], createSubId);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function assignOidProperty(obj: any, oid: ObjectIdentifier) {
|
|
218
|
+
assert(
|
|
219
|
+
isObject(obj),
|
|
220
|
+
`Only objects can be assigned OIDs, received ${JSON.stringify(obj)}`,
|
|
221
|
+
);
|
|
222
|
+
obj[OID_KEY] = oid;
|
|
223
|
+
return obj;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function maybeGetOidProperty(obj: any) {
|
|
227
|
+
if (!isObject(obj)) {
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
return obj[OID_KEY] || obj[LEGACY_OID_KEY];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function hasOidProperty(obj: any) {
|
|
234
|
+
return !!maybeGetOidProperty(obj);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function removeOidProperty(obj: any) {
|
|
238
|
+
if (!isObject(obj)) {
|
|
239
|
+
return obj;
|
|
240
|
+
}
|
|
241
|
+
delete obj[LEGACY_OID_KEY];
|
|
242
|
+
delete obj[OID_KEY];
|
|
243
|
+
return obj;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function transferOidFromSystemToProperty(obj: any) {
|
|
247
|
+
const oid = maybeGetOid(obj);
|
|
248
|
+
if (oid) {
|
|
249
|
+
assignOidProperty(obj, oid);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Assigns a special property to all objects in the given object
|
|
255
|
+
* which have an OID
|
|
256
|
+
*/
|
|
257
|
+
export function assignOidPropertiesToAllSubObjects(obj: any) {
|
|
258
|
+
transferOidFromSystemToProperty(obj);
|
|
259
|
+
|
|
260
|
+
if (Array.isArray(obj)) {
|
|
261
|
+
for (let i = 0; i < obj.length; i++) {
|
|
262
|
+
assignOidPropertiesToAllSubObjects(obj[i]);
|
|
263
|
+
}
|
|
264
|
+
} else if (isObject(obj)) {
|
|
265
|
+
for (const key of Object.keys(obj)) {
|
|
266
|
+
assignOidPropertiesToAllSubObjects(obj[key]);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function copyOidFromPropertyToSystem(obj: any) {
|
|
272
|
+
const oid = maybeGetOidProperty(obj);
|
|
273
|
+
if (oid) {
|
|
274
|
+
assignOid(obj, oid);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
*
|
|
280
|
+
* Removes the special property from all objects in the given object
|
|
281
|
+
* which have an OID, transferring the OID from the property to the OID
|
|
282
|
+
* system in-memory.
|
|
283
|
+
*/
|
|
284
|
+
export function removeOidPropertiesFromAllSubObjects(obj: any) {
|
|
285
|
+
copyOidFromPropertyToSystem(obj);
|
|
286
|
+
removeOidProperty(obj);
|
|
287
|
+
|
|
288
|
+
if (Array.isArray(obj)) {
|
|
289
|
+
for (let i = 0; i < obj.length; i++) {
|
|
290
|
+
removeOidPropertiesFromAllSubObjects(obj[i]);
|
|
291
|
+
}
|
|
292
|
+
} else if (isObject(obj)) {
|
|
293
|
+
for (const key of Object.keys(obj)) {
|
|
294
|
+
removeOidPropertiesFromAllSubObjects(obj[key]);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
export function removeOidsFromAllSubObjects(obj: any) {
|
|
300
|
+
removeOid(obj);
|
|
301
|
+
|
|
302
|
+
if (Array.isArray(obj)) {
|
|
303
|
+
for (let i = 0; i < obj.length; i++) {
|
|
304
|
+
removeOidsFromAllSubObjects(obj[i]);
|
|
305
|
+
}
|
|
306
|
+
} else if (isObject(obj)) {
|
|
307
|
+
for (const key of Object.keys(obj)) {
|
|
308
|
+
removeOidsFromAllSubObjects(obj[key]);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export function createOidSubId() {
|
|
314
|
+
return v4().slice(0, 8);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function createRef(oid: ObjectIdentifier): ObjectRef {
|
|
318
|
+
return {
|
|
319
|
+
'@@type': 'ref',
|
|
320
|
+
id: oid,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export function normalize(
|
|
325
|
+
obj: any,
|
|
326
|
+
refs: Map<ObjectIdentifier, any> = new Map(),
|
|
327
|
+
): Map<ObjectIdentifier, any> {
|
|
328
|
+
if (Array.isArray(obj)) {
|
|
329
|
+
const oid = getOid(obj);
|
|
330
|
+
const copy = assignOid([], oid);
|
|
331
|
+
for (let i = 0; i < obj.length; i++) {
|
|
332
|
+
const value = obj[i];
|
|
333
|
+
if (isObject(value)) {
|
|
334
|
+
if (isObjectRef(value)) {
|
|
335
|
+
throw new Error(
|
|
336
|
+
'An attempt was made to normalize an already normalized object! This is an error in verdant itself.',
|
|
337
|
+
);
|
|
338
|
+
} else if (isFileRef(value)) {
|
|
339
|
+
copy[i] = value;
|
|
340
|
+
continue;
|
|
341
|
+
} else {
|
|
342
|
+
const itemOid = getOid(value);
|
|
343
|
+
copy[i] = createRef(itemOid);
|
|
344
|
+
normalize(value, refs);
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
copy[i] = value;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
refs.set(oid, copy);
|
|
351
|
+
} else if (isObject(obj) && !isRef(obj)) {
|
|
352
|
+
const oid = getOid(obj);
|
|
353
|
+
const copy = assignOid({} as Record<string, any>, oid);
|
|
354
|
+
for (const key of Object.keys(obj)) {
|
|
355
|
+
const value = obj[key];
|
|
356
|
+
if (isObject(value)) {
|
|
357
|
+
if (isObjectRef(value)) {
|
|
358
|
+
throw new Error(
|
|
359
|
+
'An attempt was made to normalize an already normalized object! This is an error in verdant itself.',
|
|
360
|
+
);
|
|
361
|
+
} else if (isFileRef(value)) {
|
|
362
|
+
// stop here
|
|
363
|
+
copy[key] = value;
|
|
364
|
+
} else {
|
|
365
|
+
const itemOid = getOid(value);
|
|
366
|
+
copy[key] = createRef(itemOid);
|
|
367
|
+
normalize(value, refs);
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
370
|
+
copy[key] = value;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
refs.set(oid, copy);
|
|
374
|
+
} else if (isRef(obj)) {
|
|
375
|
+
}
|
|
376
|
+
return refs;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Only normalizes direct children. The created map
|
|
381
|
+
* of objects will still have nested objects.
|
|
382
|
+
*/
|
|
383
|
+
export function normalizeFirstLevel(obj: any) {
|
|
384
|
+
const refs = new Map<ObjectIdentifier, any>();
|
|
385
|
+
const oidKeyPairs = new Map<ObjectIdentifier, string | number>();
|
|
386
|
+
if (Array.isArray(obj)) {
|
|
387
|
+
const oid = getOid(obj);
|
|
388
|
+
const copy = assignOid([], oid);
|
|
389
|
+
for (let i = 0; i < obj.length; i++) {
|
|
390
|
+
const value = obj[i];
|
|
391
|
+
if (isObject(value)) {
|
|
392
|
+
const itemOid = getOid(value);
|
|
393
|
+
copy[i] = createRef(itemOid);
|
|
394
|
+
refs.set(itemOid, value);
|
|
395
|
+
oidKeyPairs.set(itemOid, i);
|
|
396
|
+
} else {
|
|
397
|
+
copy[i] = value;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
refs.set(oid, copy);
|
|
401
|
+
} else if (isObject(obj)) {
|
|
402
|
+
const oid = getOid(obj);
|
|
403
|
+
const copy = assignOid({} as Record<string, any>, oid);
|
|
404
|
+
for (const key of Object.keys(obj)) {
|
|
405
|
+
const value = obj[key];
|
|
406
|
+
if (isObject(value)) {
|
|
407
|
+
const itemOid = getOid(value);
|
|
408
|
+
copy[key] = createRef(itemOid);
|
|
409
|
+
refs.set(itemOid, value);
|
|
410
|
+
oidKeyPairs.set(itemOid, key);
|
|
411
|
+
} else {
|
|
412
|
+
copy[key] = value;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
refs.set(oid, copy);
|
|
416
|
+
}
|
|
417
|
+
return { refs, oidKeyPairs };
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export function getOidRoot(oid: ObjectIdentifier) {
|
|
421
|
+
const [collection, idAndPaths] = oid.split(RANDOM_SEPARATOR)[0].split('/');
|
|
422
|
+
const sepIdx = idAndPaths.indexOf(KEY_PATH_SEPARATOR);
|
|
423
|
+
const id = sepIdx !== -1 ? idAndPaths.substring(0, sepIdx) : idAndPaths;
|
|
424
|
+
return `${collection}/${id}`;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Returns an inclusive range of OIDs that represent
|
|
429
|
+
* a root object and all of its sub-objects.
|
|
430
|
+
*/
|
|
431
|
+
export function getOidRange(oid: ObjectIdentifier) {
|
|
432
|
+
const root = getOidRoot(oid);
|
|
433
|
+
return [root, `${root}${RANDOM_SEPARATOR}\uffff`];
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
export function getRoots(oids: ObjectIdentifier[]) {
|
|
437
|
+
const set = new Set<ObjectIdentifier>();
|
|
438
|
+
for (const oid of oids) {
|
|
439
|
+
set.add(getOidRoot(oid));
|
|
440
|
+
}
|
|
441
|
+
return Array.from(set);
|
|
442
|
+
}
|