@verdant-web/common 1.11.1 → 1.13.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.
Files changed (76) hide show
  1. package/dist/cjs/migration.d.ts +7 -0
  2. package/dist/cjs/migration.js +46 -6
  3. package/dist/cjs/migration.js.map +1 -1
  4. package/dist/cjs/oids.d.ts +5 -5
  5. package/dist/cjs/oids.js +37 -22
  6. package/dist/cjs/oids.js.map +1 -1
  7. package/dist/cjs/oids.test.js +62 -44
  8. package/dist/cjs/oids.test.js.map +1 -1
  9. package/dist/cjs/operation.d.ts +2 -2
  10. package/dist/cjs/operation.js +16 -7
  11. package/dist/cjs/operation.js.map +1 -1
  12. package/dist/cjs/operation.test.js +180 -30
  13. package/dist/cjs/operation.test.js.map +1 -1
  14. package/dist/cjs/patch.d.ts +3 -3
  15. package/dist/cjs/patch.js +6 -6
  16. package/dist/cjs/patch.js.map +1 -1
  17. package/dist/cjs/schema/defaults.test.js +4 -4
  18. package/dist/cjs/schema/defaults.test.js.map +1 -1
  19. package/dist/cjs/schema/{defaults.d.ts → fields.d.ts} +3 -0
  20. package/dist/cjs/schema/{defaults.js → fields.js} +48 -3
  21. package/dist/cjs/schema/fields.js.map +1 -0
  22. package/dist/cjs/schema/index.d.ts +1 -1
  23. package/dist/cjs/schema/index.js +1 -1
  24. package/dist/cjs/schema/index.js.map +1 -1
  25. package/dist/cjs/schema/validation.d.ts +3 -0
  26. package/dist/cjs/schema/validation.js +87 -0
  27. package/dist/cjs/schema/validation.js.map +1 -0
  28. package/dist/cjs/utils.d.ts +5 -1
  29. package/dist/cjs/utils.js +9 -5
  30. package/dist/cjs/utils.js.map +1 -1
  31. package/dist/esm/migration.d.ts +7 -0
  32. package/dist/esm/migration.js +47 -7
  33. package/dist/esm/migration.js.map +1 -1
  34. package/dist/esm/oids.d.ts +5 -5
  35. package/dist/esm/oids.js +34 -21
  36. package/dist/esm/oids.js.map +1 -1
  37. package/dist/esm/oids.test.js +62 -44
  38. package/dist/esm/oids.test.js.map +1 -1
  39. package/dist/esm/operation.d.ts +2 -2
  40. package/dist/esm/operation.js +18 -9
  41. package/dist/esm/operation.js.map +1 -1
  42. package/dist/esm/operation.test.js +180 -30
  43. package/dist/esm/operation.test.js.map +1 -1
  44. package/dist/esm/patch.d.ts +3 -3
  45. package/dist/esm/patch.js +6 -6
  46. package/dist/esm/patch.js.map +1 -1
  47. package/dist/esm/schema/defaults.test.js +2 -2
  48. package/dist/esm/schema/defaults.test.js.map +1 -1
  49. package/dist/esm/schema/{defaults.d.ts → fields.d.ts} +3 -0
  50. package/dist/esm/schema/{defaults.js → fields.js} +44 -2
  51. package/dist/esm/schema/fields.js.map +1 -0
  52. package/dist/esm/schema/index.d.ts +1 -1
  53. package/dist/esm/schema/index.js +1 -1
  54. package/dist/esm/schema/index.js.map +1 -1
  55. package/dist/esm/schema/validation.d.ts +3 -0
  56. package/dist/esm/schema/validation.js +82 -0
  57. package/dist/esm/schema/validation.js.map +1 -0
  58. package/dist/esm/utils.d.ts +5 -1
  59. package/dist/esm/utils.js +9 -5
  60. package/dist/esm/utils.js.map +1 -1
  61. package/dist/tsconfig-cjs.tsbuildinfo +1 -1
  62. package/dist/tsconfig.tsbuildinfo +1 -1
  63. package/package.json +1 -1
  64. package/src/migration.ts +74 -15
  65. package/src/oids.test.ts +62 -51
  66. package/src/oids.ts +35 -26
  67. package/src/operation.test.ts +189 -41
  68. package/src/operation.ts +23 -19
  69. package/src/patch.ts +5 -15
  70. package/src/schema/defaults.test.ts +2 -2
  71. package/src/schema/{defaults.ts → fields.ts} +55 -1
  72. package/src/schema/index.ts +1 -1
  73. package/src/schema/validation.ts +99 -0
  74. package/src/utils.ts +9 -5
  75. package/dist/cjs/schema/defaults.js.map +0 -1
  76. package/dist/esm/schema/defaults.js.map +0 -1
package/src/oids.test.ts CHANGED
@@ -50,71 +50,69 @@ describe('normalizing an object', () => {
50
50
  {
51
51
  foo: {
52
52
  '@@type': 'ref',
53
- id: 'test/a.foo:0',
53
+ id: 'test/a:0',
54
54
  },
55
55
  qux: {
56
56
  '@@type': 'ref',
57
- id: 'test/a.qux:2',
57
+ id: 'test/a:2',
58
58
  },
59
59
  },
60
60
  'test/a',
61
61
  ),
62
62
  );
63
- expect(result.get('test/a.foo:0')).toEqual(
63
+ expect(result.get('test/a:0')).toEqual(
64
64
  assignOid(
65
65
  {
66
66
  bar: 1,
67
67
  baz: {
68
68
  '@@type': 'ref',
69
- id: 'test/a.foo.baz:1',
69
+ id: 'test/a:1',
70
70
  },
71
71
  },
72
- 'test/a.foo:0',
72
+ 'test/a:0',
73
73
  ),
74
74
  );
75
- expect(result.get('test/a.foo.baz:1')).toEqual(
76
- assignOid([2, 3], 'test/a.foo.baz:1'),
77
- );
78
- expect(result.get('test/a.qux:2')).toEqual(
75
+ expect(result.get('test/a:1')).toEqual(assignOid([2, 3], 'test/a:1'));
76
+ expect(result.get('test/a:2')).toEqual(
79
77
  assignOid(
80
78
  [
81
79
  {
82
80
  '@@type': 'ref',
83
- id: 'test/a.qux.#:3',
81
+ id: 'test/a:3',
84
82
  },
85
83
  {
86
84
  '@@type': 'ref',
87
- id: 'test/a.qux.#:4',
85
+ id: 'test/a:4',
88
86
  },
89
87
  ],
90
- 'test/a.qux:2',
88
+ 'test/a:2',
91
89
  ),
92
90
  );
93
- expect(result.get('test/a.qux.#:3')).toEqual(
91
+ expect(result.get('test/a:3')).toEqual(
94
92
  assignOid(
95
93
  {
96
94
  corge: true,
97
95
  },
98
- 'test/a.qux.#:3',
96
+ 'test/a:3',
99
97
  ),
100
98
  );
101
- expect(result.get('test/a.qux.#:4')).toEqual(
99
+ expect(result.get('test/a:4')).toEqual(
102
100
  assignOid(
103
101
  {
104
102
  grault: {
105
103
  '@@type': 'ref',
106
- id: 'test/a.qux.#.grault:5',
104
+ id: 'test/a:5',
107
105
  },
108
106
  },
109
- 'test/a.qux.#:4',
107
+ 'test/a:4',
110
108
  ),
111
109
  );
112
- expect(result.get('test/a.qux.#.grault:5')).toEqual(
110
+ expect(result.get('test/a:5')).toEqual(
113
111
  assignOid(
114
112
  {
115
113
  garply: 4,
116
114
  },
117
- 'test/a.qux.#.grault:5',
115
+ 'test/a:5',
118
116
  ),
119
117
  );
120
118
  });
@@ -144,17 +142,17 @@ describe('normalizing an object', () => {
144
142
  {
145
143
  foo: {
146
144
  '@@type': 'ref',
147
- id: 'test/a.foo:0',
145
+ id: 'test/a:0',
148
146
  },
149
147
  qux: {
150
148
  '@@type': 'ref',
151
- id: 'test/a.qux:1',
149
+ id: 'test/a:1',
152
150
  },
153
151
  },
154
152
  'test/a',
155
153
  ),
156
154
  );
157
- expect(result.get('test/a.foo:0')).toEqual(
155
+ expect(result.get('test/a:0')).toEqual(
158
156
  assignOid(
159
157
  {
160
158
  bar: 1,
@@ -163,10 +161,10 @@ describe('normalizing an object', () => {
163
161
  id: 'abcd123',
164
162
  },
165
163
  },
166
- 'test/a.foo:0',
164
+ 'test/a:0',
167
165
  ),
168
166
  );
169
- expect(result.get('test/a.qux:1')).toEqual(
167
+ expect(result.get('test/a:1')).toEqual(
170
168
  assignOid(
171
169
  [
172
170
  {
@@ -178,7 +176,7 @@ describe('normalizing an object', () => {
178
176
  id: 'ijkl789',
179
177
  },
180
178
  ],
181
- 'test/a.qux:2',
179
+ 'test/a:2',
182
180
  ),
183
181
  );
184
182
  });
@@ -216,17 +214,36 @@ describe('normalizing the first level of an object', () => {
216
214
  {
217
215
  "foo": {
218
216
  "@@type": "ref",
219
- "id": "test/a.foo:0",
217
+ "id": "test/a:0",
220
218
  },
221
219
  "qux": {
222
220
  "@@type": "ref",
223
- "id": "test/a.qux:2",
221
+ "id": "test/a:2",
224
222
  },
225
223
  }
226
224
  `);
227
- expect(result.get('test/a:0')).toMatchInlineSnapshot('undefined');
225
+ expect(result.get('test/a:0')).toMatchInlineSnapshot(`
226
+ {
227
+ "bar": 1,
228
+ "baz": [
229
+ 2,
230
+ 3,
231
+ ],
232
+ }
233
+ `);
228
234
  expect(result.get('test/a:1')).toBeUndefined();
229
- expect(result.get('test/a:2')).toMatchInlineSnapshot('undefined');
235
+ expect(result.get('test/a:2')).toMatchInlineSnapshot(`
236
+ [
237
+ {
238
+ "corge": true,
239
+ },
240
+ {
241
+ "grault": {
242
+ "garply": 4,
243
+ },
244
+ },
245
+ ]
246
+ `);
230
247
  expect(result.get('test/a:3')).toBeUndefined();
231
248
  expect(result.get('test/a:4')).toBeUndefined();
232
249
  expect(result.get('test/a:5')).toBeUndefined();
@@ -288,7 +305,7 @@ describe('assigning OIDs as properties', () => {
288
305
  {
289
306
  "@@id": "test/a",
290
307
  "foo": {
291
- "@@id": "test/a.foo:0",
308
+ "@@id": "test/a:0",
292
309
  "bar": 1,
293
310
  "baz": [
294
311
  2,
@@ -297,13 +314,13 @@ describe('assigning OIDs as properties', () => {
297
314
  },
298
315
  "qux": [
299
316
  {
300
- "@@id": "test/a.qux.#:3",
317
+ "@@id": "test/a:3",
301
318
  "corge": true,
302
319
  },
303
320
  {
304
- "@@id": "test/a.qux.#:4",
321
+ "@@id": "test/a:4",
305
322
  "grault": {
306
- "@@id": "test/a.qux.#.grault:5",
323
+ "@@id": "test/a:5",
307
324
  "garply": 4,
308
325
  },
309
326
  },
@@ -311,7 +328,7 @@ describe('assigning OIDs as properties', () => {
311
328
  }
312
329
  `);
313
330
  // extra check needed for array since it doesn't serialize in the snapshot
314
- expect(maybeGetOidProperty(initial.qux)).toBe('test/a.qux:2');
331
+ expect(maybeGetOidProperty(initial.qux)).toBe('test/a:2');
315
332
  });
316
333
 
317
334
  it('should transfer assigned OID properties to the memory system', () => {
@@ -321,7 +338,7 @@ describe('assigning OIDs as properties', () => {
321
338
  {
322
339
  bar: 1,
323
340
  },
324
- 'test/a.foo:1',
341
+ 'test/a:1',
325
342
  ),
326
343
  qux: assignOidProperty(
327
344
  [
@@ -329,7 +346,7 @@ describe('assigning OIDs as properties', () => {
329
346
  {
330
347
  corge: true,
331
348
  },
332
- 'test/a.qux.#:2',
349
+ 'test/a:2',
333
350
  ),
334
351
  assignOidProperty(
335
352
  {
@@ -337,13 +354,13 @@ describe('assigning OIDs as properties', () => {
337
354
  {
338
355
  garply: 4,
339
356
  },
340
- 'test/a.qux.#.grault:3',
357
+ 'test/a:3',
341
358
  ),
342
359
  },
343
- 'test/a.qux.#:4',
360
+ 'test/a:4',
344
361
  ),
345
362
  ],
346
- 'test/a.qux:2',
363
+ 'test/a:2',
347
364
  ),
348
365
  },
349
366
  'test/a',
@@ -352,28 +369,22 @@ describe('assigning OIDs as properties', () => {
352
369
  removeOidPropertiesFromAllSubObjects(initial);
353
370
 
354
371
  expect(getOid(initial)).toEqual('test/a');
355
- expect(getOid(initial.foo)).toEqual('test/a.foo:1');
356
- expect(getOid(initial.qux)).toEqual('test/a.qux:2');
357
- expect(getOid(initial.qux[0])).toEqual('test/a.qux.#:2');
358
- expect(getOid(initial.qux[1])).toEqual('test/a.qux.#:4');
359
- expect(getOid(initial.qux[1].grault)).toEqual('test/a.qux.#.grault:3');
372
+ expect(getOid(initial.foo)).toEqual('test/a:1');
373
+ expect(getOid(initial.qux)).toEqual('test/a:2');
374
+ expect(getOid(initial.qux[0])).toEqual('test/a:2');
375
+ expect(getOid(initial.qux[1])).toEqual('test/a:4');
376
+ expect(getOid(initial.qux[1].grault)).toEqual('test/a:3');
360
377
  });
361
378
  });
362
379
 
363
380
  it('should handle special characters in document id or collection', () => {
364
381
  expect(
365
382
  decomposeOid(
366
- createOid(
367
- 'test.teesssst/asdf::',
368
- 'foo/bar (.) : huh:',
369
- ['baz?.quoo:/', 'qux'],
370
- 'corge',
371
- ),
383
+ createOid('test.teesssst/asdf::', 'foo/bar (.) : huh:', 'corge'),
372
384
  ),
373
385
  ).toEqual({
374
386
  collection: 'test.teesssst/asdf::',
375
387
  id: 'foo/bar (.) : huh:',
376
- keyPath: ['baz?.quoo:/', 'qux'],
377
388
  subId: 'corge',
378
389
  });
379
390
  });
package/src/oids.ts CHANGED
@@ -34,12 +34,10 @@ import { isObject, assert } from './utils.js';
34
34
  */
35
35
 
36
36
  export type ObjectIdentifier = string;
37
- export type KeyPath = (string | number)[];
38
37
 
39
38
  export const LEGACY_OID_KEY = '__@@oid_do_not_use';
40
39
  export const OID_KEY = '@@id';
41
40
 
42
- const KEY_PATH_SEPARATOR = '.';
43
41
  const COLLECTION_SEPARATOR = '/';
44
42
  const RANDOM_SEPARATOR = ':';
45
43
 
@@ -70,6 +68,9 @@ export function assignOid(obj: any, oid: ObjectIdentifier) {
70
68
  isObject(obj),
71
69
  `Only objects can be assigned OIDs, received ${JSON.stringify(obj)}`,
72
70
  );
71
+ if (hasOid(obj)) {
72
+ removeOid(obj);
73
+ }
73
74
  oidMap.set(obj, oid);
74
75
  return obj;
75
76
  }
@@ -94,11 +95,10 @@ export function isOidKey(key: string) {
94
95
  export function ensureOid(
95
96
  obj: any,
96
97
  rootOid: ObjectIdentifier,
97
- key: string | number,
98
98
  createSubId?: () => string,
99
99
  ) {
100
100
  if (!hasOid(obj)) {
101
- const oid = createSubOid(rootOid, key, createSubId);
101
+ const oid = createSubOid(rootOid, createSubId);
102
102
  assignOid(obj, oid);
103
103
  return oid;
104
104
  } else {
@@ -106,6 +106,27 @@ export function ensureOid(
106
106
  }
107
107
  }
108
108
 
109
+ export function ensureCompatibleOid(
110
+ obj: any,
111
+ rootOid: ObjectIdentifier,
112
+ createSubId?: () => string,
113
+ ) {
114
+ if (!hasOid(obj)) {
115
+ const oid = createSubOid(rootOid, createSubId);
116
+ assignOid(obj, oid);
117
+ return oid;
118
+ } else {
119
+ const existingOid = getOid(obj);
120
+ if (!areOidsRelated(existingOid, rootOid)) {
121
+ const oid = createSubOid(rootOid, createSubId);
122
+ assignOid(obj, oid);
123
+ return oid;
124
+ } else {
125
+ return getOid(obj);
126
+ }
127
+ }
128
+ }
129
+
109
130
  const SANTIIZE_PLACEHOLDERS = {
110
131
  '.': '˙',
111
132
  '/': '&slash;',
@@ -129,7 +150,6 @@ function unsanitizeFragment(id: string) {
129
150
  export function createOid(
130
151
  collection: string,
131
152
  documentId: string,
132
- keyPath: KeyPath = [],
133
153
  subId?: string,
134
154
  ) {
135
155
  let oid =
@@ -137,10 +157,6 @@ export function createOid(
137
157
  COLLECTION_SEPARATOR +
138
158
  sanitizeFragment(documentId);
139
159
  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
160
  oid += RANDOM_SEPARATOR + subId;
145
161
  }
146
162
  return oid;
@@ -148,27 +164,23 @@ export function createOid(
148
164
 
149
165
  export function createSubOid(
150
166
  root: ObjectIdentifier,
151
- key: string | number,
152
167
  createSubId: () => string = createOidSubId,
153
168
  ) {
154
- const { collection, id, keyPath } = decomposeOid(root);
155
- return createOid(collection, id, [...keyPath, key], createSubId());
169
+ const { collection, id } = decomposeOid(root);
170
+ return createOid(collection, id, createSubId());
156
171
  }
157
172
 
158
173
  export function decomposeOid(oid: ObjectIdentifier): {
159
174
  collection: string;
160
175
  id: string;
161
176
  subId?: string;
162
- keyPath: KeyPath;
163
177
  } {
164
178
  const [core, random] = oid.split(RANDOM_SEPARATOR);
165
- const [collection, paths] = core.split('/');
166
- const [id, ...keyPath] = paths.split(KEY_PATH_SEPARATOR);
179
+ const [collection, id] = core.split('/');
167
180
  return {
168
181
  collection: unsanitizeFragment(collection),
169
182
  id: unsanitizeFragment(id),
170
183
  subId: random,
171
- keyPath: keyPath.map(unsanitizeFragment),
172
184
  };
173
185
  }
174
186
 
@@ -200,14 +212,14 @@ export function assignOidsToAllSubObjects(
200
212
  for (let i = 0; i < obj.length; i++) {
201
213
  item = obj[i];
202
214
  if (isObject(item) && !isRef(item)) {
203
- ensureOid(item, rootOid, i, createSubId);
215
+ ensureCompatibleOid(item, rootOid, createSubId);
204
216
  assignOidsToAllSubObjects(item, createSubId);
205
217
  }
206
218
  }
207
219
  } else if (isObject(obj) && !isRef(obj)) {
208
220
  for (const key of Object.keys(obj)) {
209
221
  if (isObject(obj[key]) && !isRef(obj[key])) {
210
- ensureOid(obj[key], rootOid, key, createSubId);
222
+ ensureCompatibleOid(obj[key], rootOid, createSubId);
211
223
  assignOidsToAllSubObjects(obj[key], createSubId);
212
224
  }
213
225
  }
@@ -230,10 +242,6 @@ export function maybeGetOidProperty(obj: any) {
230
242
  return obj[OID_KEY] || obj[LEGACY_OID_KEY];
231
243
  }
232
244
 
233
- function hasOidProperty(obj: any) {
234
- return !!maybeGetOidProperty(obj);
235
- }
236
-
237
245
  function removeOidProperty(obj: any) {
238
246
  if (!isObject(obj)) {
239
247
  return obj;
@@ -418,10 +426,7 @@ export function normalizeFirstLevel(obj: any) {
418
426
  }
419
427
 
420
428
  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}`;
429
+ return oid.split(RANDOM_SEPARATOR)[0];
425
430
  }
426
431
 
427
432
  /**
@@ -440,3 +445,7 @@ export function getRoots(oids: ObjectIdentifier[]) {
440
445
  }
441
446
  return Array.from(set);
442
447
  }
448
+
449
+ export function areOidsRelated(oidA: ObjectIdentifier, oidB: ObjectIdentifier) {
450
+ return getOidRoot(oidA) === getOidRoot(oidB);
451
+ }