@dxos/echo-react 0.8.4-main.fcc0d83b33 → 0.8.4-staging.60fe92afc8
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/LICENSE +102 -5
- package/dist/lib/neutral/index.mjs +68 -28
- package/dist/lib/neutral/index.mjs.map +4 -4
- package/dist/lib/neutral/meta.json +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/useObject.d.ts.map +1 -1
- package/dist/types/src/useType.d.ts +14 -0
- package/dist/types/src/useType.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -6
- package/src/index.ts +1 -1
- package/src/useObject.test.tsx +1 -1
- package/src/useObject.ts +35 -9
- package/src/useQuery.__test.tsx +1 -2
- package/src/useType.ts +74 -0
- package/dist/types/src/useSchema.d.ts +0 -6
- package/dist/types/src/useSchema.d.ts.map +0 -1
- package/src/useSchema.ts +0 -42
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/echo-react",
|
|
3
|
-
"version": "0.8.4-
|
|
3
|
+
"version": "0.8.4-staging.60fe92afc8",
|
|
4
4
|
"description": "React integration for ECHO.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/dxos/dxos"
|
|
10
10
|
},
|
|
11
|
-
"license": "
|
|
11
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
12
12
|
"author": "DXOS.org",
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"type": "module",
|
|
@@ -25,10 +25,9 @@
|
|
|
25
25
|
"src"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@effect-atom/atom": "^0.5.
|
|
28
|
+
"@effect-atom/atom": "^0.5.3",
|
|
29
29
|
"@effect-atom/atom-react": "^0.5.0",
|
|
30
|
-
"@dxos/echo": "0.8.4-
|
|
31
|
-
"@dxos/echo-atom": "0.8.4-main.fcc0d83b33"
|
|
30
|
+
"@dxos/echo": "0.8.4-staging.60fe92afc8"
|
|
32
31
|
},
|
|
33
32
|
"devDependencies": {
|
|
34
33
|
"@testing-library/react": "^16.3.0",
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
"@types/react-dom": "~19.2.3",
|
|
37
36
|
"react": "~19.2.3",
|
|
38
37
|
"react-dom": "~19.2.3",
|
|
39
|
-
"@dxos/echo-
|
|
38
|
+
"@dxos/echo-client": "0.8.4-staging.60fe92afc8"
|
|
40
39
|
},
|
|
41
40
|
"peerDependencies": {
|
|
42
41
|
"react": "~19.2.3"
|
package/src/index.ts
CHANGED
package/src/useObject.test.tsx
CHANGED
|
@@ -9,7 +9,7 @@ import React, { type PropsWithChildren } from 'react';
|
|
|
9
9
|
import { describe, expect, test } from 'vitest';
|
|
10
10
|
|
|
11
11
|
import { Obj, Ref } from '@dxos/echo';
|
|
12
|
-
import { createObject } from '@dxos/echo-
|
|
12
|
+
import { createObject } from '@dxos/echo-client';
|
|
13
13
|
import { TestSchema } from '@dxos/echo/testing';
|
|
14
14
|
|
|
15
15
|
import { useObject } from './useObject';
|
package/src/useObject.ts
CHANGED
|
@@ -7,7 +7,6 @@ import * as Atom from '@effect-atom/atom/Atom';
|
|
|
7
7
|
import { useCallback, useMemo } from 'react';
|
|
8
8
|
|
|
9
9
|
import { Obj, Ref } from '@dxos/echo';
|
|
10
|
-
import { AtomObj } from '@dxos/echo-atom';
|
|
11
10
|
|
|
12
11
|
export interface ObjectUpdateCallback<T> {
|
|
13
12
|
(update: (obj: Obj.Mutable<T>) => void): void;
|
|
@@ -144,9 +143,9 @@ export const useObject: {
|
|
|
144
143
|
);
|
|
145
144
|
|
|
146
145
|
if (property !== undefined) {
|
|
147
|
-
// For
|
|
148
|
-
//
|
|
149
|
-
|
|
146
|
+
// For refs, subscribe to load event only (not full mutation tracking).
|
|
147
|
+
// Property-level updates are handled by useObjectProperty once the ref loads.
|
|
148
|
+
useRefLoad(objOrRef);
|
|
150
149
|
return [useObjectProperty(liveObj, property as any), callback];
|
|
151
150
|
} else {
|
|
152
151
|
return [useObjectValue(objOrRef), callback];
|
|
@@ -155,22 +154,49 @@ export const useObject: {
|
|
|
155
154
|
|
|
156
155
|
/**
|
|
157
156
|
* Internal hook for subscribing to an Echo object or Ref.
|
|
158
|
-
* AtomObj.make handles both objects and refs, returning snapshots.
|
|
159
157
|
*/
|
|
160
158
|
const useObjectValue = <T extends Obj.Unknown>(objOrRef: T | Ref.Ref<T> | undefined): Obj.Snapshot<T> | undefined => {
|
|
161
|
-
const atom = useMemo(() =>
|
|
159
|
+
const atom = useMemo(() => {
|
|
160
|
+
if (objOrRef == null) {
|
|
161
|
+
return Atom.make<Obj.Snapshot<T> | undefined>(() => undefined);
|
|
162
|
+
}
|
|
163
|
+
if (Ref.isRef(objOrRef)) {
|
|
164
|
+
return Obj.atom(objOrRef);
|
|
165
|
+
}
|
|
166
|
+
return Obj.atom(objOrRef);
|
|
167
|
+
}, [objOrRef]);
|
|
162
168
|
return useAtomValue(atom) as Obj.Snapshot<T> | undefined;
|
|
163
169
|
};
|
|
164
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Internal hook for subscribing to ref resolution only (load-once).
|
|
173
|
+
* Triggers a re-render when the ref target first becomes available,
|
|
174
|
+
* without subscribing to subsequent target mutations.
|
|
175
|
+
* For non-refs, this is a no-op.
|
|
176
|
+
*/
|
|
177
|
+
const useRefLoad = <T extends Obj.Unknown>(objOrRef: T | Ref.Ref<T> | undefined): void => {
|
|
178
|
+
const atom = useMemo(() => {
|
|
179
|
+
if (objOrRef == null || !Ref.isRef(objOrRef)) {
|
|
180
|
+
return Atom.make<T | undefined>(() => undefined);
|
|
181
|
+
}
|
|
182
|
+
return objOrRef.atom;
|
|
183
|
+
}, [objOrRef]);
|
|
184
|
+
useAtomValue(atom);
|
|
185
|
+
};
|
|
186
|
+
|
|
165
187
|
/**
|
|
166
188
|
* Internal hook for subscribing to a specific property of an Echo object.
|
|
167
|
-
* Uses useAtomValue directly since makeProperty returns the value directly.
|
|
168
189
|
*/
|
|
169
190
|
const useObjectProperty = <T extends Obj.Unknown, K extends keyof T>(
|
|
170
191
|
obj: T | undefined,
|
|
171
192
|
property: K,
|
|
172
193
|
): T[K] | undefined => {
|
|
173
|
-
const atom = useMemo(() =>
|
|
194
|
+
const atom = useMemo(() => {
|
|
195
|
+
if (obj == null) {
|
|
196
|
+
return Atom.make<T[K] | undefined>(() => undefined);
|
|
197
|
+
}
|
|
198
|
+
return Obj.atomProperty(obj, property);
|
|
199
|
+
}, [obj, property]);
|
|
174
200
|
return useAtomValue(atom);
|
|
175
201
|
};
|
|
176
202
|
|
|
@@ -191,7 +217,7 @@ export const useObjects = <T extends Obj.Unknown>(refs: readonly Ref.Ref<T>[]):
|
|
|
191
217
|
Atom.make((get) => {
|
|
192
218
|
const results: Obj.Snapshot<T>[] = [];
|
|
193
219
|
for (const ref of refs) {
|
|
194
|
-
const value = get(
|
|
220
|
+
const value = get(Obj.atom(ref));
|
|
195
221
|
if (value !== undefined) {
|
|
196
222
|
results.push(value as Obj.Snapshot<T>);
|
|
197
223
|
}
|
package/src/useQuery.__test.tsx
CHANGED
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
import { renderHook } from '@testing-library/react';
|
|
9
9
|
import { describe, expect, test } from 'vitest';
|
|
10
10
|
|
|
11
|
-
import { Filter } from '@dxos/
|
|
12
|
-
import { Obj, Type } from '@dxos/echo';
|
|
11
|
+
import { Filter, Obj, Type } from '@dxos/echo';
|
|
13
12
|
import { TestSchema } from '@dxos/echo/testing';
|
|
14
13
|
|
|
15
14
|
import { createClient, createClientContextProvider } from '../testing/util';
|
package/src/useType.ts
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useMemo, useSyncExternalStore } from 'react';
|
|
6
|
+
|
|
7
|
+
import { DXN, EID, URI, type Database, Filter, Query, Scope, Type } from '@dxos/echo';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Subscribe to and retrieve a type by its URI from a space: a static schema's typename DXN, or a
|
|
11
|
+
* persisted (database) schema's `echo:` EID (what `Type.getURI` / `getTypeURIFromQuery` produce).
|
|
12
|
+
*
|
|
13
|
+
* Fans across the owning space db (persisted custom types) and the shared
|
|
14
|
+
* registry (static/runtime plugin types). Persisted types live only in the db,
|
|
15
|
+
* so a registry-only lookup misses them.
|
|
16
|
+
*
|
|
17
|
+
* DXN matching is version-agnostic: `dxn:com.example/Foo` matches `dxn:com.example/Foo:0.1.0`.
|
|
18
|
+
* This lets callers pass a bare typename DXN (no version) from e.g. a `ReferenceAnnotation`.
|
|
19
|
+
*/
|
|
20
|
+
export const useType = <T extends Type.AnyEntity = Type.AnyEntity>(
|
|
21
|
+
db?: Database.Database,
|
|
22
|
+
typeUri?: URI.URI,
|
|
23
|
+
): T | undefined => {
|
|
24
|
+
const { subscribe, getType } = useMemo(() => {
|
|
25
|
+
if (!typeUri || !db) {
|
|
26
|
+
return {
|
|
27
|
+
subscribe: () => () => {},
|
|
28
|
+
getType: (): T | undefined => undefined,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const searchEid = EID.isEID(typeUri) ? EID.tryParse(typeUri) : undefined;
|
|
33
|
+
const searchDxn = DXN.isDXN(typeUri) ? DXN.tryMake(typeUri) : undefined;
|
|
34
|
+
|
|
35
|
+
const queryResult = db.query(Query.select(Filter.type(Type.Type)).from(Scope.space(), Scope.registry()));
|
|
36
|
+
let subscribed = false;
|
|
37
|
+
const find = (): T | undefined => {
|
|
38
|
+
if (!subscribed) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
return queryResult.results.find((type) => {
|
|
42
|
+
const uri = Type.getURI(type);
|
|
43
|
+
if (uri === typeUri) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
// EID matching is space-agnostic: echo:/<id> matches echo://<space>/<id>.
|
|
47
|
+
if (searchEid && EID.isEID(uri)) {
|
|
48
|
+
const typeEid = EID.tryParse(uri);
|
|
49
|
+
return typeEid != null && EID.getEntityId(typeEid) === EID.getEntityId(searchEid);
|
|
50
|
+
}
|
|
51
|
+
// DXN matching is version-agnostic so callers may pass an unversioned DXN.
|
|
52
|
+
if (searchDxn && DXN.isDXN(uri)) {
|
|
53
|
+
const typeDxn = DXN.tryMake(uri);
|
|
54
|
+
return typeDxn != null && DXN.getName(typeDxn) === DXN.getName(searchDxn);
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}) as T | undefined;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
return {
|
|
61
|
+
subscribe: (onStoreChange: () => void) => {
|
|
62
|
+
subscribed = true;
|
|
63
|
+
const unsubscribe = queryResult.subscribe(onStoreChange);
|
|
64
|
+
return () => {
|
|
65
|
+
unsubscribe();
|
|
66
|
+
subscribed = false;
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
getType: find,
|
|
70
|
+
};
|
|
71
|
+
}, [typeUri, db]);
|
|
72
|
+
|
|
73
|
+
return useSyncExternalStore(subscribe, getType);
|
|
74
|
+
};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type Database, type Type } from '@dxos/echo';
|
|
2
|
-
/**
|
|
3
|
-
* Subscribe to and retrieve schema changes from a space's schema registry.
|
|
4
|
-
*/
|
|
5
|
-
export declare const useSchema: <T extends Type.AnyEntity = Type.AnyEntity>(db?: Database.Database, typename?: string) => T | undefined;
|
|
6
|
-
//# sourceMappingURL=useSchema.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useSchema.d.ts","sourceRoot":"","sources":["../../../src/useSchema.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,OAC5D,QAAQ,CAAC,QAAQ,aACX,MAAM,KAChB,CAAC,GAAG,SA2BN,CAAC"}
|
package/src/useSchema.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2025 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { useMemo, useSyncExternalStore } from 'react';
|
|
6
|
-
|
|
7
|
-
import { type Database, type Type } from '@dxos/echo';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Subscribe to and retrieve schema changes from a space's schema registry.
|
|
11
|
-
*/
|
|
12
|
-
export const useSchema = <T extends Type.AnyEntity = Type.AnyEntity>(
|
|
13
|
-
db?: Database.Database,
|
|
14
|
-
typename?: string,
|
|
15
|
-
): T | undefined => {
|
|
16
|
-
const { subscribe, getSchema } = useMemo(() => {
|
|
17
|
-
if (!typename || !db) {
|
|
18
|
-
return {
|
|
19
|
-
subscribe: () => () => {},
|
|
20
|
-
getSchema: () => undefined,
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const query = db.schemaRegistry.query({ typename, location: ['database', 'runtime'] });
|
|
25
|
-
const initialResult = query.runSync()[0];
|
|
26
|
-
let currentSchema = initialResult;
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
subscribe: (onStoreChange: () => void) => {
|
|
30
|
-
const unsubscribe = query.subscribe(() => {
|
|
31
|
-
currentSchema = query.results[0];
|
|
32
|
-
onStoreChange();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
return unsubscribe;
|
|
36
|
-
},
|
|
37
|
-
getSchema: () => currentSchema,
|
|
38
|
-
};
|
|
39
|
-
}, [typename, db]);
|
|
40
|
-
|
|
41
|
-
return useSyncExternalStore(subscribe, getSchema) as T;
|
|
42
|
-
};
|