@ng-org/orm 0.1.2-alpha.4 → 0.1.2-alpha.6

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 (58) hide show
  1. package/README.md +1 -1
  2. package/dist/connector/applyPatches.d.ts +6 -11
  3. package/dist/connector/applyPatches.d.ts.map +1 -1
  4. package/dist/connector/applyPatches.js +48 -19
  5. package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts +45 -0
  6. package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts.map +1 -0
  7. package/dist/connector/discrete/discreteOrmConnectionHandler.js +186 -0
  8. package/dist/connector/getObjects.d.ts +10 -0
  9. package/dist/connector/getObjects.d.ts.map +1 -0
  10. package/dist/connector/getObjects.js +25 -0
  11. package/dist/connector/insertObject.d.ts +8 -0
  12. package/dist/connector/insertObject.d.ts.map +1 -0
  13. package/dist/connector/{createSignalObjectForShape.js → insertObject.js} +10 -11
  14. package/dist/connector/ormConnectionHandler.d.ts +14 -13
  15. package/dist/connector/ormConnectionHandler.d.ts.map +1 -1
  16. package/dist/connector/ormConnectionHandler.js +74 -40
  17. package/dist/connector/utils.d.ts +14 -0
  18. package/dist/connector/utils.d.ts.map +1 -0
  19. package/dist/connector/utils.js +65 -0
  20. package/dist/frontendAdapters/react/index.d.ts +2 -1
  21. package/dist/frontendAdapters/react/index.d.ts.map +1 -1
  22. package/dist/frontendAdapters/react/index.js +2 -1
  23. package/dist/frontendAdapters/react/useDiscrete.d.ts +84 -0
  24. package/dist/frontendAdapters/react/useDiscrete.d.ts.map +1 -0
  25. package/dist/frontendAdapters/react/useDiscrete.js +127 -0
  26. package/dist/frontendAdapters/react/useShape.d.ts +64 -5
  27. package/dist/frontendAdapters/react/useShape.d.ts.map +1 -1
  28. package/dist/frontendAdapters/react/useShape.js +84 -14
  29. package/dist/frontendAdapters/svelte/index.d.ts +2 -1
  30. package/dist/frontendAdapters/svelte/index.d.ts.map +1 -1
  31. package/dist/frontendAdapters/svelte/index.js +2 -1
  32. package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts +85 -0
  33. package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts.map +1 -0
  34. package/dist/frontendAdapters/svelte/useDiscrete.svelte.js +124 -0
  35. package/dist/frontendAdapters/svelte/useShape.svelte.d.ts +65 -3
  36. package/dist/frontendAdapters/svelte/useShape.svelte.d.ts.map +1 -1
  37. package/dist/frontendAdapters/svelte/useShape.svelte.js +68 -6
  38. package/dist/frontendAdapters/vue/index.d.ts +2 -1
  39. package/dist/frontendAdapters/vue/index.d.ts.map +1 -1
  40. package/dist/frontendAdapters/vue/index.js +2 -1
  41. package/dist/frontendAdapters/vue/useDiscrete.d.ts +92 -0
  42. package/dist/frontendAdapters/vue/useDiscrete.d.ts.map +1 -0
  43. package/dist/frontendAdapters/vue/useDiscrete.js +111 -0
  44. package/dist/frontendAdapters/vue/useShape.d.ts +76 -1
  45. package/dist/frontendAdapters/vue/useShape.d.ts.map +1 -1
  46. package/dist/frontendAdapters/vue/useShape.js +79 -5
  47. package/dist/index.d.ts +9 -6
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +9 -6
  50. package/dist/types.d.ts +48 -11
  51. package/dist/types.d.ts.map +1 -1
  52. package/dist/types.js +1 -1
  53. package/package.json +48 -31
  54. package/dist/connector/applyPatches.test.d.ts +0 -2
  55. package/dist/connector/applyPatches.test.d.ts.map +0 -1
  56. package/dist/connector/applyPatches.test.js +0 -772
  57. package/dist/connector/createSignalObjectForShape.d.ts +0 -14
  58. package/dist/connector/createSignalObjectForShape.d.ts.map +0 -1
package/README.md CHANGED
@@ -239,7 +239,7 @@ dogs.add({
239
239
 
240
240
  > **Note**: For nested sub-objects, `@graph` is optional — the parent's graph IRI is used.
241
241
  >
242
- > **Note**: If you want to use the ORM signal object in a non-component context, you can use `createSignalObjectForShape` function as well.
242
+ > **Note**: If you want to use the ORM signal object in a non-component context, you can create an ORM connection manually using `OrmConnection.getOrCreate()`.
243
243
 
244
244
  ### Modifying Objects
245
245
 
@@ -3,7 +3,7 @@ export type Patch = {
3
3
  path: string;
4
4
  valType?: string & {};
5
5
  value?: unknown;
6
- } & (SetAddPatch | SetRemovePatch | ObjectAddPatch | RemovePatch | LiteralAddPatch);
6
+ } & (SetAddPatch | SetRemovePatch | RemovePatch | LiteralAddPatch);
7
7
  export interface SetAddPatch {
8
8
  /** Mutation kind applied at the resolved `path`. */
9
9
  op: "add";
@@ -24,12 +24,7 @@ export interface SetRemovePatch {
24
24
  * - A single primitive
25
25
  * - An array of primitives
26
26
  */
27
- value: number | string | boolean | (number | string | boolean)[];
28
- }
29
- export interface ObjectAddPatch {
30
- /** Mutation kind applied at the resolved `path`. */
31
- op: "add";
32
- valType: "object";
27
+ value: number | string | boolean | object | (number | string | boolean | object)[];
33
28
  }
34
29
  export interface RemovePatch {
35
30
  /** Mutation kind applied at the resolved `path`. */
@@ -39,7 +34,7 @@ export interface LiteralAddPatch {
39
34
  /** Mutation kind applied at the resolved `path`. */
40
35
  op: "add";
41
36
  /** The literal value to be added at the resolved `path` */
42
- value: string | number | boolean;
37
+ value: string | number | boolean | object;
43
38
  }
44
39
  /**
45
40
  * Apply a diff to an object.
@@ -98,9 +93,9 @@ export interface LiteralAddPatch {
98
93
  * from being triggered before these identity fields are set, which would cause it to generate
99
94
  * random IDs unnecessarily.
100
95
  */
101
- export declare function applyPatches(currentState: Record<string, any>, patches: Patch[], ensurePathExists?: boolean): void;
96
+ export declare function applyPatches(currentState: Record<string, any>, patches: Patch[], ormType: "set" | "discrete", ensurePathExists?: boolean): void;
102
97
  /**
103
- * See documentation for applyDiff
98
+ * See documentation for applyPatches
104
99
  */
105
- export declare function applyPatchesToDeepSignal(currentState: object, patch: Patch[]): void;
100
+ export declare function applyPatchesToDeepSignal(currentState: object, patch: Patch[], ormType: "set" | "discrete"): void;
106
101
  //# sourceMappingURL=applyPatches.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"applyPatches.d.ts","sourceRoot":"","sources":["../../src/connector/applyPatches.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,KAAK,GAAG;IAChB,iHAAiH;IACjH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,CACE,WAAW,GACX,cAAc,GACd,cAAc,GACd,WAAW,GACX,eAAe,CACpB,CAAC;AAEF,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,KAAK,CAAC;IACf;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CACpE;AAED,MAAM,WAAW,cAAc;IAC3B,oDAAoD;IACpD,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;IACf;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CACpE;AAED,MAAM,WAAW,cAAc;IAC3B,oDAAoD;IACpD,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,QAAQ,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,EAAE,EAAE,QAAQ,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC5B,oDAAoD;IACpD,EAAE,EAAE,KAAK,CAAC;IACV,2DAA2D;IAC3D,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CACpC;AAyCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,YAAY,CACxB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,OAAO,EAAE,KAAK,EAAE,EAChB,gBAAgB,GAAE,OAAe,QAgOpC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,QAI5E"}
1
+ {"version":3,"file":"applyPatches.d.ts","sourceRoot":"","sources":["../../src/connector/applyPatches.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,KAAK,GAAG;IAChB,iHAAiH;IACjH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC;IACtB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,CAAC,WAAW,GAAG,cAAc,GAAG,WAAW,GAAG,eAAe,CAAC,CAAC;AAEnE,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,KAAK,CAAC;IACf;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CACpE;AAED,MAAM,WAAW,cAAc;IAC3B,oDAAoD;IACpD,EAAE,EAAE,QAAQ,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;IACf;;;;OAIG;IACH,KAAK,EACC,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,EAAE,CAAC;CAChD;AAED,MAAM,WAAW,WAAW;IACxB,oDAAoD;IACpD,EAAE,EAAE,QAAQ,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC5B,oDAAoD;IACpD,EAAE,EAAE,KAAK,CAAC;IACV,2DAA2D;IAC3D,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;CAC7C;AAyCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,YAAY,CACxB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,OAAO,EAAE,KAAK,EAAE,EAChB,OAAO,EAAE,KAAK,GAAG,UAAU,EAC3B,gBAAgB,GAAE,OAAe,QA6PpC;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CACpC,YAAY,EAAE,MAAM,EACpB,KAAK,EAAE,KAAK,EAAE,EACd,OAAO,EAAE,KAAK,GAAG,UAAU,QAU9B"}
@@ -101,7 +101,7 @@ function findInSetBySegment(set, seg) {
101
101
  * from being triggered before these identity fields are set, which would cause it to generate
102
102
  * random IDs unnecessarily.
103
103
  */
104
- export function applyPatches(currentState, patches, ensurePathExists = false) {
104
+ export function applyPatches(currentState, patches, ormType, ensurePathExists = false) {
105
105
  for (let patchIndex = 0; patchIndex < patches.length; patchIndex++) {
106
106
  const patch = patches[patchIndex];
107
107
  if (!patch.path.startsWith("/"))
@@ -112,7 +112,8 @@ export function applyPatches(currentState, patches, ensurePathExists = false) {
112
112
  .filter(Boolean)
113
113
  .map(decodePathSegment);
114
114
  if (pathParts.length === 0) {
115
- console.warn("[applyDiff] No path specified for patch", patch);
115
+ // Actually, this should mean replace..
116
+ console.warn("[applyPatches] No path specified for patch", patch);
116
117
  continue;
117
118
  }
118
119
  const lastKey = pathParts[pathParts.length - 1];
@@ -149,7 +150,7 @@ export function applyPatches(currentState, patches, ensurePathExists = false) {
149
150
  if (ensurePathExists) {
150
151
  if (parentVal != null && typeof parentVal === "object") {
151
152
  // Check if we need to create an object or a set:
152
- if (pathParts[i + 1]?.includes("|")) {
153
+ if (pathParts[i + 1]?.includes("|") && ormType === "set") {
153
154
  // The next path segment is an IRI, that means the new element must be a set of objects. Create a set.
154
155
  parentVal[seg] = new Set();
155
156
  }
@@ -170,13 +171,12 @@ export function applyPatches(currentState, patches, ensurePathExists = false) {
170
171
  }
171
172
  }
172
173
  if (parentMissing) {
173
- console.warn(`[applyDiff] Skipping patch due to missing parent path segment(s): ${patch.path}`);
174
+ console.warn(`[applyPatches] Skipping patch due to missing parent path segment(s): ${patch.path}`);
174
175
  continue;
175
176
  }
176
- // parentVal now should be an object or Set into which we apply lastKey
177
- if (parentVal == null ||
178
- (typeof parentVal !== "object" && !(parentVal instanceof Set))) {
179
- console.warn(`[applyDiff] Skipping patch because parent is not an object or Set: ${patch.path}`);
177
+ // parentVal now should be an object, array, or set into which we apply lastKey
178
+ if (parentVal == null || typeof parentVal !== "object") {
179
+ console.warn(`[applyPatches] Skipping patch because parent is not an object or Set: ${patch.path}`);
180
180
  continue;
181
181
  }
182
182
  const key = lastKey;
@@ -185,7 +185,9 @@ export function applyPatches(currentState, patches, ensurePathExists = false) {
185
185
  // The key represents the identifier of an object within the Set
186
186
  const targetObj = findInSetBySegment(parentVal, key);
187
187
  // Handle object creation in a Set
188
- if (patch.op === "add" && patch.valType === "object") {
188
+ if (patch.op === "add" &&
189
+ typeof patch.value === "object" &&
190
+ patch.value !== null) {
189
191
  if (!targetObj) {
190
192
  // Determine if this will be a single object or nested Set
191
193
  const hasId = patches[patchIndex + 2]?.path.endsWith("@id");
@@ -203,6 +205,8 @@ export function applyPatches(currentState, patches, ensurePathExists = false) {
203
205
  }
204
206
  }
205
207
  parentVal.add(newLeaf);
208
+ // Skip the next two add (@id + @graph) patches.
209
+ patchIndex += 2;
206
210
  }
207
211
  continue;
208
212
  }
@@ -215,14 +219,13 @@ export function applyPatches(currentState, patches, ensurePathExists = false) {
215
219
  }
216
220
  // All other operations require the target object to exist
217
221
  if (!targetObj) {
218
- console.warn(`[applyDiff] Target object with @id=${key} not found in Set for path: ${patch.path}`);
222
+ console.warn(`[applyPatches] Target object with @id=${key} not found in Set for path: ${patch.path}`);
219
223
  continue;
220
224
  }
221
225
  // This shouldn't happen - we handle all intermediate segments in the traversal loop
222
- console.warn(`[applyDiff] Unexpected: reached end of path with Set as parent: ${patch.path}`);
226
+ console.warn(`[applyPatches] Unexpected: reached end of path with Set as parent: ${patch.path}`);
223
227
  continue;
224
228
  }
225
- // Regular object handling (parentVal is a plain object, not a Set)
226
229
  // Handle primitive set additions
227
230
  if (patch.op === "add" && patch.valType === "set") {
228
231
  const existing = parentVal[key];
@@ -267,7 +270,11 @@ export function applyPatches(currentState, patches, ensurePathExists = false) {
267
270
  // Distinguish between single objects and multi-object containers:
268
271
  // - If an @id patch follows for this path, it's a single object -> create {}
269
272
  // - If no @id patch follows, it's a container for multi-valued objects -> create set.
270
- if (patch.op === "add" && patch.valType === "object") {
273
+ if (patch.op === "add" &&
274
+ typeof patch.value === "object" &&
275
+ patch.value !== null &&
276
+ ormType === "set" // TODO: The backend should preferably add valType: "set" here (we don't need ormType then).
277
+ ) {
271
278
  const leafVal = parentVal[key];
272
279
  const hasId = patches.at(patchIndex + 2)?.path.endsWith("@id");
273
280
  // If the leafVal does not exist and it should be a set, create.
@@ -286,12 +293,34 @@ export function applyPatches(currentState, patches, ensurePathExists = false) {
286
293
  newLeaf["@id"] = idPatch.value;
287
294
  }
288
295
  parentVal[key] = newLeaf;
296
+ // Skip the next two add (@id + @graph) patches.
297
+ patchIndex += 2;
289
298
  }
290
299
  continue;
291
300
  }
292
- // Literal add
293
- if (patch.op === "add" &&
294
- !(patch.path.endsWith("@id") || patch.path.endsWith("@graph"))) {
301
+ if (Array.isArray(parentVal)) {
302
+ if (key === "-") {
303
+ if (patch.op == "add") {
304
+ parentVal.push(patch.value);
305
+ }
306
+ else {
307
+ parentVal.pop();
308
+ }
309
+ }
310
+ else if (patch.op == "add") {
311
+ let keyNum = Number(key);
312
+ parentVal.splice(keyNum, 0, patch.value);
313
+ }
314
+ else {
315
+ // patch.op == remove
316
+ let keyNum = Number(key);
317
+ // Remove element at position from array in-place (will resize).
318
+ parentVal.splice(keyNum, 1);
319
+ }
320
+ continue;
321
+ }
322
+ // Basic add
323
+ if (patch.op === "add") {
295
324
  parentVal[key] = patch.value;
296
325
  continue;
297
326
  }
@@ -305,11 +334,11 @@ export function applyPatches(currentState, patches, ensurePathExists = false) {
305
334
  }
306
335
  }
307
336
  /**
308
- * See documentation for applyDiff
337
+ * See documentation for applyPatches
309
338
  */
310
- export function applyPatchesToDeepSignal(currentState, patch) {
339
+ export function applyPatchesToDeepSignal(currentState, patch, ormType) {
311
340
  batch(() => {
312
- applyPatches(currentState, patch, true);
341
+ applyPatches(currentState, patch, ormType, false);
313
342
  });
314
343
  }
315
344
  function decodePathSegment(segment) {
@@ -0,0 +1,45 @@
1
+ import { DiscreteArray, DiscreteObject } from "../../types.ts";
2
+ import { Patch } from "../applyPatches.ts";
3
+ import type { DeepPatch, DeepSignal } from "@ng-org/alien-deepsignals";
4
+ import type { BaseType } from "@ng-org/shex-orm";
5
+ export declare class DiscreteOrmConnection {
6
+ private static idToEntry;
7
+ readonly documentId: string;
8
+ private _signalObject;
9
+ private stopSignalListening;
10
+ private subscriptionId;
11
+ private refCount;
12
+ suspendDeepWatcher: boolean;
13
+ inTransaction: boolean;
14
+ /** Aggregation of patches to be sent when in transaction. */
15
+ pendingPatches: Patch[] | undefined;
16
+ /** Resolves once the data arrives */
17
+ readyPromise: Promise<void>;
18
+ private closeOrmConnection;
19
+ /** Called to resolve the readyPromise. */
20
+ private resolveReady;
21
+ private constructor();
22
+ get signalObject(): DeepSignal<DiscreteArray | DiscreteObject> | undefined;
23
+ /**
24
+ * Get or create a connection which contains the ORM and lifecycle methods.
25
+ * @param shapeType
26
+ * @param scope
27
+ * @param ng
28
+ * @returns
29
+ */
30
+ static getOrCreate: <T extends BaseType>(documentId: string) => DiscreteOrmConnection;
31
+ close: () => void;
32
+ private onSignalObjectUpdate;
33
+ private onBackendMessage;
34
+ private handleInitialResponse;
35
+ private onBackendUpdate;
36
+ beginTransaction: () => void;
37
+ commitTransaction: () => Promise<void>;
38
+ }
39
+ /**
40
+ * Converts DeepSignal patches to ORM Wasm-compatible patches
41
+ * @param patches DeepSignal patches
42
+ * @returns Patches with stringified path
43
+ */
44
+ export declare function deepPatchesToWasm(patches: DeepPatch[]): Patch[];
45
+ //# sourceMappingURL=discreteOrmConnectionHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discreteOrmConnectionHandler.d.ts","sourceRoot":"","sources":["../../../src/connector/discrete/discreteOrmConnectionHandler.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC/D,OAAO,EAA4B,KAAK,EAAE,MAAM,oBAAoB,CAAC;AASrE,OAAO,KAAK,EACR,SAAS,EACT,UAAU,EAGb,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AASjD,qBAAa,qBAAqB;IAC9B,OAAO,CAAC,MAAM,CAAC,SAAS,CAA4C;IAEpE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,aAAa,CAEL;IAChB,OAAO,CAAC,mBAAmB,CAA2B;IACtD,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAS;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAS;IAC/B,6DAA6D;IAC7D,cAAc,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;IACpC,qCAAqC;IACrC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,CAAC,kBAAkB,CAAa;IACvC,0CAA0C;IAC1C,OAAO,CAAC,YAAY,CAAc;IAElC,OAAO;IA+BP,IAAW,YAAY,2DAEtB;IAED;;;;;;OAMG;IACH,OAAc,WAAW,GAAI,CAAC,SAAS,QAAQ,EAC3C,YAAY,MAAM,KACnB,qBAAqB,CActB;IAEK,KAAK,aASV;IAEF,OAAO,CAAC,oBAAoB,CAsB1B;IAEF,OAAO,CAAC,gBAAgB,CAStB;IAEF,OAAO,CAAC,qBAAqB,CAiB3B;IAEF,OAAO,CAAC,eAAe,CAUrB;IAEK,gBAAgB,aAerB;IAEK,iBAAiB,sBAiCtB;CACL;AAED;;;;GAIG;AAEH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,CAO/D"}
@@ -0,0 +1,186 @@
1
+ // Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
2
+ // All rights reserved.
3
+ // Licensed under the Apache License, Version 2.0
4
+ // <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
5
+ // or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6
+ // at your option. All files in the project carrying such
7
+ // notice may not be copied, modified, or distributed except
8
+ // according to those terms.
9
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
10
+ import { applyPatchesToDeepSignal } from "../applyPatches.js";
11
+ import { ngSession } from "../initNg.js";
12
+ import { deepSignal, watch as watchDeepSignal, } from "@ng-org/alien-deepsignals";
13
+ /**
14
+ * Delay in ms to wait before closing connection.\
15
+ * Useful when a hook unsubscribes and resubscribes in a short time interval
16
+ * so that no new connections need to be set up.
17
+ */
18
+ const WAIT_BEFORE_CLOSE = 500;
19
+ export class DiscreteOrmConnection {
20
+ static idToEntry = new Map();
21
+ documentId;
22
+ _signalObject;
23
+ stopSignalListening;
24
+ subscriptionId;
25
+ refCount;
26
+ suspendDeepWatcher;
27
+ inTransaction = false;
28
+ /** Aggregation of patches to be sent when in transaction. */
29
+ pendingPatches;
30
+ /** Resolves once the data arrives */
31
+ readyPromise;
32
+ closeOrmConnection;
33
+ /** Called to resolve the readyPromise. */
34
+ resolveReady;
35
+ constructor(documentId) {
36
+ // @ts-expect-error
37
+ window.ormDiscreteSignalConnections = DiscreteOrmConnection.idToEntry;
38
+ // @ts-expect-error
39
+ window.OrmDiscreteConnection = DiscreteOrmConnection;
40
+ // @ts-expect-error
41
+ window.OrmDiscreteIncomingPatches = [];
42
+ this.documentId = documentId;
43
+ this.refCount = 1;
44
+ this.closeOrmConnection = () => { };
45
+ this.suspendDeepWatcher = false;
46
+ // Initialize per-entry readiness promise that resolves in setUpConnection
47
+ this.readyPromise = new Promise((resolve) => {
48
+ this.resolveReady = resolve;
49
+ });
50
+ ngSession.then(async ({ ng, session }) => {
51
+ try {
52
+ this.closeOrmConnection = await ng.orm_start_discrete(documentId, session.session_id, this.onBackendMessage);
53
+ }
54
+ catch (e) {
55
+ console.error(e);
56
+ }
57
+ });
58
+ }
59
+ get signalObject() {
60
+ return this._signalObject;
61
+ }
62
+ /**
63
+ * Get or create a connection which contains the ORM and lifecycle methods.
64
+ * @param shapeType
65
+ * @param scope
66
+ * @param ng
67
+ * @returns
68
+ */
69
+ static getOrCreate = (documentId) => {
70
+ // If we already have a connection open,
71
+ // return that signal object it and just increase the reference count.
72
+ // Otherwise, open a new one.
73
+ const existingConnection = DiscreteOrmConnection.idToEntry.get(documentId);
74
+ if (existingConnection) {
75
+ existingConnection.refCount += 1;
76
+ return existingConnection;
77
+ }
78
+ else {
79
+ const newConnection = new DiscreteOrmConnection(documentId);
80
+ DiscreteOrmConnection.idToEntry.set(documentId, newConnection);
81
+ return newConnection;
82
+ }
83
+ };
84
+ close = () => {
85
+ setTimeout(() => {
86
+ if (this.refCount > 0)
87
+ this.refCount--;
88
+ if (this.refCount === 0) {
89
+ DiscreteOrmConnection.idToEntry.delete(this.documentId);
90
+ this.closeOrmConnection();
91
+ }
92
+ }, WAIT_BEFORE_CLOSE);
93
+ };
94
+ onSignalObjectUpdate = async ({ patches, }) => {
95
+ if (this.suspendDeepWatcher || !patches.length)
96
+ return;
97
+ const ormPatches = deepPatchesToWasm(patches);
98
+ // If in transaction, collect patches immediately (no await before).
99
+ if (this.inTransaction) {
100
+ this.pendingPatches?.push(...ormPatches);
101
+ return;
102
+ }
103
+ // Wait for session and subscription to be initialized.
104
+ const { ng, session } = await ngSession;
105
+ await this.readyPromise;
106
+ ng.discrete_orm_update(this.subscriptionId, ormPatches, session.session_id);
107
+ };
108
+ onBackendMessage = (message) => {
109
+ const data = message?.V0;
110
+ if (data?.DiscreteOrmInitial) {
111
+ this.handleInitialResponse(data.DiscreteOrmInitial);
112
+ }
113
+ else if (data?.DiscreteOrmUpdate) {
114
+ this.onBackendUpdate(data.DiscreteOrmUpdate);
115
+ }
116
+ else {
117
+ console.warn("Received unknown ORM message from backend", message);
118
+ }
119
+ };
120
+ handleInitialResponse = ([initialData, subscriptionId]) => {
121
+ this.subscriptionId = subscriptionId;
122
+ const signalObject = deepSignal(initialData, {
123
+ syntheticIdPropertyName: undefined,
124
+ });
125
+ this._signalObject = signalObject;
126
+ const { stopListening } = watchDeepSignal(this._signalObject, this.onSignalObjectUpdate);
127
+ this.stopSignalListening = stopListening;
128
+ // Resolve readiness after initial data is committed and watcher armed.
129
+ this.resolveReady();
130
+ };
131
+ onBackendUpdate = (patches) => {
132
+ // @ts-expect-error
133
+ window.OrmDiscreteIncomingPatches.push(patches);
134
+ this.suspendDeepWatcher = true;
135
+ applyPatchesToDeepSignal(this._signalObject, patches, "discrete");
136
+ // Use queueMicrotask to ensure watcher is re-enabled _after_ batch completes
137
+ queueMicrotask(() => {
138
+ this.suspendDeepWatcher = false;
139
+ });
140
+ };
141
+ beginTransaction = () => {
142
+ this.inTransaction = true;
143
+ this.pendingPatches = [];
144
+ this.readyPromise.then(() => {
145
+ // Use a listener that immediately triggers on object modifications.
146
+ // We don't need the deep-signal's batching (through microtasks) here.
147
+ this.stopSignalListening?.();
148
+ const { stopListening } = watchDeepSignal(this.signalObject, this.onSignalObjectUpdate, { triggerInstantly: true });
149
+ this.stopSignalListening = stopListening;
150
+ });
151
+ };
152
+ commitTransaction = async () => {
153
+ if (!this.inTransaction) {
154
+ throw new Error("No transaction is open. Call `beginTransaction` first.");
155
+ }
156
+ const { ng, session } = await ngSession;
157
+ await this.readyPromise;
158
+ this.inTransaction = false;
159
+ if (this.pendingPatches?.length == 0) {
160
+ // Nothing to send to the backend.
161
+ }
162
+ else {
163
+ ng.discrete_orm_update(this.subscriptionId, this.pendingPatches, session.session_id);
164
+ }
165
+ this.pendingPatches = undefined;
166
+ // Go back to the regular object modification listening where we want batching
167
+ // scheduled in a microtask only triggered after the main task.
168
+ // This way we prevent excessive calls to the backend.
169
+ this.stopSignalListening();
170
+ const { stopListening } = watchDeepSignal(this.signalObject, this.onSignalObjectUpdate);
171
+ this.stopSignalListening = stopListening;
172
+ };
173
+ }
174
+ /**
175
+ * Converts DeepSignal patches to ORM Wasm-compatible patches
176
+ * @param patches DeepSignal patches
177
+ * @returns Patches with stringified path
178
+ */
179
+ export function deepPatchesToWasm(patches) {
180
+ return patches.flatMap((patch) => {
181
+ if (patch.op === "add" && patch.type === "set" && !patch.value?.length)
182
+ return [];
183
+ const path = "/" + patch.path.join("/");
184
+ return { ...patch, path };
185
+ });
186
+ }
@@ -0,0 +1,10 @@
1
+ import { BaseType, ShapeType } from "@ng-org/shex-orm";
2
+ import { Scope } from "../types.ts";
3
+ /**
4
+ * Utility for retrieving objects once without establishing a two-way subscription.
5
+ * @param shapeType The shape type of the objects to be retrieved.
6
+ * @param scope The scope of the objects to be retrieved.
7
+ * @returns A set of all objects matching the shape and scope
8
+ */
9
+ export declare function getObjects<T extends BaseType>(shapeType: ShapeType<T>, scope?: Scope): Promise<import("@ng-org/alien-deepsignals").DeepSignalSet<T>>;
10
+ //# sourceMappingURL=getObjects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getObjects.d.ts","sourceRoot":"","sources":["../../src/connector/getObjects.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGpC;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,CAAC,SAAS,QAAQ,EAC/C,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,KAAK,GAAE,KAAU,iEAUpB"}
@@ -0,0 +1,25 @@
1
+ // Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
2
+ // All rights reserved.
3
+ // Licensed under the Apache License, Version 2.0
4
+ // <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
5
+ // or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6
+ // at your option. All files in the project carrying such
7
+ // notice may not be copied, modified, or distributed except
8
+ // according to those terms.
9
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
10
+ import { OrmConnection } from "./ormConnectionHandler.js";
11
+ import { deepClone } from "./utils.js";
12
+ /**
13
+ * Utility for retrieving objects once without establishing a two-way subscription.
14
+ * @param shapeType The shape type of the objects to be retrieved.
15
+ * @param scope The scope of the objects to be retrieved.
16
+ * @returns A set of all objects matching the shape and scope
17
+ */
18
+ export async function getObjects(shapeType, scope = {}) {
19
+ const connection = OrmConnection.getOrCreate(shapeType, scope);
20
+ await connection.readyPromise;
21
+ setTimeout(() => {
22
+ connection.close();
23
+ }, 1_000);
24
+ return deepClone(connection.signalObject);
25
+ }
@@ -0,0 +1,8 @@
1
+ import { BaseType, ShapeType } from "@ng-org/shex-orm";
2
+ /**
3
+ * Utility for adding ORM-typed objects to the database without the need for subscribing to documents.
4
+ * @param shapeType The shape type of the objects to be inserted.
5
+ * @param object The object to be inserted.
6
+ */
7
+ export declare function insertObject<T extends BaseType>(shapeType: ShapeType<T>, object: T): Promise<void>;
8
+ //# sourceMappingURL=insertObject.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insertObject.d.ts","sourceRoot":"","sources":["../../src/connector/insertObject.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAGvD;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,CAAC,SAAS,QAAQ,EACjD,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EACvB,MAAM,EAAE,CAAC,iBASZ"}
@@ -9,16 +9,15 @@
9
9
  // SPDX-License-Identifier: Apache-2.0 OR MIT
10
10
  import { OrmConnection } from "./ormConnectionHandler.js";
11
11
  /**
12
- *
13
- * @param shapeType
14
- * @param scope
15
- * @returns
12
+ * Utility for adding ORM-typed objects to the database without the need for subscribing to documents.
13
+ * @param shapeType The shape type of the objects to be inserted.
14
+ * @param object The object to be inserted.
16
15
  */
17
- export function createSignalObjectForShape(shapeType, scope) {
18
- const connection = OrmConnection.getConnection(shapeType, scope || "");
19
- return {
20
- signalObject: connection.signalObject,
21
- stop: connection.release,
22
- readyPromise: connection.readyPromise,
23
- };
16
+ export async function insertObject(shapeType, object) {
17
+ const connection = OrmConnection.getOrCreate(shapeType, {
18
+ graphs: [], // Subscribe to no documents
19
+ });
20
+ await connection.readyPromise;
21
+ connection.signalObject.add(object);
22
+ connection.close();
24
23
  }
@@ -1,5 +1,6 @@
1
- import type { Diff as Patches, Scope } from "../types.ts";
2
- import type { DeepPatch, DeepSignalSet } from "@ng-org/alien-deepsignals";
1
+ import type { Scope } from "../types.ts";
2
+ import { Patch } from "./applyPatches.ts";
3
+ import type { DeepSignalSet } from "@ng-org/alien-deepsignals";
3
4
  import type { ShapeType, BaseType } from "@ng-org/shex-orm";
4
5
  export declare class OrmConnection<T extends BaseType> {
5
6
  private static idToEntry;
@@ -8,40 +9,40 @@ export declare class OrmConnection<T extends BaseType> {
8
9
  * Useful when a hook unsubscribes and resubscribes in a short time interval
9
10
  * so that no new connections need to be set up.
10
11
  */
11
- private WAIT_BEFORE_RELEASE;
12
12
  readonly shapeType: ShapeType<T>;
13
13
  readonly scope: Scope;
14
14
  readonly signalObject: DeepSignalSet<T>;
15
+ private stopSignalListening;
16
+ private subscriptionId;
15
17
  private refCount;
16
18
  /** Identifier as a combination of shape type and scope. Prevents duplications. */
17
19
  private identifier;
18
20
  suspendDeepWatcher: boolean;
21
+ inTransaction: boolean;
22
+ /** Aggregation of patches to be sent when in transaction. */
23
+ pendingPatches: Patch[] | undefined;
19
24
  readyPromise: Promise<void>;
20
- cancel: () => void;
25
+ private closeOrmConnection;
21
26
  /** Promise that resolves once initial data has been applied. */
22
27
  resolveReady: () => void;
23
28
  private static cleanupSignalRegistry;
24
29
  private constructor();
25
30
  /**
26
- * Get a connection which contains the ORM and lifecycle methods.
31
+ * Get or create a connection which contains the ORM and lifecycle methods.
27
32
  * @param shapeType
28
33
  * @param scope
29
34
  * @param ng
30
35
  * @returns
31
36
  */
32
- static getConnection: <T_1 extends BaseType>(shapeType: ShapeType<T_1>, scope: Scope) => OrmConnection<T_1>;
33
- release: () => void;
37
+ static getOrCreate: <T_1 extends BaseType>(shapeType: ShapeType<T_1>, scope: Scope) => OrmConnection<T_1>;
38
+ close: () => void;
34
39
  private onSignalObjectUpdate;
35
40
  private onBackendMessage;
36
41
  private handleInitialResponse;
37
42
  private onBackendUpdate;
38
43
  /** Function to create random subject IRIs for newly created nested objects. */
39
44
  private signalObjectPropGenerator;
45
+ beginTransaction: () => void;
46
+ commitTransaction: () => Promise<void>;
40
47
  }
41
- /**
42
- * Converts DeepSignal patches to ORM Wasm-compatible patches
43
- * @param patches DeepSignal patches
44
- * @returns Patches with stringified path
45
- */
46
- export declare function deepPatchesToWasm(patches: DeepPatch[]): Patches;
47
48
  //# sourceMappingURL=ormConnectionHandler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ormConnectionHandler.d.ts","sourceRoot":"","sources":["../../src/connector/ormConnectionHandler.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,IAAI,IAAI,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAU1D,OAAO,KAAK,EACR,SAAS,EAET,aAAa,EAEhB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5D,qBAAa,aAAa,CAAC,CAAC,SAAS,QAAQ;IACzC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAyC;IACjE;;;;OAIG;IACH,OAAO,CAAC,mBAAmB,CAAO;IAElC,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACxC,OAAO,CAAC,QAAQ,CAAS;IACzB,kFAAkF;IAClF,OAAO,CAAC,UAAU,CAAS;IAC3B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,gEAAgE;IAChE,YAAY,EAAG,MAAM,IAAI,CAAC;IAG1B,OAAO,CAAC,MAAM,CAAC,qBAAqB,CASrB;IAEf,OAAO;IAgDP;;;;;;OAMG;IACH,OAAc,aAAa,GAAI,GAAC,SAAS,QAAQ,EAC7C,WAAW,SAAS,CAAC,GAAC,CAAC,EACvB,OAAO,KAAK,KACb,aAAa,CAAC,GAAC,CAAC,CAkBjB;IAEK,OAAO,aAYZ;IAEF,OAAO,CAAC,oBAAoB,CAe1B;IAEF,OAAO,CAAC,gBAAgB,CAStB;IAEF,OAAO,CAAC,qBAAqB,CAmB3B;IACF,OAAO,CAAC,eAAe,CAOrB;IAEF,+EAA+E;IAC/E,OAAO,CAAC,yBAAyB,CAgD/B;CACL;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,OAAO,CAO/D"}
1
+ {"version":3,"file":"ormConnectionHandler.d.ts","sourceRoot":"","sources":["../../src/connector/ormConnectionHandler.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAA4B,KAAK,EAAE,MAAM,mBAAmB,CAAC;AASpE,OAAO,KAAK,EAER,aAAa,EAEhB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAK5D,qBAAa,aAAa,CAAC,CAAC,SAAS,QAAQ;IACzC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAyC;IACjE;;;;OAIG;IAEH,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACxC,OAAO,CAAC,mBAAmB,CAAa;IACxC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAS;IACzB,kFAAkF;IAClF,OAAO,CAAC,UAAU,CAAS;IAC3B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,aAAa,EAAE,OAAO,CAAS;IAC/B,6DAA6D;IAC7D,cAAc,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;IACpC,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,CAAC,kBAAkB,CAAa;IACvC,gEAAgE;IAChE,YAAY,EAAG,MAAM,IAAI,CAAC;IAG1B,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAUrB;IAEf,OAAO;IAoDP;;;;;;OAMG;IACH,OAAc,WAAW,GAAI,GAAC,SAAS,QAAQ,EAC3C,WAAW,SAAS,CAAC,GAAC,CAAC,EACvB,OAAO,KAAK,KACb,aAAa,CAAC,GAAC,CAAC,CAkBjB;IAEK,KAAK,aAYV;IAEF,OAAO,CAAC,oBAAoB,CAoB1B;IAEF,OAAO,CAAC,gBAAgB,CAStB;IAEF,OAAO,CAAC,qBAAqB,CAuB3B;IACF,OAAO,CAAC,eAAe,CAOrB;IAEF,+EAA+E;IAC/E,OAAO,CAAC,yBAAyB,CAgD/B;IAEK,gBAAgB,aAarB;IAEK,iBAAiB,sBAkCtB;CACL"}