@ng-org/orm 0.1.2-alpha.7 → 0.1.2-alpha.9

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 (53) hide show
  1. package/README.md +74 -92
  2. package/dist/connector/applyPatches.d.ts +13 -4
  3. package/dist/connector/applyPatches.d.ts.map +1 -1
  4. package/dist/connector/applyPatches.js +9 -5
  5. package/dist/connector/discrete/discreteOrmSubscriptionHandler.d.ts +156 -0
  6. package/dist/connector/discrete/discreteOrmSubscriptionHandler.d.ts.map +1 -0
  7. package/dist/connector/discrete/{discreteOrmConnectionHandler.js → discreteOrmSubscriptionHandler.js} +130 -19
  8. package/dist/connector/getObjects.js +2 -2
  9. package/dist/connector/initNg.d.ts +35 -0
  10. package/dist/connector/initNg.d.ts.map +1 -1
  11. package/dist/connector/initNg.js +35 -0
  12. package/dist/connector/insertObject.js +2 -2
  13. package/dist/connector/ormSubscriptionHandler.d.ts +166 -0
  14. package/dist/connector/ormSubscriptionHandler.d.ts.map +1 -0
  15. package/dist/connector/{ormConnectionHandler.js → ormSubscriptionHandler.js} +157 -32
  16. package/dist/frontendAdapters/react/useDiscrete.d.ts +89 -69
  17. package/dist/frontendAdapters/react/useDiscrete.d.ts.map +1 -1
  18. package/dist/frontendAdapters/react/useDiscrete.js +103 -81
  19. package/dist/frontendAdapters/react/useShape.d.ts +55 -55
  20. package/dist/frontendAdapters/react/useShape.d.ts.map +1 -1
  21. package/dist/frontendAdapters/react/useShape.js +71 -73
  22. package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts +80 -71
  23. package/dist/frontendAdapters/svelte/useDiscrete.svelte.d.ts.map +1 -1
  24. package/dist/frontendAdapters/svelte/useDiscrete.svelte.js +102 -91
  25. package/dist/frontendAdapters/svelte/useShape.svelte.d.ts +70 -64
  26. package/dist/frontendAdapters/svelte/useShape.svelte.d.ts.map +1 -1
  27. package/dist/frontendAdapters/svelte/useShape.svelte.js +73 -62
  28. package/dist/frontendAdapters/svelte4/index.d.ts +5 -0
  29. package/dist/frontendAdapters/svelte4/index.d.ts.map +1 -0
  30. package/dist/frontendAdapters/svelte4/index.js +12 -0
  31. package/dist/frontendAdapters/svelte4/useDiscrete.svelte.d.ts +85 -0
  32. package/dist/frontendAdapters/svelte4/useDiscrete.svelte.d.ts.map +1 -0
  33. package/dist/frontendAdapters/svelte4/useDiscrete.svelte.js +124 -0
  34. package/dist/frontendAdapters/svelte4/useShape.svelte.d.ts +76 -0
  35. package/dist/frontendAdapters/svelte4/useShape.svelte.d.ts.map +1 -0
  36. package/dist/frontendAdapters/svelte4/useShape.svelte.js +84 -0
  37. package/dist/frontendAdapters/vue/useDiscrete.d.ts +87 -80
  38. package/dist/frontendAdapters/vue/useDiscrete.d.ts.map +1 -1
  39. package/dist/frontendAdapters/vue/useDiscrete.js +96 -84
  40. package/dist/frontendAdapters/vue/useShape.d.ts +57 -63
  41. package/dist/frontendAdapters/vue/useShape.d.ts.map +1 -1
  42. package/dist/frontendAdapters/vue/useShape.js +59 -64
  43. package/dist/index.d.ts +6 -3
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +14 -3
  46. package/dist/types.d.ts +17 -7
  47. package/dist/types.d.ts.map +1 -1
  48. package/dist/types.js +11 -2
  49. package/package.json +7 -3
  50. package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts +0 -45
  51. package/dist/connector/discrete/discreteOrmConnectionHandler.d.ts.map +0 -1
  52. package/dist/connector/ormConnectionHandler.d.ts +0 -48
  53. package/dist/connector/ormConnectionHandler.d.ts.map +0 -1
@@ -8,96 +8,116 @@
8
8
  // according to those terms.
9
9
  // SPDX-License-Identifier: Apache-2.0 OR MIT
10
10
  import { useEffect, useMemo, useRef } from "react";
11
- import { DiscreteOrmConnection } from "../../connector/discrete/discreteOrmConnectionHandler.js";
11
+ import { DiscreteOrmSubscription } from "../../connector/discrete/discreteOrmSubscriptionHandler.js";
12
12
  import { useDeepSignal } from "@ng-org/alien-deepsignals/react";
13
13
  const EMPTY_OBJECT = {};
14
14
  /**
15
- * Hook to subscribe to discrete (JSON) CRDT documents.
15
+ * Hook to subscribe to an existing discrete (JSON) CRDT document.
16
16
  * You can modify the returned object like any other JSON object. Changes are immediately
17
- * reflected in the CRDT.
17
+ * reflected in the CRDT document.
18
18
  *
19
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.
20
+ * changes coming from the engine (or other components) cause an immediate rerender.
21
21
  *
22
- * In comparison to {@link useShape}, discrete CRDTs are untyped.
22
+ * In comparison to {@link reactUseShape}, discrete CRDTs are untyped.
23
23
  * You can put any JSON data inside and need to validate the schema yourself.
24
24
  *
25
- * @param documentId The IRI of the crdt document.
25
+ * @param documentId The IRI of the CRDT document.
26
26
  * @returns An object that contains as `data` the reactive DeepSignal object or undefined if `documentId` is undefined.
27
27
  *
28
- *@example
29
- ```tsx
30
- // We assume you have created a CRDT document already, as below.
31
- // const documentId = await ng.doc_create(
32
- // session_id,
33
- // crdt, // "Automerge" | "YMap" | "YArray". YArray is for root arrays, the other two have objects at root.
34
- // crdt === "Automerge" ? "data:json" : crdt === "YMap ? "data:map" : "data:array",
35
- // "store",
36
- // undefined
37
- // );
38
-
39
- function Expenses({documentId}: {documentId: string}) {
40
- const { data } = useDiscrete(documentIdPromise);
41
-
42
- // If the CRDT document is still empty, we need to initialize it.
43
- if (data && !data.expenses) {
44
- data.expenses = [];
45
- }
46
- const expenses = data?.expenses;
47
-
48
- const createExpense = useCallback(() => {
49
- expenses.add({
50
- title: "New expense",
51
- dateOfPurchase: obj.dateOfPurchase ?? new Date().toISOString(),
52
- });
53
- },
54
- [expenses]
55
- );
56
-
57
- // Loaded already?
58
- if (!expenses) return <div>Loading...</div>;
59
-
60
- // Note that we use expense["@id"] as a key in the expense list.
61
- // Every object added to a CRDT array gets a stable `@id` property assigned
62
- // which you can use for referencing objects in arrays even as
63
- // objects are removed from the array. The ID is an IRI with the schema `<documentId>:d:<object-specific id>`.
64
- // Since the `@id` is generated in the backend, the object is preliminarily
65
- // given a mock id which will be replaced immediately
66
-
67
- return (
68
- <div>
69
- <button
70
- onClick={() => createExpense()}
71
- >
72
- + Add expense
73
- </button>
74
- <div>
75
- {expenses.length === 0 ? (
76
- <p>
77
- No expenses yet.
78
- </p>
79
- ) : (
80
- expenses.map((expense) => (
81
- <ExpenseCard
82
- key={expense["@id"]}
83
- expense={expense}
84
- />
85
- ))
86
- )}
87
- </div>
88
- </div>
89
- );
90
- }
28
+ * @example
29
+ * ```tsx
30
+ * // We assume you have created a CRDT document already, as below.
31
+ * // const documentId = await ng.doc_create(
32
+ * // session_id,
33
+ * // crdt, // "Automerge" | "YMap" | "YArray". YArray is for root arrays, the other two have objects at root.
34
+ * // crdt === "Automerge" ? "data:json" : crdt === "YMap ? "data:map" : "data:array",
35
+ * // "store",
36
+ * // undefined
37
+ * // );
38
+ *
39
+ * function Expenses({documentId}: {documentId: string}) {
40
+ * const { data } = useDiscrete(documentId);
41
+ *
42
+ * // If the CRDT document is still empty, we need to initialize it.
43
+ * if (data && !data.expenses) {
44
+ * data.expenses = [];
45
+ * }
46
+ * const expenses = data?.expenses;
47
+ *
48
+ * const createExpense = useCallback(() => {
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 or added from the array.
53
+ * // The `@id` is an IRI with the schema `<documentId>:d:<object-specific id>`.
54
+ * // Since the `@id` is generated in the engine, the object is
55
+ * // *preliminarily given a mock id* which will be replaced immediately.
56
+ * expenses.push({
57
+ * title: "New expense",
58
+ * date: new Date().toISOString(),
59
+ * });
60
+ * },
61
+ * [expenses]
62
+ * );
63
+ *
64
+ * // Still loading (data undefined)?
65
+ * if (!data) return <div>Loading...</div>;
66
+ *
67
+ * return (
68
+ * <div>
69
+ * <button
70
+ * onClick={() => createExpense()}
71
+ * >
72
+ * + Add expense
73
+ * </button>
74
+ * <div>
75
+ * {expenses.length === 0 ? (
76
+ * <p>
77
+ * No expenses yet.
78
+ * </p>
79
+ * ) : (
80
+ * expenses.map((expense) => (
81
+ * <ExpenseCard
82
+ * key={expense["@id"]}
83
+ * expense={expense}
84
+ * />
85
+ * ))
86
+ * )}
87
+ * </div>
88
+ * </div>
89
+ * );
90
+ * }
91
+ * ```
92
+ *
93
+ * ---
94
+ * In the ExpenseCard component:
95
+ * ```tsx
96
+ * function ExpenseCard({expense}: {expense: Expense}) {
97
+ * return (
98
+ * <input
99
+ * value={expense.title}
100
+ * onChange={(e) => {
101
+ * expense.title = e.target.value; // Changes trigger rerender.
102
+ * }}
103
+ * />
104
+ * <div>
105
+ * <p>Date</p>
106
+ * <p>{expense.data}
107
+ * </div
108
+ * );
109
+ * }
110
+ * ```
91
111
  */
92
112
  export function useDiscrete(documentId) {
93
113
  const prevDocumentId = useRef(undefined);
94
- const prevOrmConnection = useRef(undefined);
114
+ const prevOrmSubscription = useRef(undefined);
95
115
  const ormConnection = useMemo(() => {
96
116
  // Close previous connection if documentId changed.
97
- if (prevOrmConnection.current &&
117
+ if (prevOrmSubscription.current &&
98
118
  prevDocumentId.current !== documentId) {
99
- prevOrmConnection.current.close();
100
- prevOrmConnection.current = undefined;
119
+ prevOrmSubscription.current.close();
120
+ prevOrmSubscription.current = undefined;
101
121
  }
102
122
  // If no documentId, return undefined.
103
123
  if (!documentId) {
@@ -105,23 +125,25 @@ export function useDiscrete(documentId) {
105
125
  return undefined;
106
126
  }
107
127
  // Create new connection only if needed.
108
- if (!prevOrmConnection.current ||
128
+ if (!prevOrmSubscription.current ||
109
129
  prevDocumentId.current !== documentId) {
110
- prevOrmConnection.current =
111
- DiscreteOrmConnection.getOrCreate(documentId);
130
+ prevOrmSubscription.current =
131
+ DiscreteOrmSubscription.getOrCreate(documentId);
112
132
  prevDocumentId.current = documentId;
113
133
  }
114
- return prevOrmConnection.current;
134
+ return prevOrmSubscription.current;
115
135
  }, [documentId]);
116
136
  useEffect(() => {
117
137
  return () => {
118
- prevOrmConnection.current?.close();
138
+ prevOrmSubscription.current?.close();
119
139
  };
120
140
  }, []);
121
141
  // useDeepSignal requires an object, so pass empty object when no connection.
122
142
  const signalSource = ormConnection?.signalObject ?? EMPTY_OBJECT;
123
- const state = useDeepSignal(signalSource);
143
+ const deepSignalValue = useDeepSignal(signalSource);
124
144
  // Only return data if we have a valid connection with a signal object.
125
- const data = ormConnection?.signalObject ? state : undefined;
126
- return { data };
145
+ const dataOrUndefined = ormConnection?.signalObject
146
+ ? deepSignalValue
147
+ : undefined;
148
+ return { doc: dataOrUndefined };
127
149
  }
@@ -1,72 +1,72 @@
1
1
  import type { BaseType } from "@ng-org/shex-orm";
2
2
  import type { ShapeType } from "@ng-org/shex-orm";
3
- import type { Scope } from "../../types.ts";
3
+ import { type Scope } from "../../types.ts";
4
4
  import { DeepSignalSet } from "@ng-org/alien-deepsignals";
5
5
  /**
6
6
  * Hook to subscribe to RDF data in the graph database using a shape, see {@link ShapeType}.
7
7
  *
8
8
  * Returns a {@link DeepSignalSet} of objects matching the shape and that are within the scope.
9
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.
10
+ * changes coming from the engine (or other components) cause an immediate rerender.
11
11
  *
12
12
  * @param shape The {@link ShapeType} the objects should have (generated by the shex-orm tool).
13
- * @param scope The {@link Scope} as graph, array of graphs or scope object with graphs and subjects.
14
- * @returns A deep {@link DeepSignalSet} with the orm objects or an empty set, if still loading.\
13
+ * @param scope The {@link Scope} as graph string or scope object with graphs and subjects.
14
+ * @returns A {@link DeepSignalSet} with the orm objects or an empty set, if still loading.\
15
15
  * If the scope is explicitly set to `undefined`, an empty set is returned which errors
16
16
  * if you try to make modifications on it.
17
17
  *
18
18
  * @example
19
- ```tsx
20
- function Expenses() {
21
- const expenses = useShape(ExpenseShapeType, {graphs: ["<graph IRI>"]});
22
-
23
- const createExpense = useCallback(
24
- () => {
25
- expenses.add({
26
- "@graph": `<graph IRI>`,
27
- "@type": "http://example.org/Expense",
28
- "@id": "", // Assigns id automatically, if set to "".
29
- title: "New expense",
30
- dateOfPurchase: obj.dateOfPurchase ?? new Date().toISOString(),
31
- });
32
- },
33
- [expenses]
34
- );
35
-
36
- const expensesSorted = [...expenses].sort((a, b) =>
37
- a.dateOfPurchase.localeCompare(b.dateOfPurchase)
38
- );
39
-
40
- // Not 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, use the combination of `@graph` and `@id`.
42
-
43
- return (
44
- <div>
45
- <button
46
- onClick={() => createExpense({})}
47
- >
48
- + Add expense
49
- </button>
50
- <div>
51
- {expensesSorted.length === 0 ? (
52
- <p>
53
- No expenses yet.
54
- </p>
55
- ) : (
56
- expensesSorted.map((expense) => (
57
- <ExpenseCard
58
- key={expense["@id"]}
59
- expense={expense}
60
- availableCategories={expenseCategories}
61
- />
62
- ))
63
- )}
64
- </div>
65
- </div>
66
- );
67
- }
68
- ```
19
+ * ```tsx
20
+ * function Expenses() {
21
+ * const expenses = useShape(ExpenseShapeType, {graphs: ["<graph IRI>"]});
22
+ *
23
+ * const createExpense = useCallback(
24
+ * () => {
25
+ * expenses.add({
26
+ * "@graph": `<graph IRI>`,
27
+ * "@type": "http://example.org/Expense",
28
+ * "@id": "", // Assigns id automatically, if set to "".
29
+ * title: "New expense",
30
+ * dateOfPurchase: obj.dateOfPurchase ?? new Date().toISOString(),
31
+ * });
32
+ * },
33
+ * [expenses]
34
+ * );
35
+ *
36
+ * const expensesSorted = [...expenses].sort((a, b) =>
37
+ * a.dateOfPurchase.localeCompare(b.dateOfPurchase)
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, use the combination of `@graph` and `@id`.
42
+ *
43
+ * return (
44
+ * <div>
45
+ * <button
46
+ * onClick={() => createExpense({})}
47
+ * >
48
+ * + Add expense
49
+ * </button>
50
+ * <div>
51
+ * {expensesSorted.length === 0 ? (
52
+ * <p>
53
+ * No expenses yet.
54
+ * </p>
55
+ * ) : (
56
+ * expensesSorted.map((expense) => (
57
+ * <ExpenseCard
58
+ * key={expense["@id"]}
59
+ * expense={expense}
60
+ * availableCategories={expenseCategories}
61
+ * />
62
+ * ))
63
+ * )}
64
+ * </div>
65
+ * </div>
66
+ * );
67
+ * }
68
+ * ```
69
69
  */
70
- declare const useShape: <T extends BaseType>(shape: ShapeType<T>, scope?: Scope | string[] | string) => DeepSignalSet<T>;
70
+ declare const useShape: <T extends BaseType>(shape: ShapeType<T>, scope: Scope | string | undefined) => DeepSignalSet<T>;
71
71
  export default useShape;
72
72
  //# sourceMappingURL=useShape.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useShape.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/react/useShape.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,QAAA,MAAM,QAAQ,GAAI,CAAC,SAAS,QAAQ,EAChC,OAAO,SAAS,CAAC,CAAC,CAAC,EACnB,QAAO,KAAK,GAAG,MAAM,EAAE,GAAG,MAAW,KA6BrB,aAAa,CAAC,CAAC,CAClC,CAAC;AAiBF,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"useShape.d.ts","sourceRoot":"","sources":["../../../src/frontendAdapters/react/useShape.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAkB,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,QAAA,MAAM,QAAQ,GAAI,CAAC,SAAS,QAAQ,EAChC,OAAO,SAAS,CAAC,CAAC,CAAC,EACnB,OAAO,KAAK,GAAG,MAAM,GAAG,SAAS,KA8BjB,aAAa,CAAC,CAAC,CAClC,CAAC;AAiBF,eAAe,QAAQ,CAAC"}
@@ -9,96 +9,94 @@
9
9
  // SPDX-License-Identifier: Apache-2.0 OR MIT
10
10
  import { useDeepSignal } from "@ng-org/alien-deepsignals/react";
11
11
  import { useEffect, useMemo, useRef } from "react";
12
- import { OrmConnection } from "../../connector/ormConnectionHandler.js";
12
+ import { normalizeScope } from "../../types.js";
13
+ import { OrmSubscription } from "../../connector/ormSubscriptionHandler.js";
13
14
  /**
14
15
  * Hook to subscribe to RDF data in the graph database using a shape, see {@link ShapeType}.
15
16
  *
16
17
  * Returns a {@link DeepSignalSet} of objects matching the shape and that are within the scope.
17
18
  * 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
+ * changes coming from the engine (or other components) cause an immediate rerender.
19
20
  *
20
21
  * @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.\
22
+ * @param scope The {@link Scope} as graph string or scope object with graphs and subjects.
23
+ * @returns A {@link DeepSignalSet} with the orm objects or an empty set, if still loading.\
23
24
  * If the scope is explicitly set to `undefined`, an empty set is returned which errors
24
25
  * if you try to make modifications on it.
25
26
  *
26
27
  * @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
- ```
28
+ * ```tsx
29
+ * function Expenses() {
30
+ * const expenses = useShape(ExpenseShapeType, {graphs: ["<graph IRI>"]});
31
+ *
32
+ * const createExpense = useCallback(
33
+ * () => {
34
+ * expenses.add({
35
+ * "@graph": `<graph IRI>`,
36
+ * "@type": "http://example.org/Expense",
37
+ * "@id": "", // Assigns id automatically, if set to "".
38
+ * title: "New expense",
39
+ * dateOfPurchase: obj.dateOfPurchase ?? new Date().toISOString(),
40
+ * });
41
+ * },
42
+ * [expenses]
43
+ * );
44
+ *
45
+ * const expensesSorted = [...expenses].sort((a, b) =>
46
+ * a.dateOfPurchase.localeCompare(b.dateOfPurchase)
47
+ * );
48
+ *
49
+ * // Note that if you use `@id` (the subject IRI) as key, you need to ensure that it is unique within your scope.
50
+ * // If it is not, use the combination of `@graph` and `@id`.
51
+ *
52
+ * return (
53
+ * <div>
54
+ * <button
55
+ * onClick={() => createExpense({})}
56
+ * >
57
+ * + Add expense
58
+ * </button>
59
+ * <div>
60
+ * {expensesSorted.length === 0 ? (
61
+ * <p>
62
+ * No expenses yet.
63
+ * </p>
64
+ * ) : (
65
+ * expensesSorted.map((expense) => (
66
+ * <ExpenseCard
67
+ * key={expense["@id"]}
68
+ * expense={expense}
69
+ * availableCategories={expenseCategories}
70
+ * />
71
+ * ))
72
+ * )}
73
+ * </div>
74
+ * </div>
75
+ * );
76
+ * }
77
+ * ```
77
78
  */
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)
79
+ const useShape = (shape, scope) => {
80
+ const parsedScope = !scope ? undefined : normalizeScope(scope);
81
+ const prevOrmSubscription = useRef(undefined);
82
+ const ormSubscription = useMemo(() => {
83
+ if (parsedScope === undefined)
87
84
  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]);
85
+ if (prevOrmSubscription.current)
86
+ prevOrmSubscription.current.close();
87
+ // TODO: Add flag to indicate that we want branch replacement of proxies on nested changes.
88
+ const newOrmSubscription = OrmSubscription.getOrCreate(shape, parsedScope);
89
+ prevOrmSubscription.current = newOrmSubscription;
90
+ return newOrmSubscription;
91
+ }, [shape, scope, parsedScope?.graphs, parsedScope?.subjects]);
94
92
  useEffect(() => {
95
- if (!ormConnection)
93
+ if (!ormSubscription)
96
94
  return;
97
95
  return () => {
98
- ormConnection.close();
96
+ ormSubscription.close();
99
97
  };
100
- }, [ormConnection]);
101
- const state = useDeepSignal(ormConnection?.signalObject ?? readOnlySet);
98
+ }, [ormSubscription]);
99
+ const state = useDeepSignal(ormSubscription?.signalObject ?? readOnlySet);
102
100
  return state;
103
101
  };
104
102
  const readOnlySet = new Proxy(new Set(), {