@dxos/echo-atom 0.8.4-main.bc674ce → 0.8.4-main.d05673bc65

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/echo-atom",
3
- "version": "0.8.4-main.bc674ce",
3
+ "version": "0.8.4-main.d05673bc65",
4
4
  "description": "Effect Atom wrappers for ECHO objects with explicit subscriptions.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -16,8 +16,7 @@
16
16
  ".": {
17
17
  "source": "./src/index.ts",
18
18
  "types": "./dist/types/src/index.d.ts",
19
- "browser": "./dist/lib/browser/index.mjs",
20
- "node": "./dist/lib/node-esm/index.mjs"
19
+ "default": "./dist/lib/neutral/index.mjs"
21
20
  }
22
21
  },
23
22
  "types": "dist/types/src/index.d.ts",
@@ -29,17 +28,19 @@
29
28
  "src"
30
29
  ],
31
30
  "dependencies": {
32
- "@effect-atom/atom": "^0.4.13",
33
- "lodash.isequal": "^4.5.0",
34
- "@dxos/echo": "0.8.4-main.bc674ce",
35
- "@dxos/echo-db": "0.8.4-main.bc674ce",
36
- "@dxos/util": "0.8.4-main.bc674ce",
37
- "@dxos/invariant": "0.8.4-main.bc674ce"
31
+ "@effect-atom/atom": "^0.5.1",
32
+ "@dxos/echo": "0.8.4-main.d05673bc65",
33
+ "@dxos/util": "0.8.4-main.d05673bc65",
34
+ "@dxos/echo-db": "0.8.4-main.d05673bc65",
35
+ "@dxos/invariant": "0.8.4-main.d05673bc65"
38
36
  },
39
37
  "devDependencies": {
40
- "@types/lodash.isequal": "^4.5.0",
41
- "@dxos/random": "0.8.4-main.bc674ce",
42
- "@dxos/test-utils": "0.8.4-main.bc674ce"
38
+ "effect": "3.19.16",
39
+ "@dxos/random": "0.8.4-main.d05673bc65",
40
+ "@dxos/test-utils": "0.8.4-main.d05673bc65"
41
+ },
42
+ "peerDependencies": {
43
+ "effect": "3.19.16"
43
44
  },
44
45
  "publishConfig": {
45
46
  "access": "public"
package/src/atom.test.ts CHANGED
@@ -3,11 +3,12 @@
3
3
  //
4
4
 
5
5
  import * as Registry from '@effect-atom/atom/Registry';
6
- import { describe, expect, test } from 'vitest';
6
+ import { afterEach, beforeEach, describe, expect, test } from 'vitest';
7
7
 
8
- import { Obj } from '@dxos/echo';
8
+ import { Obj, Ref } from '@dxos/echo';
9
9
  import { TestSchema } from '@dxos/echo/testing';
10
10
  import { createObject } from '@dxos/echo-db';
11
+ import { EchoTestBuilder } from '@dxos/echo-db/testing';
11
12
 
12
13
  import * as AtomObj from './atom';
13
14
 
@@ -283,4 +284,115 @@ describe('Echo Atom - Referential Equality', () => {
283
284
  expect(updateCount).toBe(2);
284
285
  expect(registry.get(atom1)).toBe('Updated');
285
286
  });
287
+
288
+ test('AtomObj.make returns same atom instance for different ref instances with same DXN', ({ expect }) => {
289
+ const org = createObject(Obj.make(TestSchema.Organization, { name: 'DXOS' }));
290
+ const person = createObject(
291
+ Obj.make(TestSchema.Person, {
292
+ name: 'Test',
293
+ username: 'test',
294
+ email: 'test@example.com',
295
+ employer: Ref.make(org),
296
+ }),
297
+ );
298
+
299
+ // Each property access returns a new Ref instance from the ECHO proxy.
300
+ const ref1 = person.employer!;
301
+ const ref2 = person.employer!;
302
+ expect(ref1).not.toBe(ref2);
303
+
304
+ // Despite being different Ref instances, they should resolve to the same atom.
305
+ const atom1 = AtomObj.make(ref1);
306
+ const atom2 = AtomObj.make(ref2);
307
+ expect(atom1).toBe(atom2);
308
+ });
309
+ });
310
+
311
+ describe('AtomObj.makeWithReactive', () => {
312
+ test('returns object for direct obj input', () => {
313
+ const obj = createObject(
314
+ Obj.make(TestSchema.Person, { name: 'Test', username: 'test', email: 'test@example.com' }),
315
+ );
316
+
317
+ const registry = Registry.make();
318
+ const atom = AtomObj.makeWithReactive(obj);
319
+ const result = registry.get(atom);
320
+
321
+ expect(result).toBe(obj);
322
+ expect(result?.name).toBe('Test');
323
+ });
324
+
325
+ test('returns same atom for same object', () => {
326
+ const obj = createObject(
327
+ Obj.make(TestSchema.Person, { name: 'Test', username: 'test', email: 'test@example.com' }),
328
+ );
329
+
330
+ const atom1 = AtomObj.makeWithReactive(obj);
331
+ const atom2 = AtomObj.makeWithReactive(obj);
332
+
333
+ expect(atom1).toBe(atom2);
334
+ });
335
+
336
+ describe('with ref input', () => {
337
+ let testBuilder: InstanceType<typeof EchoTestBuilder>;
338
+
339
+ beforeEach(async () => {
340
+ testBuilder = await new EchoTestBuilder().open();
341
+ });
342
+
343
+ afterEach(async () => {
344
+ await testBuilder.close();
345
+ });
346
+
347
+ test('returns resolved object for ref', async ({ expect }) => {
348
+ const { db } = await testBuilder.createDatabase({
349
+ types: [TestSchema.Person, TestSchema.Task],
350
+ });
351
+ const task = db.add(Obj.make(TestSchema.Task, { title: 'Task 1' }));
352
+ const person = db.add(
353
+ Obj.make(TestSchema.Person, {
354
+ name: 'Test',
355
+ username: 'test',
356
+ email: 'test@example.com',
357
+ tasks: [Ref.make(task)],
358
+ }),
359
+ );
360
+ await db.flush();
361
+
362
+ const ref = person.tasks![0];
363
+ const registry = Registry.make();
364
+ const atom = AtomObj.makeWithReactive(ref);
365
+ const result = registry.get(atom);
366
+
367
+ expect(result).toBe(task);
368
+ expect(result?.title).toBe('Task 1');
369
+ });
370
+
371
+ test('returns undefined when ref target was removed', async ({ expect }) => {
372
+ const { db } = await testBuilder.createDatabase({
373
+ types: [TestSchema.Person, TestSchema.Task],
374
+ });
375
+ const task = db.add(Obj.make(TestSchema.Task, { title: 'Task 1' }));
376
+ const person = db.add(
377
+ Obj.make(TestSchema.Person, {
378
+ name: 'Test',
379
+ username: 'test',
380
+ email: 'test@example.com',
381
+ tasks: [Ref.make(task)],
382
+ }),
383
+ );
384
+ await db.flush();
385
+
386
+ const ref = person.tasks![0];
387
+ const registry = Registry.make();
388
+ const atom = AtomObj.makeWithReactive(ref);
389
+
390
+ expect(registry.get(atom)).toBe(task);
391
+
392
+ db.remove(task);
393
+ await db.flush();
394
+
395
+ expect(registry.get(atom)).toBeUndefined();
396
+ });
397
+ });
286
398
  });
package/src/atom.ts CHANGED
@@ -3,9 +3,12 @@
3
3
  //
4
4
 
5
5
  import * as Atom from '@effect-atom/atom/Atom';
6
- import isEqual from 'lodash.isequal';
6
+ import * as Result from '@effect-atom/atom/Result';
7
+ import * as Effect from 'effect/Effect';
8
+ import * as Function from 'effect/Function';
9
+ import * as Option from 'effect/Option';
7
10
 
8
- import { Obj, Ref } from '@dxos/echo';
11
+ import { type Entity, Obj, Ref, Relation } from '@dxos/echo';
9
12
  import { assertArgument } from '@dxos/invariant';
10
13
 
11
14
  import { loadRefTarget } from './ref-utils';
@@ -23,13 +26,13 @@ const objectFamily = Atom.family(<T extends Obj.Unknown>(obj: T): Atom.Atom<Obj.
23
26
  get.addFinalizer(() => unsubscribe());
24
27
 
25
28
  return Obj.getSnapshot(obj);
26
- });
29
+ }).pipe(Atom.keepAlive);
27
30
  });
28
31
 
29
32
  /**
30
- * Internal helper to create an atom from a Ref.
31
- * Handles async loading and subscribes to the target for reactive updates.
32
- * Uses Atom.family internally - same ref reference returns same atom instance.
33
+ * Atom family for ECHO refs.
34
+ * RefImpl implements Effect's Hash/Equal traits using DXN, so different Ref instances
35
+ * pointing to the same object resolve to the same atom.
33
36
  */
34
37
  const refFamily = Atom.family(<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<Obj.Snapshot<T> | undefined> => {
35
38
  return Atom.make<Obj.Snapshot<T> | undefined>((get) => {
@@ -43,10 +46,12 @@ const refFamily = Atom.family(<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Ato
43
46
  return Obj.getSnapshot(target);
44
47
  };
45
48
 
46
- get.addFinalizer(() => unsubscribeTarget?.());
49
+ get.addFinalizer(() => {
50
+ unsubscribeTarget?.();
51
+ });
47
52
 
48
53
  return loadRefTarget(ref, get, setupTargetSubscription);
49
- });
54
+ }).pipe(Atom.keepAlive);
50
55
  });
51
56
 
52
57
  /**
@@ -78,7 +83,7 @@ const propertyFamily = Atom.family(<T extends Obj.Unknown>(obj: T) =>
78
83
 
79
84
  const unsubscribe = Obj.subscribe(obj, () => {
80
85
  const newValue = obj[key];
81
- if (!isEqual(previousSnapshot, newValue)) {
86
+ if (previousSnapshot !== newValue) {
82
87
  previousSnapshot = snapshotForComparison(newValue);
83
88
  // Return a snapshot copy so React sees a new reference.
84
89
  get.setSelf(snapshotForComparison(newValue));
@@ -89,43 +94,46 @@ const propertyFamily = Atom.family(<T extends Obj.Unknown>(obj: T) =>
89
94
 
90
95
  // Return a snapshot copy so React sees a new reference.
91
96
  return snapshotForComparison(obj[key]);
92
- });
97
+ }).pipe(Atom.keepAlive);
93
98
  }),
94
99
  );
95
100
 
96
101
  /**
97
- * Create a read-only atom for a reactive object or ref.
98
- * Works with Echo objects, plain reactive objects (from Obj.make), and Refs.
99
- * Returns immutable snapshots of the object data (branded with SnapshotKindId).
102
+ * Create a read-only atom for a single reactive object or ref.
103
+ * Returns {@link Obj.Snapshot} (immutable plain data), not the live reactive object.
104
+ * Use this when you need one object's data for display or React dependency tracking.
100
105
  * The atom updates automatically when the object is mutated.
101
106
  * For refs, automatically handles async loading.
102
- * Uses Atom.family internally - same object/ref reference returns same atom instance.
107
+ * Uses Atom.family internally - same object/ref returns same atom instance.
103
108
  *
104
109
  * @param objOrRef - The reactive object or ref to create an atom for, or undefined.
105
- * @returns An atom that returns the object snapshot. Returns undefined only for refs (async loading) or undefined input.
110
+ * @returns An atom that returns the object snapshot (plain data). Returns undefined only for refs (async loading) or undefined input.
106
111
  */
107
112
  export function make<T extends Obj.Unknown>(obj: T): Atom.Atom<Obj.Snapshot<T>>;
113
+ export function make<T extends Relation.Unknown>(relation: T): Atom.Atom<Relation.Snapshot<T>>;
114
+ export function make<T extends Entity.Unknown>(entity: T): Atom.Atom<Entity.Snapshot>;
108
115
  export function make<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<Obj.Snapshot<T> | undefined>;
109
116
  export function make<T extends Obj.Unknown>(
110
117
  objOrRef: T | Ref.Ref<T> | undefined,
111
118
  ): Atom.Atom<Obj.Snapshot<T> | undefined>;
112
- export function make<T extends Obj.Unknown>(
119
+ export function make<T extends Entity.Unknown>(
113
120
  objOrRef: T | Ref.Ref<T> | undefined,
114
- ): Atom.Atom<Obj.Snapshot<T> | undefined> {
121
+ ): Atom.Atom<Entity.Snapshot | undefined> {
115
122
  if (objOrRef === undefined) {
116
- return Atom.make<Obj.Snapshot<T> | undefined>(() => undefined);
123
+ return Atom.make<Entity.Snapshot | undefined>(() => undefined);
117
124
  }
118
125
 
119
126
  // Handle Ref inputs.
120
127
  if (Ref.isRef(objOrRef)) {
121
- return refFamily(objOrRef as Ref.Ref<T>);
128
+ return refFamily(objOrRef as any);
122
129
  }
123
130
 
124
131
  // At this point, objOrRef is definitely T (not a Ref).
125
132
  const obj = objOrRef as T;
126
- assertArgument(Obj.isObject(obj), 'obj', 'Object must be a reactive object');
133
+ assertArgument(Obj.isObject(obj) || Relation.isRelation(obj), 'obj', 'Object must be a reactive object');
127
134
 
128
- return objectFamily(obj);
135
+ // TODO(dmaretskyi): Fix echo types during review.
136
+ return objectFamily(obj as any);
129
137
  }
130
138
 
131
139
  /**
@@ -156,3 +164,65 @@ export function makeProperty<T extends Obj.Unknown, K extends keyof T>(
156
164
  assertArgument(key in obj, 'key', 'Property must exist on object');
157
165
  return propertyFamily(obj)(key);
158
166
  }
167
+
168
+ /**
169
+ * Atom family for ECHO objects - returns the live object, not a snapshot.
170
+ * Same as objectFamily but returns T instead of Obj.Snapshot<T>.
171
+ */
172
+ const objectWithReactiveFamily = Atom.family(<T extends Obj.Unknown>(obj: T): Atom.Atom<T> => {
173
+ return Atom.make<T>((get) => {
174
+ const unsubscribe = Obj.subscribe(obj, () => {
175
+ get.setSelf(obj);
176
+ });
177
+
178
+ get.addFinalizer(() => unsubscribe());
179
+
180
+ return obj;
181
+ }).pipe(Atom.keepAlive);
182
+ });
183
+
184
+ /**
185
+ * Atom family for ECHO refs - returns the live reactive object, not a snapshot.
186
+ * Resolves the ref via the database; returns undefined while loading or if unresolved.
187
+ */
188
+ const refWithReactiveFamily = Atom.family(<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<T | undefined> => {
189
+ const effect = (get: Atom.Context) =>
190
+ Effect.gen(function* () {
191
+ const snapshot = get(make(ref));
192
+ if (snapshot == null) return undefined;
193
+ const option = yield* Obj.getReactiveOption(snapshot);
194
+ return Option.getOrElse(option, () => undefined);
195
+ });
196
+
197
+ return Function.pipe(
198
+ Atom.make(effect),
199
+ Atom.map((result) => Result.getOrElse(result, () => undefined)),
200
+ );
201
+ });
202
+
203
+ /**
204
+ * Like {@link make} but returns the live reactive object instead of a snapshot.
205
+ * Same input: Obj or Ref.Ref. Same output shape: Atom that updates when the object mutates.
206
+ * Prefer {@link make} (snapshot) unless you need the live Obj.Obj for generic mutations (e.g. Obj.change).
207
+ *
208
+ * @param objOrRef - The reactive object or ref.
209
+ * @returns An atom that returns the live object. Returns undefined for refs (async loading) or undefined input.
210
+ */
211
+ export function makeWithReactive<T extends Obj.Unknown>(obj: T): Atom.Atom<T>;
212
+ export function makeWithReactive<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<T | undefined>;
213
+ export function makeWithReactive<T extends Obj.Unknown>(objOrRef: T | Ref.Ref<T> | undefined): Atom.Atom<T | undefined>;
214
+ export function makeWithReactive<T extends Obj.Unknown>(
215
+ objOrRef: T | Ref.Ref<T> | undefined,
216
+ ): Atom.Atom<T | undefined> {
217
+ if (objOrRef === undefined) {
218
+ return Atom.make<T | undefined>(() => undefined);
219
+ }
220
+
221
+ if (Ref.isRef(objOrRef)) {
222
+ return refWithReactiveFamily(objOrRef as Ref.Ref<T>);
223
+ }
224
+
225
+ const obj = objOrRef as T;
226
+ assertArgument(Obj.isObject(obj), 'obj', 'Object must be a reactive object');
227
+ return objectWithReactiveFamily(obj);
228
+ }
@@ -7,8 +7,9 @@ import * as Schema from 'effect/Schema';
7
7
  import { afterEach, beforeEach, describe, expect, test } from 'vitest';
8
8
 
9
9
  import { Obj, type QueryResult, Type } from '@dxos/echo';
10
+ import { Filter, Query } from '@dxos/echo';
10
11
  import { TestSchema } from '@dxos/echo/testing';
11
- import { type EchoDatabase, Filter, Query } from '@dxos/echo-db';
12
+ import { type EchoDatabase } from '@dxos/echo-db';
12
13
  import { EchoTestBuilder } from '@dxos/echo-db/testing';
13
14
  import { SpaceId } from '@dxos/keys';
14
15
 
@@ -22,7 +23,7 @@ const TestItem = Schema.Struct({
22
23
  value: Schema.Number,
23
24
  }).pipe(
24
25
  Type.object({
25
- typename: 'example.com/type/TestItem',
26
+ typename: 'com.example.type.test-item',
26
27
  version: '0.1.0',
27
28
  }),
28
29
  );
@@ -47,7 +48,7 @@ describe('AtomQuery', () => {
47
48
  test('creates atom with initial results', async () => {
48
49
  db.add(Obj.make(TestItem, { name: 'Object 1', value: 100 }));
49
50
  db.add(Obj.make(TestItem, { name: 'Object 2', value: 100 }));
50
- await db.flush({ indexes: true });
51
+ await db.flush();
51
52
 
52
53
  const queryResult: QueryResult.QueryResult<TestItem> = db.query(
53
54
  Query.select(Filter.type(TestItem, { value: 100 })),
@@ -63,7 +64,7 @@ describe('AtomQuery', () => {
63
64
 
64
65
  test('registry.subscribe fires on QueryResult changes', async () => {
65
66
  db.add(Obj.make(TestItem, { name: 'Initial', value: 200 }));
66
- await db.flush({ indexes: true });
67
+ await db.flush();
67
68
 
68
69
  const queryResult: QueryResult.QueryResult<TestItem> = db.query(
69
70
  Query.select(Filter.type(TestItem, { value: 200 })),
@@ -86,7 +87,7 @@ describe('AtomQuery', () => {
86
87
 
87
88
  // Add a new object that matches the query.
88
89
  db.add(Obj.make(TestItem, { name: 'New Object', value: 200 }));
89
- await db.flush({ indexes: true, updates: true });
90
+ await db.flush({ updates: true });
90
91
 
91
92
  // Subscription should have fired.
92
93
  expect(updateCount).toBeGreaterThan(0);
@@ -96,7 +97,7 @@ describe('AtomQuery', () => {
96
97
  test('registry.subscribe fires when objects are removed', async () => {
97
98
  const obj1 = db.add(Obj.make(TestItem, { name: 'Object 1', value: 300 }));
98
99
  db.add(Obj.make(TestItem, { name: 'Object 2', value: 300 }));
99
- await db.flush({ indexes: true });
100
+ await db.flush();
100
101
 
101
102
  const queryResult: QueryResult.QueryResult<TestItem> = db.query(
102
103
  Query.select(Filter.type(TestItem, { value: 300 })),
@@ -119,7 +120,7 @@ describe('AtomQuery', () => {
119
120
 
120
121
  // Remove an object.
121
122
  db.remove(obj1);
122
- await db.flush({ indexes: true, updates: true });
123
+ await db.flush({ updates: true });
123
124
 
124
125
  // Subscription should have fired.
125
126
  expect(updateCount).toBeGreaterThan(0);
@@ -129,7 +130,7 @@ describe('AtomQuery', () => {
129
130
 
130
131
  test('unsubscribing from registry stops receiving updates', async () => {
131
132
  db.add(Obj.make(TestItem, { name: 'Initial', value: 400 }));
132
- await db.flush({ indexes: true });
133
+ await db.flush();
133
134
 
134
135
  const queryResult: QueryResult.QueryResult<TestItem> = db.query(
135
136
  Query.select(Filter.type(TestItem, { value: 400 })),
@@ -150,7 +151,7 @@ describe('AtomQuery', () => {
150
151
 
151
152
  // Add object and verify subscription fires.
152
153
  db.add(Obj.make(TestItem, { name: 'Object 2', value: 400 }));
153
- await db.flush({ indexes: true, updates: true });
154
+ await db.flush({ updates: true });
154
155
  const countAfterFirstAdd = updateCount;
155
156
  expect(countAfterFirstAdd).toBeGreaterThan(0);
156
157
 
@@ -159,7 +160,7 @@ describe('AtomQuery', () => {
159
160
 
160
161
  // Add another object.
161
162
  db.add(Obj.make(TestItem, { name: 'Object 3', value: 400 }));
162
- await db.flush({ indexes: true, updates: true });
163
+ await db.flush({ updates: true });
163
164
 
164
165
  // Update count should not have changed after unsubscribe.
165
166
  expect(updateCount).toBe(countAfterFirstAdd);
@@ -179,7 +180,7 @@ describe('AtomQuery', () => {
179
180
 
180
181
  test('multiple atoms from same query share underlying subscription', async () => {
181
182
  db.add(Obj.make(TestItem, { name: 'Object', value: 500 }));
182
- await db.flush({ indexes: true });
183
+ await db.flush();
183
184
 
184
185
  const queryResult: QueryResult.QueryResult<TestItem> = db.query(
185
186
  Query.select(Filter.type(TestItem, { value: 500 })),
package/src/query-atom.ts CHANGED
@@ -37,7 +37,7 @@ const queryableRegistry = new WeakDictionary<string, Database.Queryable>();
37
37
  // Key separator that won't appear in identifiers (DXN strings use colons).
38
38
  const KEY_SEPARATOR = '~';
39
39
 
40
- // Atom.family keyed by "identifier\0serializedAST".
40
+ // Atom.family keyed by "identifier~serializedAST".
41
41
  const queryFamily = Atom.family((key: string) => {
42
42
  // Parse key outside Atom.make - runs once per key.
43
43
  const separatorIndex = key.indexOf(KEY_SEPARATOR);
@@ -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
 
@@ -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
  });
@@ -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({ indexes: true });
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({ indexes: true });
51
+ await db.flush();
52
52
 
53
53
  const ref = Ref.make(targetObj);
54
54
  const atom = AtomRef.make(ref);
@@ -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({ indexes: true });
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({ indexes: true });
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 different atoms for refs created separately to same target', async () => {
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({ indexes: true });
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
- // Different ref objects mean different atoms (keyed by ref reference).
149
- expect(atom1).not.toBe(atom2);
148
+ // Refs with the same DXN resolve to the same atom via Hash/Equal traits.
149
+ expect(atom1).toBe(atom2);
150
150
 
151
- // But both atoms should return the same target data.
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({ indexes: true });
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({ indexes: true });
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": ";;;;;;;AAAA;;cAAAA;EAAA;;AAIA,YAAYC,UAAU;AACtB,OAAOC,aAAa;AAEpB,SAASC,KAAKC,WAAW;AACzB,SAASC,sBAAsB;;;ACUxB,IAAMC,gBAAgB,CAC3BC,KACAC,KACAC,sBAAAA;AAEA,QAAMC,gBAAgBH,IAAII;AAC1B,MAAID,eAAe;AACjB,WAAOD,kBAAkBC,aAAAA;EAC3B;AAGA,OAAKH,IACFK,KAAI,EACJC,KAAK,CAACC,iBAAAA;AACLN,QAAIO,QAAQN,kBAAkBK,YAAAA,CAAAA;EAChC,CAAA,EACCE,MAAM,MAAA;EAEP,CAAA;AAEF,SAAOC;AACT;;;ADvBA,IAAMC,eAAoBC,YAAO,CAAwBC,QAAAA;AACvD,SAAYC,UAAsB,CAACC,QAAAA;AACjC,UAAMC,cAAcC,IAAIC,UAAUL,KAAK,MAAA;AACrCE,UAAII,QAAQF,IAAIG,YAAYP,GAAAA,CAAAA;IAC9B,CAAA;AAEAE,QAAIM,aAAa,MAAML,YAAAA,CAAAA;AAEvB,WAAOC,IAAIG,YAAYP,GAAAA;EACzB,CAAA;AACF,CAAA;AAOA,IAAMS,YAAiBV,YAAO,CAAwBW,QAAAA;AACpD,SAAYT,UAAkC,CAACC,QAAAA;AAC7C,QAAIS;AAEJ,UAAMC,0BAA0B,CAACC,WAAAA;AAC/BF,0BAAAA;AACAA,0BAAoBP,IAAIC,UAAUQ,QAAQ,MAAA;AACxCX,YAAII,QAAQF,IAAIG,YAAYM,MAAAA,CAAAA;MAC9B,CAAA;AACA,aAAOT,IAAIG,YAAYM,MAAAA;IACzB;AAEAX,QAAIM,aAAa,MAAMG,oBAAAA,CAAAA;AAEvB,WAAOG,cAAcJ,KAAKR,KAAKU,uBAAAA;EACjC,CAAA;AACF,CAAA;AAQA,IAAMG,wBAAwB,CAAIC,UAAAA;AAChC,MAAIC,MAAMC,QAAQF,KAAAA,GAAQ;AACxB,WAAO;SAAIA;;EACb;AACA,MAAIA,UAAU,QAAQ,OAAOA,UAAU,UAAU;AAC/C,WAAO;MAAE,GAAGA;IAAM;EACpB;AACA,SAAOA;AACT;AAOA,IAAMG,iBAAsBpB,YAAO,CAAwBC,QACpDD,YAAO,CAAoBqB,QAAAA;AAC9B,SAAYnB,UAAW,CAACC,QAAAA;AAEtB,QAAImB,mBAAmBN,sBAAsBf,IAAIoB,GAAAA,CAAI;AAErD,UAAMjB,cAAcC,IAAIC,UAAUL,KAAK,MAAA;AACrC,YAAMsB,WAAWtB,IAAIoB,GAAAA;AACrB,UAAI,CAACG,QAAQF,kBAAkBC,QAAAA,GAAW;AACxCD,2BAAmBN,sBAAsBO,QAAAA;AAEzCpB,YAAII,QAAQS,sBAAsBO,QAAAA,CAAAA;MACpC;IACF,CAAA;AAEApB,QAAIM,aAAa,MAAML,YAAAA,CAAAA;AAGvB,WAAOY,sBAAsBf,IAAIoB,GAAAA,CAAI;EACvC,CAAA;AACF,CAAA,CAAA;AAmBK,SAASnB,MACduB,UAAoC;AAEpC,MAAIA,aAAaC,QAAW;AAC1B,WAAYxB,UAAkC,MAAMwB,MAAAA;EACtD;AAGA,MAAIC,IAAIC,MAAMH,QAAAA,GAAW;AACvB,WAAOf,UAAUe,QAAAA;EACnB;AAGA,QAAMxB,MAAMwB;AACZI,iBAAexB,IAAIyB,SAAS7B,GAAAA,GAAM,OAAO,kCAAA;AAEzC,SAAOF,aAAaE,GAAAA;AACtB;AAkBO,SAAS8B,aACd9B,KACAoB,KAAM;AAEN,MAAIpB,QAAQyB,QAAW;AACrB,WAAYxB,UAAuB,MAAMwB,MAAAA;EAC3C;AAEAG,iBAAexB,IAAIyB,SAAS7B,GAAAA,GAAM,OAAO,kCAAA;AACzC4B,iBAAeR,OAAOpB,KAAK,OAAO,+BAAA;AAClC,SAAOmB,eAAenB,GAAAA,EAAKoB,GAAAA;AAC7B;;;AE7JA;;;cAAAW;;AAIA,SAASC,QAAAA,aAAY;AAErB,SAASC,KAAKC,UAAoCC,aAA+B;AACjF,SAASC,sBAAsB;AAYxB,IAAMC,YAAY,CAA2BC,gBAClDC,MAAKC,KAAK,CAACC,QAAAA;AAGT,QAAMC,cAAcJ,YAAYK,UAAU,MAAA;AACxCF,QAAIG,QAAQN,YAAYO,OAAO;EACjC,CAAA;AAGAJ,MAAIK,aAAaJ,WAAAA;AAEjB,SAAOJ,YAAYO;AACrB,CAAA;AAGF,IAAME,oBAAoB,IAAIC,eAAAA;AAG9B,IAAMC,gBAAgB;AAGtB,IAAMC,cAAcX,MAAKY,OAAO,CAACC,QAAAA;AAE/B,QAAMC,iBAAiBD,IAAIE,QAAQL,aAAAA;AACnC,QAAMM,aAAaH,IAAII,MAAM,GAAGH,cAAAA;AAChC,QAAMI,gBAAgBL,IAAII,MAAMH,iBAAiB,CAAA;AAGjD,QAAMK,YAAYX,kBAAkBN,IAAIc,UAAAA;AACxC,MAAI,CAACG,WAAW;AACd,WAAOnB,MAAKC,KAAK,MAAM,CAAA,CAAE;EAC3B;AAGA,QAAMmB,MAAMC,KAAKC,MAAMJ,aAAAA;AACvB,QAAMnB,cAAcoB,UAAUI,MAAMC,MAAMC,QAAQL,GAAAA,CAAAA;AAElD,SAAOpB,MAAKC,KAAK,CAACC,QAAAA;AAChB,UAAMC,cAAcJ,YAAYK,UAAU,MAAA;AACxCF,UAAIG,QAAQN,YAAYO,OAAO;IACjC,CAAA;AACAJ,QAAIK,aAAaJ,WAAAA;AAEjB,WAAOJ,YAAYO;EACrB,CAAA;AACF,CAAA;AAMA,IAAMoB,yBAAyB,CAACP,cAAAA;AAE9B,MAAIQ,SAASC,WAAWT,SAAAA,GAAY;AAClC,WAAOA,UAAUU;EACnB;AAEA,MAAI,SAASV,aAAaA,UAAUW,eAAeC,KAAK;AACtD,WAAOZ,UAAUW,IAAIE,SAAQ;EAC/B;AAEA,MAAI,QAAQb,aAAa,OAAOA,UAAUc,OAAO,UAAU;AACzD,WAAOd,UAAUc;EACnB;AACA,QAAM,IAAIC,MAAM,6CAAA;AAClB;AAWO,IAAMjC,QAAO,CAClBkB,WACAgB,kBAAAA;AAEA,QAAMnB,aAAaU,uBAAuBP,SAAAA;AAC1C,SAAOiB,cAAcjB,WAAWH,YAAYmB,aAAAA;AAC9C;AAKA,IAAMC,gBAAgB,CACpBjB,WACAH,YACAmB,kBAAAA;AAGA3B,oBAAkB6B,IAAIrB,YAAYG,SAAAA;AAGlC,QAAMmB,kBAA6Bd,MAAMe,GAAGJ,aAAAA,IACxCA,gBACAX,MAAMgB,OAAOL,aAAAA;AAGjB,QAAMtB,MAAM,GAAGG,UAAAA,GAAaN,aAAAA,GAAgBW,KAAKoB,UAAUH,gBAAgBlB,GAAG,CAAA;AAE9E,SAAOT,YAAYE,GAAAA;AACrB;;;AC3HA;;cAAA6B;;AAIA,YAAYC,WAAU;AAYtB,IAAMC,aAAiBC,aAAO,CAAIC,QAAAA;AAChC,SAAYC,WAAoB,CAACC,QAAAA;AAC/B,WAAOC,cAAcH,KAAKE,KAAK,CAACE,WAAWA,MAAAA;EAC7C,CAAA;AACF,CAAA;AAWO,IAAMH,QAAOH;",
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}}}