@ng-org/orm 0.1.2-alpha.5 → 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 +40 -24
  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
@@ -8,27 +8,97 @@
8
8
  // according to those terms.
9
9
  // SPDX-License-Identifier: Apache-2.0 OR MIT
10
10
  import { useDeepSignal } from "@ng-org/alien-deepsignals/react";
11
- import { useEffect, useMemo } from "react";
12
- import { createSignalObjectForShape } from "../../connector/createSignalObjectForShape.js";
11
+ import { useEffect, useMemo, useRef } from "react";
12
+ import { OrmConnection } from "../../connector/ormConnectionHandler.js";
13
13
  /**
14
+ * Hook to subscribe to RDF data in the graph database using a shape, see {@link ShapeType}.
14
15
  *
15
- * @param shape The shape type
16
- * @param scope The document scope (IRI of named graph)
17
- * @returns A deep signal set with the orm objects, an empty set if still loading,
18
- * or an empty set which errors on modifications if scope is undefined.
16
+ * Returns a {@link DeepSignalSet} of objects matching the shape and that are within the scope.
17
+ * Establishes a 2-way binding: Modifications to the object are immediately committed,
18
+ * changes coming from the backend (or other components) cause an immediate rerender.
19
+ *
20
+ * @param shape The {@link ShapeType} the objects should have (generated by the shex-orm tool).
21
+ * @param scope The {@link Scope} as graph, array of graphs or scope object with graphs and subjects.
22
+ * @returns A deep {@link DeepSignalSet} with the orm objects or an empty set, if still loading.\
23
+ * If the scope is explicitly set to `undefined`, an empty set is returned which errors
24
+ * if you try to make modifications on it.
25
+ *
26
+ * @example
27
+ ```tsx
28
+ function Expenses() {
29
+ const expenses = useShape(ExpenseShapeType, {graphs: ["<graph IRI>"]});
30
+
31
+ const createExpense = useCallback(
32
+ () => {
33
+ expenses.add({
34
+ "@graph": `<graph IRI>`,
35
+ "@type": "http://example.org/Expense",
36
+ "@id": "", // Assigns id automatically, if set to "".
37
+ title: "New expense",
38
+ dateOfPurchase: obj.dateOfPurchase ?? new Date().toISOString(),
39
+ });
40
+ },
41
+ [expenses]
42
+ );
43
+
44
+ const expensesSorted = [...expenses].sort((a, b) =>
45
+ a.dateOfPurchase.localeCompare(b.dateOfPurchase)
46
+ );
47
+
48
+ // Not that if you use `@id` (the subject IRI) as key, you need to ensure that it is unique within your scope.
49
+ // If it is not, use the combination of `@graph` and `@id`.
50
+
51
+ return (
52
+ <div>
53
+ <button
54
+ onClick={() => createExpense({})}
55
+ >
56
+ + Add expense
57
+ </button>
58
+ <div>
59
+ {expensesSorted.length === 0 ? (
60
+ <p>
61
+ No expenses yet.
62
+ </p>
63
+ ) : (
64
+ expensesSorted.map((expense) => (
65
+ <ExpenseCard
66
+ key={expense["@id"]}
67
+ expense={expense}
68
+ availableCategories={expenseCategories}
69
+ />
70
+ ))
71
+ )}
72
+ </div>
73
+ </div>
74
+ );
75
+ }
76
+ ```
19
77
  */
20
- const useShape = (shape, scope = "") => {
21
- const signalHandler = useMemo(() => scope === undefined
22
- ? undefined
23
- : createSignalObjectForShape(shape, scope), [shape, scope]);
78
+ const useShape = (shape, scope = {}) => {
79
+ const parsedScope = typeof scope === "string"
80
+ ? { graphs: [scope] }
81
+ : Array.isArray(scope)
82
+ ? { graphs: scope }
83
+ : scope;
84
+ const prevOrmConnection = useRef(undefined);
85
+ const ormConnection = useMemo(() => {
86
+ if (scope === undefined)
87
+ return undefined;
88
+ if (prevOrmConnection.current)
89
+ prevOrmConnection.current.close();
90
+ const newOrmConnection = OrmConnection.getOrCreate(shape, parsedScope);
91
+ prevOrmConnection.current = newOrmConnection;
92
+ return newOrmConnection;
93
+ }, [shape, scope, parsedScope.graphs, parsedScope.subjects]);
24
94
  useEffect(() => {
25
- if (!signalHandler)
95
+ if (!ormConnection)
26
96
  return;
27
97
  return () => {
28
- signalHandler.stop();
98
+ ormConnection.close();
29
99
  };
30
- }, [signalHandler]);
31
- const state = useDeepSignal(signalHandler?.signalObject ?? readOnlySet);
100
+ }, [ormConnection]);
101
+ const state = useDeepSignal(ormConnection?.signalObject ?? readOnlySet);
32
102
  return state;
33
103
  };
34
104
  const readOnlySet = new Proxy(new Set(), {
@@ -1,3 +1,4 @@
1
1
  import useShape from "./useShape.svelte.ts";
2
- export { useShape };
2
+ import { useDiscrete } from "./useDiscrete.svelte.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/svelte/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/svelte/index.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,OAAO,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC"}
@@ -1,2 +1,3 @@
1
1
  import useShape from "./useShape.svelte.js";
2
- export { useShape };
2
+ import { useDiscrete } from "./useDiscrete.svelte.js";
3
+ export { useShape, useDiscrete };
@@ -0,0 +1,85 @@
1
+ import { UseDeepSignalResult } from "@ng-org/alien-deepsignals/svelte";
2
+ import { DiscreteRootArray, DiscreteRootObject } from "../../types.ts";
3
+ /**
4
+ * Svelte version 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 `useShape`, discrete CRDTs are untyped.
12
+ * You can put any JSON data inside and need to validate the schema yourself.
13
+ *
14
+ * @param documentId The IRI of the CRDT document.
15
+ * @returns The reactive JSON object of the CRDT document.
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 data = useDiscrete(documentIdPromise);
30
+
31
+ // If the CRDT document is still empty, we need to initialize it.
32
+ if (data && !data.expenses) {
33
+ data.expenses = [];
34
+ }
35
+
36
+ // Call data.expenses.push({title: "Example title"}), to add new elements.
37
+
38
+
39
+ // Note that we use expense["@id"] as a key in the expense list.
40
+ // Every object added to a CRDT array gets a stable `@id` property assigned
41
+ // which you can use for referencing objects in arrays even as
42
+ // objects are removed from the array. The ID is an IRI with the schema `<documentId>:d:<object-specific id>`.
43
+ // Since the `@id` is generated in the backend, the object is preliminarily
44
+ // given a mock id which will be replaced immediately
45
+ </script>
46
+
47
+ <section>
48
+ <div>
49
+ {#if !data}
50
+ Loading...
51
+ {:else if data.expenses.length === 0}
52
+ <p>
53
+ Nothing tracked yet - log your first purchase to kick things off.
54
+ </p>
55
+ {:else}
56
+ {#each data.expenses as expense, index (expense['@id']) }
57
+ <ExpenseCard
58
+ expense={expense}
59
+ />
60
+ {/each}
61
+ {/if}
62
+ </div>
63
+ </section>
64
+ ```
65
+
66
+ ---
67
+ In the ExpenseCard component:
68
+ ```svelte
69
+ let {
70
+ expense = $bindable(),
71
+ }: { expense: Expense; } = $props();
72
+ </script>
73
+
74
+ <div>
75
+ <input
76
+ value={expense.title ?? ""}
77
+ oninput={(event) => {expense.title = event.currentTarget?.value ?? ""}}
78
+ placeholder="Expense title"
79
+ />
80
+ </div>
81
+ ```
82
+
83
+ */
84
+ export declare function useDiscrete(documentIdOrPromise: string | Promise<string>): UseDeepSignalResult<DiscreteRootArray | DiscreteRootObject | undefined>;
85
+ //# sourceMappingURL=useDiscrete.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDiscrete.svelte.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/svelte/useDiscrete.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,EAEH,mBAAmB,EACtB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgFG;AACH,wBAAgB,WAAW,CACvB,mBAAmB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,GAC9C,mBAAmB,CAAC,iBAAiB,GAAG,kBAAkB,GAAG,SAAS,CAAC,CAgCzE"}
@@ -0,0 +1,124 @@
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/svelte";
12
+ import { DiscreteOrmConnection } from "../../connector/discrete/discreteOrmConnectionHandler.js";
13
+ /**
14
+ * Svelte version 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 `useShape`, discrete CRDTs are untyped.
22
+ * You can put any JSON data inside and need to validate the schema yourself.
23
+ *
24
+ * @param documentId The IRI of the CRDT document.
25
+ * @returns The reactive JSON object of the CRDT document.
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 data = useDiscrete(documentIdPromise);
40
+
41
+ // If the CRDT document is still empty, we need to initialize it.
42
+ if (data && !data.expenses) {
43
+ data.expenses = [];
44
+ }
45
+
46
+ // Call data.expenses.push({title: "Example title"}), to add new elements.
47
+
48
+
49
+ // Note that we use expense["@id"] as a key in the expense list.
50
+ // Every object added to a CRDT array gets a stable `@id` property assigned
51
+ // which you can use for referencing objects in arrays even as
52
+ // objects are removed from the array. The ID is an IRI with the schema `<documentId>:d:<object-specific id>`.
53
+ // Since the `@id` is generated in the backend, the object is preliminarily
54
+ // given a mock id which will be replaced immediately
55
+ </script>
56
+
57
+ <section>
58
+ <div>
59
+ {#if !data}
60
+ Loading...
61
+ {:else if data.expenses.length === 0}
62
+ <p>
63
+ Nothing tracked yet - log your first purchase to kick things off.
64
+ </p>
65
+ {:else}
66
+ {#each data.expenses 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
+ let {
80
+ expense = $bindable(),
81
+ }: { expense: Expense; } = $props();
82
+ </script>
83
+
84
+ <div>
85
+ <input
86
+ value={expense.title ?? ""}
87
+ oninput={(event) => {expense.title = event.currentTarget?.value ?? ""}}
88
+ placeholder="Expense title"
89
+ />
90
+ </div>
91
+ ```
92
+
93
+ */
94
+ export function useDiscrete(documentIdOrPromise) {
95
+ let connection;
96
+ let isDestroyed = false;
97
+ const objectPromise = new Promise((resolve) => {
98
+ const init = (docId) => {
99
+ if (isDestroyed)
100
+ return;
101
+ connection = DiscreteOrmConnection.getOrCreate(docId);
102
+ connection.readyPromise.then(() => {
103
+ if (isDestroyed) {
104
+ connection?.close();
105
+ return;
106
+ }
107
+ resolve(connection.signalObject);
108
+ });
109
+ };
110
+ if (typeof documentIdOrPromise === "string") {
111
+ init(documentIdOrPromise);
112
+ }
113
+ else {
114
+ documentIdOrPromise.then(init);
115
+ }
116
+ });
117
+ onDestroy(() => {
118
+ isDestroyed = true;
119
+ if (connection) {
120
+ connection.close();
121
+ }
122
+ });
123
+ return useDeepSignal(objectPromise);
124
+ }
@@ -7,8 +7,70 @@ export interface UseShapeRuneResult<T extends object> extends UseDeepSignalResul
7
7
  root: any;
8
8
  }
9
9
  /**
10
- * Shape-specific rune: constructs the signal object for a shape then delegates to {@link useDeepSignal}.
10
+ * Hook to subscribe to RDF data in the graph database using a shape, see {@link ShapeType}.
11
+ *
12
+ * Returns a {@link DeepSignalSet} of 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} in which the objects should be.
18
+ * @returns A {@link DeepSignalSet} containing the objects matching the shape type and scope.
19
+ *
20
+ * @example
21
+ ```svelte
22
+ <script lang="ts">
23
+ // Gets all expense objects with `@id` <s1 IRI> or <s2 IRI> and `@graph` <g1 IRI> or <g2 IRI>
24
+ const expenses: DeepSignalSet<Expense> = useShape(ExpenseShape,
25
+ {graphs: ["<g1 IRI>", "<g2 IRI>"],
26
+ subjects: ["<s1 IRI>", "<s2 IRI>"]});
27
+
28
+ const expensesSorted = computed(() => expenses.sort((a, b) =>
29
+ a.dateOfPurchase.localeCompare(b.dateOfPurchase)
30
+ ));
31
+
32
+ // Call expenses.add({"@graph": "<g1 or g2 IRI>", "@id": "", title: "Example title"}), to add new elements.
33
+ // Leave `@id` an empty string to auto-generate a subject IRI (adjust your scope accordingly).
34
+
35
+ // Not that if you use `@id` (the subject IRI) as key, you need to ensure that it is unique within your scope.
36
+ // If it is not, use the combination of `@graph` and `@id`.
37
+ </script>
38
+
39
+ <section>
40
+ <div>
41
+ {# if expensesSorted.length === 0}
42
+ <p>
43
+ No expense yet.
44
+ </p>
45
+ {:else}
46
+ {#each expensesSorted as expense, index (expense['@id']) }
47
+ <ExpenseCard
48
+ expense={expense}
49
+ />
50
+ {/each}
51
+ {/if}
52
+ </div>
53
+ </section>
54
+ ```
55
+
56
+ ---
57
+ In the ExpenseCard component:
58
+ ```svelte
59
+
60
+ let {
61
+ expense = $bindable(),
62
+ }: { expense: Expense; } = $props();
63
+ </script>
64
+
65
+ <div>
66
+ <input
67
+ value={expense.title ?? ""}
68
+ oninput={(event) => {expense.title = event.currentTarget!.value}}
69
+ placeholder="Expense title"
70
+ />
71
+ </div>
72
+ ```
11
73
  */
12
- export declare function useShapeRune<T extends BaseType>(shape: ShapeType<T>, scope?: Scope): UseShapeRuneResult<Set<T>>;
13
- export default useShapeRune;
74
+ export declare function useShape<T extends BaseType>(shape: ShapeType<T>, scope?: Scope): UseShapeRuneResult<Set<T>>;
75
+ export default useShape;
14
76
  //# 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,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;AAG1C,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EACvC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EACnB,KAAK,GAAE,KAAU,GAClB,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAU5B;AAED,eAAe,QAAQ,CAAC"}
@@ -7,16 +7,78 @@
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";
11
10
  import { onDestroy } from "svelte";
12
11
  import { useDeepSignal, } from "@ng-org/alien-deepsignals/svelte";
12
+ import { OrmConnection } from "../../connector/ormConnectionHandler.js";
13
13
  /**
14
- * Shape-specific rune: constructs the signal object for a shape then delegates to {@link useDeepSignal}.
14
+ * Hook to subscribe to RDF data in the graph database using a shape, see {@link ShapeType}.
15
+ *
16
+ * Returns a {@link DeepSignalSet} of objects matching the shape and that are within the scope.
17
+ * Establishes a 2-way binding: Modifications to the object are immediately committed,
18
+ * changes coming from the backend (or other components) cause an immediate rerender.
19
+ *
20
+ * @param shape The {@link ShapeType} the objects should have (generated by the shex-orm tool).
21
+ * @param scope The {@link Scope} in which the objects should be.
22
+ * @returns A {@link DeepSignalSet} containing the objects matching the shape type and scope.
23
+ *
24
+ * @example
25
+ ```svelte
26
+ <script lang="ts">
27
+ // Gets all expense objects with `@id` <s1 IRI> or <s2 IRI> and `@graph` <g1 IRI> or <g2 IRI>
28
+ const expenses: DeepSignalSet<Expense> = useShape(ExpenseShape,
29
+ {graphs: ["<g1 IRI>", "<g2 IRI>"],
30
+ subjects: ["<s1 IRI>", "<s2 IRI>"]});
31
+
32
+ const expensesSorted = computed(() => expenses.sort((a, b) =>
33
+ a.dateOfPurchase.localeCompare(b.dateOfPurchase)
34
+ ));
35
+
36
+ // Call expenses.add({"@graph": "<g1 or g2 IRI>", "@id": "", title: "Example title"}), to add new elements.
37
+ // Leave `@id` an empty string to auto-generate a subject IRI (adjust your scope accordingly).
38
+
39
+ // Not that if you use `@id` (the subject IRI) as key, you need to ensure that it is unique within your scope.
40
+ // If it is not, use the combination of `@graph` and `@id`.
41
+ </script>
42
+
43
+ <section>
44
+ <div>
45
+ {# if expensesSorted.length === 0}
46
+ <p>
47
+ No expense yet.
48
+ </p>
49
+ {:else}
50
+ {#each expensesSorted as expense, index (expense['@id']) }
51
+ <ExpenseCard
52
+ expense={expense}
53
+ />
54
+ {/each}
55
+ {/if}
56
+ </div>
57
+ </section>
58
+ ```
59
+
60
+ ---
61
+ In the ExpenseCard component:
62
+ ```svelte
63
+
64
+ let {
65
+ expense = $bindable(),
66
+ }: { expense: Expense; } = $props();
67
+ </script>
68
+
69
+ <div>
70
+ <input
71
+ value={expense.title ?? ""}
72
+ oninput={(event) => {expense.title = event.currentTarget!.value}}
73
+ placeholder="Expense title"
74
+ />
75
+ </div>
76
+ ```
15
77
  */
16
- export function useShapeRune(shape, scope) {
17
- const { signalObject: rootSignal, stop } = createSignalObjectForShape(shape, scope);
18
- onDestroy(stop);
78
+ export function useShape(shape, scope = {}) {
79
+ const { signalObject: rootSignal, close } = OrmConnection.getOrCreate(shape, scope);
80
+ onDestroy(close);
19
81
  const ds = useDeepSignal(rootSignal);
20
82
  return { root: rootSignal, ...ds };
21
83
  }
22
- export default useShapeRune;
84
+ export default useShape;
@@ -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"}
@@ -1,2 +1,3 @@
1
1
  import useShape from "./useShape.js";
2
- export { useShape };
2
+ import { useDiscrete } from "./useDiscrete.js";
3
+ export { useShape, useDiscrete };
@@ -0,0 +1,92 @@
1
+ import { MaybeRefOrGetter } from "vue";
2
+ import { type DeepSignal } from "@ng-org/alien-deepsignals";
3
+ import { DiscreteArray, DiscreteObject } from "../../types.ts";
4
+ /**
5
+ * Hook to subscribe to discrete (JSON) CRDT documents.
6
+ * You can modify the returned object like any other JSON object. Changes are immediately
7
+ * reflected in the CRDT.
8
+ *
9
+ * Establishes a 2-way binding: Modifications to the object are immediately committed,
10
+ * changes coming from the backend (or other components) cause an immediate rerender.
11
+ *
12
+ * In comparison to `useShape`, discrete CRDTs are untyped.
13
+ * You can put any JSON data inside and need to validate the schema yourself.
14
+ *
15
+ * @param documentId The IRI of the crdt document.
16
+ * @returns An object that contains as `data` the reactive DeepSignal object.
17
+ *
18
+ *@example
19
+ ```html
20
+ <script lang="ts">
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 { data } = useDiscrete(documentId);
30
+
31
+ // If document is new, we need to set up the basic structure.
32
+ if (data && !data.expenses) {
33
+ data.expenses = [];
34
+ }
35
+
36
+ // Note that we use expense["@id"] as a key in the expense list.
37
+ // Every object added to a CRDT array gets a stable `@id` property assigned
38
+ // which you can use for referencing objects in arrays even as
39
+ // objects are removed from the array. The ID is an IRI with the schema `<documentId>:d:<object-specific id>`.
40
+ // Since the `@id` is generated in the backend, the object is preliminarily
41
+ // given a mock id which will be replaced immediately
42
+ </script>
43
+
44
+ <template>
45
+ <div v-if="!data">
46
+ Loading...
47
+ </div>
48
+ <div v-else>
49
+ <p v-if="expensesSorted.length === 0">
50
+ No expenses yet.
51
+ </p>
52
+ <template v-else>
53
+ <ExpenseCard
54
+ v-for="expense in expenses"
55
+ :key="expense['@id']"
56
+ :expense="expense"
57
+ />
58
+ </template>
59
+ </div>
60
+ </template>
61
+ ```
62
+
63
+ In the `ExpenseCard` component:
64
+ ```html
65
+ <script lang="ts">
66
+ const props = defineProps<{
67
+ expense: DeepSignal<Expense>;
68
+ }>();
69
+
70
+ // Important!
71
+ // In vue, you need to wrap children into useDeepSignal hooks,
72
+ // to ensure the component re-renders on changes coming from
73
+ // other components or the backend.
74
+ const expense = useDeepSignal(props.expense);
75
+
76
+ // If you modify expense in the component,
77
+ // the changes are immediately propagated to the other components
78
+ // And persisted in the database.
79
+ </script>
80
+
81
+ <template>
82
+ <input
83
+ v-model="expense.title"
84
+ placeholder="Expense title"
85
+ />
86
+ </template>
87
+ ```
88
+ */
89
+ export declare function useDiscrete(documentId: MaybeRefOrGetter<string | undefined>): {
90
+ data: import("vue").ComputedRef<DeepSignal<DiscreteArray | DiscreteObject> | undefined>;
91
+ };
92
+ //# sourceMappingURL=useDiscrete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useDiscrete.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/vue/useDiscrete.ts"],"names":[],"mappings":"AAUA,OAAO,EAAY,gBAAgB,EAA4B,MAAM,KAAK,CAAC;AAC3E,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAG5D,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAI/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoFG;AACH,wBAAgB,WAAW,CAAC,UAAU,EAAE,gBAAgB,CAAC,MAAM,GAAG,SAAS,CAAC;;EAuB3E"}