@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.
- package/README.md +1 -1
- package/dist/connector/applyPatches.d.ts +6 -11
- package/dist/connector/applyPatches.d.ts.map +1 -1
- package/dist/connector/applyPatches.js +48 -19
- package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts +45 -0
- package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts.map +1 -0
- package/dist/connector/discrete/discreteOrmConnectionHandler.js +186 -0
- package/dist/connector/getObjects.d.ts +10 -0
- package/dist/connector/getObjects.d.ts.map +1 -0
- package/dist/connector/getObjects.js +25 -0
- package/dist/connector/insertObject.d.ts +8 -0
- package/dist/connector/insertObject.d.ts.map +1 -0
- package/dist/connector/{createSignalObjectForShape.js → insertObject.js} +10 -11
- package/dist/connector/ormConnectionHandler.d.ts +14 -13
- package/dist/connector/ormConnectionHandler.d.ts.map +1 -1
- package/dist/connector/ormConnectionHandler.js +74 -40
- package/dist/connector/utils.d.ts +14 -0
- package/dist/connector/utils.d.ts.map +1 -0
- package/dist/connector/utils.js +65 -0
- package/dist/frontendAdapters/react/index.d.ts +2 -1
- package/dist/frontendAdapters/react/index.d.ts.map +1 -1
- package/dist/frontendAdapters/react/index.js +2 -1
- package/dist/frontendAdapters/react/useDiscrete.d.ts +84 -0
- package/dist/frontendAdapters/react/useDiscrete.d.ts.map +1 -0
- package/dist/frontendAdapters/react/useDiscrete.js +127 -0
- package/dist/frontendAdapters/react/useShape.d.ts +64 -5
- package/dist/frontendAdapters/react/useShape.d.ts.map +1 -1
- package/dist/frontendAdapters/react/useShape.js +84 -14
- package/dist/frontendAdapters/svelte/index.d.ts +2 -1
- package/dist/frontendAdapters/svelte/index.d.ts.map +1 -1
- package/dist/frontendAdapters/svelte/index.js +2 -1
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts +85 -0
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts.map +1 -0
- package/dist/frontendAdapters/svelte/useDiscrete.svelte.js +124 -0
- package/dist/frontendAdapters/svelte/useShape.svelte.d.ts +65 -3
- package/dist/frontendAdapters/svelte/useShape.svelte.d.ts.map +1 -1
- package/dist/frontendAdapters/svelte/useShape.svelte.js +68 -6
- package/dist/frontendAdapters/vue/index.d.ts +2 -1
- package/dist/frontendAdapters/vue/index.d.ts.map +1 -1
- package/dist/frontendAdapters/vue/index.js +2 -1
- package/dist/frontendAdapters/vue/useDiscrete.d.ts +92 -0
- package/dist/frontendAdapters/vue/useDiscrete.d.ts.map +1 -0
- package/dist/frontendAdapters/vue/useDiscrete.js +111 -0
- package/dist/frontendAdapters/vue/useShape.d.ts +76 -1
- package/dist/frontendAdapters/vue/useShape.d.ts.map +1 -1
- package/dist/frontendAdapters/vue/useShape.js +79 -5
- package/dist/index.d.ts +9 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -6
- package/dist/types.d.ts +48 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/package.json +48 -31
- package/dist/connector/applyPatches.test.d.ts +0 -2
- package/dist/connector/applyPatches.test.d.ts.map +0 -1
- package/dist/connector/applyPatches.test.js +0 -772
- package/dist/connector/createSignalObjectForShape.d.ts +0 -14
- 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
|
|
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 |
|
|
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
|
|
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,
|
|
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
|
-
|
|
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(`[
|
|
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
|
|
177
|
-
if (parentVal == null ||
|
|
178
|
-
(
|
|
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" &&
|
|
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(`[
|
|
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(`[
|
|
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" &&
|
|
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
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
18
|
-
const connection = OrmConnection.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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 {
|
|
2
|
-
import
|
|
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
|
-
|
|
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
|
|
33
|
-
|
|
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,
|
|
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"}
|