@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/README.md +28 -18
- package/dist/lib/neutral/index.mjs +7 -8
- package/dist/lib/neutral/index.mjs.map +3 -3
- package/dist/lib/neutral/meta.json +1 -1
- package/dist/types/src/atom.d.ts +3 -1
- package/dist/types/src/atom.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +7 -9
- package/src/atom.ts +15 -13
- package/src/query-atom.test.ts +3 -2
- package/src/query-atom.ts +1 -1
- package/src/reactivity.test.ts +27 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-atom",
|
|
3
|
-
"version": "0.8.4-main.
|
|
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
|
-
"
|
|
33
|
-
"@dxos/echo": "0.8.4-main.
|
|
34
|
-
"@dxos/
|
|
35
|
-
"@dxos/
|
|
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.
|
|
42
|
-
"@dxos/random": "0.8.4-main.
|
|
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 (
|
|
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
|
|
119
|
+
export function make<T extends Entity.Unknown>(
|
|
119
120
|
objOrRef: T | Ref.Ref<T> | undefined,
|
|
120
|
-
): Atom.Atom<
|
|
121
|
+
): Atom.Atom<Entity.Snapshot | undefined> {
|
|
121
122
|
if (objOrRef === undefined) {
|
|
122
|
-
return Atom.make<
|
|
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
|
|
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
|
-
|
|
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
|
/**
|
package/src/query-atom.test.ts
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
|
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);
|
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
|
|
|
@@ -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
|
});
|