@ng-org/orm 0.1.2-alpha.1 → 0.1.2-alpha.10

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 (89) hide show
  1. package/README.md +151 -230
  2. package/dist/connector/applyPatches.d.ts +19 -49
  3. package/dist/connector/applyPatches.d.ts.map +1 -1
  4. package/dist/connector/applyPatches.js +57 -58
  5. package/dist/connector/discrete/discreteOrmSubscriptionHandler.d.ts +168 -0
  6. package/dist/connector/discrete/discreteOrmSubscriptionHandler.d.ts.map +1 -0
  7. package/dist/connector/discrete/discreteOrmSubscriptionHandler.js +309 -0
  8. package/dist/connector/getObjects.d.ts +11 -0
  9. package/dist/connector/getObjects.d.ts.map +1 -0
  10. package/dist/connector/getObjects.js +26 -0
  11. package/dist/connector/initNg.d.ts +33 -2
  12. package/dist/connector/initNg.d.ts.map +1 -1
  13. package/dist/connector/initNg.js +30 -0
  14. package/dist/connector/insertObject.d.ts +9 -0
  15. package/dist/connector/insertObject.d.ts.map +1 -0
  16. package/dist/connector/insertObject.js +24 -0
  17. package/dist/connector/ormSubscriptionHandler.d.ts +167 -0
  18. package/dist/connector/ormSubscriptionHandler.d.ts.map +1 -0
  19. package/dist/connector/ormSubscriptionHandler.js +400 -0
  20. package/dist/connector/utils.d.ts +17 -0
  21. package/dist/connector/utils.d.ts.map +1 -0
  22. package/dist/connector/utils.js +68 -0
  23. package/dist/frontendAdapters/react/index.d.ts +2 -1
  24. package/dist/frontendAdapters/react/index.d.ts.map +1 -1
  25. package/dist/frontendAdapters/react/index.js +2 -1
  26. package/dist/frontendAdapters/react/useDiscrete.d.ts +104 -0
  27. package/dist/frontendAdapters/react/useDiscrete.d.ts.map +1 -0
  28. package/dist/frontendAdapters/react/useDiscrete.js +151 -0
  29. package/dist/frontendAdapters/react/useShape.d.ts +69 -2
  30. package/dist/frontendAdapters/react/useShape.d.ts.map +1 -1
  31. package/dist/frontendAdapters/react/useShape.js +89 -8
  32. package/dist/frontendAdapters/svelte/index.d.ts +2 -1
  33. package/dist/frontendAdapters/svelte/index.d.ts.map +1 -1
  34. package/dist/frontendAdapters/svelte/index.js +2 -1
  35. package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts +94 -0
  36. package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts.map +1 -0
  37. package/dist/frontendAdapters/svelte/useDiscrete.svelte.js +137 -0
  38. package/dist/frontendAdapters/svelte/useShape.svelte.d.ts +79 -10
  39. package/dist/frontendAdapters/svelte/useShape.svelte.d.ts.map +1 -1
  40. package/dist/frontendAdapters/svelte/useShape.svelte.js +88 -9
  41. package/dist/frontendAdapters/svelte4/index.d.ts +5 -0
  42. package/dist/frontendAdapters/svelte4/index.d.ts.map +1 -0
  43. package/dist/{connector/createSignalObjectForShape.js → frontendAdapters/svelte4/index.js} +3 -15
  44. package/dist/frontendAdapters/svelte4/useDiscrete.svelte.d.ts +84 -0
  45. package/dist/frontendAdapters/svelte4/useDiscrete.svelte.d.ts.map +1 -0
  46. package/dist/frontendAdapters/svelte4/useDiscrete.svelte.js +126 -0
  47. package/dist/frontendAdapters/svelte4/useShape.svelte.d.ts +77 -0
  48. package/dist/frontendAdapters/svelte4/useShape.svelte.d.ts.map +1 -0
  49. package/dist/frontendAdapters/svelte4/useShape.svelte.js +91 -0
  50. package/dist/frontendAdapters/utils.d.ts +2 -0
  51. package/dist/frontendAdapters/utils.d.ts.map +1 -0
  52. package/dist/frontendAdapters/utils.js +14 -0
  53. package/dist/frontendAdapters/vue/index.d.ts +2 -1
  54. package/dist/frontendAdapters/vue/index.d.ts.map +1 -1
  55. package/dist/frontendAdapters/vue/index.js +2 -1
  56. package/dist/frontendAdapters/vue/useDiscrete.d.ts +99 -0
  57. package/dist/frontendAdapters/vue/useDiscrete.d.ts.map +1 -0
  58. package/dist/frontendAdapters/vue/useDiscrete.js +123 -0
  59. package/dist/frontendAdapters/vue/useShape.d.ts +73 -2
  60. package/dist/frontendAdapters/vue/useShape.d.ts.map +1 -1
  61. package/dist/frontendAdapters/vue/useShape.js +79 -4
  62. package/dist/index.d.ts +13 -6
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +21 -6
  65. package/dist/types.d.ts +63 -11
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/types.js +13 -1
  68. package/package.json +37 -48
  69. package/dist/connector/applyPatches.test.d.ts +0 -2
  70. package/dist/connector/applyPatches.test.d.ts.map +0 -1
  71. package/dist/connector/applyPatches.test.js +0 -772
  72. package/dist/connector/createSignalObjectForShape.d.ts +0 -14
  73. package/dist/connector/createSignalObjectForShape.d.ts.map +0 -1
  74. package/dist/connector/ormConnectionHandler.d.ts +0 -47
  75. package/dist/connector/ormConnectionHandler.d.ts.map +0 -1
  76. package/dist/connector/ormConnectionHandler.js +0 -240
  77. package/src/connector/applyPatches.test.ts +0 -842
  78. package/src/connector/applyPatches.ts +0 -404
  79. package/src/connector/createSignalObjectForShape.ts +0 -35
  80. package/src/connector/initNg.ts +0 -30
  81. package/src/connector/ormConnectionHandler.ts +0 -300
  82. package/src/frontendAdapters/react/index.ts +0 -2
  83. package/src/frontendAdapters/react/useShape.ts +0 -36
  84. package/src/frontendAdapters/svelte/index.ts +0 -2
  85. package/src/frontendAdapters/svelte/useShape.svelte.ts +0 -46
  86. package/src/frontendAdapters/vue/index.ts +0 -2
  87. package/src/frontendAdapters/vue/useShape.ts +0 -33
  88. package/src/index.ts +0 -14
  89. package/src/types.ts +0 -26
@@ -1,14 +1,83 @@
1
- import type { Scope } from "../../types.ts";
1
+ import { type Scope } from "../../types.ts";
2
2
  import type { BaseType, ShapeType } from "@ng-org/shex-orm";
3
- import { type UseDeepSignalResult } from "@ng-org/alien-deepsignals/svelte";
4
- export type { UseDeepSignalResult } from "@ng-org/alien-deepsignals/svelte";
5
- /** Extended result including the originating root signal wrapper from shape logic. */
6
- export interface UseShapeRuneResult<T extends object> extends UseDeepSignalResult<T> {
7
- root: any;
8
- }
3
+ import { DeepSignalSet } from "@ng-org/alien-deepsignals";
9
4
  /**
10
- * Shape-specific rune: constructs the signal object for a shape then delegates to {@link useDeepSignal}.
5
+ * Svelte 5 hook to subscribe to RDF data in the graph database using a shape, see {@link ShapeType}.
6
+ *
7
+ * Returns a {@link DeepSignalSet} that contain the objects matching the shape and that are within the scope.
8
+ * Establishes a 2-way binding: Modifications to the object are immediately committed,
9
+ * changes coming from the engine (or other components) cause an immediate rerender.
10
+ *
11
+ * @param shape The {@link ShapeType} the objects should have (generated by the `@ng-org/shex-orm` tool).
12
+ * @param scope The {@link Scope} as graph string or scope object with graphs and subjects.
13
+ * @returns A {@link DeepSignalSet} with the orm objects or an empty set, if still loading.\
14
+ * If the scope is explicitly set to `undefined`, an empty set is returned which errors
15
+ * if you try to make modifications on it.
16
+ *
17
+ * @example
18
+ * ```svelte
19
+ * <script lang="ts">
20
+ * // Gets all expense objects with `@id` <s1 IRI> or <s2 IRI> and `@graph` <g1 NURI> or <g2 NURI>
21
+ * const expenses: DeepSignal<Set<Expense>> = useShape(ExpenseShapeType,
22
+ * {graphs: ["<g1 NURI>", "<g2 NURI>"],
23
+ * subjects: ["<s1 NURI>", "<s2 NURI>"]});
24
+ *
25
+ * const expensesSorted = computed(() => expenses.sort((a, b) =>
26
+ * a.dateOfPurchase.localeCompare(b.dateOfPurchase)
27
+ * ));
28
+ *
29
+ * const createExpense = () => {
30
+ * expenses.add({
31
+ * "@graph": `<graph NURI>`,
32
+ * "@type": "http://example.org/Expense",
33
+ * "@id": "", // Assigns ID automatically, if set to "".
34
+ * title: "New expense",
35
+ * dateOfPurchase: obj.dateOfPurchase ?? new Date().toISOString(),
36
+ * });
37
+ * };
38
+ *
39
+ *
40
+ * // Note that if you use `@id` (the subject IRI) as key, you need to ensure that it is unique within your scope.
41
+ * // If it is not (i.e. there are two graphs with the same subject), use the combination of `@graph` and `@id`.
42
+ * </script>
43
+ *
44
+ * <section>
45
+ * <div>
46
+ * <button on:click={() => createExpense()}>
47
+ * + Add expense
48
+ * </button>
49
+ *
50
+ * {# if expensesSorted.length === 0}
51
+ * <p>
52
+ * No expense yet.
53
+ * </p>
54
+ * {:else}
55
+ * {#each expensesSorted as expense, index (expense['@id']) }
56
+ * <ExpenseCard
57
+ * expense={expense}
58
+ * />
59
+ * {/each}
60
+ * {/if}
61
+ * </div>
62
+ * </section>
63
+ * ```
64
+ *
65
+ * ---
66
+ * In the ExpenseCard component:
67
+ * ```svelte
68
+ * <script lang="ts">
69
+ * let {
70
+ * expense,
71
+ * }: { expense: DeepSignal<Expense>; } = $props();
72
+ * </script>
73
+ *
74
+ * <div>
75
+ * <input
76
+ * bind:value={expense.title}
77
+ * />
78
+ * </div>
79
+ * ```
11
80
  */
12
- export declare function useShapeRune<T extends BaseType>(shape: ShapeType<T>, scope?: Scope): UseShapeRuneResult<Set<T>>;
13
- export default useShapeRune;
81
+ export declare function useShape<T extends BaseType>(shape: ShapeType<T>, scope: Scope | string | undefined): DeepSignalSet<T>;
82
+ export default useShape;
14
83
  //# sourceMappingURL=useShape.svelte.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useShape.svelte.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/svelte/useShape.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAEH,KAAK,mBAAmB,EAC3B,MAAM,kCAAkC,CAAC;AAE1C,YAAY,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAE5E,sFAAsF;AACtF,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAChD,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC9B,IAAI,EAAE,GAAG,CAAC;CACb;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,QAAQ,EAC3C,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EACnB,KAAK,CAAC,EAAE,KAAK,GACd,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAU5B;AAED,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"useShape.svelte.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/svelte/useShape.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,EAAkB,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAK1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4EG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EACvC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EACnB,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,GAClC,aAAa,CAAC,CAAC,CAAC,CAclB;AAED,eAAe,QAAQ,CAAC"}
@@ -7,16 +7,95 @@
7
7
  // notice may not be copied, modified, or distributed except
8
8
  // according to those terms.
9
9
  // SPDX-License-Identifier: Apache-2.0 OR MIT
10
- import { createSignalObjectForShape } from "../../connector/createSignalObjectForShape.js";
10
+ import { normalizeScope } from "../../types.js";
11
11
  import { onDestroy } from "svelte";
12
- import { useDeepSignal, } from "@ng-org/alien-deepsignals/svelte";
12
+ import { useDeepSignal } from "@ng-org/alien-deepsignals/svelte";
13
+ import { OrmSubscription } from "../../connector/ormSubscriptionHandler.js";
14
+ import { readOnlySet } from "../utils.js";
13
15
  /**
14
- * Shape-specific rune: constructs the signal object for a shape then delegates to {@link useDeepSignal}.
16
+ * Svelte 5 hook to subscribe to RDF data in the graph database using a shape, see {@link ShapeType}.
17
+ *
18
+ * Returns a {@link DeepSignalSet} that contain the objects matching the shape and that are within the scope.
19
+ * Establishes a 2-way binding: Modifications to the object are immediately committed,
20
+ * changes coming from the engine (or other components) cause an immediate rerender.
21
+ *
22
+ * @param shape The {@link ShapeType} the objects should have (generated by the `@ng-org/shex-orm` tool).
23
+ * @param scope The {@link Scope} as graph string or scope object with graphs and subjects.
24
+ * @returns A {@link DeepSignalSet} with the orm objects or an empty set, if still loading.\
25
+ * If the scope is explicitly set to `undefined`, an empty set is returned which errors
26
+ * if you try to make modifications on it.
27
+ *
28
+ * @example
29
+ * ```svelte
30
+ * <script lang="ts">
31
+ * // Gets all expense objects with `@id` <s1 IRI> or <s2 IRI> and `@graph` <g1 NURI> or <g2 NURI>
32
+ * const expenses: DeepSignal<Set<Expense>> = useShape(ExpenseShapeType,
33
+ * {graphs: ["<g1 NURI>", "<g2 NURI>"],
34
+ * subjects: ["<s1 NURI>", "<s2 NURI>"]});
35
+ *
36
+ * const expensesSorted = computed(() => expenses.sort((a, b) =>
37
+ * a.dateOfPurchase.localeCompare(b.dateOfPurchase)
38
+ * ));
39
+ *
40
+ * const createExpense = () => {
41
+ * expenses.add({
42
+ * "@graph": `<graph NURI>`,
43
+ * "@type": "http://example.org/Expense",
44
+ * "@id": "", // Assigns ID automatically, if set to "".
45
+ * title: "New expense",
46
+ * dateOfPurchase: obj.dateOfPurchase ?? new Date().toISOString(),
47
+ * });
48
+ * };
49
+ *
50
+ *
51
+ * // Note that if you use `@id` (the subject IRI) as key, you need to ensure that it is unique within your scope.
52
+ * // If it is not (i.e. there are two graphs with the same subject), use the combination of `@graph` and `@id`.
53
+ * </script>
54
+ *
55
+ * <section>
56
+ * <div>
57
+ * <button on:click={() => createExpense()}>
58
+ * + Add expense
59
+ * </button>
60
+ *
61
+ * {# if expensesSorted.length === 0}
62
+ * <p>
63
+ * No expense yet.
64
+ * </p>
65
+ * {:else}
66
+ * {#each expensesSorted as expense, index (expense['@id']) }
67
+ * <ExpenseCard
68
+ * expense={expense}
69
+ * />
70
+ * {/each}
71
+ * {/if}
72
+ * </div>
73
+ * </section>
74
+ * ```
75
+ *
76
+ * ---
77
+ * In the ExpenseCard component:
78
+ * ```svelte
79
+ * <script lang="ts">
80
+ * let {
81
+ * expense,
82
+ * }: { expense: DeepSignal<Expense>; } = $props();
83
+ * </script>
84
+ *
85
+ * <div>
86
+ * <input
87
+ * bind:value={expense.title}
88
+ * />
89
+ * </div>
90
+ * ```
15
91
  */
16
- export function useShapeRune(shape, scope) {
17
- const { signalObject: rootSignal, stop } = createSignalObjectForShape(shape, scope);
18
- onDestroy(stop);
19
- const ds = useDeepSignal(rootSignal);
20
- return { root: rootSignal, ...ds };
92
+ export function useShape(shape, scope) {
93
+ if (scope === undefined) {
94
+ return useDeepSignal(readOnlySet);
95
+ }
96
+ const { signalObject: rootSignal, close } = OrmSubscription.getOrCreate(shape, normalizeScope(scope));
97
+ onDestroy(close);
98
+ const shapeSignalSet = useDeepSignal(rootSignal);
99
+ return shapeSignalSet;
21
100
  }
22
- export default useShapeRune;
101
+ export default useShape;
@@ -0,0 +1,5 @@
1
+ import useShape from "./useShape.svelte.ts";
2
+ import { useDiscrete } from "./useDiscrete.svelte.ts";
3
+ import { UseDeepSignalResult } from "./useShape.svelte.ts";
4
+ export { useShape, useDiscrete, type UseDeepSignalResult };
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/svelte4/index.ts"],"names":[],"mappings":"AAUA,OAAO,QAAQ,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,KAAK,mBAAmB,EAAE,CAAC"}
@@ -7,18 +7,6 @@
7
7
  // notice may not be copied, modified, or distributed except
8
8
  // according to those terms.
9
9
  // SPDX-License-Identifier: Apache-2.0 OR MIT
10
- import { OrmConnection } from "./ormConnectionHandler.js";
11
- /**
12
- *
13
- * @param shapeType
14
- * @param scope
15
- * @returns
16
- */
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
- };
24
- }
10
+ import useShape from "./useShape.svelte.js";
11
+ import { useDiscrete } from "./useDiscrete.svelte.js";
12
+ export { useShape, useDiscrete };
@@ -0,0 +1,84 @@
1
+ import { UseDeepSignalResult } from "@ng-org/alien-deepsignals/svelte4";
2
+ import { DiscreteRoot } from "../../types.ts";
3
+ /**
4
+ * Svelte 3/4 hook to subscribe to discrete (JSON) CRDT documents.
5
+ * You can modify the returned object like any other JSON object. Changes are immediately
6
+ * reflected in the CRDT.
7
+ *
8
+ * Establishes a 2-way binding: Modifications to the object are immediately committed,
9
+ * changes coming from the backend (or other components) cause an immediate rerender.
10
+ *
11
+ * In comparison to {@link svelte4UseShape}, discrete CRDTs are untyped.
12
+ * You can put any JSON data inside and need to validate the schema yourself.
13
+ *
14
+ * @param documentIdOrPromise The NURI of the CRDT document or a promise to that.
15
+ * @returns The store of the reactive JSON object of the CRDT document or undefined.
16
+ *
17
+ * @example
18
+ * ```svelte
19
+ * <script lang="ts">
20
+ *
21
+ * // We assume you have created a CRDT document already, as below.
22
+ * // const documentId = await ng.doc_create(
23
+ * // session_id,
24
+ * // crdt, // "Automerge" | "YMap" | "YArray"
25
+ * // crdt === "Automerge" ? "data:json" : crdt === "YMap ? "data:map" : "data:array",
26
+ * // "store",
27
+ * // undefined
28
+ *
29
+ * const doc = useDiscrete(documentIdPromise);
30
+ *
31
+ * // If the CRDT document is still empty, we need to initialize it.
32
+ * $: if (doc && !doc.expenses) {
33
+ * doc.expenses = [];
34
+ * }
35
+ *
36
+ * // Call doc.expenses.push({title: "Example title"}), to add new elements.
37
+ *
38
+ * // Note that we use expense["@id"] NURI as a key in the expense list.
39
+ * // Every object added to a CRDT array gets a stable `@id` property assigned
40
+ * // which you can use for referencing objects in arrays even as
41
+ * // objects are removed from the array.
42
+ * // Since the `@id` is generated in the backend, the object is preliminarily
43
+ * // given a mock ID which will be replaced immediately
44
+ * </script>
45
+ *
46
+ * <section>
47
+ * <div>
48
+ * {#if !doc}
49
+ * Loading...
50
+ * {:else if doc.expenses.length === 0}
51
+ * <p>
52
+ * Nothing tracked yet - log your first purchase to kick things off.
53
+ * </p>
54
+ * {:else}
55
+ * {#each doc.expenses as expense, index (expense['@id']) }
56
+ * <ExpenseCard
57
+ * expense={expense}
58
+ * />
59
+ * {/each}
60
+ * {/if}
61
+ * </div>
62
+ * </section>
63
+ * ```
64
+ *
65
+ * ---
66
+ * In the ExpenseCard component:
67
+ * ```svelte
68
+ * let {
69
+ * expense = $bindable(),
70
+ * }: { expense: Expense; } = $props();
71
+ * </script>
72
+ *
73
+ * <div>
74
+ * <input
75
+ * value={expense.title ?? ""}
76
+ * oninput={(event) => {expense.title = event.currentTarget?.value ?? ""}}
77
+ * placeholder="Expense title"
78
+ * />
79
+ * </div>
80
+ * ```
81
+ *
82
+ */
83
+ export declare function useDiscrete<T extends DiscreteRoot = DiscreteRoot>(documentIdOrPromise: string | Promise<string> | undefined): UseDeepSignalResult<T | undefined>;
84
+ //# sourceMappingURL=useDiscrete.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDiscrete.svelte.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/svelte4/useDiscrete.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,EAEH,mBAAmB,EACtB,MAAM,mCAAmC,CAAC;AAE3C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,EAC7D,mBAAmB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS,GAC1D,mBAAmB,CAAC,CAAC,GAAG,SAAS,CAAC,CAkCpC"}
@@ -0,0 +1,126 @@
1
+ // Copyright (c) 2026 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 { onDestroy } from "svelte";
11
+ import { useDeepSignal, } from "@ng-org/alien-deepsignals/svelte4";
12
+ import { DiscreteOrmSubscription } from "../../connector/discrete/discreteOrmSubscriptionHandler.js";
13
+ /**
14
+ * Svelte 3/4 hook to subscribe to discrete (JSON) CRDT documents.
15
+ * You can modify the returned object like any other JSON object. Changes are immediately
16
+ * reflected in the CRDT.
17
+ *
18
+ * Establishes a 2-way binding: Modifications to the object are immediately committed,
19
+ * changes coming from the backend (or other components) cause an immediate rerender.
20
+ *
21
+ * In comparison to {@link svelte4UseShape}, discrete CRDTs are untyped.
22
+ * You can put any JSON data inside and need to validate the schema yourself.
23
+ *
24
+ * @param documentIdOrPromise The NURI of the CRDT document or a promise to that.
25
+ * @returns The store of the reactive JSON object of the CRDT document or undefined.
26
+ *
27
+ * @example
28
+ * ```svelte
29
+ * <script lang="ts">
30
+ *
31
+ * // We assume you have created a CRDT document already, as below.
32
+ * // const documentId = await ng.doc_create(
33
+ * // session_id,
34
+ * // crdt, // "Automerge" | "YMap" | "YArray"
35
+ * // crdt === "Automerge" ? "data:json" : crdt === "YMap ? "data:map" : "data:array",
36
+ * // "store",
37
+ * // undefined
38
+ *
39
+ * const doc = useDiscrete(documentIdPromise);
40
+ *
41
+ * // If the CRDT document is still empty, we need to initialize it.
42
+ * $: if (doc && !doc.expenses) {
43
+ * doc.expenses = [];
44
+ * }
45
+ *
46
+ * // Call doc.expenses.push({title: "Example title"}), to add new elements.
47
+ *
48
+ * // Note that we use expense["@id"] NURI as a key in the expense list.
49
+ * // Every object added to a CRDT array gets a stable `@id` property assigned
50
+ * // which you can use for referencing objects in arrays even as
51
+ * // objects are removed from the array.
52
+ * // Since the `@id` is generated in the backend, the object is preliminarily
53
+ * // given a mock ID which will be replaced immediately
54
+ * </script>
55
+ *
56
+ * <section>
57
+ * <div>
58
+ * {#if !doc}
59
+ * Loading...
60
+ * {:else if doc.expenses.length === 0}
61
+ * <p>
62
+ * Nothing tracked yet - log your first purchase to kick things off.
63
+ * </p>
64
+ * {:else}
65
+ * {#each doc.expenses as expense, index (expense['@id']) }
66
+ * <ExpenseCard
67
+ * expense={expense}
68
+ * />
69
+ * {/each}
70
+ * {/if}
71
+ * </div>
72
+ * </section>
73
+ * ```
74
+ *
75
+ * ---
76
+ * In the ExpenseCard component:
77
+ * ```svelte
78
+ * let {
79
+ * expense = $bindable(),
80
+ * }: { expense: Expense; } = $props();
81
+ * </script>
82
+ *
83
+ * <div>
84
+ * <input
85
+ * value={expense.title ?? ""}
86
+ * oninput={(event) => {expense.title = event.currentTarget?.value ?? ""}}
87
+ * placeholder="Expense title"
88
+ * />
89
+ * </div>
90
+ * ```
91
+ *
92
+ */
93
+ export function useDiscrete(documentIdOrPromise) {
94
+ let connection;
95
+ let isDestroyed = false;
96
+ const objectPromise = new Promise((resolve) => {
97
+ const init = (docId) => {
98
+ if (isDestroyed)
99
+ return;
100
+ connection = DiscreteOrmSubscription.getOrCreate(docId);
101
+ connection.readyPromise.then(() => {
102
+ if (isDestroyed) {
103
+ connection?.close();
104
+ return;
105
+ }
106
+ resolve(connection.signalObject);
107
+ });
108
+ };
109
+ if (typeof documentIdOrPromise === "string") {
110
+ init(documentIdOrPromise);
111
+ }
112
+ else if (documentIdOrPromise === undefined) {
113
+ // There is nothing to do without a document ID.
114
+ }
115
+ else {
116
+ documentIdOrPromise.then(init);
117
+ }
118
+ });
119
+ onDestroy(() => {
120
+ isDestroyed = true;
121
+ if (connection) {
122
+ connection.close();
123
+ }
124
+ });
125
+ return useDeepSignal(objectPromise);
126
+ }
@@ -0,0 +1,77 @@
1
+ import { type Scope } from "../../types.ts";
2
+ import type { BaseType, ShapeType } from "@ng-org/shex-orm";
3
+ import { type UseDeepSignalResult } from "@ng-org/alien-deepsignals/svelte4";
4
+ export type { UseDeepSignalResult } from "@ng-org/alien-deepsignals/svelte4";
5
+ /** Extended result including the originating root signal wrapper from shape logic. */
6
+ export interface UseShapeRuneResult<T extends object> extends UseDeepSignalResult<T> {
7
+ root: any;
8
+ }
9
+ /**
10
+ * Svelte 3/4 hook to subscribe to RDF data in the graph database using a shape, see {@link ShapeType}.
11
+ *
12
+ * Returns a {@link DeepSignalSet} store containing the objects matching the shape and that are within the scope.
13
+ * Establishes a 2-way binding: Modifications to the object are immediately committed,
14
+ * changes coming from the backend (or other components) cause an immediate rerender.
15
+ *
16
+ * @param shape The {@link ShapeType} the objects should have (generated by the shex-orm tool).
17
+ * @param scope The {@link Scope} as graph string or scope object with graphs and subjects.
18
+ * @returns A {@link DeepSignalSet} with the orm objects or an empty set, if still loading.\
19
+ * If the scope is explicitly set to `undefined`, an empty set is returned which errors
20
+ * if you try to make modifications on it.
21
+ *
22
+ * @example
23
+ * ```svelte
24
+ * <script lang="ts">
25
+ * // Gets all expense objects with `@id` <s1 IRI> or <s2 IRI> and `@graph` <g1 NURI> or <g2 NURI>
26
+ * const expenses: DeepSignal<Set<Expense>> = useShape(ExpenseShape,
27
+ * {graphs: ["<g1 NURI>", "<g2 NURI>"],
28
+ * subjects: ["<s1 NURI>", "<s2 NURI>"]});
29
+ *
30
+ * const expensesSorted = computed(() => expenses.sort((a, b) =>
31
+ * a.dateOfPurchase.localeCompare(b.dateOfPurchase)
32
+ * ));
33
+ *
34
+ * // Call expenses.add({"@graph": "<g1 or g2 NURI>", "@id": "", title: "Example title"}), to add new elements.
35
+ * // Leave `@id` an empty string to auto-generate a subject IRI (adjust your scope accordingly).
36
+ *
37
+ * // Note that if you use `@id` (the subject IRI) as key, you need to ensure that it is unique within your scope.
38
+ * // If it is not (i.e. there are two graphs with the same subject), use the combination of `@graph` and `@id`.
39
+ * </script>
40
+ *
41
+ * <section>
42
+ * <div>
43
+ * {# if expensesSorted.length === 0}
44
+ * <p>
45
+ * No expense yet.
46
+ * </p>
47
+ * {:else}
48
+ * {#each expensesSorted as expense, index (expense['@id']) }
49
+ * <ExpenseCard
50
+ * expense={expense}
51
+ * />
52
+ * {/each}
53
+ * {/if}
54
+ * </div>
55
+ * </section>
56
+ * ```
57
+ *
58
+ * ---
59
+ * In the ExpenseCard component:
60
+ * ```svelte
61
+ *
62
+ * let {
63
+ * expense = $bindable(),
64
+ * }: { expense: Expense; } = $props();
65
+ * </script>
66
+ *
67
+ * <div>
68
+ * <input
69
+ * bind:value={expense.title}
70
+ * placeholder="Expense title"
71
+ * />
72
+ * </div>
73
+ * ```
74
+ */
75
+ export declare function useShape<T extends BaseType>(shape: ShapeType<T>, scope: Scope | string | undefined): UseShapeRuneResult<Set<T>>;
76
+ export default useShape;
77
+ //# sourceMappingURL=useShape.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useShape.svelte.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/svelte4/useShape.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,EAAkB,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAEH,KAAK,mBAAmB,EAC3B,MAAM,mCAAmC,CAAC;AAK3C,YAAY,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAE7E,sFAAsF;AACtF,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAChD,SAAQ,mBAAmB,CAAC,CAAC,CAAC;IAC9B,IAAI,EAAE,GAAG,CAAC;CACb;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EACvC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EACnB,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,GAClC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAe5B;AAED,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,91 @@
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 { normalizeScope } from "../../types.js";
11
+ import { onDestroy } from "svelte";
12
+ import { useDeepSignal, } from "@ng-org/alien-deepsignals/svelte4";
13
+ import { OrmSubscription } from "../../connector/ormSubscriptionHandler.js";
14
+ import { readOnlySet } from "../utils.js";
15
+ /**
16
+ * Svelte 3/4 hook to subscribe to RDF data in the graph database using a shape, see {@link ShapeType}.
17
+ *
18
+ * Returns a {@link DeepSignalSet} store containing the objects matching the shape and that are within the scope.
19
+ * Establishes a 2-way binding: Modifications to the object are immediately committed,
20
+ * changes coming from the backend (or other components) cause an immediate rerender.
21
+ *
22
+ * @param shape The {@link ShapeType} the objects should have (generated by the shex-orm tool).
23
+ * @param scope The {@link Scope} as graph string or scope object with graphs and subjects.
24
+ * @returns A {@link DeepSignalSet} with the orm objects or an empty set, if still loading.\
25
+ * If the scope is explicitly set to `undefined`, an empty set is returned which errors
26
+ * if you try to make modifications on it.
27
+ *
28
+ * @example
29
+ * ```svelte
30
+ * <script lang="ts">
31
+ * // Gets all expense objects with `@id` <s1 IRI> or <s2 IRI> and `@graph` <g1 NURI> or <g2 NURI>
32
+ * const expenses: DeepSignal<Set<Expense>> = useShape(ExpenseShape,
33
+ * {graphs: ["<g1 NURI>", "<g2 NURI>"],
34
+ * subjects: ["<s1 NURI>", "<s2 NURI>"]});
35
+ *
36
+ * const expensesSorted = computed(() => expenses.sort((a, b) =>
37
+ * a.dateOfPurchase.localeCompare(b.dateOfPurchase)
38
+ * ));
39
+ *
40
+ * // Call expenses.add({"@graph": "<g1 or g2 NURI>", "@id": "", title: "Example title"}), to add new elements.
41
+ * // Leave `@id` an empty string to auto-generate a subject IRI (adjust your scope accordingly).
42
+ *
43
+ * // Note that if you use `@id` (the subject IRI) as key, you need to ensure that it is unique within your scope.
44
+ * // If it is not (i.e. there are two graphs with the same subject), use the combination of `@graph` and `@id`.
45
+ * </script>
46
+ *
47
+ * <section>
48
+ * <div>
49
+ * {# if expensesSorted.length === 0}
50
+ * <p>
51
+ * No expense yet.
52
+ * </p>
53
+ * {:else}
54
+ * {#each expensesSorted as expense, index (expense['@id']) }
55
+ * <ExpenseCard
56
+ * expense={expense}
57
+ * />
58
+ * {/each}
59
+ * {/if}
60
+ * </div>
61
+ * </section>
62
+ * ```
63
+ *
64
+ * ---
65
+ * In the ExpenseCard component:
66
+ * ```svelte
67
+ *
68
+ * let {
69
+ * expense = $bindable(),
70
+ * }: { expense: Expense; } = $props();
71
+ * </script>
72
+ *
73
+ * <div>
74
+ * <input
75
+ * bind:value={expense.title}
76
+ * placeholder="Expense title"
77
+ * />
78
+ * </div>
79
+ * ```
80
+ */
81
+ export function useShape(shape, scope) {
82
+ if (scope === undefined) {
83
+ const ds = useDeepSignal(readOnlySet);
84
+ return { root: readOnlySet, ...ds };
85
+ }
86
+ const { signalObject: rootSignal, close } = OrmSubscription.getOrCreate(shape, normalizeScope(scope));
87
+ onDestroy(close);
88
+ const ds = useDeepSignal(rootSignal);
89
+ return { root: rootSignal, ...ds };
90
+ }
91
+ export default useShape;
@@ -0,0 +1,2 @@
1
+ export declare const readOnlySet: Set<unknown>;
2
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/frontendAdapters/utils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,cAatB,CAAC"}
@@ -0,0 +1,14 @@
1
+ export const readOnlySet = new Proxy(new Set(), {
2
+ get(target, key, receiver) {
3
+ if (key === "add" || key === "delete" || key === "clear") {
4
+ return () => {
5
+ throw new Error("Set is readonly because scope is empty.");
6
+ };
7
+ }
8
+ const value = target[key];
9
+ if (typeof value === "function") {
10
+ return value.bind(target);
11
+ }
12
+ return value;
13
+ },
14
+ });
@@ -1,3 +1,4 @@
1
1
  import useShape from "./useShape.ts";
2
- export { useShape };
2
+ import { useDiscrete } from "./useDiscrete.ts";
3
+ export { useShape, useDiscrete };
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/vue/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/vue/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC"}