@dxos/echo 0.9.0 → 0.9.1-staging.ee54ba693a
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/dist/lib/neutral/Annotation.mjs +30 -8
- package/dist/lib/neutral/Database.mjs +7 -3
- package/dist/lib/neutral/Entity.mjs +15 -11
- package/dist/lib/neutral/Err.mjs +7 -3
- package/dist/lib/neutral/Feed.mjs +15 -19
- package/dist/lib/neutral/Filter.mjs +13 -11
- package/dist/lib/neutral/Format.mjs +3 -7
- package/dist/lib/neutral/JsonSchema.mjs +7 -7
- package/dist/lib/neutral/Migration.mjs +11 -11
- package/dist/lib/neutral/Obj.mjs +19 -13
- package/dist/lib/neutral/Order.mjs +1 -1
- package/dist/lib/neutral/Query.mjs +17 -17
- package/dist/lib/neutral/QueryResult.mjs +1 -1
- package/dist/lib/neutral/Ref.mjs +6 -6
- package/dist/lib/neutral/Registry.mjs +1 -1
- package/dist/lib/neutral/Relation.mjs +12 -12
- package/dist/lib/neutral/Tag.mjs +15 -15
- package/dist/lib/neutral/Text.mjs +25 -0
- package/dist/lib/neutral/Text.mjs.map +7 -0
- package/dist/lib/neutral/Type.mjs +10 -10
- package/dist/lib/neutral/chunk-2HKGAQM4.mjs +1394 -0
- package/dist/lib/neutral/chunk-2HKGAQM4.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-O6BH7EPN.mjs → chunk-2ZHECXUR.mjs} +5 -4
- package/dist/lib/neutral/chunk-2ZHECXUR.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-QQIYS74I.mjs → chunk-3KTE2Y7Q.mjs} +20 -20
- package/dist/lib/neutral/chunk-3KTE2Y7Q.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-R5W6DXR4.mjs → chunk-3TN2VMI4.mjs} +72 -36
- package/dist/lib/neutral/chunk-3TN2VMI4.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-5SMDBFVB.mjs → chunk-42L4HJ3Z.mjs} +11 -15
- package/dist/lib/neutral/chunk-42L4HJ3Z.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-7PI7C4EF.mjs → chunk-4A2NWKRJ.mjs} +5 -5
- package/dist/lib/neutral/chunk-ACFAOTKJ.mjs +1531 -0
- package/dist/lib/neutral/chunk-ACFAOTKJ.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-SBVFRTST.mjs → chunk-BEFR7LEA.mjs} +20 -10
- package/dist/lib/neutral/chunk-BEFR7LEA.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-6M2Z6WBH.mjs → chunk-BXIKXT7Q.mjs} +2 -6
- package/dist/lib/neutral/{chunk-6M2Z6WBH.mjs.map → chunk-BXIKXT7Q.mjs.map} +1 -1
- package/dist/lib/neutral/{chunk-UUP46KUQ.mjs → chunk-C57XMMBY.mjs} +21 -20
- package/dist/lib/neutral/chunk-C57XMMBY.mjs.map +7 -0
- package/dist/lib/neutral/chunk-C62JYFAD.mjs +183 -0
- package/dist/lib/neutral/chunk-C62JYFAD.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-MZ7K3MLL.mjs → chunk-FSGX2R62.mjs} +3 -3
- package/dist/lib/neutral/chunk-FSGX2R62.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-TYGKCRMK.mjs → chunk-HBXT3GSB.mjs} +37 -15
- package/dist/lib/neutral/chunk-HBXT3GSB.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-WU3GIANS.mjs → chunk-IQR45HAD.mjs} +1 -1
- package/dist/lib/neutral/chunk-IQR45HAD.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-TFEWTY5A.mjs → chunk-KCBW4BR6.mjs} +4 -8
- package/dist/lib/neutral/chunk-KCBW4BR6.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-WTQJHC75.mjs → chunk-L3E5IXAE.mjs} +225 -45
- package/dist/lib/neutral/chunk-L3E5IXAE.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-EVK6XBXO.mjs → chunk-PRC4FEKJ.mjs} +2 -2
- package/dist/lib/neutral/chunk-PRC4FEKJ.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-6YDI3J37.mjs → chunk-SCT6FFCR.mjs} +9 -4
- package/dist/lib/neutral/chunk-SCT6FFCR.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-BBFJWWAV.mjs → chunk-TOCVVFK2.mjs} +26 -2
- package/dist/lib/neutral/chunk-TOCVVFK2.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-RIVWNMSF.mjs → chunk-U53LSQQA.mjs} +3 -3
- package/dist/lib/neutral/{chunk-RIVWNMSF.mjs.map → chunk-U53LSQQA.mjs.map} +1 -1
- package/dist/lib/neutral/{chunk-IGK6FN65.mjs → chunk-UGFFV2FR.mjs} +2 -2
- package/dist/lib/neutral/{chunk-35INCYOE.mjs → chunk-UWLP4RFM.mjs} +1 -1
- package/dist/lib/neutral/chunk-UWLP4RFM.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-LWXVKPPW.mjs → chunk-UZHCQ6NI.mjs} +13 -11
- package/dist/lib/neutral/{chunk-LWXVKPPW.mjs.map → chunk-UZHCQ6NI.mjs.map} +3 -3
- package/dist/lib/neutral/{chunk-5SUJPHAE.mjs → chunk-WKIT2YY5.mjs} +2 -2
- package/dist/lib/neutral/{chunk-T6E37YIP.mjs → chunk-XB6ISRAO.mjs} +38 -18
- package/dist/lib/neutral/chunk-XB6ISRAO.mjs.map +7 -0
- package/dist/lib/neutral/chunk-XXL42WCB.mjs +84 -0
- package/dist/lib/neutral/chunk-XXL42WCB.mjs.map +7 -0
- package/dist/lib/neutral/{chunk-ZGNNFYHS.mjs → chunk-XXWK4J53.mjs} +27 -42
- package/dist/lib/neutral/chunk-XXWK4J53.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +30 -26
- package/dist/lib/neutral/internal/index.mjs +111 -69
- package/dist/lib/neutral/meta.json +1 -1
- package/dist/lib/neutral/testing/index.mjs +79 -60
- package/dist/lib/neutral/testing/index.mjs.map +3 -3
- package/dist/types/src/Annotation.d.ts +35 -1
- package/dist/types/src/Annotation.d.ts.map +1 -1
- package/dist/types/src/Collection.d.ts +9 -8
- package/dist/types/src/Collection.d.ts.map +1 -1
- package/dist/types/src/Database.d.ts +38 -0
- package/dist/types/src/Database.d.ts.map +1 -1
- package/dist/types/src/Dataset.d.ts +1 -16
- package/dist/types/src/Dataset.d.ts.map +1 -1
- package/dist/types/src/Entity.d.ts +2 -0
- package/dist/types/src/Entity.d.ts.map +1 -1
- package/dist/types/src/Err.d.ts +67 -0
- package/dist/types/src/Err.d.ts.map +1 -1
- package/dist/types/src/Feed.d.ts +59 -80
- package/dist/types/src/Feed.d.ts.map +1 -1
- package/dist/types/src/Filter.d.ts +9 -1
- package/dist/types/src/Filter.d.ts.map +1 -1
- package/dist/types/src/Hypergraph.d.ts +2 -9
- package/dist/types/src/Hypergraph.d.ts.map +1 -1
- package/dist/types/src/Migration.d.ts +1 -1
- package/dist/types/src/Migration.d.ts.map +1 -1
- package/dist/types/src/Obj.d.ts +33 -6
- package/dist/types/src/Obj.d.ts.map +1 -1
- package/dist/types/src/Order.d.ts +1 -1
- package/dist/types/src/Order.d.ts.map +1 -1
- package/dist/types/src/Query.d.ts +1 -1
- package/dist/types/src/Query.d.ts.map +1 -1
- package/dist/types/src/QueryResult.d.ts +2 -2
- package/dist/types/src/QueryResult.d.ts.map +1 -1
- package/dist/types/src/Ref.d.ts +10 -6
- package/dist/types/src/Ref.d.ts.map +1 -1
- package/dist/types/src/Registry.d.ts +1 -1
- package/dist/types/src/Relation.d.ts.map +1 -1
- package/dist/types/src/Tag.d.ts +11 -13
- package/dist/types/src/Tag.d.ts.map +1 -1
- package/dist/types/src/Text.d.ts +41 -0
- package/dist/types/src/Text.d.ts.map +1 -0
- package/dist/types/src/Text.test.d.ts +2 -0
- package/dist/types/src/Text.test.d.ts.map +1 -0
- package/dist/types/src/Type.d.ts +38 -21
- package/dist/types/src/Type.d.ts.map +1 -1
- package/dist/types/src/View.d.ts +8 -9
- package/dist/types/src/View.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/internal/Annotation/annotations.d.ts +50 -0
- package/dist/types/src/internal/Annotation/annotations.d.ts.map +1 -1
- package/dist/types/src/internal/Annotation/atoms.d.ts +14 -0
- package/dist/types/src/internal/Annotation/atoms.d.ts.map +1 -0
- package/dist/types/src/internal/Annotation/entity-dictionary.d.ts +13 -0
- package/dist/types/src/internal/Annotation/entity-dictionary.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/api.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/guard.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/model.d.ts +1 -1
- package/dist/types/src/internal/Entity/model.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/object.d.ts +5 -1
- package/dist/types/src/internal/Entity/object.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/relation.d.ts +7 -0
- package/dist/types/src/internal/Entity/relation.d.ts.map +1 -1
- package/dist/types/src/internal/Entity/type-uri.d.ts.map +1 -1
- package/dist/types/src/internal/Filter/index.d.ts +2 -0
- package/dist/types/src/internal/Filter/index.d.ts.map +1 -0
- package/dist/types/src/internal/Filter/match.d.ts +22 -0
- package/dist/types/src/internal/Filter/match.d.ts.map +1 -0
- package/dist/types/src/internal/Format/format.d.ts +0 -2
- package/dist/types/src/internal/Format/format.d.ts.map +1 -1
- package/dist/types/src/internal/Format/index.d.ts +2 -2
- package/dist/types/src/internal/Format/index.d.ts.map +1 -1
- package/dist/types/src/internal/Format/string.d.ts +0 -8
- package/dist/types/src/internal/Format/string.d.ts.map +1 -1
- package/dist/types/src/internal/Format/types.d.ts +2 -1
- package/dist/types/src/internal/Format/types.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/atoms.d.ts +12 -3
- package/dist/types/src/internal/Obj/atoms.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/clone.d.ts +2 -1
- package/dist/types/src/internal/Obj/clone.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/create-object.d.ts +3 -4
- package/dist/types/src/internal/Obj/create-object.d.ts.map +1 -1
- package/dist/types/src/internal/Obj/json-serializer.d.ts.map +1 -1
- package/dist/types/src/internal/Query/index.d.ts +1 -0
- package/dist/types/src/internal/Query/index.d.ts.map +1 -1
- package/dist/types/src/internal/Query/query-result-effect.d.ts +9 -0
- package/dist/types/src/internal/Query/query-result-effect.d.ts.map +1 -0
- package/dist/types/src/internal/Ref/atoms.d.ts +1 -2
- package/dist/types/src/internal/Ref/atoms.d.ts.map +1 -1
- package/dist/types/src/internal/Ref/index.d.ts +1 -0
- package/dist/types/src/internal/Ref/index.d.ts.map +1 -1
- package/dist/types/src/internal/Ref/ref.d.ts +79 -8
- package/dist/types/src/internal/Ref/ref.d.ts.map +1 -1
- package/dist/types/src/internal/Ref/strong-deps.d.ts +35 -0
- package/dist/types/src/internal/Ref/strong-deps.d.ts.map +1 -0
- package/dist/types/src/internal/common/atom-snapshot.d.ts +8 -0
- package/dist/types/src/internal/common/atom-snapshot.d.ts.map +1 -0
- package/dist/types/src/internal/common/proxy/make-object.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/proxy-types.d.ts +12 -0
- package/dist/types/src/internal/common/proxy/proxy-types.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/proxy-utils.d.ts +25 -0
- package/dist/types/src/internal/common/proxy/proxy-utils.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/schema-validator.d.ts +5 -0
- package/dist/types/src/internal/common/proxy/schema-validator.d.ts.map +1 -1
- package/dist/types/src/internal/common/proxy/typed-handler.d.ts +17 -0
- package/dist/types/src/internal/common/proxy/typed-handler.d.ts.map +1 -1
- package/dist/types/src/internal/common/types/entity.d.ts +4 -4
- package/dist/types/src/internal/common/types/entity.d.ts.map +1 -1
- package/dist/types/src/internal/common/types/meta.d.ts +2 -4
- package/dist/types/src/internal/common/types/meta.d.ts.map +1 -1
- package/dist/types/src/internal/index.d.ts +1 -0
- package/dist/types/src/internal/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-schema.d.ts +42 -171
- package/dist/types/src/testing/test-schema.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +20 -13
- package/src/Annotation.test.ts +257 -10
- package/src/Annotation.ts +58 -4
- package/src/Collection.ts +10 -12
- package/src/Database.ts +55 -21
- package/src/Entity.ts +3 -1
- package/src/Err.ts +22 -0
- package/src/Feed.ts +117 -141
- package/src/Filter.test.ts +38 -0
- package/src/Filter.ts +17 -3
- package/src/Hypergraph.ts +2 -11
- package/src/Migration.ts +1 -1
- package/src/Obj.test.ts +92 -12
- package/src/Obj.ts +38 -5
- package/src/Order.ts +3 -3
- package/src/Query.test.ts +2 -2
- package/src/Query.ts +16 -16
- package/src/QueryResult.ts +2 -2
- package/src/Ref.ts +14 -6
- package/src/Registry.ts +1 -1
- package/src/Relation.ts +1 -1
- package/src/Tag.ts +13 -16
- package/src/Text.test.ts +186 -0
- package/src/Text.ts +116 -0
- package/src/Type.test.ts +52 -24
- package/src/Type.ts +85 -33
- package/src/View.ts +22 -30
- package/src/index.ts +1 -1
- package/src/internal/Annotation/annotations.ts +71 -2
- package/src/internal/Annotation/atoms.ts +107 -0
- package/src/internal/Annotation/entity-dictionary.ts +40 -37
- package/src/internal/Entity/api.ts +1 -1
- package/src/internal/Entity/guard.ts +2 -1
- package/src/internal/Entity/model.ts +2 -2
- package/src/internal/Entity/object.ts +14 -1
- package/src/internal/Entity/relation.ts +19 -0
- package/src/internal/Entity/type-uri.ts +3 -1
- package/src/internal/Filter/index.ts +5 -0
- package/src/internal/Filter/match.ts +284 -0
- package/src/internal/Format/format.ts +0 -2
- package/src/internal/Format/index.ts +2 -2
- package/src/internal/Format/string.ts +0 -12
- package/src/internal/Format/types.ts +4 -2
- package/src/internal/JsonSchema/json-schema.test.ts +82 -54
- package/src/internal/Obj/atoms.ts +62 -18
- package/src/internal/Obj/clone.ts +61 -20
- package/src/internal/Obj/create-object.test.ts +3 -3
- package/src/internal/Obj/create-object.ts +3 -4
- package/src/internal/Obj/inspect.ts +1 -1
- package/src/internal/Obj/json-serializer.test.ts +7 -5
- package/src/internal/Obj/json-serializer.ts +4 -4
- package/src/internal/Obj/schema-validator.test.ts +53 -0
- package/src/internal/Obj/set-value.test.ts +29 -19
- package/src/internal/Query/index.ts +1 -0
- package/src/internal/Query/query-result-effect.ts +35 -0
- package/src/internal/Ref/atoms.ts +23 -3
- package/src/internal/Ref/index.ts +1 -0
- package/src/internal/Ref/ref.test.ts +8 -8
- package/src/internal/Ref/ref.ts +122 -39
- package/src/internal/Ref/strong-deps.ts +107 -0
- package/src/internal/common/atom-snapshot.ts +19 -0
- package/src/internal/common/proxy/make-object.ts +1 -1
- package/src/internal/common/proxy/proxy-types.ts +20 -0
- package/src/internal/common/proxy/proxy-utils.ts +37 -1
- package/src/internal/common/proxy/reactive-array.ts +3 -3
- package/src/internal/common/proxy/reactive.test.ts +1 -1
- package/src/internal/common/proxy/schema-validator.ts +181 -8
- package/src/internal/common/proxy/typed-handler.ts +254 -76
- package/src/internal/common/types/entity.ts +15 -7
- package/src/internal/index.ts +1 -0
- package/src/testing/test-schema.ts +75 -90
- package/dist/lib/neutral/chunk-35INCYOE.mjs.map +0 -7
- package/dist/lib/neutral/chunk-3PBP4V4O.mjs +0 -101
- package/dist/lib/neutral/chunk-3PBP4V4O.mjs.map +0 -7
- package/dist/lib/neutral/chunk-4ZUHOTCG.mjs +0 -184
- package/dist/lib/neutral/chunk-4ZUHOTCG.mjs.map +0 -7
- package/dist/lib/neutral/chunk-5SMDBFVB.mjs.map +0 -7
- package/dist/lib/neutral/chunk-6YDI3J37.mjs.map +0 -7
- package/dist/lib/neutral/chunk-7FPIAJIV.mjs +0 -2279
- package/dist/lib/neutral/chunk-7FPIAJIV.mjs.map +0 -7
- package/dist/lib/neutral/chunk-BBFJWWAV.mjs.map +0 -7
- package/dist/lib/neutral/chunk-EVK6XBXO.mjs.map +0 -7
- package/dist/lib/neutral/chunk-MZ7K3MLL.mjs.map +0 -7
- package/dist/lib/neutral/chunk-O6BH7EPN.mjs.map +0 -7
- package/dist/lib/neutral/chunk-QQIYS74I.mjs.map +0 -7
- package/dist/lib/neutral/chunk-R5W6DXR4.mjs.map +0 -7
- package/dist/lib/neutral/chunk-SBVFRTST.mjs.map +0 -7
- package/dist/lib/neutral/chunk-T6E37YIP.mjs.map +0 -7
- package/dist/lib/neutral/chunk-TFEWTY5A.mjs.map +0 -7
- package/dist/lib/neutral/chunk-TYGKCRMK.mjs.map +0 -7
- package/dist/lib/neutral/chunk-UUP46KUQ.mjs.map +0 -7
- package/dist/lib/neutral/chunk-WTQJHC75.mjs.map +0 -7
- package/dist/lib/neutral/chunk-WU3GIANS.mjs.map +0 -7
- package/dist/lib/neutral/chunk-ZGNNFYHS.mjs.map +0 -7
- package/dist/types/src/exemplars.test.d.ts +0 -2
- package/dist/types/src/exemplars.test.d.ts.map +0 -1
- package/dist/types/src/hierarchy.test.d.ts +0 -2
- package/dist/types/src/hierarchy.test.d.ts.map +0 -1
- package/src/exemplars.test.ts +0 -21
- package/src/hierarchy.test.ts +0 -33
- /package/dist/lib/neutral/{chunk-7PI7C4EF.mjs.map → chunk-4A2NWKRJ.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-IGK6FN65.mjs.map → chunk-UGFFV2FR.mjs.map} +0 -0
- /package/dist/lib/neutral/{chunk-5SUJPHAE.mjs.map → chunk-WKIT2YY5.mjs.map} +0 -0
package/src/internal/Ref/ref.ts
CHANGED
|
@@ -81,7 +81,8 @@ export const getReferenceAst = (ast: SchemaAST.AST): RefereneAST | undefined =>
|
|
|
81
81
|
};
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
85
|
+
export const RefTypeId: unique symbol = Symbol.for('@dxos/echo/internal/Ref') as any;
|
|
85
86
|
|
|
86
87
|
/**
|
|
87
88
|
* Reference Schema.
|
|
@@ -188,7 +189,14 @@ export interface Ref<T> extends Pipeable.Pipeable {
|
|
|
188
189
|
/**
|
|
189
190
|
* @returns Promise that will resolves with the target object.
|
|
190
191
|
* Will load the object from disk if it is not present in the working set.
|
|
192
|
+
* Short-circuits immediately when the target is already loaded.
|
|
191
193
|
* @throws If the object is not available locally.
|
|
194
|
+
*
|
|
195
|
+
* @idiom org.dxos.echo.refLoad
|
|
196
|
+
* applies: Resolving a ref inside an async function or Effect handler — guarantees the object is available before proceeding
|
|
197
|
+
* instead-of: `ref.target` — not guaranteed to be defined in async contexts; use `await ref.load()` (or `yield* Database.load(ref)` in Effect) to ensure the target is present
|
|
198
|
+
* uses: {@link load}
|
|
199
|
+
* related: org.dxos.echo-react.useObjectReactive
|
|
192
200
|
*/
|
|
193
201
|
load(): Promise<T>;
|
|
194
202
|
|
|
@@ -253,7 +261,7 @@ export declare namespace Ref {
|
|
|
253
261
|
}
|
|
254
262
|
|
|
255
263
|
Ref.isRef = (obj: any): obj is Ref<any> => {
|
|
256
|
-
return obj && typeof obj === 'object' && RefTypeId in obj;
|
|
264
|
+
return obj != null && typeof obj === 'object' && RefTypeId in obj;
|
|
257
265
|
};
|
|
258
266
|
|
|
259
267
|
Ref.hasEntityId = (id: EntityId) => (ref: Ref<any>) => {
|
|
@@ -278,7 +286,8 @@ Ref.make = <T extends AnyProperties>(obj: T): Ref<T> => {
|
|
|
278
286
|
const id = obj.id;
|
|
279
287
|
invariant(EntityId.isValid(id), 'Invalid object ID');
|
|
280
288
|
const uri = EID.make({ entityId: id });
|
|
281
|
-
|
|
289
|
+
const ref = new RefImpl(uri, obj);
|
|
290
|
+
return ref;
|
|
282
291
|
};
|
|
283
292
|
|
|
284
293
|
Ref.fromURI = (uri: URI.URI): Ref<any> => {
|
|
@@ -380,22 +389,79 @@ const getSchemaExpectedName = (ast: SchemaAST.Annotated): string | undefined =>
|
|
|
380
389
|
);
|
|
381
390
|
};
|
|
382
391
|
|
|
392
|
+
/**
|
|
393
|
+
* Load-source ceiling for a resolution request. Cheaper tiers are always probed first; the
|
|
394
|
+
* ceiling caps how far a request is allowed to reach:
|
|
395
|
+
* - `working-set`: synchronous, in-memory only. A miss settles `unavailable` immediately.
|
|
396
|
+
* - `disk`: probe the working set, then local storage; never the network.
|
|
397
|
+
* - `network`: probe the working set, disk, then fetch from peers.
|
|
398
|
+
*
|
|
399
|
+
* `unavailable` is always relative to the requested ceiling (disk-unavailable ≠ network-unavailable).
|
|
400
|
+
*/
|
|
401
|
+
export type RefSource = 'working-set' | 'disk' | 'network';
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* A stateful, closure-aware handle to an in-flight (or settled) reference resolution.
|
|
405
|
+
*
|
|
406
|
+
* `ready` means the entity is FULLY USABLE: its own body and its strong-dependency closure are all
|
|
407
|
+
* materialized. The body-vs-closure split is internal; `requesting` transparently covers "a
|
|
408
|
+
* dependency is still loading".
|
|
409
|
+
*/
|
|
410
|
+
export interface RefResolverRequest {
|
|
411
|
+
/**
|
|
412
|
+
* `pending` is a one-way entry state (never re-entered). `requesting` means the body and/or
|
|
413
|
+
* closure is still loading. `ready` means fully usable. `unavailable` means unreachable at the
|
|
414
|
+
* requested ceiling.
|
|
415
|
+
*/
|
|
416
|
+
readonly state: 'pending' | 'requesting' | 'ready' | 'unavailable';
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Fires (deferred to a microtask) whenever {@link state} changes.
|
|
420
|
+
*/
|
|
421
|
+
readonly stateChanged: Event<void>;
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Synchronous snapshot of the resolved entity; defined iff `state === 'ready'`.
|
|
425
|
+
*/
|
|
426
|
+
getResult(): AnyProperties | undefined;
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Settles when `state ∈ {ready, unavailable}`.
|
|
430
|
+
*/
|
|
431
|
+
wait(): Promise<AnyProperties | undefined>;
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Decrements the refcount on the shared load op(s); cancels the underlying IO at zero.
|
|
435
|
+
*/
|
|
436
|
+
abort(): void;
|
|
437
|
+
}
|
|
438
|
+
|
|
383
439
|
export interface RefResolver {
|
|
440
|
+
/**
|
|
441
|
+
* Resolve a reference, returning a stateful handle. `source` is a required ceiling; cheaper
|
|
442
|
+
* tiers are always probed first.
|
|
443
|
+
*/
|
|
444
|
+
resolve(uri: URI.URI, options: { source: RefSource }): RefResolverRequest;
|
|
445
|
+
|
|
384
446
|
/**
|
|
385
447
|
* Resolve ref synchronously from the objects in the working set.
|
|
386
448
|
*
|
|
387
449
|
* @param uri
|
|
388
450
|
* @param load If true the resolver should attempt to load the object from disk.
|
|
389
451
|
* @param onLoad Callback to call when the object is loaded.
|
|
452
|
+
* @deprecated Use {@link resolve} with `{ source: 'working-set' }`. Removed in Task 11.
|
|
390
453
|
*/
|
|
391
454
|
resolveSync(uri: URI.URI, load: boolean, onLoad?: () => void): AnyProperties | undefined;
|
|
392
455
|
|
|
393
456
|
/**
|
|
394
457
|
* Resolver ref asynchronously.
|
|
458
|
+
* @deprecated Use {@link resolve} with `{ source: 'network' }`. Removed in Task 11.
|
|
395
459
|
*/
|
|
396
|
-
|
|
460
|
+
resolveLegacy(uri: URI.URI): Promise<AnyProperties | undefined>;
|
|
397
461
|
|
|
398
|
-
|
|
462
|
+
/**
|
|
463
|
+
* @deprecated Use {@link resolve} + `Type.getSchema`. Removed in Task 11.
|
|
464
|
+
*/
|
|
399
465
|
resolveSchema(uri: URI.URI): Promise<Schema.Schema.AnyNoContext | undefined>;
|
|
400
466
|
|
|
401
467
|
/**
|
|
@@ -404,14 +470,30 @@ export interface RefResolver {
|
|
|
404
470
|
* via `Obj.getType` / `Entity.getType`. Optional — resolvers that only
|
|
405
471
|
* carry raw schemas may leave this unimplemented; the deserializer falls
|
|
406
472
|
* back to leaving the type entity unset.
|
|
473
|
+
* @deprecated Use {@link resolve}. Removed in Task 11.
|
|
407
474
|
*/
|
|
408
475
|
resolveType?(uri: URI.URI): Promise<unknown | undefined>;
|
|
409
476
|
}
|
|
410
477
|
|
|
478
|
+
/**
|
|
479
|
+
* A settled {@link RefResolverRequest} with a fixed `state` and `result`, used by resolvers
|
|
480
|
+
* whose backends are fully synchronous (e.g. {@link StaticRefResolver}).
|
|
481
|
+
*/
|
|
482
|
+
export const makeSettledRequest = (
|
|
483
|
+
state: RefResolverRequest['state'],
|
|
484
|
+
result: AnyProperties | undefined,
|
|
485
|
+
): RefResolverRequest => ({
|
|
486
|
+
state,
|
|
487
|
+
stateChanged: new Event<void>(),
|
|
488
|
+
getResult: () => (state === 'ready' ? result : undefined),
|
|
489
|
+
wait: async () => (state === 'ready' ? result : undefined),
|
|
490
|
+
abort: () => {},
|
|
491
|
+
});
|
|
492
|
+
|
|
411
493
|
export class RefImpl<T> implements Ref<T> {
|
|
494
|
+
[RefTypeId] = refVariance;
|
|
495
|
+
|
|
412
496
|
#uri: URI.URI;
|
|
413
|
-
#resolver?: RefResolver = undefined;
|
|
414
|
-
#resolved = new Event<void>();
|
|
415
497
|
|
|
416
498
|
/**
|
|
417
499
|
* Target is set when the reference is created from a specific object.
|
|
@@ -419,6 +501,9 @@ export class RefImpl<T> implements Ref<T> {
|
|
|
419
501
|
*/
|
|
420
502
|
#target: T | undefined = undefined;
|
|
421
503
|
|
|
504
|
+
#resolver?: RefResolver = undefined;
|
|
505
|
+
#resolved = new Event<void>();
|
|
506
|
+
|
|
422
507
|
/**
|
|
423
508
|
* Callback to issue a reactive notification when object is resolved.
|
|
424
509
|
*/
|
|
@@ -438,13 +523,6 @@ export class RefImpl<T> implements Ref<T> {
|
|
|
438
523
|
return this.#uri;
|
|
439
524
|
}
|
|
440
525
|
|
|
441
|
-
/**
|
|
442
|
-
* @inheritdoc
|
|
443
|
-
*/
|
|
444
|
-
get isAvailable(): boolean {
|
|
445
|
-
return this.#target !== undefined || this.#resolver !== undefined;
|
|
446
|
-
}
|
|
447
|
-
|
|
448
526
|
/**
|
|
449
527
|
* @inheritdoc
|
|
450
528
|
*/
|
|
@@ -457,6 +535,17 @@ export class RefImpl<T> implements Ref<T> {
|
|
|
457
535
|
return this.#resolver.resolveSync(this.#uri, true, this.#resolverCallback) as T | undefined;
|
|
458
536
|
}
|
|
459
537
|
|
|
538
|
+
/**
|
|
539
|
+
* @inheritdoc
|
|
540
|
+
*/
|
|
541
|
+
get isAvailable(): boolean {
|
|
542
|
+
return this.#target !== undefined || this.#resolver !== undefined;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
get atom(): Atom.Atom<T | undefined> {
|
|
546
|
+
return RefAtoms.refSimpleFamily(this);
|
|
547
|
+
}
|
|
548
|
+
|
|
460
549
|
/**
|
|
461
550
|
* @inheritdoc
|
|
462
551
|
*/
|
|
@@ -465,7 +554,7 @@ export class RefImpl<T> implements Ref<T> {
|
|
|
465
554
|
return this.#target;
|
|
466
555
|
}
|
|
467
556
|
invariant(this.#resolver, 'Resolver is not set');
|
|
468
|
-
const obj = await this.#resolver.
|
|
557
|
+
const obj = await this.#resolver.resolveLegacy(this.#uri);
|
|
469
558
|
if (obj == null) {
|
|
470
559
|
throw new Error('Object not found');
|
|
471
560
|
}
|
|
@@ -480,7 +569,7 @@ export class RefImpl<T> implements Ref<T> {
|
|
|
480
569
|
return this.#target;
|
|
481
570
|
}
|
|
482
571
|
invariant(this.#resolver, 'Resolver is not set');
|
|
483
|
-
return (await this.#resolver.
|
|
572
|
+
return (await this.#resolver.resolveLegacy(this.#uri)) as T | undefined;
|
|
484
573
|
}
|
|
485
574
|
|
|
486
575
|
/**
|
|
@@ -530,8 +619,6 @@ export class RefImpl<T> implements Ref<T> {
|
|
|
530
619
|
return this.toString();
|
|
531
620
|
};
|
|
532
621
|
|
|
533
|
-
[RefTypeId] = refVariance;
|
|
534
|
-
|
|
535
622
|
/**
|
|
536
623
|
* Effect Hash trait. Required for MutableHashMap-based caches (e.g., Atom.family)
|
|
537
624
|
* to deduplicate Ref instances that point to the same object.
|
|
@@ -547,13 +634,12 @@ export class RefImpl<T> implements Ref<T> {
|
|
|
547
634
|
return that instanceof RefImpl && this.#uri === that.uri;
|
|
548
635
|
}
|
|
549
636
|
|
|
550
|
-
|
|
551
|
-
|
|
637
|
+
pipe() {
|
|
638
|
+
// eslint-disable-next-line prefer-rest-params
|
|
639
|
+
return Pipeable.pipeArguments(this, arguments);
|
|
552
640
|
}
|
|
553
641
|
|
|
554
642
|
/**
|
|
555
|
-
* Internal method to set the resolver.
|
|
556
|
-
*
|
|
557
643
|
* @internal
|
|
558
644
|
*/
|
|
559
645
|
_setResolver(resolver: RefResolver): void {
|
|
@@ -566,11 +652,6 @@ export class RefImpl<T> implements Ref<T> {
|
|
|
566
652
|
_getSavedTarget(): T | undefined {
|
|
567
653
|
return this.#target;
|
|
568
654
|
}
|
|
569
|
-
|
|
570
|
-
pipe() {
|
|
571
|
-
// eslint-disable-next-line prefer-rest-params
|
|
572
|
-
return Pipeable.pipeArguments(this, arguments);
|
|
573
|
-
}
|
|
574
655
|
}
|
|
575
656
|
|
|
576
657
|
/**
|
|
@@ -624,27 +705,29 @@ export class StaticRefResolver implements RefResolver {
|
|
|
624
705
|
return this;
|
|
625
706
|
}
|
|
626
707
|
|
|
708
|
+
resolve(uri: URI.URI, _options: { source: RefSource }): RefResolverRequest {
|
|
709
|
+
const obj = this.#lookup(uri);
|
|
710
|
+
return makeSettledRequest(obj ? 'ready' : 'unavailable', obj);
|
|
711
|
+
}
|
|
712
|
+
|
|
627
713
|
resolveSync(uri: URI.URI, _load: boolean, _onLoad?: () => void): AnyProperties | undefined {
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
if (id == null) {
|
|
631
|
-
return undefined;
|
|
632
|
-
}
|
|
714
|
+
return this.#lookup(uri);
|
|
715
|
+
}
|
|
633
716
|
|
|
634
|
-
|
|
717
|
+
async resolveLegacy(uri: URI.URI): Promise<AnyProperties | undefined> {
|
|
718
|
+
return this.#lookup(uri);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
async resolveSchema(uri: URI.URI): Promise<Schema.Schema.AnyNoContext | undefined> {
|
|
722
|
+
return this.schemas.get(uri);
|
|
635
723
|
}
|
|
636
724
|
|
|
637
|
-
|
|
725
|
+
#lookup(uri: URI.URI): AnyProperties | undefined {
|
|
638
726
|
const echoUri = EID.tryParse(uri);
|
|
639
727
|
const id = echoUri ? EID.getEntityId(echoUri) : undefined;
|
|
640
728
|
if (id == null) {
|
|
641
729
|
return undefined;
|
|
642
730
|
}
|
|
643
|
-
|
|
644
731
|
return this.objects.get(id);
|
|
645
732
|
}
|
|
646
|
-
|
|
647
|
-
async resolveSchema(uri: URI.URI): Promise<Schema.Schema.AnyNoContext | undefined> {
|
|
648
|
-
return this.schemas.get(uri);
|
|
649
|
-
}
|
|
650
733
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { EID, type SpaceId, type URI } from '@dxos/keys';
|
|
6
|
+
|
|
7
|
+
import { EntityKind } from '../common/types/entity';
|
|
8
|
+
import { RelationSourceDXNId, RelationTargetDXNId } from '../common/types/model-symbols';
|
|
9
|
+
import { ParentId, TypeId } from '../common/types/typename';
|
|
10
|
+
import { getObjectEchoUri } from '../Entity/util';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Normalized view of the references that make up an entity's strong-dependency set, store-agnostic
|
|
14
|
+
* so it can be filled from an {@link ObjectCore} (db) or a decoded entity (queue item / snapshot).
|
|
15
|
+
*/
|
|
16
|
+
export interface StrongDepRefs {
|
|
17
|
+
/** Entity kind — relations additionally depend on their source/target. */
|
|
18
|
+
kind: EntityKind;
|
|
19
|
+
/** Type reference URI (`echo:` for a persisted stored schema, `dxn:` for a static/registry type). */
|
|
20
|
+
type?: URI.URI;
|
|
21
|
+
/** Relation source reference URI. */
|
|
22
|
+
source?: URI.URI;
|
|
23
|
+
/** Relation target reference URI. */
|
|
24
|
+
target?: URI.URI;
|
|
25
|
+
/** Parent reference URI. */
|
|
26
|
+
parent?: URI.URI;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Direct strong-dependency URIs of an entity: the schema (only when stored as an object), the
|
|
31
|
+
* relation source/target, and the parent. Returns URIs (cross-space `echo:` EIDs and queue-item
|
|
32
|
+
* EIDs included); a persisted schema is an `echo:` URI, a static/registry type is a `dxn:` URI.
|
|
33
|
+
*
|
|
34
|
+
* A static/registry type (`dxn:`) is intentionally NOT a strong dep: it is always synchronously
|
|
35
|
+
* available through the registry, so gating on it would needlessly delay surfacing. Only a
|
|
36
|
+
* persisted (db-backed) schema, addressed by an `echo:` URI, must load before its instances.
|
|
37
|
+
*/
|
|
38
|
+
export const getStrongDependencyUris = (refs: StrongDepRefs, owningSpaceId?: SpaceId): URI.URI[] => {
|
|
39
|
+
const res: URI.URI[] = [];
|
|
40
|
+
|
|
41
|
+
if (refs.type != null && EID.tryParse(refs.type) != null) {
|
|
42
|
+
res.push(qualifyToSpace(refs.type, owningSpaceId));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (refs.kind === EntityKind.Relation) {
|
|
46
|
+
if (refs.source != null) {
|
|
47
|
+
res.push(qualifyToSpace(refs.source, owningSpaceId));
|
|
48
|
+
}
|
|
49
|
+
if (refs.target != null) {
|
|
50
|
+
res.push(qualifyToSpace(refs.target, owningSpaceId));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (refs.parent != null) {
|
|
55
|
+
res.push(qualifyToSpace(refs.parent, owningSpaceId));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return res;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Qualifies a space-less (relative) `echo:` dependency URI with the space of the entity that owns it.
|
|
63
|
+
* Same-space references are persisted relative; resolution needs them absolute to route to a single
|
|
64
|
+
* space. Already-qualified URIs (cross-space) and non-`echo:` URIs (registry `dxn:`) pass through.
|
|
65
|
+
*/
|
|
66
|
+
const qualifyToSpace = (uri: URI.URI, owningSpaceId: SpaceId | undefined): URI.URI => {
|
|
67
|
+
if (owningSpaceId == null) {
|
|
68
|
+
return uri;
|
|
69
|
+
}
|
|
70
|
+
const eid = EID.tryParse(uri);
|
|
71
|
+
if (eid == null || !EID.isLocal(eid)) {
|
|
72
|
+
return uri;
|
|
73
|
+
}
|
|
74
|
+
const entityId = EID.getEntityId(eid);
|
|
75
|
+
return entityId != null ? EID.make({ spaceId: owningSpaceId, entityId }) : uri;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Direct strong-dependency URIs of a live (decoded) entity — a queue item, cross-space proxy, or
|
|
80
|
+
* snapshot. Reads the reference URIs off the entity's hidden model symbols. ECHO db cores compute
|
|
81
|
+
* the same set from their raw encoded references via {@link getStrongDependencyUris}.
|
|
82
|
+
*/
|
|
83
|
+
export const getStrongDependencies = (entity: unknown): URI.URI[] => {
|
|
84
|
+
if (entity == null || typeof entity !== 'object') {
|
|
85
|
+
return [];
|
|
86
|
+
}
|
|
87
|
+
const props = entity as Record<symbol, unknown>;
|
|
88
|
+
const kind =
|
|
89
|
+
props[RelationSourceDXNId] != null || props[RelationTargetDXNId] != null ? EntityKind.Relation : EntityKind.Object;
|
|
90
|
+
const parent = props[ParentId];
|
|
91
|
+
|
|
92
|
+
// The entity's own space qualifies any relative dependency URIs it carries.
|
|
93
|
+
const selfUri = getObjectEchoUri(entity);
|
|
94
|
+
const owningSpaceId = selfUri != null ? EID.getSpaceId(selfUri) : undefined;
|
|
95
|
+
|
|
96
|
+
return getStrongDependencyUris(
|
|
97
|
+
{
|
|
98
|
+
kind,
|
|
99
|
+
type: typeof props[TypeId] === 'string' ? (props[TypeId] as URI.URI) : undefined,
|
|
100
|
+
source: props[RelationSourceDXNId] as URI.URI | undefined,
|
|
101
|
+
target: props[RelationTargetDXNId] as URI.URI | undefined,
|
|
102
|
+
// A parent is held as the resolved entity; derive its absolute URI for the dependency edge.
|
|
103
|
+
parent: parent != null ? getObjectEchoUri(parent) : undefined,
|
|
104
|
+
},
|
|
105
|
+
owningSpaceId,
|
|
106
|
+
);
|
|
107
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Snapshot a value to create a new reference for atom change-detection and React dependency tracking.
|
|
7
|
+
* Objects and arrays are shallow-copied (a fresh reference each read, so an in-place mutation is
|
|
8
|
+
* observed); primitives are returned as-is (so they dedupe via `!==`). Shared by the object-property
|
|
9
|
+
* and annotation atom families.
|
|
10
|
+
*/
|
|
11
|
+
export const snapshotForComparison = <V>(value: V): V => {
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
return [...value] as V;
|
|
14
|
+
}
|
|
15
|
+
if (value !== null && typeof value === 'object') {
|
|
16
|
+
return { ...value } as V;
|
|
17
|
+
}
|
|
18
|
+
return value;
|
|
19
|
+
};
|
|
@@ -13,7 +13,7 @@ import { MetaId } from '../types/model-symbols';
|
|
|
13
13
|
import { defineHiddenProperty } from './define-hidden-property';
|
|
14
14
|
import { attachTypedJsonSerializer } from './json-serializer';
|
|
15
15
|
import { createProxy, getProxyTarget, isValidProxyTarget } from './proxy-utils';
|
|
16
|
-
import {
|
|
16
|
+
import { TypedReactiveHandler, TypeSource, prepareTypedTarget, setMetaOwner } from './typed-handler';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
*
|
|
@@ -15,6 +15,26 @@ export interface ReactiveHandler<T extends object> extends ProxyHandler<T> {
|
|
|
15
15
|
* Called when a proxy is created for this target.
|
|
16
16
|
*/
|
|
17
17
|
init(target: T): void;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Replace the string CRDT value at `path` with `newText`, applying a minimal diff so cursors and
|
|
21
|
+
* concurrent edits are preserved. Implemented by handlers that back string fields; `Text.update`
|
|
22
|
+
* dispatches here. Must run inside a change context (`Obj.update`).
|
|
23
|
+
*/
|
|
24
|
+
textUpdate?(target: T, path: readonly (string | number)[], newText: string): void;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Splice the string CRDT value at `path` (character indices, mirroring `Array.splice`): remove
|
|
28
|
+
* `deleteCount` characters at `start` and insert `insert`. Returns the removed substring. `Text.splice`
|
|
29
|
+
* dispatches here. Must run inside a change context (`Obj.update`).
|
|
30
|
+
*/
|
|
31
|
+
textSplice?(
|
|
32
|
+
target: T,
|
|
33
|
+
path: readonly (string | number)[],
|
|
34
|
+
start: number,
|
|
35
|
+
deleteCount: number,
|
|
36
|
+
insert: string,
|
|
37
|
+
): string;
|
|
18
38
|
}
|
|
19
39
|
|
|
20
40
|
/**
|
|
@@ -8,11 +8,30 @@ import { type ReactiveHandler } from './proxy-types';
|
|
|
8
8
|
|
|
9
9
|
export const symbolIsProxy = Symbol.for('@dxos/schema/Proxy');
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Marker placed on a reactive-object behaviour prototype (e.g. the typed handler's
|
|
13
|
+
* `TypedObject` prototype). A record target whose prototype chain carries this marker
|
|
14
|
+
* is a reactive data record — equivalent, for the purposes of the "plain object" gates
|
|
15
|
+
* below, to one rooted directly at `Object.prototype`. This lets a handler move its
|
|
16
|
+
* per-object metadata onto an intermediate prototype without those gates mistaking the
|
|
17
|
+
* record for a foreign class instance.
|
|
18
|
+
*/
|
|
19
|
+
export const symbolReactivePrototype = Symbol.for('@dxos/echo/ReactivePrototype');
|
|
20
|
+
|
|
11
21
|
/**
|
|
12
22
|
* Internal api.
|
|
13
23
|
*/
|
|
14
24
|
export const isProxy = (value: unknown) => !!(value as any)?.[symbolIsProxy];
|
|
15
25
|
|
|
26
|
+
/**
|
|
27
|
+
* True if `value` is a plain data record — either rooted at `Object.prototype` or carrying
|
|
28
|
+
* a reactive behaviour prototype (see {@link symbolReactivePrototype}).
|
|
29
|
+
*/
|
|
30
|
+
export const isReactiveRecord = (value: any): boolean => {
|
|
31
|
+
const proto = Object.getPrototypeOf(value);
|
|
32
|
+
return proto === Object.prototype || (proto != null && !!proto[symbolReactivePrototype]);
|
|
33
|
+
};
|
|
34
|
+
|
|
16
35
|
export const isValidProxyTarget = (value: any): value is object => {
|
|
17
36
|
if (value == null || value[symbolIsProxy]) {
|
|
18
37
|
return false;
|
|
@@ -21,7 +40,7 @@ export const isValidProxyTarget = (value: any): value is object => {
|
|
|
21
40
|
return true;
|
|
22
41
|
}
|
|
23
42
|
|
|
24
|
-
return typeof value === 'object' &&
|
|
43
|
+
return typeof value === 'object' && isReactiveRecord(value);
|
|
25
44
|
};
|
|
26
45
|
|
|
27
46
|
/**
|
|
@@ -41,6 +60,23 @@ export const getProxyHandler = <T extends object>(proxy: any): ReactiveHandler<T
|
|
|
41
60
|
return getProxySlot<T>(proxy).handler;
|
|
42
61
|
};
|
|
43
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Clamp a splice range to `Array.prototype.splice` semantics for a string of the given `length`:
|
|
65
|
+
* a negative `start` counts from the end, a `start` past the end appends, and `deleteCount` is bounded
|
|
66
|
+
* to the remaining characters. Shared by both reactive handlers so the string CRDT API behaves
|
|
67
|
+
* identically across the in-memory and Automerge backends (Automerge's `splice` otherwise throws on
|
|
68
|
+
* out-of-range indices).
|
|
69
|
+
*/
|
|
70
|
+
export const normalizeSpliceRange = (
|
|
71
|
+
length: number,
|
|
72
|
+
start: number,
|
|
73
|
+
deleteCount: number,
|
|
74
|
+
): { start: number; deleteCount: number } => {
|
|
75
|
+
const safeStart = start < 0 ? Math.max(length + start, 0) : Math.min(start, length);
|
|
76
|
+
const safeDeleteCount = Math.max(0, Math.min(deleteCount, length - safeStart));
|
|
77
|
+
return { start: safeStart, deleteCount: safeDeleteCount };
|
|
78
|
+
};
|
|
79
|
+
|
|
44
80
|
/**
|
|
45
81
|
* Unsafe method to override id for debugging/testing and migration purposes.
|
|
46
82
|
* @deprecated
|
|
@@ -7,7 +7,7 @@ import { createArrayMethodError } from './errors';
|
|
|
7
7
|
import { batchEvents } from './event-batch';
|
|
8
8
|
import { getEchoRoot } from './ownership';
|
|
9
9
|
import { getProxyTarget, isProxy } from './proxy-utils';
|
|
10
|
-
import {
|
|
10
|
+
import { EventId } from './symbols';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Check if array mutation is allowed (inside a change context).
|
|
@@ -20,8 +20,8 @@ const checkArrayMutationAllowed = (arr: any, method: string): void => {
|
|
|
20
20
|
// Find the root ECHO object.
|
|
21
21
|
const echoRoot = getEchoRoot(target);
|
|
22
22
|
|
|
23
|
-
//
|
|
24
|
-
const isInitialized =
|
|
23
|
+
// A root object owns an `EventId` once initialized; before that, mutations are allowed.
|
|
24
|
+
const isInitialized = EventId in echoRoot;
|
|
25
25
|
if (!isInitialized) {
|
|
26
26
|
// Array is still being initialized, allow mutations.
|
|
27
27
|
return;
|
|
@@ -36,7 +36,7 @@ describe('Obj.subscribe', () => {
|
|
|
36
36
|
test('returns a no-op unsubscribe for non-proxy inputs', () => {
|
|
37
37
|
const queueShaped: any = {
|
|
38
38
|
id: '01KQ5NKXJWSKMRPVTVG2GHV8V3',
|
|
39
|
-
|
|
39
|
+
skills: { added: [], removed: [] },
|
|
40
40
|
objects: { added: [], removed: [] },
|
|
41
41
|
};
|
|
42
42
|
const unsubscribe = Obj.subscribe(queueShaped, () => {});
|