@dxos/echo-atom 0.8.4-main.52d7546f51 → 0.8.4-main.6fa680abb7

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.52d7546f51",
3
+ "version": "0.8.4-main.6fa680abb7",
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",
@@ -29,17 +29,15 @@
29
29
  ],
30
30
  "dependencies": {
31
31
  "@effect-atom/atom": "^0.5.1",
32
- "lodash.isequal": "^4.5.0",
33
- "@dxos/echo": "0.8.4-main.52d7546f51",
34
- "@dxos/echo-db": "0.8.4-main.52d7546f51",
35
- "@dxos/invariant": "0.8.4-main.52d7546f51",
36
- "@dxos/util": "0.8.4-main.52d7546f51"
32
+ "@dxos/echo-db": "0.8.4-main.6fa680abb7",
33
+ "@dxos/echo": "0.8.4-main.6fa680abb7",
34
+ "@dxos/invariant": "0.8.4-main.6fa680abb7",
35
+ "@dxos/util": "0.8.4-main.6fa680abb7"
37
36
  },
38
37
  "devDependencies": {
39
- "@types/lodash.isequal": "^4.5.0",
40
38
  "effect": "3.19.16",
41
- "@dxos/test-utils": "0.8.4-main.52d7546f51",
42
- "@dxos/random": "0.8.4-main.52d7546f51"
39
+ "@dxos/test-utils": "0.8.4-main.6fa680abb7",
40
+ "@dxos/random": "0.8.4-main.6fa680abb7"
43
41
  },
44
42
  "peerDependencies": {
45
43
  "effect": "3.19.16"
package/src/atom.ts CHANGED
@@ -7,9 +7,8 @@ import * as Result from '@effect-atom/atom/Result';
7
7
  import * as Effect from 'effect/Effect';
8
8
  import * as Function from 'effect/Function';
9
9
  import * as Option from 'effect/Option';
10
- import isEqual from 'lodash.isequal';
11
10
 
12
- import { Obj, Ref } from '@dxos/echo';
11
+ import { type Entity, Obj, Ref, Relation } from '@dxos/echo';
13
12
  import { assertArgument } from '@dxos/invariant';
14
13
 
15
14
  import { loadRefTarget } from './ref-utils';
@@ -27,7 +26,7 @@ const objectFamily = Atom.family(<T extends Obj.Unknown>(obj: T): Atom.Atom<Obj.
27
26
  get.addFinalizer(() => unsubscribe());
28
27
 
29
28
  return Obj.getSnapshot(obj);
30
- });
29
+ }).pipe(Atom.keepAlive);
31
30
  });
32
31
 
33
32
  /**
@@ -52,7 +51,7 @@ const refFamily = Atom.family(<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Ato
52
51
  });
53
52
 
54
53
  return loadRefTarget(ref, get, setupTargetSubscription);
55
- });
54
+ }).pipe(Atom.keepAlive);
56
55
  });
57
56
 
58
57
  /**
@@ -84,7 +83,7 @@ const propertyFamily = Atom.family(<T extends Obj.Unknown>(obj: T) =>
84
83
 
85
84
  const unsubscribe = Obj.subscribe(obj, () => {
86
85
  const newValue = obj[key];
87
- if (!isEqual(previousSnapshot, newValue)) {
86
+ if (previousSnapshot !== newValue) {
88
87
  previousSnapshot = snapshotForComparison(newValue);
89
88
  // Return a snapshot copy so React sees a new reference.
90
89
  get.setSelf(snapshotForComparison(newValue));
@@ -95,7 +94,7 @@ const propertyFamily = Atom.family(<T extends Obj.Unknown>(obj: T) =>
95
94
 
96
95
  // Return a snapshot copy so React sees a new reference.
97
96
  return snapshotForComparison(obj[key]);
98
- });
97
+ }).pipe(Atom.keepAlive);
99
98
  }),
100
99
  );
101
100
 
@@ -111,27 +110,30 @@ const propertyFamily = Atom.family(<T extends Obj.Unknown>(obj: T) =>
111
110
  * @returns An atom that returns the object snapshot (plain data). Returns undefined only for refs (async loading) or undefined input.
112
111
  */
113
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>;
114
115
  export function make<T extends Obj.Unknown>(ref: Ref.Ref<T>): Atom.Atom<Obj.Snapshot<T> | undefined>;
115
116
  export function make<T extends Obj.Unknown>(
116
117
  objOrRef: T | Ref.Ref<T> | undefined,
117
118
  ): Atom.Atom<Obj.Snapshot<T> | undefined>;
118
- export function make<T extends Obj.Unknown>(
119
+ export function make<T extends Entity.Unknown>(
119
120
  objOrRef: T | Ref.Ref<T> | undefined,
120
- ): Atom.Atom<Obj.Snapshot<T> | undefined> {
121
+ ): Atom.Atom<Entity.Snapshot | undefined> {
121
122
  if (objOrRef === undefined) {
122
- return Atom.make<Obj.Snapshot<T> | undefined>(() => undefined);
123
+ return Atom.make<Entity.Snapshot | undefined>(() => undefined);
123
124
  }
124
125
 
125
126
  // Handle Ref inputs.
126
127
  if (Ref.isRef(objOrRef)) {
127
- return refFamily(objOrRef as Ref.Ref<T>);
128
+ return refFamily(objOrRef as any);
128
129
  }
129
130
 
130
131
  // At this point, objOrRef is definitely T (not a Ref).
131
132
  const obj = objOrRef as T;
132
- 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');
133
134
 
134
- return objectFamily(obj);
135
+ // TODO(dmaretskyi): Fix echo types during review.
136
+ return objectFamily(obj as any);
135
137
  }
136
138
 
137
139
  /**
@@ -176,7 +178,7 @@ const objectWithReactiveFamily = Atom.family(<T extends Obj.Unknown>(obj: T): At
176
178
  get.addFinalizer(() => unsubscribe());
177
179
 
178
180
  return obj;
179
- });
181
+ }).pipe(Atom.keepAlive);
180
182
  });
181
183
 
182
184
  /**
@@ -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
  );
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
  });