@ng-org/alien-deepsignals 0.1.2-alpha.6 → 0.1.2-alpha.7
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 +39 -339
- package/dist/core.d.ts +67 -2
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +70 -10
- package/dist/deepSignal.d.ts +16 -4
- package/dist/deepSignal.d.ts.map +1 -1
- package/dist/deepSignal.js +19 -8
- package/dist/hooks/react/useDeepSignal.d.ts +1 -1
- package/dist/hooks/react/useDeepSignal.js +1 -1
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts +2 -2
- package/dist/hooks/svelte/useDeepSignal.svelte.d.ts.map +1 -1
- package/dist/hooks/svelte/useDeepSignal.svelte.js +1 -1
- package/dist/hooks/svelte4/useDeepSignal.svelte.d.ts +5 -5
- package/dist/hooks/svelte4/useDeepSignal.svelte.js +3 -3
- package/dist/hooks/vue/useDeepSignal.d.ts +1 -1
- package/dist/hooks/vue/useDeepSignal.d.ts.map +1 -1
- package/dist/hooks/vue/useDeepSignal.js +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/test/frontend/utils/mockData.js +3 -3
- package/dist/test/lib/watch.test.js +0 -10
- package/dist/types.d.ts +64 -13
- package/dist/types.d.ts.map +1 -1
- package/dist/watch.d.ts +27 -1
- package/dist/watch.d.ts.map +1 -1
- package/dist/watch.js +27 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,20 +4,23 @@ Deep structural reactivity for plain objects / arrays / Sets built on top of `al
|
|
|
4
4
|
|
|
5
5
|
Hooks for Svelte, Vue, and React.
|
|
6
6
|
|
|
7
|
-
Core idea: wrap a data tree in a `Proxy` that lazily creates per-property signals the first time you read them. Deep mutations emit
|
|
7
|
+
Core idea: wrap a data tree in a `Proxy` that lazily creates per-property signals the first time you read them. Deep mutations emit batched patch objects (in a JSON-patch inspired style) that you can track with `watch()`.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
11
11
|
- Lazy: signals & child proxies created only when touched.
|
|
12
12
|
- Deep: nested objects, arrays, Sets proxied.
|
|
13
|
-
- Per-property signals: fine‑grained invalidation without traversal on each change.
|
|
14
13
|
- Patch stream: microtask‑batched granular mutations (paths + op) for syncing external stores / framework adapters.
|
|
15
14
|
- Getter => computed: property getters become derived (readonly) signals automatically.
|
|
16
|
-
- Sets:
|
|
17
|
-
- Configurable synthetic IDs: custom property generator - the synthetic
|
|
15
|
+
- Sets: `add/delete/clear/...` methods emit patches; object entries get synthetic stable ids.
|
|
16
|
+
- Configurable synthetic IDs: custom property generator - the synthetic ID is used in the paths of patches to identify objects in sets. By default attached as `@id` property.
|
|
18
17
|
- Read-only properties: protect specific properties from modification.
|
|
19
18
|
- Shallow escape hatch: wrap sub-objects with `shallow(obj)` to track only reference replacement.
|
|
20
19
|
|
|
20
|
+
## Reference documentation
|
|
21
|
+
|
|
22
|
+
[Reference documentation is available here on docs.nextgraph.org](https://docs.nextgraph.org/en/reference/alien-deepsignals/).
|
|
23
|
+
|
|
21
24
|
## Install
|
|
22
25
|
|
|
23
26
|
```bash
|
|
@@ -46,15 +49,37 @@ state.settings.add("beta");
|
|
|
46
49
|
|
|
47
50
|
## Frontend Hooks
|
|
48
51
|
|
|
49
|
-
We provide hooks for Svelte 3/4, Svelte 5, Vue, and React so that you can use deepSignal objects in your frontend framework. Modifying the object within those components works as usual, just that the component will rerender automatically
|
|
52
|
+
We provide hooks for Svelte 3/4, Svelte 5, Vue, and React so that you can use deepSignal objects in your frontend framework. Modifying the object within those components works as usual, just that the component will rerender automatically when the object changed (by a modification in the component or a modification from elsewhere).
|
|
53
|
+
|
|
54
|
+
Note that you can pass existing deepSignal objects to useDeepSignal (that you are using elsewhere too, for example as shared state) as well as plain JavaScript objects (which are then wrapped).
|
|
55
|
+
|
|
56
|
+
You can (and are often advised to) use deepSignals as a shared state (and sub objects thereof) across components.
|
|
50
57
|
|
|
51
|
-
|
|
58
|
+
### React
|
|
52
59
|
|
|
53
60
|
```tsx
|
|
54
61
|
import { useDeepSignal } from "@ng-org/alien-deepsignals/react";
|
|
62
|
+
import { DeepSignal } from "@ng-org/alien-deepsignals";
|
|
63
|
+
import UserComponent from "./User.tsx";
|
|
64
|
+
import type { User } from "./types.ts";
|
|
55
65
|
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
function UserManager() {
|
|
67
|
+
const users: DeepSignal<User[]> = useDeepSignal([{ username: "Bob" }]);
|
|
68
|
+
|
|
69
|
+
return users.map((user) => <UserComponent key={user.id} user={user} />);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
In child component `User.tsx`:
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
function UserComponent({ user }: { user: DeepSignal<User> }) {
|
|
77
|
+
// Modifications here will trigger a re-render in the parent component
|
|
78
|
+
// which updates this component.
|
|
79
|
+
// For performance reasons, you are advised to call `useDeepSignal`
|
|
80
|
+
// close to where its return value is used.
|
|
81
|
+
return <input type="text" value={user.name} />;
|
|
82
|
+
}
|
|
58
83
|
```
|
|
59
84
|
|
|
60
85
|
### Vue
|
|
@@ -63,12 +88,12 @@ In component `UserManager.vue`
|
|
|
63
88
|
|
|
64
89
|
```vue
|
|
65
90
|
<script setup lang="ts">
|
|
66
|
-
import { DeepSignal } from "@ng-org/alien-deepsignals";
|
|
67
91
|
import { useDeepSignal } from "@ng-org/alien-deepsignals/vue";
|
|
92
|
+
import { DeepSignal } from "@ng-org/alien-deepsignals";
|
|
68
93
|
import UserComponent from "./User.vue";
|
|
69
|
-
import { User } from "./types.ts";
|
|
94
|
+
import type { User } from "./types.ts";
|
|
70
95
|
|
|
71
|
-
const users: DeepSignal<User> = useDeepSignal([{ username: "Bob", id: 1 }]);
|
|
96
|
+
const users: DeepSignal<User[]> = useDeepSignal([{ username: "Bob", id: 1 }]);
|
|
72
97
|
</script>
|
|
73
98
|
|
|
74
99
|
<template>
|
|
@@ -80,14 +105,12 @@ In a child component, `User.vue`
|
|
|
80
105
|
|
|
81
106
|
```vue
|
|
82
107
|
<script setup lang="ts">
|
|
83
|
-
import { useDeepSignal } from "@ng-org/alien-deepsignals/vue";
|
|
84
|
-
|
|
85
108
|
const props = defineProps<{
|
|
86
109
|
user: DeepSignal<User>;
|
|
87
110
|
}>();
|
|
88
111
|
|
|
89
112
|
// The component only rerenders when user.name changes.
|
|
90
|
-
// It behaves the same as an object
|
|
113
|
+
// It behaves the same as an object wrapped with `reactive()`
|
|
91
114
|
const user = props.user;
|
|
92
115
|
</script>
|
|
93
116
|
<template>
|
|
@@ -113,332 +136,9 @@ import { useDeepSignal } from "@ng-org/alien-deepsignals/svelte";
|
|
|
113
136
|
const users = useDeepSignal([{ username: "Bob" }]);
|
|
114
137
|
```
|
|
115
138
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
`deepSignal(obj, options?)` accepts an optional configuration object:
|
|
119
|
-
|
|
120
|
-
```ts
|
|
121
|
-
type DeepSignalOptions = {
|
|
122
|
-
propGenerator?: (props: {
|
|
123
|
-
path: (string | number)[];
|
|
124
|
-
inSet: boolean;
|
|
125
|
-
object: any;
|
|
126
|
-
}) => {
|
|
127
|
-
syntheticId?: string;
|
|
128
|
-
extraProps?: Record<string, unknown>;
|
|
129
|
-
};
|
|
130
|
-
syntheticIdPropertyName?: string;
|
|
131
|
-
readOnlyProps?: string[];
|
|
132
|
-
};
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
### Property generator function
|
|
136
|
-
|
|
137
|
-
The `propGenerator` function is called when a new object is added to the deep signal tree. It receives:
|
|
138
|
-
|
|
139
|
-
- `path`: The path of the newly added object
|
|
140
|
-
- `inSet`: Whether the object is being added to a Set (true) or not (false)
|
|
141
|
-
- `object`: The newly added object itself
|
|
142
|
-
|
|
143
|
-
It can return:
|
|
144
|
-
|
|
145
|
-
- `syntheticId`: A custom identifier for the object (used in Set entry paths and optionally as a property)
|
|
146
|
-
- `extraProps`: Additional properties to be added to the object (overwriting existing ones).
|
|
147
|
-
|
|
148
|
-
```ts
|
|
149
|
-
let counter = 0;
|
|
150
|
-
const state = deepSignal(
|
|
151
|
-
{ items: new Set() },
|
|
152
|
-
{
|
|
153
|
-
propGenerator: ({ path, inSet, object }) => ({
|
|
154
|
-
syntheticId: inSet
|
|
155
|
-
? `urn:item:${++counter}`
|
|
156
|
-
: `urn:obj:${path.join("-")}`,
|
|
157
|
-
extraProps: { createdAt: new Date().toISOString() },
|
|
158
|
-
}),
|
|
159
|
-
syntheticIdPropertyName: "@id",
|
|
160
|
-
}
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
state.items.add({ name: "Item 1" }); // Gets @id: "urn:item:1" and createdAt property
|
|
164
|
-
state.items.add({ name: "Item 2" }); // Gets @id: "urn:item:2"
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### Synthetic ID property name
|
|
168
|
-
|
|
169
|
-
When `syntheticIdPropertyName` is set (e.g., to `"@id"`), objects receive a readonly, enumerable property with the generated synthetic ID:
|
|
170
|
-
|
|
171
|
-
```ts
|
|
172
|
-
const state = deepSignal(
|
|
173
|
-
{ data: {} },
|
|
174
|
-
{
|
|
175
|
-
propGenerator: ({ path, inSet, object }) => ({
|
|
176
|
-
syntheticId: `urn:uuid:${crypto.randomUUID()}`,
|
|
177
|
-
}),
|
|
178
|
-
syntheticIdPropertyName: "@id",
|
|
179
|
-
}
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
state.data.user = { name: "Ada" };
|
|
183
|
-
console.log(state.data.user["@id"]); // e.g., "urn:uuid:550e8400-e29b-41d4-a716-446655440000"
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
### Read-only properties
|
|
187
|
-
|
|
188
|
-
The `readOnlyProps` option lets you specify property names that cannot be modified:
|
|
189
|
-
|
|
190
|
-
```ts
|
|
191
|
-
const state = deepSignal(
|
|
192
|
-
{ data: {} },
|
|
193
|
-
{
|
|
194
|
-
propGenerator: ({ path, inSet, object }) => ({
|
|
195
|
-
syntheticId: `urn:uuid:${crypto.randomUUID()}`,
|
|
196
|
-
}),
|
|
197
|
-
syntheticIdPropertyName: "@id",
|
|
198
|
-
readOnlyProps: ["@id", "@graph"],
|
|
199
|
-
}
|
|
200
|
-
);
|
|
201
|
-
|
|
202
|
-
state.data.user = { name: "Ada" };
|
|
203
|
-
state.data.user["@id"] = "new-id"; // TypeError: Cannot modify readonly property '@id'
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
**Key behaviors:**
|
|
207
|
-
|
|
208
|
-
- Synthetic IDs are assigned **before** the object is proxied, ensuring availability immediately
|
|
209
|
-
- Properties specified in `readOnlyProps` are **readonly** and **enumerable**
|
|
210
|
-
- Synthetic ID assignment emits a patch just like any other property
|
|
211
|
-
- Objects with existing properties matching `syntheticIdPropertyName` keep their values (not overwritten)
|
|
212
|
-
- Options propagate to all nested objects created after initialization
|
|
213
|
-
- The `propGenerator` function is called for both Set entries (`inSet: true`) and regular objects (`inSet: false`)
|
|
214
|
-
|
|
215
|
-
## Watching patches
|
|
216
|
-
|
|
217
|
-
`watch(root, cb, options?)` observes a deepSignal root and invokes your callback with microtask‑batched mutation patches plus snapshots.
|
|
218
|
-
|
|
219
|
-
```ts
|
|
220
|
-
import { watch } from "alien-deepsignals";
|
|
221
|
-
|
|
222
|
-
const stop = watch(state, ({ patches, oldValue, newValue }) => {
|
|
223
|
-
for (const p of patches) {
|
|
224
|
-
console.log(p.op, p.path.join("."), "value" in p ? p.value : p.type);
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
state.user.name = "Lin";
|
|
229
|
-
state.items[0].qty = 3;
|
|
230
|
-
await Promise.resolve(); // flush microtask
|
|
231
|
-
stop();
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
## Computed (derived) values
|
|
235
|
-
|
|
236
|
-
Use the `computed()` function to create lazy derived signals that automatically track their dependencies and recompute only when needed.
|
|
237
|
-
|
|
238
|
-
```ts
|
|
239
|
-
import { computed } from "@ng-org/alien-deepsignals";
|
|
240
|
-
|
|
241
|
-
const state = deepSignal({
|
|
242
|
-
firstName: "Ada",
|
|
243
|
-
lastName: "Lovelace",
|
|
244
|
-
items: [1, 2, 3],
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// Create a computed signal that derives from reactive state
|
|
248
|
-
const fullNaAdd documentationme = computed(() => `${state.firstName} ${state.lastName}`);
|
|
249
|
-
const itemCount = computed(() => state.items.length);
|
|
250
|
-
|
|
251
|
-
console.log(fullName()); // "Ada Lovelace" - computes on first access
|
|
252
|
-
console.log(itemCount()); // 3
|
|
253
|
-
|
|
254
|
-
state.firstName = "Grace";
|
|
255
|
-
console.log(fullName()); // "Grace Lovelace" - recomputes automatically
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
**Key benefits:**
|
|
259
|
-
|
|
260
|
-
- **Lazy evaluation**: The computation runs only when you actually read the computed value. If you never access `fullName()`, the concatenation never happens—no wasted CPU cycles.
|
|
261
|
-
- **Automatic caching**: Once computed, the result is cached until a dependency changes. Multiple reads return the cached value without re-running the getter.
|
|
262
|
-
- **Fine-grained reactivity**: Only recomputes when its tracked dependencies change. Unrelated state mutations don't trigger unnecessary recalculation.
|
|
263
|
-
- **Composable**: Computed signals can depend on other computed signals, forming efficient dependency chains.
|
|
264
|
-
|
|
265
|
-
```ts
|
|
266
|
-
// Expensive computation only runs when accessed and dependencies change
|
|
267
|
-
const expensiveResult = computed(() => {
|
|
268
|
-
console.log("Computing...");
|
|
269
|
-
return state.items.reduce((sum, n) => sum + n * n, 0);
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
// No computation happens yet!
|
|
273
|
-
state.items.push(4);
|
|
274
|
-
// Still no computation...
|
|
275
|
-
|
|
276
|
-
console.log(expensiveResult()); // "Computing..." + result
|
|
277
|
-
console.log(expensiveResult()); // Cached, no log
|
|
278
|
-
state.items.push(5);
|
|
279
|
-
console.log(expensiveResult()); // "Computing..." again (dependency changed)
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
### Callback event shape
|
|
283
|
-
|
|
284
|
-
```ts
|
|
285
|
-
type WatchPatchEvent<T> = {
|
|
286
|
-
patches: DeepPatch[]; // empty only on immediate
|
|
287
|
-
oldValue: T | undefined; // deep-cloned snapshot before batch
|
|
288
|
-
newValue: T; // live proxy (already mutated)
|
|
289
|
-
registerCleanup(fn): void; // register disposer for next batch/stop
|
|
290
|
-
stopListening(): void; // unsubscribe
|
|
291
|
-
};
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### Options
|
|
295
|
-
|
|
296
|
-
| Option | Type | Default | Description |
|
|
297
|
-
| ----------- | ------- | ------- | -------------------------------------------------- |
|
|
298
|
-
| `immediate` | boolean | false | Fire once right away with `patches: []`. |
|
|
299
|
-
| `once` | boolean | false | Auto stop after first callback (immediate counts). |
|
|
300
|
-
|
|
301
|
-
`observe()` is an alias of `watch()`.
|
|
302
|
-
|
|
303
|
-
## DeepPatch format
|
|
304
|
-
|
|
305
|
-
```ts
|
|
306
|
-
type DeepPatch = {
|
|
307
|
-
root: symbol; // stable id per deepSignal root
|
|
308
|
-
path: (string | number)[]; // root-relative segments
|
|
309
|
-
} & (
|
|
310
|
-
| { op: "add"; type: "object" } // assigned object/array/Set entry object
|
|
311
|
-
| { op: "add"; value: string | number | boolean } // primitive write
|
|
312
|
-
| { op: "remove" } // deletion
|
|
313
|
-
| { op: "add"; type: "set"; value: [] } // Set.clear()
|
|
314
|
-
| {
|
|
315
|
-
op: "add";
|
|
316
|
-
type: "set";
|
|
317
|
-
value: (string | number | boolean)[] | { [id: string]: object };
|
|
318
|
-
} // (reserved)
|
|
319
|
-
);
|
|
320
|
-
```
|
|
321
|
-
|
|
322
|
-
Notes:
|
|
323
|
-
|
|
324
|
-
- `type:'object'` omits value to avoid deep cloning; read from `newValue` if needed.
|
|
325
|
-
- `Set.add(entry)` emits object vs primitive form depending on entry type; path ends with synthetic id.
|
|
326
|
-
- `Set.clear()` emits one structural patch and suppresses per‑entry removals in same batch.
|
|
327
|
-
|
|
328
|
-
## Sets & synthetic ids
|
|
329
|
-
|
|
330
|
-
Object entries inside Sets need a stable key for patch paths. The synthetic ID resolution follows this priority:
|
|
331
|
-
|
|
332
|
-
1. Explicit custom ID via `setSetEntrySyntheticId(entry, 'myId')` (before `add`)
|
|
333
|
-
2. Custom ID property specified by `syntheticIdPropertyName` option (e.g., `entry['@id']`)
|
|
334
|
-
3. Auto-generated blank node ID (`_bN` format)
|
|
335
|
-
|
|
336
|
-
### Working with Sets
|
|
337
|
-
|
|
338
|
-
```ts
|
|
339
|
-
import { addWithId, setSetEntrySyntheticId } from "@ng-org/alien-deepsignals";
|
|
340
|
-
|
|
341
|
-
// Option 1: Use automatic ID generation via propGenerator
|
|
342
|
-
const state = deepSignal(
|
|
343
|
-
{ items: new Set() },
|
|
344
|
-
{
|
|
345
|
-
propGenerator: ({ path, inSet, object }) => ({
|
|
346
|
-
syntheticId: inSet ? `urn:uuid:${crypto.randomUUID()}` : undefined,
|
|
347
|
-
}),
|
|
348
|
-
syntheticIdPropertyName: "@id",
|
|
349
|
-
}
|
|
350
|
-
);
|
|
351
|
-
const item = { name: "Item 1" };
|
|
352
|
-
state.items.add(item); // Automatically gets @id before being added
|
|
353
|
-
console.log(item["@id"]); // e.g., "urn:uuid:550e8400-..."
|
|
354
|
-
|
|
355
|
-
// Option 2: Manually set synthetic ID
|
|
356
|
-
const obj = { value: 42 };
|
|
357
|
-
setSetEntrySyntheticId(obj, "urn:custom:my-id");
|
|
358
|
-
state.items.add(obj);
|
|
359
|
-
|
|
360
|
-
// Option 3: Use convenience helper
|
|
361
|
-
addWithId(state.items as any, { value: 99 }, "urn:item:special");
|
|
362
|
-
|
|
363
|
-
// Option 4: Pre-assign property matching syntheticIdPropertyName
|
|
364
|
-
const preTagged = { "@id": "urn:explicit:123", data: "..." };
|
|
365
|
-
state.items.add(preTagged); // Uses "urn:explicit:123" as synthetic ID
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
### Set entry patches and paths
|
|
369
|
-
|
|
370
|
-
When objects are added to Sets, their **synthetic ID becomes part of the patch path**. This allows patches to uniquely identify which Set entry is being mutated.
|
|
371
|
-
|
|
372
|
-
```ts
|
|
373
|
-
const state = deepSignal(
|
|
374
|
-
{ s: new Set() },
|
|
375
|
-
{
|
|
376
|
-
propGenerator: ({ inSet }) => ({
|
|
377
|
-
syntheticId: inSet ? "urn:entry:set-entry-1" : undefined,
|
|
378
|
-
}),
|
|
379
|
-
syntheticIdPropertyName: "@id",
|
|
380
|
-
}
|
|
381
|
-
);
|
|
382
|
-
|
|
383
|
-
watch(state, ({ patches }) => {
|
|
384
|
-
console.log(JSON.stringify(patches));
|
|
385
|
-
// [
|
|
386
|
-
// {"path":["s","urn:entry:set-entry-1"],"op":"add","type":"object"},
|
|
387
|
-
// {"path":["s","urn:entry:set-entry-1","@id"],"op":"add","value":"urn:entry:set-entry-1"},
|
|
388
|
-
// {"path":["s","urn:entry:set-entry-1","data"],"op":"add","value":"test"}
|
|
389
|
-
// ]
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
state.s.add({ data: "test" });
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
**Path structure explained:**
|
|
396
|
-
|
|
397
|
-
- `["s", "urn:entry:set-entry-1"]` - The structural Set patch; the IRI identifies the entry
|
|
398
|
-
- `["s", "urn:entry:set-entry-1", "@id"]` - Patch for the @id property assignment
|
|
399
|
-
- `["s", "urn:entry:set-entry-1", "data"]` - Nested property patch; the IRI identifies which Set entry
|
|
400
|
-
- The synthetic ID (the IRI) is stable across mutations, allowing tracking of the same object
|
|
139
|
+
### Other Frameworks
|
|
401
140
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
```ts
|
|
405
|
-
const state = deepSignal(
|
|
406
|
-
{ users: new Set() },
|
|
407
|
-
{
|
|
408
|
-
propGenerator: ({ path, inSet }) => ({
|
|
409
|
-
syntheticId: inSet ? `urn:user:${crypto.randomUUID()}` : undefined,
|
|
410
|
-
}),
|
|
411
|
-
syntheticIdPropertyName: "@id",
|
|
412
|
-
}
|
|
413
|
-
);
|
|
414
|
-
const user = { name: "Ada", age: 30 };
|
|
415
|
-
state.users.add(user); // Gets @id, e.g., "urn:user:550e8400-..."
|
|
416
|
-
|
|
417
|
-
watch(state, ({ patches }) => {
|
|
418
|
-
console.log(JSON.stringify(patches));
|
|
419
|
-
// [{"path":["users","urn:user:550e8400-...","age"],"op":"add","value":31}]
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
// Later mutation: synthetic ID identifies which Set entry changed
|
|
423
|
-
user.age = 31;
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
The path `["users", "urn:user:550e8400-...", "age"]` shows:
|
|
427
|
-
|
|
428
|
-
1. `users` - the Set container
|
|
429
|
-
2. `urn:user:550e8400-...` - the IRI identifying which object in the Set
|
|
430
|
-
3. `age` - the property being mutated
|
|
431
|
-
|
|
432
|
-
This structure enables precise tracking of nested changes within Set entries, critical for syncing state changes or implementing undo/redo.
|
|
433
|
-
|
|
434
|
-
## Shallow
|
|
435
|
-
|
|
436
|
-
Skip deep proxying of a subtree (only reference replacement tracked):
|
|
437
|
-
|
|
438
|
-
```ts
|
|
439
|
-
import { shallow } from "alien-deepsignals";
|
|
440
|
-
state.config = shallow({ huge: { blob: true } });
|
|
441
|
-
```
|
|
141
|
+
Integrating new frontend frameworks is fairly easy. Get in touch if you are interested.
|
|
442
142
|
|
|
443
143
|
## License
|
|
444
144
|
|
package/dist/core.d.ts
CHANGED
|
@@ -1,16 +1,81 @@
|
|
|
1
|
-
|
|
1
|
+
import { computed as alienComputed, signal as alienSignal_, effect as alienEffect } from "alien-signals";
|
|
2
2
|
/**
|
|
3
3
|
* Execute multiple signal writes in a single batched update frame.
|
|
4
4
|
* All downstream computed/effect re-evaluations are deferred until the function exits.
|
|
5
5
|
*
|
|
6
|
-
* IMPORTANT: The callback
|
|
6
|
+
* IMPORTANT: The callback must be synchronous. If it returns a Promise the batch will
|
|
7
7
|
* still end immediately after scheduling, possibly causing mid-async flushes.
|
|
8
8
|
*
|
|
9
9
|
* @example
|
|
10
|
+
* ```ts
|
|
10
11
|
* batch(() => {
|
|
11
12
|
* count(count() + 1);
|
|
12
13
|
* other(other() + 2);
|
|
13
14
|
* }); // effects observing both run only once
|
|
15
|
+
* ```
|
|
14
16
|
*/
|
|
15
17
|
export declare function batch<T>(fn: () => T): T;
|
|
18
|
+
/**
|
|
19
|
+
* Re-export of alien-signals computed function.
|
|
20
|
+
*
|
|
21
|
+
* Use the `computed()` function to create lazy derived signals that automatically
|
|
22
|
+
* track their dependencies and recompute only when needed.
|
|
23
|
+
*
|
|
24
|
+
* Key features:
|
|
25
|
+
* - **Lazy evaluation**: The computation runs only when you actually read the computed value.
|
|
26
|
+
* If you never access `fullName()`, the concatenation never happens—no wasted CPU cycles.
|
|
27
|
+
* - **Automatic caching**: Once computed, the result is cached until a dependency changes.
|
|
28
|
+
* Multiple reads return the cached value without re-running the getter.
|
|
29
|
+
* - **Fine-grained reactivity**: Only recomputes when its tracked dependencies change.
|
|
30
|
+
* Unrelated state mutations don't trigger unnecessary recalculation.
|
|
31
|
+
* - **Composable**: Computed signals can depend on other computed signals,
|
|
32
|
+
* forming efficient dependency chains.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { computed } from "@ng-org/alien-deepsignals";
|
|
37
|
+
*
|
|
38
|
+
* const state = deepSignal({
|
|
39
|
+
* firstName: "Ada",
|
|
40
|
+
* lastName: "Lovelace",
|
|
41
|
+
* items: [1, 2, 3],
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* // Create a computed signal that derives from reactive state
|
|
45
|
+
* const fullName = computed(() => `${state.firstName} ${state.lastName}`);
|
|
46
|
+
*
|
|
47
|
+
* console.log(fullName()); // "Ada Lovelace" - computes on first access
|
|
48
|
+
*
|
|
49
|
+
* state.firstName = "Grace";
|
|
50
|
+
* console.log(fullName()); // "Grace Lovelace" - recomputes automatically
|
|
51
|
+
*
|
|
52
|
+
* // Expensive computation only runs when accessed and dependencies change
|
|
53
|
+
* const expensiveResult = computed(() => {
|
|
54
|
+
* console.log("Computing...");
|
|
55
|
+
* return state.items.reduce((sum, n) => sum + n * n, 0);
|
|
56
|
+
* });
|
|
57
|
+
*
|
|
58
|
+
* // No computation happens yet!
|
|
59
|
+
* state.items.push(4);
|
|
60
|
+
* // Still no computation...
|
|
61
|
+
*
|
|
62
|
+
* console.log(expensiveResult()); // "Computing..." + result
|
|
63
|
+
* console.log(expensiveResult()); // Cached, no log
|
|
64
|
+
* state.items.push(5);
|
|
65
|
+
* console.log(expensiveResult()); // "Computing..." again (dependency changed)
|
|
66
|
+
*
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare const computed: typeof alienComputed;
|
|
70
|
+
/**
|
|
71
|
+
* Re-export of alien-signals `signal` function which creates a basic signal.
|
|
72
|
+
*/
|
|
73
|
+
export declare const alienSignal: typeof alienSignal_;
|
|
74
|
+
/**
|
|
75
|
+
* Re-export of alien-signals effect function.
|
|
76
|
+
*
|
|
77
|
+
* Callback reruns on every signal modification that is used within its callback.
|
|
78
|
+
*
|
|
79
|
+
*/
|
|
80
|
+
export declare const effect: typeof alienEffect;
|
|
16
81
|
//# sourceMappingURL=core.d.ts.map
|
package/dist/core.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":"AAUA,OAAO,EAGH,QAAQ,IAAI,aAAa,EACzB,MAAM,IAAI,YAAY,EACtB,MAAM,IAAI,WAAW,EACxB,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAOvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,eAAO,MAAM,QAAQ,sBAAgB,CAAC;AAEtC;;GAEG;AACH,eAAO,MAAM,WAAW,qBAAe,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,MAAM,oBAAc,CAAC"}
|
package/dist/core.js
CHANGED
|
@@ -9,33 +9,93 @@
|
|
|
9
9
|
// according to those terms.
|
|
10
10
|
// SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.
|
|
12
|
+
exports.effect = exports.alienSignal = exports.computed = void 0;
|
|
13
13
|
exports.batch = batch;
|
|
14
|
-
|
|
15
|
-
var alien_signals_1 = require("alien-signals");
|
|
16
|
-
Object.defineProperty(exports, "alienSignal", { enumerable: true, get: function () { return alien_signals_1.signal; } });
|
|
17
|
-
Object.defineProperty(exports, "alienComputed", { enumerable: true, get: function () { return alien_signals_1.computed; } });
|
|
18
|
-
Object.defineProperty(exports, "alienEffect", { enumerable: true, get: function () { return alien_signals_1.effect; } });
|
|
19
|
-
const alien_signals_2 = require("alien-signals");
|
|
14
|
+
const alien_signals_1 = require("alien-signals");
|
|
20
15
|
/**
|
|
21
16
|
* Execute multiple signal writes in a single batched update frame.
|
|
22
17
|
* All downstream computed/effect re-evaluations are deferred until the function exits.
|
|
23
18
|
*
|
|
24
|
-
* IMPORTANT: The callback
|
|
19
|
+
* IMPORTANT: The callback must be synchronous. If it returns a Promise the batch will
|
|
25
20
|
* still end immediately after scheduling, possibly causing mid-async flushes.
|
|
26
21
|
*
|
|
27
22
|
* @example
|
|
23
|
+
* ```ts
|
|
28
24
|
* batch(() => {
|
|
29
25
|
* count(count() + 1);
|
|
30
26
|
* other(other() + 2);
|
|
31
27
|
* }); // effects observing both run only once
|
|
28
|
+
* ```
|
|
32
29
|
*/
|
|
33
30
|
function batch(fn) {
|
|
34
|
-
(0,
|
|
31
|
+
(0, alien_signals_1.startBatch)();
|
|
35
32
|
try {
|
|
36
33
|
return fn();
|
|
37
34
|
}
|
|
38
35
|
finally {
|
|
39
|
-
(0,
|
|
36
|
+
(0, alien_signals_1.endBatch)();
|
|
40
37
|
}
|
|
41
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Re-export of alien-signals computed function.
|
|
41
|
+
*
|
|
42
|
+
* Use the `computed()` function to create lazy derived signals that automatically
|
|
43
|
+
* track their dependencies and recompute only when needed.
|
|
44
|
+
*
|
|
45
|
+
* Key features:
|
|
46
|
+
* - **Lazy evaluation**: The computation runs only when you actually read the computed value.
|
|
47
|
+
* If you never access `fullName()`, the concatenation never happens—no wasted CPU cycles.
|
|
48
|
+
* - **Automatic caching**: Once computed, the result is cached until a dependency changes.
|
|
49
|
+
* Multiple reads return the cached value without re-running the getter.
|
|
50
|
+
* - **Fine-grained reactivity**: Only recomputes when its tracked dependencies change.
|
|
51
|
+
* Unrelated state mutations don't trigger unnecessary recalculation.
|
|
52
|
+
* - **Composable**: Computed signals can depend on other computed signals,
|
|
53
|
+
* forming efficient dependency chains.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* import { computed } from "@ng-org/alien-deepsignals";
|
|
58
|
+
*
|
|
59
|
+
* const state = deepSignal({
|
|
60
|
+
* firstName: "Ada",
|
|
61
|
+
* lastName: "Lovelace",
|
|
62
|
+
* items: [1, 2, 3],
|
|
63
|
+
* });
|
|
64
|
+
*
|
|
65
|
+
* // Create a computed signal that derives from reactive state
|
|
66
|
+
* const fullName = computed(() => `${state.firstName} ${state.lastName}`);
|
|
67
|
+
*
|
|
68
|
+
* console.log(fullName()); // "Ada Lovelace" - computes on first access
|
|
69
|
+
*
|
|
70
|
+
* state.firstName = "Grace";
|
|
71
|
+
* console.log(fullName()); // "Grace Lovelace" - recomputes automatically
|
|
72
|
+
*
|
|
73
|
+
* // Expensive computation only runs when accessed and dependencies change
|
|
74
|
+
* const expensiveResult = computed(() => {
|
|
75
|
+
* console.log("Computing...");
|
|
76
|
+
* return state.items.reduce((sum, n) => sum + n * n, 0);
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* // No computation happens yet!
|
|
80
|
+
* state.items.push(4);
|
|
81
|
+
* // Still no computation...
|
|
82
|
+
*
|
|
83
|
+
* console.log(expensiveResult()); // "Computing..." + result
|
|
84
|
+
* console.log(expensiveResult()); // Cached, no log
|
|
85
|
+
* state.items.push(5);
|
|
86
|
+
* console.log(expensiveResult()); // "Computing..." again (dependency changed)
|
|
87
|
+
*
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
exports.computed = alien_signals_1.computed;
|
|
91
|
+
/**
|
|
92
|
+
* Re-export of alien-signals `signal` function which creates a basic signal.
|
|
93
|
+
*/
|
|
94
|
+
exports.alienSignal = alien_signals_1.signal;
|
|
95
|
+
/**
|
|
96
|
+
* Re-export of alien-signals effect function.
|
|
97
|
+
*
|
|
98
|
+
* Callback reruns on every signal modification that is used within its callback.
|
|
99
|
+
*
|
|
100
|
+
*/
|
|
101
|
+
exports.effect = alien_signals_1.effect;
|
package/dist/deepSignal.d.ts
CHANGED
|
@@ -2,9 +2,14 @@ import { DeepPatchJITSubscriber, DeepPatchSubscriber, DeepSignal, DeepSignalOpti
|
|
|
2
2
|
/** Runtime guard that checks whether a value is a deepSignal proxy. */
|
|
3
3
|
export declare function isDeepSignal(value: unknown): value is DeepSignal<any>;
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* MAIN ENTRY POINT to create a deep reactive proxy for objects, arrays or Sets.
|
|
6
|
+
*
|
|
7
|
+
* If input is a deepSignal already and options are provided,
|
|
8
|
+
* the added subscriberFactories are joined with the existing ones
|
|
9
|
+
* and `replaceProxiesInBranchOnChange` is or-ed with the current value.
|
|
10
|
+
*
|
|
11
|
+
*
|
|
12
|
+
* @throws if provided with unsupported input types.
|
|
8
13
|
*/
|
|
9
14
|
export declare function deepSignal<T extends object>(input: T, options?: DeepSignalOptions): DeepSignal<T>;
|
|
10
15
|
/**
|
|
@@ -17,7 +22,14 @@ export declare function subscribeDeepMutations(root: object | symbol, cb: DeepPa
|
|
|
17
22
|
export declare function getDeepSignalRootId(value: any): symbol | undefined;
|
|
18
23
|
/** Retrieve the current patch version for a deepSignal root (if tracked). */
|
|
19
24
|
export declare function getDeepSignalVersion(root: object | symbol): number | undefined;
|
|
20
|
-
/** Mark an object so deepSignal skips proxying it (shallow boundary).
|
|
25
|
+
/** Mark an object so deepSignal skips proxying it (shallow boundary).
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* import { shallow } from "alien-deepsignals";
|
|
30
|
+
* state.config = shallow({ huge: { blob: true } });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
21
33
|
export declare function shallow<T extends object>(obj: T): T;
|
|
22
34
|
/** Force a specific synthetic ID to be used for a Set entry prior to insertion. */
|
|
23
35
|
export declare function setSetEntrySyntheticId(obj: object, id: string | number): void;
|
package/dist/deepSignal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deepSignal.d.ts","sourceRoot":"","sources":["../src/deepSignal.ts"],"names":[],"mappings":"AAWA,OAAO,EAGH,sBAAsB,EACtB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,
|
|
1
|
+
{"version":3,"file":"deepSignal.d.ts","sourceRoot":"","sources":["../src/deepSignal.ts"],"names":[],"mappings":"AAWA,OAAO,EAGH,sBAAsB,EACtB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EAMpB,MAAM,SAAS,CAAC;AAywCjB,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU,CAAC,GAAG,CAAC,CAErE;AAED;;;;;;;;;GASG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,MAAM,EACvC,KAAK,EAAE,CAAC,EACR,OAAO,CAAC,EAAE,iBAAiB,GAC5B,UAAU,CAAC,CAAC,CAAC,CAwCf;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAClC,IAAI,EAAE,MAAM,GAAG,MAAM,EACrB,EAAE,EAAE,mBAAmB,GAAG,sBAAsB,EAChD,gBAAgB,GAAE,OAAe,GAClC,MAAM,IAAI,CAoBZ;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAElE;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,CAChC,IAAI,EAAE,MAAM,GAAG,MAAM,GACtB,MAAM,GAAG,SAAS,CAIpB;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAGnD;AAED,mFAAmF;AACnF,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,QAItE;AAED,2FAA2F;AAC3F,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,CAAC,CAa1E;AAED,oDAAoD;AACpD,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,OAEhE"}
|
package/dist/deepSignal.js
CHANGED
|
@@ -146,7 +146,7 @@ function ensureProxiedGetter(signals, target, key, receiver) {
|
|
|
146
146
|
typeof Object.getOwnPropertyDescriptor(target, key)?.get === "function" // If we have a getter?
|
|
147
147
|
) {
|
|
148
148
|
signals.set(key, {
|
|
149
|
-
alienSignal: (0, core_1.
|
|
149
|
+
alienSignal: (0, core_1.computed)(() => Reflect.get(target, key, receiver)),
|
|
150
150
|
externalSubscribers: new Map(),
|
|
151
151
|
});
|
|
152
152
|
}
|
|
@@ -382,8 +382,8 @@ function ensureSetInfo(meta) {
|
|
|
382
382
|
* Assign (or reuse) a synthetic identifier for a Set entry, respecting user options:
|
|
383
383
|
* - Use user-provided propGenerator for synthetic ids and add add returned extra properties
|
|
384
384
|
* - Check if the object has a property of `syntheticIdPropertyName` (default `@id`)
|
|
385
|
-
* - Use a blank node
|
|
386
|
-
* - Add object and
|
|
385
|
+
* - Use a blank node ID as a fallback.
|
|
386
|
+
* - Add object and ID to `idForObject` and `objectForId` maps.
|
|
387
387
|
*/
|
|
388
388
|
function assignSyntheticId(meta, entry, path, inSet) {
|
|
389
389
|
const rawEntry = entry?.[RAW_KEY] ?? entry;
|
|
@@ -1013,15 +1013,19 @@ function isDeepSignal(value) {
|
|
|
1013
1013
|
return !!value?.[RAW_KEY];
|
|
1014
1014
|
}
|
|
1015
1015
|
/**
|
|
1016
|
-
*
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1016
|
+
* MAIN ENTRY POINT to create a deep reactive proxy for objects, arrays or Sets.
|
|
1017
|
+
*
|
|
1018
|
+
* If input is a deepSignal already and options are provided,
|
|
1019
|
+
* the added subscriberFactories are joined with the existing ones
|
|
1020
|
+
* and `replaceProxiesInBranchOnChange` is or-ed with the current value.
|
|
1021
|
+
*
|
|
1022
|
+
*
|
|
1023
|
+
* @throws if provided with unsupported input types.
|
|
1019
1024
|
*/
|
|
1020
1025
|
function deepSignal(input, options) {
|
|
1021
1026
|
// Is the input already a signal?
|
|
1022
1027
|
if (isDeepSignal(input)) {
|
|
1023
1028
|
// Add possibly new external subscribers to existing ones.
|
|
1024
|
-
// TODO: Document this behavior.
|
|
1025
1029
|
const meta = rawToMeta.get(input[RAW_KEY]);
|
|
1026
1030
|
meta.options.subscriberFactories =
|
|
1027
1031
|
meta.options.subscriberFactories.union(options?.subscriberFactories ?? new Set());
|
|
@@ -1088,7 +1092,14 @@ function getDeepSignalVersion(root) {
|
|
|
1088
1092
|
return undefined;
|
|
1089
1093
|
return rootStates.get(rootId)?.version;
|
|
1090
1094
|
}
|
|
1091
|
-
/** Mark an object so deepSignal skips proxying it (shallow boundary).
|
|
1095
|
+
/** Mark an object so deepSignal skips proxying it (shallow boundary).
|
|
1096
|
+
*
|
|
1097
|
+
* @example
|
|
1098
|
+
* ```ts
|
|
1099
|
+
* import { shallow } from "alien-deepsignals";
|
|
1100
|
+
* state.config = shallow({ huge: { blob: true } });
|
|
1101
|
+
* ```
|
|
1102
|
+
*/
|
|
1092
1103
|
function shallow(obj) {
|
|
1093
1104
|
ignored.add(obj);
|
|
1094
1105
|
return obj;
|
|
@@ -6,7 +6,7 @@ import { DeepSignalOptions } from "../..";
|
|
|
6
6
|
* is rerendered as well.
|
|
7
7
|
*
|
|
8
8
|
* @param object The object that should become reactive
|
|
9
|
-
* @param
|
|
9
|
+
* @param options When the object is not a deepSignal already, options passed to {@link deepSignal}.
|
|
10
10
|
* @returns The deepSignal object of the object param. On every change, the returned object will change (a new no-op proxy is created) around the deepSignal object.
|
|
11
11
|
*/
|
|
12
12
|
declare const useSignal: <T extends object>(object: T, deepSignalOptions?: DeepSignalOptions) => import("../..").DeepSignal<T>;
|
|
@@ -19,7 +19,7 @@ const __1 = require("../..");
|
|
|
19
19
|
* is rerendered as well.
|
|
20
20
|
*
|
|
21
21
|
* @param object The object that should become reactive
|
|
22
|
-
* @param
|
|
22
|
+
* @param options When the object is not a deepSignal already, options passed to {@link deepSignal}.
|
|
23
23
|
* @returns The deepSignal object of the object param. On every change, the returned object will change (a new no-op proxy is created) around the deepSignal object.
|
|
24
24
|
*/
|
|
25
25
|
const useSignal = (object, deepSignalOptions) => {
|
|
@@ -7,9 +7,9 @@ import { DeepSignalOptions, DeepSignal } from "../../index";
|
|
|
7
7
|
* is rerendered as well.
|
|
8
8
|
*
|
|
9
9
|
* @param object The object that should become reactive
|
|
10
|
-
* @param
|
|
10
|
+
* @param options Options passed to {@link deepSignal}.
|
|
11
11
|
* @returns A rune for using the deepSignal object in svelte.
|
|
12
12
|
*/
|
|
13
|
-
export declare function useDeepSignal<T extends object>(object: T, options?: DeepSignalOptions): T extends DeepSignal<any> ? T : DeepSignal<T
|
|
13
|
+
export declare function useDeepSignal<T extends object>(object: T, options?: DeepSignalOptions): T extends DeepSignal<any> ? T : DeepSignal<T>;
|
|
14
14
|
export default useDeepSignal;
|
|
15
15
|
//# sourceMappingURL=useDeepSignal.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDeepSignal.svelte.d.ts","sourceRoot":"","sources":["../../../src/hooks/svelte/useDeepSignal.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,iBAAiB,EAAc,UAAU,EAAE,MAAM,aAAa,CAAC;AAExE;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC1C,MAAM,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,iBAAiB,GAab,CAAC,SAAS,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"useDeepSignal.svelte.d.ts","sourceRoot":"","sources":["../../../src/hooks/svelte/useDeepSignal.svelte.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,iBAAiB,EAAc,UAAU,EAAE,MAAM,aAAa,CAAC;AAExE;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC1C,MAAM,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,iBAAiB,GAab,CAAC,SAAS,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAC9D;AAyBD,eAAe,aAAa,CAAC"}
|
|
@@ -20,7 +20,7 @@ const index_1 = require("../../index");
|
|
|
20
20
|
* is rerendered as well.
|
|
21
21
|
*
|
|
22
22
|
* @param object The object that should become reactive
|
|
23
|
-
* @param
|
|
23
|
+
* @param options Options passed to {@link deepSignal}.
|
|
24
24
|
* @returns A rune for using the deepSignal object in svelte.
|
|
25
25
|
*/
|
|
26
26
|
function useDeepSignal(object, options) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type Readable } from "svelte/store";
|
|
2
|
-
import { DeepSignalOptions,
|
|
2
|
+
import { DeepSignalOptions, UnwrapDeepSignal } from "../../index";
|
|
3
3
|
/** Base result contract for a deepSignal-backed Svelte integration. */
|
|
4
4
|
export interface UseDeepSignalResult<T> extends Readable<T> {
|
|
5
5
|
/** Derive a nested selection; re-runs when the underlying tree version increments. */
|
|
@@ -12,16 +12,16 @@ export interface UseDeepSignalResult<T> extends Readable<T> {
|
|
|
12
12
|
update(updater: (current: T) => T | void): void;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
* Create a
|
|
15
|
+
* Create a store from a deepSignal object (creates one if it is just a regular object).
|
|
16
16
|
*
|
|
17
17
|
* Modifications to the returned deepSignal object cause an immediate rerender.
|
|
18
18
|
* If modifications of the object are made from somewhere else, the component
|
|
19
19
|
* is rerendered as well.
|
|
20
20
|
*
|
|
21
21
|
* @param object The object that should become reactive
|
|
22
|
-
* @param
|
|
23
|
-
* @returns A
|
|
22
|
+
* @param options Options passed to {@link deepSignal}.
|
|
23
|
+
* @returns A store for using the deepSignal object in svelte.
|
|
24
24
|
*/
|
|
25
|
-
export declare function useDeepSignal<T extends object>(object: T | Promise<T>, options?: DeepSignalOptions): UseDeepSignalResult<
|
|
25
|
+
export declare function useDeepSignal<T extends object>(object: T | Promise<T>, options?: DeepSignalOptions): UseDeepSignalResult<UnwrapDeepSignal<T>>;
|
|
26
26
|
export default useDeepSignal;
|
|
27
27
|
//# sourceMappingURL=useDeepSignal.svelte.d.ts.map
|
|
@@ -15,15 +15,15 @@ const svelte_1 = require("svelte");
|
|
|
15
15
|
const index_1 = require("../../index");
|
|
16
16
|
const deepSignal_1 = require("../../deepSignal");
|
|
17
17
|
/**
|
|
18
|
-
* Create a
|
|
18
|
+
* Create a store from a deepSignal object (creates one if it is just a regular object).
|
|
19
19
|
*
|
|
20
20
|
* Modifications to the returned deepSignal object cause an immediate rerender.
|
|
21
21
|
* If modifications of the object are made from somewhere else, the component
|
|
22
22
|
* is rerendered as well.
|
|
23
23
|
*
|
|
24
24
|
* @param object The object that should become reactive
|
|
25
|
-
* @param
|
|
26
|
-
* @returns A
|
|
25
|
+
* @param options Options passed to {@link deepSignal}.
|
|
26
|
+
* @returns A store for using the deepSignal object in svelte.
|
|
27
27
|
*/
|
|
28
28
|
function useDeepSignal(object, options) {
|
|
29
29
|
const version = (0, store_1.writable)(-1);
|
|
@@ -7,7 +7,7 @@ import { DeepSignal, DeepSignalOptions } from "../../";
|
|
|
7
7
|
* is rerendered as well.
|
|
8
8
|
*
|
|
9
9
|
* @param object The object that should become reactive (can be a ref or getter)
|
|
10
|
-
* @param options
|
|
10
|
+
* @param options Options passed to {@link deepSignal}.
|
|
11
11
|
* @returns The deepSignal object of the object param.
|
|
12
12
|
*
|
|
13
13
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDeepSignal.d.ts","sourceRoot":"","sources":["../../../src/hooks/vue/useDeepSignal.ts"],"names":[],"mappings":"AAUA,OAAO,
|
|
1
|
+
{"version":3,"file":"useDeepSignal.d.ts","sourceRoot":"","sources":["../../../src/hooks/vue/useDeepSignal.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,gBAAgB,EAAsB,MAAM,KAAK,CAAC;AAEhE,OAAO,EAAE,UAAU,EAAc,iBAAiB,EAAS,MAAM,QAAQ,CAAC;AAE1E;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,MAAM,EAC1C,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,EAC3B,OAAO,CAAC,EAAE,iBAAiB,GAC5B,UAAU,CAAC,CAAC,CAAC,CAaf;AAkBD,eAAe,aAAa,CAAC"}
|
|
@@ -19,7 +19,7 @@ const __1 = require("../../");
|
|
|
19
19
|
* is rerendered as well.
|
|
20
20
|
*
|
|
21
21
|
* @param object The object that should become reactive (can be a ref or getter)
|
|
22
|
-
* @param options
|
|
22
|
+
* @param options Options passed to {@link deepSignal}.
|
|
23
23
|
* @returns The deepSignal object of the object param.
|
|
24
24
|
*
|
|
25
25
|
*/
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EACT,UAAU,EACV,MAAM,EACN,YAAY,EACZ,OAAO,EACP,sBAAsB,GACzB,MAAM,cAAc,CAAC;AACtB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,SAAS,EACT,UAAU,EACV,MAAM,EACN,YAAY,EACZ,OAAO,EACP,sBAAsB,GACzB,MAAM,cAAc,CAAC;AACtB,cAAc,QAAQ,CAAC;AACvB,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -24,5 +24,4 @@ Object.defineProperty(exports, "shallow", { enumerable: true, get: function () {
|
|
|
24
24
|
Object.defineProperty(exports, "subscribeDeepMutations", { enumerable: true, get: function () { return deepSignal_1.subscribeDeepMutations; } });
|
|
25
25
|
__exportStar(require("./core"), exports);
|
|
26
26
|
__exportStar(require("./watch"), exports);
|
|
27
|
-
__exportStar(require("./effect"), exports);
|
|
28
27
|
__exportStar(require("./types"), exports);
|
|
@@ -15,12 +15,12 @@ exports.buildInitialState = buildInitialState;
|
|
|
15
15
|
const buildDefaultObjectSetEntries = () => {
|
|
16
16
|
const baseEntries = [
|
|
17
17
|
{
|
|
18
|
-
"@id": "
|
|
18
|
+
"@id": "did:ng:x:alpha",
|
|
19
19
|
label: "Alpha",
|
|
20
20
|
count: 1,
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
|
-
"@id": "
|
|
23
|
+
"@id": "did:ng:x:beta",
|
|
24
24
|
label: "Beta",
|
|
25
25
|
count: 3,
|
|
26
26
|
},
|
|
@@ -28,7 +28,7 @@ const buildDefaultObjectSetEntries = () => {
|
|
|
28
28
|
const extraEntries = Array.from({ length: 2 }, (_, index) => {
|
|
29
29
|
const idNumber = (index + 1).toString().padStart(3, "0");
|
|
30
30
|
return {
|
|
31
|
-
"@id": `
|
|
31
|
+
"@id": `did:ng:x:item-${idNumber}`,
|
|
32
32
|
label: `Item ${idNumber}`,
|
|
33
33
|
count: 5 + index,
|
|
34
34
|
};
|
|
@@ -12,7 +12,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
const vitest_1 = require("vitest");
|
|
13
13
|
const __1 = require("../../");
|
|
14
14
|
const watch_1 = require("../../watch");
|
|
15
|
-
const effect_1 = require("../../effect");
|
|
16
15
|
const deepSignal_1 = require("../../deepSignal");
|
|
17
16
|
(0, vitest_1.describe)("watch", () => {
|
|
18
17
|
(0, vitest_1.it)("watch immediate", () => {
|
|
@@ -84,15 +83,6 @@ const deepSignal_1 = require("../../deepSignal");
|
|
|
84
83
|
(0, vitest_1.expect)(versions.length).toBe(1);
|
|
85
84
|
(0, vitest_1.expect)(versions[0]).toBeGreaterThan(0);
|
|
86
85
|
});
|
|
87
|
-
(0, vitest_1.it)("effect runs and cleans up", () => {
|
|
88
|
-
const calls = [];
|
|
89
|
-
const dispose = (0, effect_1.effect)((registerCleanup) => {
|
|
90
|
-
calls.push("run");
|
|
91
|
-
registerCleanup?.(() => calls.push("cleanup"));
|
|
92
|
-
});
|
|
93
|
-
dispose();
|
|
94
|
-
(0, vitest_1.expect)(calls).toEqual(["run", "cleanup"]);
|
|
95
|
-
});
|
|
96
86
|
});
|
|
97
87
|
(0, vitest_1.describe)("watch (patch mode)", () => {
|
|
98
88
|
(0, vitest_1.it)("emits set patches with correct paths and batching", async () => {
|
package/dist/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { computed, alienSignal } from "./core";
|
|
2
2
|
/** Deep mutation emitted from a deepSignal root. */
|
|
3
3
|
export type DeepPatch = {
|
|
4
4
|
path: (string | number)[];
|
|
@@ -16,20 +16,29 @@ export interface DeepPatchBatch {
|
|
|
16
16
|
version: number;
|
|
17
17
|
patches: DeepPatch[];
|
|
18
18
|
}
|
|
19
|
-
/** Batched patch payload for justInTime listeners. */
|
|
19
|
+
/** @ignore Batched patch payload for justInTime listeners. */
|
|
20
20
|
export interface DeepPatchJITBatch {
|
|
21
21
|
patches: DeepPatch[];
|
|
22
22
|
}
|
|
23
|
+
/** @ignore */
|
|
23
24
|
export type DeepPatchSubscriber = (batch: DeepPatchBatch) => void;
|
|
25
|
+
/** @ignore */
|
|
24
26
|
export type DeepPatchJITSubscriber = (batch: DeepPatchJITBatch) => void;
|
|
25
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Options to pass to {@link deepSignal}
|
|
29
|
+
* @internal
|
|
30
|
+
*/
|
|
26
31
|
export interface DeepSignalOptions {
|
|
27
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* An optional function that is called when new objects are attached and
|
|
34
|
+
* that may return additional properties to be attached.
|
|
35
|
+
*/
|
|
28
36
|
propGenerator?: DeepSignalPropGenFn;
|
|
29
37
|
/**
|
|
30
38
|
* The property name which should be used as an object identifier in sets.
|
|
31
39
|
* You will see it when patches are generated with a path to an object in a set.
|
|
32
40
|
* The `syntheticId` will be a patch element then.
|
|
41
|
+
* Objects with existing properties matching `syntheticIdPropertyName` keep their values (not overwritten).
|
|
33
42
|
*/
|
|
34
43
|
syntheticIdPropertyName?: string;
|
|
35
44
|
/**
|
|
@@ -50,14 +59,49 @@ export type ExternalSubscriberFactory<T = any> = () => {
|
|
|
50
59
|
onGet: () => void;
|
|
51
60
|
onSet: (newVal: T) => void;
|
|
52
61
|
};
|
|
62
|
+
/**
|
|
63
|
+
* @internal
|
|
64
|
+
*
|
|
65
|
+
* The `propGenerator` function is called when a new object is added to the deep signal tree.
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* let counter = 0;
|
|
69
|
+
* const state = deepSignal(
|
|
70
|
+
* { items: new Set() },
|
|
71
|
+
* {
|
|
72
|
+
* propGenerator: ({ path, inSet, object }) => ({
|
|
73
|
+
* syntheticId: inSet
|
|
74
|
+
* ? `urn:item:${++counter}`
|
|
75
|
+
* : `urn:obj:${path.join("-")}`,
|
|
76
|
+
* extraProps: { createdAt: new Date().toISOString() },
|
|
77
|
+
* }),
|
|
78
|
+
* syntheticIdPropertyName: "@id",
|
|
79
|
+
* }
|
|
80
|
+
* );
|
|
81
|
+
*
|
|
82
|
+
* state.items.add({ name: "Item 1" });
|
|
83
|
+
* // Attaches `{ name: "Item 1", `@id`: "urn:item:1", createdAt: <current date>`
|
|
84
|
+
*
|
|
85
|
+
* state.foo = {bar: 42};
|
|
86
|
+
* // Attaches `{bar: 42, "@id": "urn:obj:foo", createdAt: <current date>}`
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
53
89
|
export type DeepSignalPropGenFn = (props: {
|
|
90
|
+
/**
|
|
91
|
+
* The path of the newly added object.
|
|
92
|
+
*/
|
|
54
93
|
path: (string | number)[];
|
|
94
|
+
/** Whether the object is being added to a Set (true) or not (false) */
|
|
55
95
|
inSet: boolean;
|
|
96
|
+
/** The newly added object itself */
|
|
56
97
|
object: any;
|
|
57
98
|
}) => {
|
|
99
|
+
/** A custom identifier for the object (used in Set entry paths and optionally as a property). */
|
|
58
100
|
syntheticId?: string | number;
|
|
101
|
+
/** Additional properties to be added to the object (overwriting existing ones). */
|
|
59
102
|
extraProps?: Record<string, unknown>;
|
|
60
103
|
};
|
|
104
|
+
/**@ignore*/
|
|
61
105
|
export interface ProxyMeta {
|
|
62
106
|
raw: object;
|
|
63
107
|
parent?: ProxyMeta;
|
|
@@ -67,10 +111,12 @@ export interface ProxyMeta {
|
|
|
67
111
|
options: DeepSignalOptions;
|
|
68
112
|
setInfo?: SetMeta;
|
|
69
113
|
}
|
|
114
|
+
/** @hidden */
|
|
70
115
|
export interface SetMeta {
|
|
71
116
|
idForObject: WeakMap<object, string>;
|
|
72
117
|
objectForId: Map<string, object>;
|
|
73
118
|
}
|
|
119
|
+
/**@ignore*/
|
|
74
120
|
export interface RootState {
|
|
75
121
|
options?: DeepSignalOptions;
|
|
76
122
|
version: number;
|
|
@@ -78,12 +124,15 @@ export interface RootState {
|
|
|
78
124
|
listeners: Set<DeepPatchSubscriber>;
|
|
79
125
|
pendingPatches: DeepPatch[];
|
|
80
126
|
}
|
|
127
|
+
/** @ignore */
|
|
81
128
|
export type WritableSignal<T = any> = ReturnType<typeof alienSignal<T>>;
|
|
82
|
-
export type ComputedSignal<T = any> = ReturnType<typeof
|
|
129
|
+
export type ComputedSignal<T = any> = ReturnType<typeof computed<T>>;
|
|
83
130
|
export type SignalLike<T = any> = WritableSignal<T> | ComputedSignal<T>;
|
|
84
|
-
/** Raw and meta key. */
|
|
131
|
+
/** @ignore Raw and meta key. */
|
|
85
132
|
export type DeepSignalObjectProps<T> = {
|
|
133
|
+
/** The original raw object. */
|
|
86
134
|
__raw__: T;
|
|
135
|
+
/** @ignore meta information */
|
|
87
136
|
__meta__: ProxyMeta;
|
|
88
137
|
};
|
|
89
138
|
/** Utility functions for sets. */
|
|
@@ -99,13 +148,16 @@ export type DeepSignalSetProps<T> = {
|
|
|
99
148
|
/**
|
|
100
149
|
* Retrieve an object from the Set by its `@graph` and `@id`.
|
|
101
150
|
*
|
|
102
|
-
* @param graphIri - The `@graph`
|
|
151
|
+
* @param graphIri - The `@graph` NURI of the object.
|
|
103
152
|
* @param subjectIri - The `@subject` IRI of the object.
|
|
104
153
|
* @returns The proxied entry if found, undefined otherwise.
|
|
105
154
|
*/
|
|
106
155
|
getBy(graphIri: string, subjectIri: string): DeepSignal<T> | undefined;
|
|
107
156
|
};
|
|
108
|
-
/**
|
|
157
|
+
/**
|
|
158
|
+
* Type alias for `DeepSignal<Set<T>>` and reactive Set wrapper that accepts raw or proxied entries.
|
|
159
|
+
* Additionally it is decorated with {@link DeepSignalSetProps}.
|
|
160
|
+
*/
|
|
109
161
|
export interface DeepSignalSet<T> extends Set<DeepSignal<T>>, DeepSignalObjectProps<Set<T>>, SetIterator<DeepSignal<T>>, DeepSignalSetProps<T> {
|
|
110
162
|
add(value: T | DeepSignal<T>): this;
|
|
111
163
|
delete(value: T | DeepSignal<T>): boolean;
|
|
@@ -114,16 +166,15 @@ export interface DeepSignalSet<T> extends Set<DeepSignal<T>>, DeepSignalObjectPr
|
|
|
114
166
|
forEach(callbackfn: (value: DeepSignal<T>, index: number) => void, thisArg?: any): void;
|
|
115
167
|
}
|
|
116
168
|
/**
|
|
117
|
-
* The object returned by the @
|
|
118
|
-
* It is decorated with utility functions for sets
|
|
119
|
-
* `__raw__` prop to get the underlying non-reactive object
|
|
120
|
-
* and `__meta__` prop, to get the internal metadata.
|
|
169
|
+
* The object returned by the {@link deepSignal} function.
|
|
170
|
+
* It is decorated with utility functions for sets, see {@link DeepSignalSetProps}
|
|
171
|
+
* and a `__raw__` prop to get the underlying non-reactive object.
|
|
121
172
|
*/
|
|
122
173
|
export type DeepSignal<T> = T extends Function ? T : T extends string | number | boolean ? T : T extends DeepSignalObjectProps<any> | DeepSignalObjectProps<any>[] ? T : T extends Array<infer I> ? DeepSignal<I>[] : T extends Set<infer S> ? DeepSignalSet<S> : T extends object ? DeepSignalObject<T> : T;
|
|
123
174
|
export type DeepSignalObject<T extends object> = {
|
|
124
175
|
[K in keyof T]: DeepSignal<T[K]>;
|
|
125
176
|
};
|
|
126
|
-
export type
|
|
177
|
+
export type UnwrapDeepSignal<T> = T extends DeepSignal<infer S> ? S : T;
|
|
127
178
|
/** Union allowing a plain value or a writable signal wrapping that value. */
|
|
128
179
|
export type MaybeSignal<T = any> = T | ReturnType<typeof alienSignal>;
|
|
129
180
|
/** Union allowing value, writable signal, computed signal or plain getter function. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/watch.d.ts
CHANGED
|
@@ -25,11 +25,37 @@ export type WatchPatchCallback<T extends object> = (event: WatchPatchEvent<T>) =
|
|
|
25
25
|
/**
|
|
26
26
|
* Watch for changes to a deepSignal.
|
|
27
27
|
*
|
|
28
|
-
* Whenever a change is made, `callback` with the patches describing the change and the
|
|
28
|
+
* Whenever a change is made, `callback` is called with the patches describing the change and the new value.
|
|
29
29
|
* If you set `triggerInstantly`, the callback is called on every property change.
|
|
30
30
|
* If not, all changes are aggregated and `callback` is called in a microtask when
|
|
31
31
|
* the current task finishes, e.g. `await` is called (meaning it supports batching).
|
|
32
32
|
*
|
|
33
|
+
* When objects are added to Sets, their **synthetic ID (usually `@id`) becomes part of the patch path**. This allows patches to uniquely identify which Set entry is being mutated.
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* const state = deepSignal(
|
|
37
|
+
* { s: new Set() },
|
|
38
|
+
* { ...}
|
|
39
|
+
* );
|
|
40
|
+
*
|
|
41
|
+
* watch(state, ({ patches }) => {
|
|
42
|
+
* console.log(JSON.stringify(patches));
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* state.s.add({ data: "test" });
|
|
46
|
+
* // Will log:
|
|
47
|
+
* // [
|
|
48
|
+
* // {"path":["s","did:ng:o:123"],"op":"add","type":"object"},
|
|
49
|
+
* // {"path":["s","did:ng:o:123","@id"],"op":"add","value":"did:ng:o:123"},
|
|
50
|
+
* // {"path":["s","did:ng:o:123","data"],"op":"add","value":"test"}
|
|
51
|
+
* // ]
|
|
52
|
+
*
|
|
53
|
+
* state.s.getById("did:ng:o:123")!.data = "new value"
|
|
54
|
+
* // Will log:
|
|
55
|
+
* // [
|
|
56
|
+
* // {"path":["s","did:ng:o:123","data"],"op":"add","value":"new value"}
|
|
57
|
+
* // ]
|
|
58
|
+
* ```
|
|
33
59
|
*/
|
|
34
60
|
export declare function watch<T extends object>(source: DeepSignalSet<T> | DeepSignalObject<T> | DeepSignal<T>, callback: WatchPatchCallback<T>, options?: WatchOptions): {
|
|
35
61
|
stopListening: () => void;
|
package/dist/watch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAgBA,OAAO,EACH,SAAS,EAET,UAAU,EACV,gBAAgB,EAChB,aAAa,EAChB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAE9D,MAAM,WAAW,YAAY;IACzB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM;IAC7C,uBAAuB;IACvB,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CAC3B;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,MAAM,IAAI,CAC/C,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KACxB,IAAI,CAAC;AAEV
|
|
1
|
+
{"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../src/watch.ts"],"names":[],"mappings":"AAgBA,OAAO,EACH,SAAS,EAET,UAAU,EACV,gBAAgB,EAChB,aAAa,EAChB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAE9D,MAAM,WAAW,YAAY;IACzB,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM;IAC7C,uBAAuB;IACvB,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,sCAAsC;IACtC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC;CAC3B;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,SAAS,MAAM,IAAI,CAC/C,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KACxB,IAAI,CAAC;AAEV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,KAAK,CAAC,CAAC,SAAS,MAAM,EAClC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,EAC9D,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC/B,OAAO,GAAE,YAAiB;;;EAiE7B"}
|
package/dist/watch.js
CHANGED
|
@@ -14,11 +14,37 @@ const deepSignal_1 = require("./deepSignal");
|
|
|
14
14
|
/**
|
|
15
15
|
* Watch for changes to a deepSignal.
|
|
16
16
|
*
|
|
17
|
-
* Whenever a change is made, `callback` with the patches describing the change and the
|
|
17
|
+
* Whenever a change is made, `callback` is called with the patches describing the change and the new value.
|
|
18
18
|
* If you set `triggerInstantly`, the callback is called on every property change.
|
|
19
19
|
* If not, all changes are aggregated and `callback` is called in a microtask when
|
|
20
20
|
* the current task finishes, e.g. `await` is called (meaning it supports batching).
|
|
21
21
|
*
|
|
22
|
+
* When objects are added to Sets, their **synthetic ID (usually `@id`) becomes part of the patch path**. This allows patches to uniquely identify which Set entry is being mutated.
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* const state = deepSignal(
|
|
26
|
+
* { s: new Set() },
|
|
27
|
+
* { ...}
|
|
28
|
+
* );
|
|
29
|
+
*
|
|
30
|
+
* watch(state, ({ patches }) => {
|
|
31
|
+
* console.log(JSON.stringify(patches));
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* state.s.add({ data: "test" });
|
|
35
|
+
* // Will log:
|
|
36
|
+
* // [
|
|
37
|
+
* // {"path":["s","did:ng:o:123"],"op":"add","type":"object"},
|
|
38
|
+
* // {"path":["s","did:ng:o:123","@id"],"op":"add","value":"did:ng:o:123"},
|
|
39
|
+
* // {"path":["s","did:ng:o:123","data"],"op":"add","value":"test"}
|
|
40
|
+
* // ]
|
|
41
|
+
*
|
|
42
|
+
* state.s.getById("did:ng:o:123")!.data = "new value"
|
|
43
|
+
* // Will log:
|
|
44
|
+
* // [
|
|
45
|
+
* // {"path":["s","did:ng:o:123","data"],"op":"add","value":"new value"}
|
|
46
|
+
* // ]
|
|
47
|
+
* ```
|
|
22
48
|
*/
|
|
23
49
|
function watch(source, callback, options = {}) {
|
|
24
50
|
if (!(0, deepSignal_1.isDeepSignal)(source)) {
|