@dxos/echo-atom 0.8.4-main.9735255 → 0.8.4-main.bcb3aa67d6
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 +28 -18
- package/dist/lib/{browser → neutral}/index.mjs +46 -12
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/types/src/atom.d.ts +19 -6
- package/dist/types/src/atom.d.ts.map +1 -1
- package/dist/types/src/query-atom.d.ts +2 -2
- package/dist/types/src/query-atom.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -12
- package/src/atom.test.ts +124 -12
- package/src/atom.ts +91 -22
- package/src/batching.test.ts +14 -14
- package/src/query-atom.test.ts +137 -11
- package/src/query-atom.ts +5 -10
- package/src/reactivity.test.ts +44 -17
- package/src/ref-atom.test.ts +13 -13
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/node-esm/index.mjs +0 -179
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
package/src/query-atom.test.ts
CHANGED
|
@@ -6,9 +6,11 @@ import * as Registry from '@effect-atom/atom/Registry';
|
|
|
6
6
|
import * as Schema from 'effect/Schema';
|
|
7
7
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
8
8
|
|
|
9
|
+
import { sleep } from '@dxos/async';
|
|
9
10
|
import { Obj, type QueryResult, Type } from '@dxos/echo';
|
|
11
|
+
import { Filter, Query } from '@dxos/echo';
|
|
10
12
|
import { TestSchema } from '@dxos/echo/testing';
|
|
11
|
-
import { type EchoDatabase,
|
|
13
|
+
import { type EchoDatabase, RuntimeSchemaRegistry } from '@dxos/echo-db';
|
|
12
14
|
import { EchoTestBuilder } from '@dxos/echo-db/testing';
|
|
13
15
|
import { SpaceId } from '@dxos/keys';
|
|
14
16
|
|
|
@@ -22,7 +24,7 @@ const TestItem = Schema.Struct({
|
|
|
22
24
|
value: Schema.Number,
|
|
23
25
|
}).pipe(
|
|
24
26
|
Type.object({
|
|
25
|
-
typename: 'example.
|
|
27
|
+
typename: 'com.example.type.test-item',
|
|
26
28
|
version: '0.1.0',
|
|
27
29
|
}),
|
|
28
30
|
);
|
|
@@ -47,7 +49,7 @@ describe('AtomQuery', () => {
|
|
|
47
49
|
test('creates atom with initial results', async () => {
|
|
48
50
|
db.add(Obj.make(TestItem, { name: 'Object 1', value: 100 }));
|
|
49
51
|
db.add(Obj.make(TestItem, { name: 'Object 2', value: 100 }));
|
|
50
|
-
await db.flush(
|
|
52
|
+
await db.flush();
|
|
51
53
|
|
|
52
54
|
const queryResult: QueryResult.QueryResult<TestItem> = db.query(
|
|
53
55
|
Query.select(Filter.type(TestItem, { value: 100 })),
|
|
@@ -63,7 +65,7 @@ describe('AtomQuery', () => {
|
|
|
63
65
|
|
|
64
66
|
test('registry.subscribe fires on QueryResult changes', async () => {
|
|
65
67
|
db.add(Obj.make(TestItem, { name: 'Initial', value: 200 }));
|
|
66
|
-
await db.flush(
|
|
68
|
+
await db.flush();
|
|
67
69
|
|
|
68
70
|
const queryResult: QueryResult.QueryResult<TestItem> = db.query(
|
|
69
71
|
Query.select(Filter.type(TestItem, { value: 200 })),
|
|
@@ -86,7 +88,7 @@ describe('AtomQuery', () => {
|
|
|
86
88
|
|
|
87
89
|
// Add a new object that matches the query.
|
|
88
90
|
db.add(Obj.make(TestItem, { name: 'New Object', value: 200 }));
|
|
89
|
-
await db.flush({
|
|
91
|
+
await db.flush({ updates: true });
|
|
90
92
|
|
|
91
93
|
// Subscription should have fired.
|
|
92
94
|
expect(updateCount).toBeGreaterThan(0);
|
|
@@ -96,7 +98,7 @@ describe('AtomQuery', () => {
|
|
|
96
98
|
test('registry.subscribe fires when objects are removed', async () => {
|
|
97
99
|
const obj1 = db.add(Obj.make(TestItem, { name: 'Object 1', value: 300 }));
|
|
98
100
|
db.add(Obj.make(TestItem, { name: 'Object 2', value: 300 }));
|
|
99
|
-
await db.flush(
|
|
101
|
+
await db.flush();
|
|
100
102
|
|
|
101
103
|
const queryResult: QueryResult.QueryResult<TestItem> = db.query(
|
|
102
104
|
Query.select(Filter.type(TestItem, { value: 300 })),
|
|
@@ -119,7 +121,7 @@ describe('AtomQuery', () => {
|
|
|
119
121
|
|
|
120
122
|
// Remove an object.
|
|
121
123
|
db.remove(obj1);
|
|
122
|
-
await db.flush({
|
|
124
|
+
await db.flush({ updates: true });
|
|
123
125
|
|
|
124
126
|
// Subscription should have fired.
|
|
125
127
|
expect(updateCount).toBeGreaterThan(0);
|
|
@@ -129,7 +131,7 @@ describe('AtomQuery', () => {
|
|
|
129
131
|
|
|
130
132
|
test('unsubscribing from registry stops receiving updates', async () => {
|
|
131
133
|
db.add(Obj.make(TestItem, { name: 'Initial', value: 400 }));
|
|
132
|
-
await db.flush(
|
|
134
|
+
await db.flush();
|
|
133
135
|
|
|
134
136
|
const queryResult: QueryResult.QueryResult<TestItem> = db.query(
|
|
135
137
|
Query.select(Filter.type(TestItem, { value: 400 })),
|
|
@@ -150,7 +152,7 @@ describe('AtomQuery', () => {
|
|
|
150
152
|
|
|
151
153
|
// Add object and verify subscription fires.
|
|
152
154
|
db.add(Obj.make(TestItem, { name: 'Object 2', value: 400 }));
|
|
153
|
-
await db.flush({
|
|
155
|
+
await db.flush({ updates: true });
|
|
154
156
|
const countAfterFirstAdd = updateCount;
|
|
155
157
|
expect(countAfterFirstAdd).toBeGreaterThan(0);
|
|
156
158
|
|
|
@@ -159,7 +161,7 @@ describe('AtomQuery', () => {
|
|
|
159
161
|
|
|
160
162
|
// Add another object.
|
|
161
163
|
db.add(Obj.make(TestItem, { name: 'Object 3', value: 400 }));
|
|
162
|
-
await db.flush({
|
|
164
|
+
await db.flush({ updates: true });
|
|
163
165
|
|
|
164
166
|
// Update count should not have changed after unsubscribe.
|
|
165
167
|
expect(updateCount).toBe(countAfterFirstAdd);
|
|
@@ -179,7 +181,7 @@ describe('AtomQuery', () => {
|
|
|
179
181
|
|
|
180
182
|
test('multiple atoms from same query share underlying subscription', async () => {
|
|
181
183
|
db.add(Obj.make(TestItem, { name: 'Object', value: 500 }));
|
|
182
|
-
await db.flush(
|
|
184
|
+
await db.flush();
|
|
183
185
|
|
|
184
186
|
const queryResult: QueryResult.QueryResult<TestItem> = db.query(
|
|
185
187
|
Query.select(Filter.type(TestItem, { value: 500 })),
|
|
@@ -260,3 +262,127 @@ describe('AtomQuery with queues', () => {
|
|
|
260
262
|
expect(results[0].name).toEqual('jane');
|
|
261
263
|
});
|
|
262
264
|
});
|
|
265
|
+
|
|
266
|
+
const SchemaA = Schema.Struct({
|
|
267
|
+
name: Schema.String,
|
|
268
|
+
}).pipe(
|
|
269
|
+
Type.object({
|
|
270
|
+
typename: 'com.example.type.a',
|
|
271
|
+
version: '0.1.0',
|
|
272
|
+
}),
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
const SchemaB = Schema.Struct({
|
|
276
|
+
value: Schema.Number,
|
|
277
|
+
}).pipe(
|
|
278
|
+
Type.object({
|
|
279
|
+
typename: 'com.example.type.b',
|
|
280
|
+
version: '0.1.0',
|
|
281
|
+
}),
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
describe('AtomQuery.fromQuery with schema registry', () => {
|
|
285
|
+
let schemaRegistry: RuntimeSchemaRegistry;
|
|
286
|
+
let registry: Registry.Registry;
|
|
287
|
+
|
|
288
|
+
beforeEach(() => {
|
|
289
|
+
schemaRegistry = new RuntimeSchemaRegistry([]);
|
|
290
|
+
registry = Registry.make();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test('creates atom with initial results from schema query', async ({ expect }) => {
|
|
294
|
+
await schemaRegistry.register([SchemaA]);
|
|
295
|
+
|
|
296
|
+
const queryResult = schemaRegistry.query();
|
|
297
|
+
const atom = AtomQuery.fromQuery(queryResult);
|
|
298
|
+
const results = registry.get(atom);
|
|
299
|
+
|
|
300
|
+
expect(results).toHaveLength(1);
|
|
301
|
+
expect(Type.getTypename(results[0])).toBe('com.example.type.a');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
test('atom updates when new schemas are registered', async ({ expect }) => {
|
|
305
|
+
await schemaRegistry.register([SchemaA]);
|
|
306
|
+
|
|
307
|
+
const queryResult = schemaRegistry.query();
|
|
308
|
+
const atom = AtomQuery.fromQuery(queryResult);
|
|
309
|
+
|
|
310
|
+
// Get initial results and subscribe.
|
|
311
|
+
const initialResults = registry.get(atom);
|
|
312
|
+
expect(initialResults).toHaveLength(1);
|
|
313
|
+
|
|
314
|
+
let updateCount = 0;
|
|
315
|
+
let latestResults: Type.AnyEntity[] = [];
|
|
316
|
+
registry.subscribe(atom, () => {
|
|
317
|
+
updateCount++;
|
|
318
|
+
latestResults = registry.get(atom);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
// Allow reactive query to start (deferred via queueMicrotask).
|
|
322
|
+
await sleep(10);
|
|
323
|
+
|
|
324
|
+
// Register a new schema.
|
|
325
|
+
await schemaRegistry.register([SchemaB]);
|
|
326
|
+
|
|
327
|
+
expect(updateCount).toBeGreaterThan(0);
|
|
328
|
+
expect(latestResults).toHaveLength(2);
|
|
329
|
+
expect(latestResults.map(Type.getTypename).sort()).toEqual(['com.example.type.a', 'com.example.type.b']);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
test('atom works with empty initial results', ({ expect }) => {
|
|
333
|
+
const queryResult = schemaRegistry.query();
|
|
334
|
+
const atom = AtomQuery.fromQuery(queryResult);
|
|
335
|
+
const results = registry.get(atom);
|
|
336
|
+
|
|
337
|
+
expect(results).toHaveLength(0);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test('atom with filtered query only reflects matching schemas', async ({ expect }) => {
|
|
341
|
+
const queryResult = schemaRegistry.query({ typename: 'com.example.type.a' });
|
|
342
|
+
const atom = AtomQuery.fromQuery(queryResult);
|
|
343
|
+
|
|
344
|
+
// Get initial (empty) results and subscribe.
|
|
345
|
+
const initialResults = registry.get(atom);
|
|
346
|
+
expect(initialResults).toHaveLength(0);
|
|
347
|
+
|
|
348
|
+
let latestResults: Type.AnyEntity[] = [];
|
|
349
|
+
registry.subscribe(atom, () => {
|
|
350
|
+
latestResults = registry.get(atom);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
await sleep(10);
|
|
354
|
+
|
|
355
|
+
// Register non-matching schema.
|
|
356
|
+
await schemaRegistry.register([SchemaB]);
|
|
357
|
+
// Results updated but still empty for this filter.
|
|
358
|
+
expect(latestResults).toHaveLength(0);
|
|
359
|
+
|
|
360
|
+
// Register matching schema.
|
|
361
|
+
await schemaRegistry.register([SchemaA]);
|
|
362
|
+
expect(latestResults).toHaveLength(1);
|
|
363
|
+
expect(Type.getTypename(latestResults[0])).toBe('com.example.type.a');
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test('unsubscribing from atom stops updates', async ({ expect }) => {
|
|
367
|
+
const queryResult = schemaRegistry.query();
|
|
368
|
+
const atom = AtomQuery.fromQuery(queryResult);
|
|
369
|
+
|
|
370
|
+
registry.get(atom);
|
|
371
|
+
|
|
372
|
+
let updateCount = 0;
|
|
373
|
+
const unsubscribe = registry.subscribe(atom, () => {
|
|
374
|
+
updateCount++;
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
await sleep(10);
|
|
378
|
+
|
|
379
|
+
await schemaRegistry.register([SchemaA]);
|
|
380
|
+
const countAfterFirst = updateCount;
|
|
381
|
+
expect(countAfterFirst).toBeGreaterThan(0);
|
|
382
|
+
|
|
383
|
+
unsubscribe();
|
|
384
|
+
|
|
385
|
+
await schemaRegistry.register([SchemaB]);
|
|
386
|
+
expect(updateCount).toBe(countAfterFirst);
|
|
387
|
+
});
|
|
388
|
+
});
|
package/src/query-atom.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { DXN, Database, type Entity, type Filter, Query, type QueryResult } from
|
|
|
8
8
|
import { WeakDictionary } from '@dxos/util';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Create a self-updating atom from
|
|
11
|
+
* Create a self-updating atom from any QueryResult (e.g. schema registry queries).
|
|
12
12
|
* Internally subscribes to queryResult and uses get.setSelf to update.
|
|
13
13
|
* Cleanup is handled via get.addFinalizer.
|
|
14
14
|
*
|
|
@@ -17,18 +17,13 @@ import { WeakDictionary } from '@dxos/util';
|
|
|
17
17
|
* @param queryResult - The QueryResult to wrap.
|
|
18
18
|
* @returns An atom that automatically updates when query results change.
|
|
19
19
|
*/
|
|
20
|
-
export const fromQuery = <T
|
|
20
|
+
export const fromQuery = <T>(queryResult: QueryResult.QueryResult<T>): Atom.Atom<T[]> =>
|
|
21
21
|
Atom.make((get) => {
|
|
22
|
-
// TODO(wittjosiah): Consider subscribing to individual objects here as well, and grabbing their snapshots.
|
|
23
|
-
// Subscribe to QueryResult changes.
|
|
24
22
|
const unsubscribe = queryResult.subscribe(() => {
|
|
25
|
-
get.setSelf(queryResult.
|
|
23
|
+
get.setSelf(queryResult.runSync());
|
|
26
24
|
});
|
|
27
|
-
|
|
28
|
-
// Register cleanup for when atom is no longer used.
|
|
29
25
|
get.addFinalizer(unsubscribe);
|
|
30
|
-
|
|
31
|
-
return queryResult.results;
|
|
26
|
+
return queryResult.runSync();
|
|
32
27
|
});
|
|
33
28
|
|
|
34
29
|
// Registry: key → Queryable (WeakRef with auto-cleanup when GC'd).
|
|
@@ -37,7 +32,7 @@ const queryableRegistry = new WeakDictionary<string, Database.Queryable>();
|
|
|
37
32
|
// Key separator that won't appear in identifiers (DXN strings use colons).
|
|
38
33
|
const KEY_SEPARATOR = '~';
|
|
39
34
|
|
|
40
|
-
// Atom.family keyed by "identifier
|
|
35
|
+
// Atom.family keyed by "identifier~serializedAST".
|
|
41
36
|
const queryFamily = Atom.family((key: string) => {
|
|
42
37
|
// Parse key outside Atom.make - runs once per key.
|
|
43
38
|
const separatorIndex = key.indexOf(KEY_SEPARATOR);
|
package/src/reactivity.test.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { describe, expect, test } from 'vitest';
|
|
|
8
8
|
import { Obj } from '@dxos/echo';
|
|
9
9
|
import { TestSchema } from '@dxos/echo/testing';
|
|
10
10
|
import { createObject } from '@dxos/echo-db';
|
|
11
|
+
import { arrayMove } from '@dxos/util';
|
|
11
12
|
|
|
12
13
|
import * as AtomObj from './atom';
|
|
13
14
|
|
|
@@ -28,8 +29,8 @@ describe('Echo Atom - Reactivity', () => {
|
|
|
28
29
|
registry.subscribe(atom, () => {});
|
|
29
30
|
|
|
30
31
|
// Update the object via Obj.change.
|
|
31
|
-
Obj.change(obj, (
|
|
32
|
-
|
|
32
|
+
Obj.change(obj, (obj) => {
|
|
33
|
+
obj.name = 'Updated';
|
|
33
34
|
});
|
|
34
35
|
|
|
35
36
|
const updatedSnapshot = registry.get(atom);
|
|
@@ -50,8 +51,8 @@ describe('Echo Atom - Reactivity', () => {
|
|
|
50
51
|
registry.subscribe(atom, () => {});
|
|
51
52
|
|
|
52
53
|
// Update the property via Obj.change.
|
|
53
|
-
Obj.change(obj, (
|
|
54
|
-
|
|
54
|
+
Obj.change(obj, (obj) => {
|
|
55
|
+
obj.name = 'Updated';
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
expect(registry.get(atom)).toBe('Updated');
|
|
@@ -82,8 +83,8 @@ describe('Echo Atom - Reactivity', () => {
|
|
|
82
83
|
});
|
|
83
84
|
|
|
84
85
|
// Update only email property via Obj.change.
|
|
85
|
-
Obj.change(obj, (
|
|
86
|
-
|
|
86
|
+
Obj.change(obj, (obj) => {
|
|
87
|
+
obj.email = 'updated@example.com';
|
|
87
88
|
});
|
|
88
89
|
|
|
89
90
|
// Name atom should NOT have changed.
|
|
@@ -109,9 +110,9 @@ describe('Echo Atom - Reactivity', () => {
|
|
|
109
110
|
registry.subscribe(emailAtom, () => {});
|
|
110
111
|
|
|
111
112
|
// Update multiple properties via Obj.change.
|
|
112
|
-
Obj.change(obj, (
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
Obj.change(obj, (obj) => {
|
|
114
|
+
obj.name = 'Updated';
|
|
115
|
+
obj.email = 'updated@example.com';
|
|
115
116
|
});
|
|
116
117
|
|
|
117
118
|
expect(registry.get(nameAtom)).toBe('Updated');
|
|
@@ -140,11 +141,11 @@ describe('Echo Atom - Reactivity', () => {
|
|
|
140
141
|
expect(initialCount).toBe(1);
|
|
141
142
|
|
|
142
143
|
// Update object via Obj.change.
|
|
143
|
-
Obj.change(obj, (
|
|
144
|
-
|
|
144
|
+
Obj.change(obj, (obj) => {
|
|
145
|
+
obj.name = 'Updated';
|
|
145
146
|
});
|
|
146
|
-
Obj.change(obj, (
|
|
147
|
-
|
|
147
|
+
Obj.change(obj, (obj) => {
|
|
148
|
+
obj.email = 'updated@example.com';
|
|
148
149
|
});
|
|
149
150
|
|
|
150
151
|
// Updates fire through Obj.subscribe (one per Obj.change call).
|
|
@@ -166,8 +167,8 @@ describe('Echo Atom - Reactivity', () => {
|
|
|
166
167
|
});
|
|
167
168
|
|
|
168
169
|
actions.push('before');
|
|
169
|
-
Obj.change(obj, (
|
|
170
|
-
|
|
170
|
+
Obj.change(obj, (obj) => {
|
|
171
|
+
obj.name = 'Updated';
|
|
171
172
|
});
|
|
172
173
|
actions.push('after');
|
|
173
174
|
|
|
@@ -190,8 +191,8 @@ describe('Echo Atom - Reactivity', () => {
|
|
|
190
191
|
});
|
|
191
192
|
|
|
192
193
|
actions.push('before');
|
|
193
|
-
Obj.change(obj, (
|
|
194
|
-
|
|
194
|
+
Obj.change(obj, (obj) => {
|
|
195
|
+
obj.stringArray!.splice(1, 1);
|
|
195
196
|
});
|
|
196
197
|
actions.push('after');
|
|
197
198
|
|
|
@@ -203,4 +204,30 @@ describe('Echo Atom - Reactivity', () => {
|
|
|
203
204
|
|
|
204
205
|
unsubscribe();
|
|
205
206
|
});
|
|
207
|
+
|
|
208
|
+
test('property atom for array property updates when array is reordered in place', () => {
|
|
209
|
+
// Verifies that makeProperty(obj, 'columns')-style atoms subscribe to in-place
|
|
210
|
+
// array mutations (e.g. arrayMove), so UI stays in sync after column reorder.
|
|
211
|
+
const obj = createObject(Obj.make(TestSchema.Example, { stringArray: ['a', 'b', 'c'] }));
|
|
212
|
+
|
|
213
|
+
const registry = Registry.make();
|
|
214
|
+
const atom = AtomObj.makeProperty(obj, 'stringArray');
|
|
215
|
+
|
|
216
|
+
const initial = registry.get(atom);
|
|
217
|
+
expect(initial).toEqual(['a', 'b', 'c']);
|
|
218
|
+
|
|
219
|
+
let updateCount = 0;
|
|
220
|
+
registry.subscribe(atom, () => {
|
|
221
|
+
updateCount++;
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
// Reorder in place (e.g. move first to last).
|
|
225
|
+
Obj.change(obj, (obj) => {
|
|
226
|
+
arrayMove(obj.stringArray!, 0, 2);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(updateCount).toBe(1);
|
|
230
|
+
const afterReorder = registry.get(atom);
|
|
231
|
+
expect(afterReorder).toEqual(['b', 'c', 'a']);
|
|
232
|
+
});
|
|
206
233
|
});
|
package/src/ref-atom.test.ts
CHANGED
|
@@ -33,7 +33,7 @@ describe('AtomRef - Basic Functionality', () => {
|
|
|
33
33
|
|
|
34
34
|
const targetObj = Obj.make(TestSchema.Person, { name: 'Target', username: 'target', email: 'target@example.com' });
|
|
35
35
|
db.add(targetObj);
|
|
36
|
-
await db.flush(
|
|
36
|
+
await db.flush();
|
|
37
37
|
|
|
38
38
|
const ref = Ref.make(targetObj);
|
|
39
39
|
const atom = AtomRef.make(ref);
|
|
@@ -48,7 +48,7 @@ describe('AtomRef - Basic Functionality', () => {
|
|
|
48
48
|
|
|
49
49
|
const targetObj = Obj.make(TestSchema.Person, { name: 'Target', username: 'target', email: 'target@example.com' });
|
|
50
50
|
db.add(targetObj);
|
|
51
|
-
await db.flush(
|
|
51
|
+
await db.flush();
|
|
52
52
|
|
|
53
53
|
const ref = Ref.make(targetObj);
|
|
54
54
|
const atom = AtomRef.make(ref);
|
|
@@ -60,8 +60,8 @@ describe('AtomRef - Basic Functionality', () => {
|
|
|
60
60
|
expect(updateCount).toBe(1);
|
|
61
61
|
|
|
62
62
|
// Mutate target - ref atom does NOT react to this.
|
|
63
|
-
Obj.change(targetObj, (
|
|
64
|
-
|
|
63
|
+
Obj.change(targetObj, (obj) => {
|
|
64
|
+
obj.name = 'Updated';
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
// Update count should still be 1 - ref atom doesn't subscribe to target changes.
|
|
@@ -90,7 +90,7 @@ describe('AtomRef - Referential Equality', () => {
|
|
|
90
90
|
|
|
91
91
|
const targetObj = Obj.make(TestSchema.Person, { name: 'Target', username: 'target', email: 'target@example.com' });
|
|
92
92
|
db.add(targetObj);
|
|
93
|
-
await db.flush(
|
|
93
|
+
await db.flush();
|
|
94
94
|
|
|
95
95
|
const ref = Ref.make(targetObj);
|
|
96
96
|
|
|
@@ -116,7 +116,7 @@ describe('AtomRef - Referential Equality', () => {
|
|
|
116
116
|
});
|
|
117
117
|
db.add(targetObj1);
|
|
118
118
|
db.add(targetObj2);
|
|
119
|
-
await db.flush(
|
|
119
|
+
await db.flush();
|
|
120
120
|
|
|
121
121
|
const ref1 = Ref.make(targetObj1);
|
|
122
122
|
const ref2 = Ref.make(targetObj2);
|
|
@@ -128,12 +128,12 @@ describe('AtomRef - Referential Equality', () => {
|
|
|
128
128
|
expect(atom1).not.toBe(atom2);
|
|
129
129
|
});
|
|
130
130
|
|
|
131
|
-
test('AtomRef.make returns
|
|
131
|
+
test('AtomRef.make returns same atom for refs created separately to same target', async () => {
|
|
132
132
|
await db.graph.schemaRegistry.register([TestSchema.Person]);
|
|
133
133
|
|
|
134
134
|
const targetObj = Obj.make(TestSchema.Person, { name: 'Target', username: 'target', email: 'target@example.com' });
|
|
135
135
|
db.add(targetObj);
|
|
136
|
-
await db.flush(
|
|
136
|
+
await db.flush();
|
|
137
137
|
|
|
138
138
|
// Create two separate refs to the same target.
|
|
139
139
|
const ref1 = Ref.make(targetObj);
|
|
@@ -145,10 +145,10 @@ describe('AtomRef - Referential Equality', () => {
|
|
|
145
145
|
const atom1 = AtomRef.make(ref1);
|
|
146
146
|
const atom2 = AtomRef.make(ref2);
|
|
147
147
|
|
|
148
|
-
//
|
|
149
|
-
expect(atom1).
|
|
148
|
+
// Refs with the same DXN resolve to the same atom via Hash/Equal traits.
|
|
149
|
+
expect(atom1).toBe(atom2);
|
|
150
150
|
|
|
151
|
-
//
|
|
151
|
+
// Both atoms should return the same target data.
|
|
152
152
|
expect(registry.get(atom1)?.name).toBe('Target');
|
|
153
153
|
expect(registry.get(atom2)?.name).toBe('Target');
|
|
154
154
|
});
|
|
@@ -158,7 +158,7 @@ describe('AtomRef - Referential Equality', () => {
|
|
|
158
158
|
|
|
159
159
|
const targetObj = Obj.make(TestSchema.Person, { name: 'Target', username: 'target', email: 'target@example.com' });
|
|
160
160
|
db.add(targetObj);
|
|
161
|
-
await db.flush(
|
|
161
|
+
await db.flush();
|
|
162
162
|
|
|
163
163
|
const ref = Ref.make(targetObj);
|
|
164
164
|
|
|
@@ -197,7 +197,7 @@ describe('AtomRef - Expando Objects', () => {
|
|
|
197
197
|
test('works with Expando objects', async () => {
|
|
198
198
|
const targetObj = Obj.make(TestSchema.Expando, { name: 'Expando Target', value: 42 });
|
|
199
199
|
db.add(targetObj);
|
|
200
|
-
await db.flush(
|
|
200
|
+
await db.flush();
|
|
201
201
|
|
|
202
202
|
const ref = Ref.make(targetObj);
|
|
203
203
|
const atom = AtomRef.make(ref);
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/atom.ts", "../../../src/ref-utils.ts", "../../../src/query-atom.ts", "../../../src/ref-atom.ts"],
|
|
4
|
-
"sourcesContent": ["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Atom from '@effect-atom/atom/Atom';\nimport isEqual from 'lodash.isequal';\n\nimport { Obj, Ref } from '@dxos/echo';\nimport { assertArgument } from '@dxos/invariant';\n\nimport { loadRefTarget } from './ref-utils';\n\n/**\n * Atom family for ECHO objects.\n * Uses object reference as key - same object returns same atom.\n */\nconst objectFamily = Atom.family(<T extends Obj.Unknown>(obj: T): Atom.Atom<Obj.Snapshot<T>> => {\n return Atom.make<Obj.Snapshot<T>>((get) => {\n const unsubscribe = Obj.subscribe(obj, () => {\n get.setSelf(Obj.getSnapshot(obj));\n });\n\n get.addFinalizer(() => unsubscribe());\n\n return Obj.getSnapshot(obj);\n });\n});\n\n/**\n * Internal helper to create an atom from a Ref.\n * Handles async loading and subscribes to the target for reactive updates.\n * Uses Atom.family internally - same ref reference returns same atom instance.\n */\nconst refFamily = Atom.family(<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<Obj.Snapshot<T> | undefined> => {\n return Atom.make<Obj.Snapshot<T> | undefined>((get) => {\n let unsubscribeTarget: (() => void) | undefined;\n\n const setupTargetSubscription = (target: T): Obj.Snapshot<T> => {\n unsubscribeTarget?.();\n unsubscribeTarget = Obj.subscribe(target, () => {\n get.setSelf(Obj.getSnapshot(target));\n });\n return Obj.getSnapshot(target);\n };\n\n get.addFinalizer(() => unsubscribeTarget?.());\n\n return loadRefTarget(ref, get, setupTargetSubscription);\n });\n});\n\n/**\n * Snapshot a value to create a new reference for comparison and React dependency tracking.\n * Arrays and plain objects are shallow-copied so that:\n * 1. The snapshot is isolated from mutations to the original value.\n * 2. React's shallow comparison (Object.is) detects changes via new reference identity.\n */\nconst snapshotForComparison = <V>(value: V): V => {\n if (Array.isArray(value)) {\n return [...value] as V;\n }\n if (value !== null && typeof value === 'object') {\n return { ...value } as V;\n }\n return value;\n};\n\n/**\n * Atom family for ECHO object properties.\n * Uses nested families: outer keyed by object, inner keyed by property key.\n * Same object+key combination returns same atom instance.\n */\nconst propertyFamily = Atom.family(<T extends Obj.Unknown>(obj: T) =>\n Atom.family(<K extends keyof T>(key: K): Atom.Atom<T[K]> => {\n return Atom.make<T[K]>((get) => {\n // Snapshot the initial value for comparison (arrays/objects need copying).\n let previousSnapshot = snapshotForComparison(obj[key]);\n\n const unsubscribe = Obj.subscribe(obj, () => {\n const newValue = obj[key];\n if (!isEqual(previousSnapshot, newValue)) {\n previousSnapshot = snapshotForComparison(newValue);\n // Return a snapshot copy so React sees a new reference.\n get.setSelf(snapshotForComparison(newValue));\n }\n });\n\n get.addFinalizer(() => unsubscribe());\n\n // Return a snapshot copy so React sees a new reference.\n return snapshotForComparison(obj[key]);\n });\n }),\n);\n\n/**\n * Create a read-only atom for a reactive object or ref.\n * Works with Echo objects, plain reactive objects (from Obj.make), and Refs.\n * Returns immutable snapshots of the object data (branded with SnapshotKindId).\n * The atom updates automatically when the object is mutated.\n * For refs, automatically handles async loading.\n * Uses Atom.family internally - same object/ref reference returns same atom instance.\n *\n * @param objOrRef - The reactive object or ref to create an atom for, or undefined.\n * @returns An atom that returns the object snapshot. Returns undefined only for refs (async loading) or undefined input.\n */\nexport function make<T extends Obj.Unknown>(obj: T): Atom.Atom<Obj.Snapshot<T>>;\nexport function make<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<Obj.Snapshot<T> | undefined>;\nexport function make<T extends Obj.Unknown>(\n objOrRef: T | Ref.Ref<T> | undefined,\n): Atom.Atom<Obj.Snapshot<T> | undefined>;\nexport function make<T extends Obj.Unknown>(\n objOrRef: T | Ref.Ref<T> | undefined,\n): Atom.Atom<Obj.Snapshot<T> | undefined> {\n if (objOrRef === undefined) {\n return Atom.make<Obj.Snapshot<T> | undefined>(() => undefined);\n }\n\n // Handle Ref inputs.\n if (Ref.isRef(objOrRef)) {\n return refFamily(objOrRef as Ref.Ref<T>);\n }\n\n // At this point, objOrRef is definitely T (not a Ref).\n const obj = objOrRef as T;\n assertArgument(Obj.isObject(obj), 'obj', 'Object must be a reactive object');\n\n return objectFamily(obj);\n}\n\n/**\n * Create a read-only atom for a specific property of a reactive object.\n * Works with both Echo objects (from createObject) and plain live objects (from Obj.make).\n * The atom updates automatically when the property is mutated.\n * Only fires updates when the property value actually changes.\n * Uses Atom.family internally - same object+key combination returns same atom instance.\n *\n * @param obj - The reactive object to create an atom for, or undefined.\n * @param key - The property key to subscribe to.\n * @returns An atom that returns the property value, or undefined if obj is undefined.\n */\nexport function makeProperty<T extends Obj.Unknown, K extends keyof T>(obj: T, key: K): Atom.Atom<T[K]>;\nexport function makeProperty<T extends Obj.Unknown, K extends keyof T>(\n obj: T | undefined,\n key: K,\n): Atom.Atom<T[K] | undefined>;\nexport function makeProperty<T extends Obj.Unknown, K extends keyof T>(\n obj: T | undefined,\n key: K,\n): Atom.Atom<T[K] | undefined> {\n if (obj === undefined) {\n return Atom.make<T[K] | undefined>(() => undefined);\n }\n\n assertArgument(Obj.isObject(obj), 'obj', 'Object must be a reactive object');\n assertArgument(key in obj, 'key', 'Property must exist on object');\n return propertyFamily(obj)(key);\n}\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport type * as Atom from '@effect-atom/atom/Atom';\n\nimport type { Ref } from '@dxos/echo';\n\n/**\n * Internal helper for loading ref targets in atoms.\n * Handles the common pattern of checking for loaded target and triggering async load.\n *\n * @param ref - The ref to load.\n * @param get - The atom context for setSelf.\n * @param onTargetAvailable - Callback invoked when target is available (sync or async).\n * Should return the value to use for the atom.\n * @returns The result of onTargetAvailable if target is already loaded, undefined otherwise.\n */\nexport const loadRefTarget = <T, R>(\n ref: Ref.Ref<T>,\n get: Atom.Context,\n onTargetAvailable: (target: T) => R,\n): R | undefined => {\n const currentTarget = ref.target;\n if (currentTarget) {\n return onTargetAvailable(currentTarget);\n }\n\n // Target not loaded yet - trigger async load.\n void ref\n .load()\n .then((loadedTarget) => {\n get.setSelf(onTargetAvailable(loadedTarget));\n })\n .catch(() => {\n // Loading failed, keep target as undefined.\n });\n\n return undefined;\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { Atom } from '@effect-atom/atom';\n\nimport { DXN, Database, type Entity, type Filter, Query, type QueryResult } from '@dxos/echo';\nimport { WeakDictionary } from '@dxos/util';\n\n/**\n * Create a self-updating atom from an existing QueryResult.\n * Internally subscribes to queryResult and uses get.setSelf to update.\n * Cleanup is handled via get.addFinalizer.\n *\n * Note: This creates a new atom each time. For memoization, use `make` instead.\n *\n * @param queryResult - The QueryResult to wrap.\n * @returns An atom that automatically updates when query results change.\n */\nexport const fromQuery = <T extends Entity.Unknown>(queryResult: QueryResult.QueryResult<T>): Atom.Atom<T[]> =>\n Atom.make((get) => {\n // TODO(wittjosiah): Consider subscribing to individual objects here as well, and grabbing their snapshots.\n // Subscribe to QueryResult changes.\n const unsubscribe = queryResult.subscribe(() => {\n get.setSelf(queryResult.results);\n });\n\n // Register cleanup for when atom is no longer used.\n get.addFinalizer(unsubscribe);\n\n return queryResult.results;\n });\n\n// Registry: key → Queryable (WeakRef with auto-cleanup when GC'd).\nconst queryableRegistry = new WeakDictionary<string, Database.Queryable>();\n\n// Key separator that won't appear in identifiers (DXN strings use colons).\nconst KEY_SEPARATOR = '~';\n\n// Atom.family keyed by \"identifier\\0serializedAST\".\nconst queryFamily = Atom.family((key: string) => {\n // Parse key outside Atom.make - runs once per key.\n const separatorIndex = key.indexOf(KEY_SEPARATOR);\n const identifier = key.slice(0, separatorIndex);\n const serializedAst = key.slice(separatorIndex + 1);\n\n // Get queryable outside Atom.make - keeps Queryable alive via closure.\n const queryable = queryableRegistry.get(identifier);\n if (!queryable) {\n return Atom.make(() => [] as Entity.Unknown[]);\n }\n\n // Create query outside Atom.make - runs once, not on every recompute.\n const ast = JSON.parse(serializedAst);\n const queryResult = queryable.query(Query.fromAst(ast)) as QueryResult.QueryResult<Entity.Unknown>;\n\n return Atom.make((get) => {\n const unsubscribe = queryResult.subscribe(() => {\n get.setSelf(queryResult.results);\n });\n get.addFinalizer(unsubscribe);\n\n return queryResult.results;\n });\n});\n\n/**\n * Derive a stable identifier from a Queryable.\n * Supports Database (spaceId), Queue (dxn), and objects with id.\n */\nconst getQueryableIdentifier = (queryable: Database.Queryable): string => {\n // Database: use spaceId.\n if (Database.isDatabase(queryable)) {\n return queryable.spaceId;\n }\n // Queue or similar: use dxn if it's a DXN instance.\n if ('dxn' in queryable && queryable.dxn instanceof DXN) {\n return queryable.dxn.toString();\n }\n // Fallback: use id if it's a string.\n if ('id' in queryable && typeof queryable.id === 'string') {\n return queryable.id;\n }\n throw new Error('Unable to derive identifier from queryable.');\n};\n\n/**\n * Get a memoized query atom for any Queryable (Database, Queue, etc.).\n * Uses a single Atom.family keyed by queryable identifier + serialized query AST.\n * Same queryable + query/filter = same atom instance (proper memoization).\n *\n * @param queryable - The queryable to query (Database, Queue, etc.).\n * @param queryOrFilter - A Query or Filter to execute.\n * @returns A memoized atom that updates when query results change.\n */\nexport const make = <T extends Entity.Unknown>(\n queryable: Database.Queryable,\n queryOrFilter: Query.Query<T> | Filter.Filter<T>,\n): Atom.Atom<T[]> => {\n const identifier = getQueryableIdentifier(queryable);\n return fromQueryable(queryable, identifier, queryOrFilter);\n};\n\n/**\n * Internal: Get a memoized query atom for any Queryable with a custom identifier.\n */\nconst fromQueryable = <T extends Entity.Unknown>(\n queryable: Database.Queryable,\n identifier: string,\n queryOrFilter: Query.Query<T> | Filter.Filter<T>,\n): Atom.Atom<T[]> => {\n // Register queryable in registry (WeakDictionary handles cleanup automatically).\n queryableRegistry.set(identifier, queryable);\n\n // Normalize to Query.\n const normalizedQuery: Query.Any = Query.is(queryOrFilter)\n ? queryOrFilter\n : Query.select(queryOrFilter as Filter.Filter<T>);\n\n // Build key: identifier\\0serializedAST (using null char as separator to avoid DXN colon conflicts).\n const key = `${identifier}${KEY_SEPARATOR}${JSON.stringify(normalizedQuery.ast)}`;\n\n return queryFamily(key) as Atom.Atom<T[]>;\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Atom from '@effect-atom/atom/Atom';\n\nimport { type Ref } from '@dxos/echo';\n\nimport { loadRefTarget } from './ref-utils';\n\n/**\n * Atom family for ECHO refs.\n * Uses ref reference as key - same ref returns same atom.\n * This atom only updates once when the ref loads - it does not subscribe to object changes.\n * Use AtomObj.make with a ref if you need reactive snapshots of ECHO objects.\n */\nconst refFamily = Atom.family(<T>(ref: Ref.Ref<T>): Atom.Atom<T | undefined> => {\n return Atom.make<T | undefined>((get) => {\n return loadRefTarget(ref, get, (target) => target);\n });\n});\n\n/**\n * Create a read-only atom for a reference target.\n * Returns undefined if the target hasn't loaded yet.\n * Updates when the ref loads but does NOT subscribe to target object changes.\n * Use AtomObj.make with a ref if you need reactive snapshots of ECHO objects.\n * Uses Atom.family internally - same ref reference returns same atom instance.\n *\n * Supports refs to any target type including ECHO objects and Queues.\n */\nexport const make = refFamily;\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["make", "Atom", "isEqual", "Obj", "Ref", "assertArgument", "loadRefTarget", "ref", "get", "onTargetAvailable", "currentTarget", "target", "load", "then", "loadedTarget", "setSelf", "catch", "undefined", "objectFamily", "family", "obj", "make", "get", "unsubscribe", "Obj", "subscribe", "setSelf", "getSnapshot", "addFinalizer", "refFamily", "ref", "unsubscribeTarget", "setupTargetSubscription", "target", "loadRefTarget", "snapshotForComparison", "value", "Array", "isArray", "propertyFamily", "key", "previousSnapshot", "newValue", "isEqual", "objOrRef", "undefined", "Ref", "isRef", "assertArgument", "isObject", "makeProperty", "make", "Atom", "DXN", "Database", "Query", "WeakDictionary", "fromQuery", "queryResult", "Atom", "make", "get", "unsubscribe", "subscribe", "setSelf", "results", "addFinalizer", "queryableRegistry", "WeakDictionary", "KEY_SEPARATOR", "queryFamily", "family", "key", "separatorIndex", "indexOf", "identifier", "slice", "serializedAst", "queryable", "ast", "JSON", "parse", "query", "Query", "fromAst", "getQueryableIdentifier", "Database", "isDatabase", "spaceId", "dxn", "DXN", "toString", "id", "Error", "queryOrFilter", "fromQueryable", "set", "normalizedQuery", "is", "select", "stringify", "make", "Atom", "refFamily", "family", "ref", "make", "get", "loadRefTarget", "target"]
|
|
7
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"inputs":{"src/ref-utils.ts":{"bytes":3291,"imports":[],"format":"esm"},"src/atom.ts":{"bytes":14945,"imports":[{"path":"@effect-atom/atom/Atom","kind":"import-statement","external":true},{"path":"lodash.isequal","kind":"import-statement","external":true},{"path":"@dxos/echo","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"src/ref-utils.ts","kind":"import-statement","original":"./ref-utils"}],"format":"esm"},"src/query-atom.ts":{"bytes":13559,"imports":[{"path":"@effect-atom/atom","kind":"import-statement","external":true},{"path":"@dxos/echo","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true}],"format":"esm"},"src/ref-atom.ts":{"bytes":3183,"imports":[{"path":"@effect-atom/atom/Atom","kind":"import-statement","external":true},{"path":"src/ref-utils.ts","kind":"import-statement","original":"./ref-utils"}],"format":"esm"},"src/index.ts":{"bytes":816,"imports":[{"path":"src/atom.ts","kind":"import-statement","original":"./atom"},{"path":"src/query-atom.ts","kind":"import-statement","original":"./query-atom"},{"path":"src/ref-atom.ts","kind":"import-statement","original":"./ref-atom"}],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":18123},"dist/lib/browser/index.mjs":{"imports":[{"path":"@effect-atom/atom/Atom","kind":"import-statement","external":true},{"path":"lodash.isequal","kind":"import-statement","external":true},{"path":"@dxos/echo","kind":"import-statement","external":true},{"path":"@dxos/invariant","kind":"import-statement","external":true},{"path":"@effect-atom/atom","kind":"import-statement","external":true},{"path":"@dxos/echo","kind":"import-statement","external":true},{"path":"@dxos/util","kind":"import-statement","external":true},{"path":"@effect-atom/atom/Atom","kind":"import-statement","external":true}],"exports":["AtomObj","AtomQuery","AtomRef"],"entryPoint":"src/index.ts","inputs":{"src/atom.ts":{"bytesInOutput":2313},"src/ref-utils.ts":{"bytesInOutput":301},"src/index.ts":{"bytesInOutput":0},"src/query-atom.ts":{"bytesInOutput":2044},"src/ref-atom.ts":{"bytesInOutput":291}},"bytes":5350}}}
|