@dxos/echo 0.8.4-main.7ace549 → 0.8.4-main.937b3ca
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 +1 -2
- package/dist/lib/browser/Annotation.mjs +33 -0
- package/dist/lib/browser/Annotation.mjs.map +7 -0
- package/dist/lib/browser/Database.mjs +18 -0
- package/dist/lib/browser/Database.mjs.map +7 -0
- package/dist/lib/browser/Entity.mjs +52 -0
- package/dist/lib/browser/Entity.mjs.map +7 -0
- package/dist/lib/browser/Err.mjs +11 -0
- package/dist/lib/browser/Err.mjs.map +7 -0
- package/dist/lib/browser/Filter.mjs +62 -0
- package/dist/lib/browser/Filter.mjs.map +7 -0
- package/dist/lib/browser/Format.mjs +67 -0
- package/dist/lib/browser/Format.mjs.map +7 -0
- package/dist/lib/browser/JsonSchema.mjs +20 -0
- package/dist/lib/browser/JsonSchema.mjs.map +7 -0
- package/dist/lib/browser/Key.mjs +13 -0
- package/dist/lib/browser/Key.mjs.map +7 -0
- package/dist/lib/browser/Obj.mjs +91 -0
- package/dist/lib/browser/Obj.mjs.map +7 -0
- package/dist/lib/browser/Order.mjs +13 -0
- package/dist/lib/browser/Order.mjs.map +7 -0
- package/dist/lib/browser/Query.mjs +27 -0
- package/dist/lib/browser/Query.mjs.map +7 -0
- package/dist/lib/browser/QueryResult.mjs +3 -0
- package/dist/lib/browser/QueryResult.mjs.map +7 -0
- package/dist/lib/browser/Ref.mjs +23 -0
- package/dist/lib/browser/Ref.mjs.map +7 -0
- package/dist/lib/browser/Relation.mjs +85 -0
- package/dist/lib/browser/Relation.mjs.map +7 -0
- package/dist/lib/browser/SchemaRegistry.mjs +3 -0
- package/dist/lib/browser/SchemaRegistry.mjs.map +7 -0
- package/dist/lib/browser/Tag.mjs +26 -0
- package/dist/lib/browser/Tag.mjs.map +7 -0
- package/dist/lib/browser/Type.mjs +48 -0
- package/dist/lib/browser/Type.mjs.map +7 -0
- package/dist/lib/browser/chunk-22JMFST2.mjs +24 -0
- package/dist/lib/browser/chunk-22JMFST2.mjs.map +7 -0
- package/dist/lib/browser/chunk-2SBB7OWV.mjs +250 -0
- package/dist/lib/browser/chunk-2SBB7OWV.mjs.map +7 -0
- package/dist/lib/browser/chunk-6L5HHUVU.mjs +158 -0
- package/dist/lib/browser/chunk-6L5HHUVU.mjs.map +7 -0
- package/dist/lib/browser/chunk-7STIBCP7.mjs +133 -0
- package/dist/lib/browser/chunk-7STIBCP7.mjs.map +7 -0
- package/dist/lib/browser/{chunk-7GH6RXJ3.mjs → chunk-BJPE6CIC.mjs} +1999 -1835
- package/dist/lib/browser/chunk-BJPE6CIC.mjs.map +7 -0
- package/dist/lib/browser/chunk-CGS2ULMK.mjs +11 -0
- package/dist/lib/browser/chunk-CGS2ULMK.mjs.map +7 -0
- package/dist/lib/browser/chunk-CJ5YELTO.mjs +39 -0
- package/dist/lib/browser/chunk-CJ5YELTO.mjs.map +7 -0
- package/dist/lib/browser/chunk-FPOISFQK.mjs +40 -0
- package/dist/lib/browser/chunk-FPOISFQK.mjs.map +7 -0
- package/dist/lib/browser/chunk-FRDT7RA4.mjs +403 -0
- package/dist/lib/browser/chunk-FRDT7RA4.mjs.map +7 -0
- package/dist/lib/browser/chunk-INHXFXY5.mjs +22 -0
- package/dist/lib/browser/chunk-INHXFXY5.mjs.map +7 -0
- package/dist/lib/browser/chunk-IXVWLTG7.mjs +9 -0
- package/dist/lib/browser/chunk-IXVWLTG7.mjs.map +7 -0
- package/dist/lib/browser/chunk-JMKVF2YQ.mjs +43 -0
- package/dist/lib/browser/chunk-JMKVF2YQ.mjs.map +7 -0
- package/dist/lib/browser/chunk-KQQGVHFN.mjs +143 -0
- package/dist/lib/browser/chunk-KQQGVHFN.mjs.map +7 -0
- package/dist/lib/browser/chunk-MYCCGG2T.mjs +15 -0
- package/dist/lib/browser/chunk-MYCCGG2T.mjs.map +7 -0
- package/dist/lib/browser/chunk-NOPVNWPT.mjs +204 -0
- package/dist/lib/browser/chunk-NOPVNWPT.mjs.map +7 -0
- package/dist/lib/browser/chunk-PQZW3S6L.mjs +74 -0
- package/dist/lib/browser/chunk-PQZW3S6L.mjs.map +7 -0
- package/dist/lib/browser/chunk-RK4Z4JUZ.mjs +283 -0
- package/dist/lib/browser/chunk-RK4Z4JUZ.mjs.map +7 -0
- package/dist/lib/browser/chunk-U2J7TA7K.mjs +57 -0
- package/dist/lib/browser/chunk-U2J7TA7K.mjs.map +7 -0
- package/dist/lib/browser/chunk-W5D2GWAW.mjs +98 -0
- package/dist/lib/browser/chunk-W5D2GWAW.mjs.map +7 -0
- package/dist/lib/browser/chunk-XDIUHAAX.mjs +41 -0
- package/dist/lib/browser/chunk-XDIUHAAX.mjs.map +7 -0
- package/dist/lib/browser/chunk-YKTSSMDS.mjs +69 -0
- package/dist/lib/browser/chunk-YKTSSMDS.mjs.map +7 -0
- package/dist/lib/browser/chunk-ZAGAOZVY.mjs +288 -0
- package/dist/lib/browser/chunk-ZAGAOZVY.mjs.map +7 -0
- package/dist/lib/browser/chunk-ZHXZGIXD.mjs +9 -0
- package/dist/lib/browser/chunk-ZHXZGIXD.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +65 -25
- package/dist/lib/browser/internal/index.mjs +213 -92
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +80 -41
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/Annotation.mjs +33 -0
- package/dist/lib/node-esm/Annotation.mjs.map +7 -0
- package/dist/lib/node-esm/Database.mjs +18 -0
- package/dist/lib/node-esm/Database.mjs.map +7 -0
- package/dist/lib/node-esm/Entity.mjs +52 -0
- package/dist/lib/node-esm/Entity.mjs.map +7 -0
- package/dist/lib/node-esm/Err.mjs +11 -0
- package/dist/lib/node-esm/Err.mjs.map +7 -0
- package/dist/lib/node-esm/Filter.mjs +62 -0
- package/dist/lib/node-esm/Filter.mjs.map +7 -0
- package/dist/lib/node-esm/Format.mjs +67 -0
- package/dist/lib/node-esm/Format.mjs.map +7 -0
- package/dist/lib/node-esm/JsonSchema.mjs +20 -0
- package/dist/lib/node-esm/JsonSchema.mjs.map +7 -0
- package/dist/lib/node-esm/Key.mjs +13 -0
- package/dist/lib/node-esm/Key.mjs.map +7 -0
- package/dist/lib/node-esm/Obj.mjs +91 -0
- package/dist/lib/node-esm/Obj.mjs.map +7 -0
- package/dist/lib/node-esm/Order.mjs +13 -0
- package/dist/lib/node-esm/Order.mjs.map +7 -0
- package/dist/lib/node-esm/Query.mjs +27 -0
- package/dist/lib/node-esm/Query.mjs.map +7 -0
- package/dist/lib/node-esm/QueryResult.mjs +3 -0
- package/dist/lib/node-esm/QueryResult.mjs.map +7 -0
- package/dist/lib/node-esm/Ref.mjs +23 -0
- package/dist/lib/node-esm/Ref.mjs.map +7 -0
- package/dist/lib/node-esm/Relation.mjs +85 -0
- package/dist/lib/node-esm/Relation.mjs.map +7 -0
- package/dist/lib/node-esm/SchemaRegistry.mjs +3 -0
- package/dist/lib/node-esm/SchemaRegistry.mjs.map +7 -0
- package/dist/lib/node-esm/Tag.mjs +26 -0
- package/dist/lib/node-esm/Tag.mjs.map +7 -0
- package/dist/lib/node-esm/Type.mjs +48 -0
- package/dist/lib/node-esm/Type.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-4PNXQA64.mjs +250 -0
- package/dist/lib/node-esm/chunk-4PNXQA64.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-5OBN7GZW.mjs +158 -0
- package/dist/lib/node-esm/chunk-5OBN7GZW.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-AJEMYSIR.mjs +22 -0
- package/dist/lib/node-esm/chunk-AJEMYSIR.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-ANLVLWME.mjs +98 -0
- package/dist/lib/node-esm/chunk-ANLVLWME.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-DMR7OAFK.mjs +57 -0
- package/dist/lib/node-esm/chunk-DMR7OAFK.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-FWTSPIFF.mjs +133 -0
- package/dist/lib/node-esm/chunk-FWTSPIFF.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +11 -0
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-ILMLLM4R.mjs +204 -0
- package/dist/lib/node-esm/chunk-ILMLLM4R.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-K37NA7PO.mjs +43 -0
- package/dist/lib/node-esm/chunk-K37NA7PO.mjs.map +7 -0
- package/dist/lib/node-esm/{chunk-M4B6BMD2.mjs → chunk-LT3H4JOX.mjs} +1999 -1835
- package/dist/lib/node-esm/chunk-LT3H4JOX.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-MOLNWFNL.mjs +9 -0
- package/dist/lib/node-esm/chunk-MOLNWFNL.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-MOWUEW5P.mjs +15 -0
- package/dist/lib/node-esm/chunk-MOWUEW5P.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-NBWL7UCZ.mjs +40 -0
- package/dist/lib/node-esm/chunk-NBWL7UCZ.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-NRN3ZW2T.mjs +143 -0
- package/dist/lib/node-esm/chunk-NRN3ZW2T.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-QLI2EIJ2.mjs +41 -0
- package/dist/lib/node-esm/chunk-QLI2EIJ2.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-QYR67VBV.mjs +288 -0
- package/dist/lib/node-esm/chunk-QYR67VBV.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-UKGVOINP.mjs +9 -0
- package/dist/lib/node-esm/chunk-UKGVOINP.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-W6QIEBTQ.mjs +403 -0
- package/dist/lib/node-esm/chunk-W6QIEBTQ.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-XHJRMQZD.mjs +69 -0
- package/dist/lib/node-esm/chunk-XHJRMQZD.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-Y75VU7LB.mjs +74 -0
- package/dist/lib/node-esm/chunk-Y75VU7LB.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-YOLH5KS4.mjs +283 -0
- package/dist/lib/node-esm/chunk-YOLH5KS4.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-YQ2NWGL5.mjs +39 -0
- package/dist/lib/node-esm/chunk-YQ2NWGL5.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-ZBB67AKD.mjs +24 -0
- package/dist/lib/node-esm/chunk-ZBB67AKD.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +65 -25
- package/dist/lib/node-esm/internal/index.mjs +213 -92
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +80 -41
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/types/src/Annotation.d.ts +1 -1
- package/dist/types/src/Annotation.d.ts.map +1 -1
- package/dist/types/src/Database.d.ts +117 -60
- package/dist/types/src/Database.d.ts.map +1 -1
- package/dist/types/src/Entity.d.ts +111 -5
- package/dist/types/src/Entity.d.ts.map +1 -1
- package/dist/types/src/Entity.test.d.ts +2 -0
- package/dist/types/src/Entity.test.d.ts.map +1 -0
- package/dist/types/src/{errors.d.ts → Err.d.ts} +13 -17
- package/dist/types/src/Err.d.ts.map +1 -0
- package/dist/types/src/Filter.d.ts +120 -0
- package/dist/types/src/Filter.d.ts.map +1 -0
- package/dist/types/src/Format.d.ts.map +1 -1
- package/dist/types/src/Hypergraph.d.ts +60 -0
- package/dist/types/src/Hypergraph.d.ts.map +1 -0
- package/dist/types/src/Obj.d.ts +268 -76
- package/dist/types/src/Obj.d.ts.map +1 -1
- package/dist/types/src/Obj.test.d.ts +2 -0
- package/dist/types/src/Obj.test.d.ts.map +1 -0
- package/dist/types/src/Order.d.ts +16 -0
- package/dist/types/src/Order.d.ts.map +1 -0
- package/dist/types/src/{query/query.d.ts → Query.d.ts} +53 -50
- package/dist/types/src/Query.d.ts.map +1 -0
- package/dist/types/src/Query.test.d.ts +2 -0
- package/dist/types/src/Query.test.d.ts.map +1 -0
- package/dist/types/src/QueryResult.d.ts +80 -0
- package/dist/types/src/QueryResult.d.ts.map +1 -0
- package/dist/types/src/Ref.d.ts +9 -7
- package/dist/types/src/Ref.d.ts.map +1 -1
- package/dist/types/src/Relation.d.ts +235 -18
- package/dist/types/src/Relation.d.ts.map +1 -1
- package/dist/types/src/Relation.test.d.ts +2 -0
- package/dist/types/src/Relation.test.d.ts.map +1 -0
- package/dist/types/src/SchemaRegistry.d.ts +84 -0
- package/dist/types/src/SchemaRegistry.d.ts.map +1 -0
- package/dist/types/src/Tag.d.ts +6 -6
- package/dist/types/src/Tag.d.ts.map +1 -1
- package/dist/types/src/Type.d.ts +213 -50
- package/dist/types/src/Type.d.ts.map +1 -1
- package/dist/types/src/Type.test.d.ts +2 -0
- package/dist/types/src/Type.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +8 -2
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/internal/annotations/annotations.d.ts +23 -27
- package/dist/types/src/internal/annotations/annotations.d.ts.map +1 -1
- package/dist/types/src/internal/annotations/util.d.ts +1 -0
- package/dist/types/src/internal/annotations/util.d.ts.map +1 -1
- package/dist/types/src/internal/api/annotations.d.ts +23 -0
- package/dist/types/src/internal/api/annotations.d.ts.map +1 -0
- package/dist/types/src/internal/api/entity.d.ts +13 -0
- package/dist/types/src/internal/api/entity.d.ts.map +1 -0
- package/dist/types/src/internal/api/index.d.ts +15 -0
- package/dist/types/src/internal/api/index.d.ts.map +1 -0
- package/dist/types/src/internal/api/meta.d.ts +42 -0
- package/dist/types/src/internal/api/meta.d.ts.map +1 -0
- package/dist/types/src/internal/api/sorting.d.ts +24 -0
- package/dist/types/src/internal/api/sorting.d.ts.map +1 -0
- package/dist/types/src/internal/api/version.d.ts +42 -0
- package/dist/types/src/internal/api/version.d.ts.map +1 -0
- package/dist/types/src/internal/entities/entity.d.ts +13 -3
- package/dist/types/src/internal/entities/entity.d.ts.map +1 -1
- package/dist/types/src/internal/entities/index.d.ts +1 -1
- package/dist/types/src/internal/entities/index.d.ts.map +1 -1
- package/dist/types/src/internal/entities/model.d.ts +14 -7
- package/dist/types/src/internal/entities/model.d.ts.map +1 -1
- package/dist/types/src/internal/entities/object.d.ts +8 -1
- package/dist/types/src/internal/entities/object.d.ts.map +1 -1
- package/dist/types/src/internal/entities/relation.d.ts +8 -1
- package/dist/types/src/internal/entities/relation.d.ts.map +1 -1
- package/dist/types/src/internal/formats/format.d.ts +4 -4
- package/dist/types/src/internal/formats/format.d.ts.map +1 -1
- package/dist/types/src/internal/formats/select.d.ts +5 -3
- package/dist/types/src/internal/formats/select.d.ts.map +1 -1
- package/dist/types/src/internal/formats/string.d.ts +4 -0
- package/dist/types/src/internal/formats/string.d.ts.map +1 -1
- package/dist/types/src/internal/formats/types.d.ts +6 -2
- package/dist/types/src/internal/formats/types.d.ts.map +1 -1
- package/dist/types/src/internal/index.d.ts +2 -1
- package/dist/types/src/internal/index.d.ts.map +1 -1
- package/dist/types/src/internal/json-schema/json-schema.d.ts.map +1 -1
- package/dist/types/src/internal/object/clone.d.ts +8 -0
- package/dist/types/src/internal/object/clone.d.ts.map +1 -0
- package/dist/types/src/internal/object/create-object.d.ts +1 -1
- package/dist/types/src/internal/object/index.d.ts +3 -0
- package/dist/types/src/internal/object/index.d.ts.map +1 -1
- package/dist/types/src/internal/object/json-serializer.d.ts +13 -7
- package/dist/types/src/internal/object/json-serializer.d.ts.map +1 -1
- package/dist/types/src/internal/object/schema-validator.d.ts +1 -14
- package/dist/types/src/internal/object/schema-validator.d.ts.map +1 -1
- package/dist/types/src/internal/object/set-value.d.ts +7 -0
- package/dist/types/src/internal/object/set-value.d.ts.map +1 -0
- package/dist/types/src/internal/object/set-value.test.d.ts +2 -0
- package/dist/types/src/internal/object/set-value.test.d.ts.map +1 -0
- package/dist/types/src/internal/object/snapshot.d.ts +6 -0
- package/dist/types/src/internal/object/snapshot.d.ts.map +1 -0
- package/dist/types/src/internal/object/typed-object.d.ts +7 -13
- package/dist/types/src/internal/object/typed-object.d.ts.map +1 -1
- package/dist/types/src/internal/proxy/change-context.d.ts +55 -0
- package/dist/types/src/internal/proxy/change-context.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/change.test.d.ts +2 -0
- package/dist/types/src/internal/proxy/change.test.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/define-hidden-property.d.ts +5 -0
- package/dist/types/src/internal/proxy/define-hidden-property.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/errors.d.ts +19 -0
- package/dist/types/src/internal/proxy/errors.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/event-batch.d.ts +10 -0
- package/dist/types/src/internal/proxy/event-batch.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/index.d.ts +11 -0
- package/dist/types/src/internal/proxy/index.d.ts.map +1 -1
- package/dist/types/src/internal/proxy/json-serializer.d.ts +6 -0
- package/dist/types/src/internal/proxy/json-serializer.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/make-object.d.ts +2 -4
- package/dist/types/src/internal/proxy/make-object.d.ts.map +1 -1
- package/dist/types/src/internal/proxy/ownership.d.ts +57 -0
- package/dist/types/src/internal/proxy/ownership.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/proxy-types.d.ts +18 -0
- package/dist/types/src/internal/proxy/proxy-types.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/proxy-utils.d.ts +47 -0
- package/dist/types/src/internal/proxy/proxy-utils.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/reactive-array.d.ts +8 -0
- package/dist/types/src/internal/proxy/reactive-array.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/reactive.d.ts +39 -0
- package/dist/types/src/internal/proxy/reactive.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/schema-validator.d.ts +15 -0
- package/dist/types/src/internal/proxy/schema-validator.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/symbols.d.ts +3 -0
- package/dist/types/src/internal/proxy/symbols.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/typed-handler.d.ts +14 -11
- package/dist/types/src/internal/proxy/typed-handler.d.ts.map +1 -1
- package/dist/types/src/internal/ref/ref-array.d.ts +4 -4
- package/dist/types/src/internal/ref/ref-array.d.ts.map +1 -1
- package/dist/types/src/internal/ref/ref.d.ts +14 -6
- package/dist/types/src/internal/ref/ref.d.ts.map +1 -1
- package/dist/types/src/internal/schema/compose.d.ts.map +1 -1
- package/dist/types/src/internal/schema/echo-schema.d.ts +15 -3
- package/dist/types/src/internal/schema/echo-schema.d.ts.map +1 -1
- package/dist/types/src/internal/schema/index.d.ts +0 -2
- package/dist/types/src/internal/schema/index.d.ts.map +1 -1
- package/dist/types/src/internal/schema/persistent-schema.d.ts +9 -7
- package/dist/types/src/internal/schema/persistent-schema.d.ts.map +1 -1
- package/dist/types/src/internal/types/base.d.ts +5 -16
- package/dist/types/src/internal/types/base.d.ts.map +1 -1
- package/dist/types/src/internal/types/entity.d.ts +27 -2
- package/dist/types/src/internal/types/entity.d.ts.map +1 -1
- package/dist/types/src/internal/types/meta.d.ts +1 -9
- package/dist/types/src/internal/types/meta.d.ts.map +1 -1
- package/dist/types/src/testing/test-schema.d.ts +141 -175
- package/dist/types/src/testing/test-schema.d.ts.map +1 -1
- package/dist/types/src/testing/util.d.ts +6 -1
- package/dist/types/src/testing/util.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +130 -23
- package/src/Annotation.ts +5 -4
- package/src/Database.ts +229 -82
- package/src/Entity.test.ts +22 -0
- package/src/Entity.ts +173 -7
- package/src/{errors.ts → Err.ts} +2 -2
- package/src/Filter.ts +376 -0
- package/src/Format.ts +0 -2
- package/src/Hypergraph.ts +74 -0
- package/src/Obj.test.ts +386 -0
- package/src/Obj.ts +338 -250
- package/src/{query/order.ts → Order.ts} +19 -9
- package/src/{query/query.test.ts → Query.test.ts} +180 -49
- package/src/{query/query.ts → Query.ts} +115 -122
- package/src/QueryResult.ts +106 -0
- package/src/Ref.ts +20 -3
- package/src/Relation.test.ts +82 -0
- package/src/Relation.ts +364 -27
- package/src/SchemaRegistry.ts +105 -0
- package/src/Tag.ts +1 -1
- package/src/Type.test.ts +52 -0
- package/src/Type.ts +322 -88
- package/src/index.ts +9 -5
- package/src/internal/README.md +36 -17
- package/src/internal/annotations/annotations.test.ts +6 -6
- package/src/internal/annotations/annotations.ts +73 -76
- package/src/internal/annotations/util.ts +2 -0
- package/src/internal/api/annotations.ts +60 -0
- package/src/internal/api/entity.ts +29 -0
- package/src/internal/api/index.ts +19 -0
- package/src/internal/api/meta.ts +88 -0
- package/src/internal/api/sorting.ts +53 -0
- package/src/internal/api/version.ts +96 -0
- package/src/internal/entities/entity.ts +36 -19
- package/src/internal/entities/index.ts +1 -1
- package/src/internal/entities/model.ts +17 -12
- package/src/internal/entities/object.ts +20 -5
- package/src/internal/entities/relation.ts +22 -4
- package/src/internal/formats/format.ts +7 -8
- package/src/internal/formats/object.ts +2 -2
- package/src/internal/formats/select.ts +5 -3
- package/src/internal/formats/string.ts +5 -0
- package/src/internal/formats/types.ts +9 -3
- package/src/internal/index.ts +2 -1
- package/src/internal/json-schema/json-schema-type.ts +1 -1
- package/src/internal/json-schema/json-schema.test.ts +19 -17
- package/src/internal/json-schema/json-schema.ts +4 -4
- package/src/internal/object/clone.ts +48 -0
- package/src/internal/object/create-object.ts +2 -2
- package/src/internal/object/index.ts +3 -0
- package/src/internal/object/inspect.ts +3 -3
- package/src/internal/object/json-serializer.test.ts +4 -1
- package/src/internal/object/json-serializer.ts +28 -70
- package/src/internal/object/schema-validator.ts +2 -238
- package/src/internal/object/set-value.test.ts +281 -0
- package/src/internal/object/set-value.ts +165 -0
- package/src/internal/object/snapshot.ts +70 -0
- package/src/internal/object/typed-object.test.ts +11 -11
- package/src/internal/object/typed-object.ts +8 -72
- package/src/internal/proxy/change-context.ts +138 -0
- package/src/internal/proxy/change.test.ts +519 -0
- package/src/internal/proxy/define-hidden-property.ts +14 -0
- package/src/internal/proxy/errors.ts +42 -0
- package/src/internal/proxy/event-batch.ts +44 -0
- package/src/internal/proxy/handler.test.ts +30 -80
- package/src/internal/proxy/index.ts +11 -0
- package/src/internal/proxy/json-serializer.ts +87 -0
- package/src/internal/proxy/make-object.ts +33 -50
- package/src/internal/proxy/ownership.ts +253 -0
- package/src/internal/proxy/proxy-types.ts +23 -0
- package/src/internal/proxy/proxy-utils.ts +150 -0
- package/src/internal/proxy/reactive-array.ts +71 -0
- package/src/internal/proxy/reactive.ts +69 -0
- package/src/internal/proxy/schema-validator.ts +244 -0
- package/src/internal/proxy/schema.test.ts +23 -15
- package/src/internal/proxy/symbols.ts +7 -0
- package/src/internal/proxy/typed-handler.test.ts +251 -35
- package/src/internal/proxy/typed-handler.ts +265 -56
- package/src/internal/proxy/typed-object.test.ts +26 -15
- package/src/internal/ref/ref-array.ts +4 -4
- package/src/internal/ref/ref.ts +62 -39
- package/src/internal/schema/compose.test.ts +3 -3
- package/src/internal/schema/compose.ts +1 -2
- package/src/internal/schema/echo-schema.ts +49 -11
- package/src/internal/schema/index.ts +0 -2
- package/src/internal/schema/persistent-schema.ts +3 -4
- package/src/internal/types/base.ts +6 -21
- package/src/internal/types/entity.ts +35 -4
- package/src/internal/types/meta.ts +1 -11
- package/src/testing/api.test.ts +31 -5
- package/src/testing/test-schema.ts +55 -30
- package/src/testing/util.ts +22 -15
- package/dist/lib/browser/chunk-7GH6RXJ3.mjs.map +0 -7
- package/dist/lib/browser/chunk-E4UTVJNF.mjs +0 -1111
- package/dist/lib/browser/chunk-E4UTVJNF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-JE5RXM2I.mjs +0 -1111
- package/dist/lib/node-esm/chunk-JE5RXM2I.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-M4B6BMD2.mjs.map +0 -7
- package/dist/types/src/errors.d.ts.map +0 -1
- package/dist/types/src/internal/entities/expando.d.ts +0 -16
- package/dist/types/src/internal/entities/expando.d.ts.map +0 -1
- package/dist/types/src/internal/schema/runtime-schema-registry.d.ts +0 -18
- package/dist/types/src/internal/schema/runtime-schema-registry.d.ts.map +0 -1
- package/dist/types/src/internal/schema/snapshot.d.ts +0 -6
- package/dist/types/src/internal/schema/snapshot.d.ts.map +0 -1
- package/dist/types/src/query/filter.d.ts +0 -167
- package/dist/types/src/query/filter.d.ts.map +0 -1
- package/dist/types/src/query/index.d.ts +0 -5
- package/dist/types/src/query/index.d.ts.map +0 -1
- package/dist/types/src/query/order.d.ts +0 -12
- package/dist/types/src/query/order.d.ts.map +0 -1
- package/dist/types/src/query/query.d.ts.map +0 -1
- package/dist/types/src/query/query.test.d.ts +0 -2
- package/dist/types/src/query/query.test.d.ts.map +0 -1
- package/dist/types/src/query/testing.d.ts +0 -51
- package/dist/types/src/query/testing.d.ts.map +0 -1
- package/dist/types/src/query/types.d.ts +0 -17
- package/dist/types/src/query/types.d.ts.map +0 -1
- package/dist/types/src/query/util.d.ts +0 -8
- package/dist/types/src/query/util.d.ts.map +0 -1
- package/src/internal/entities/expando.ts +0 -23
- package/src/internal/schema/runtime-schema-registry.ts +0 -78
- package/src/internal/schema/snapshot.ts +0 -25
- package/src/query/filter.ts +0 -455
- package/src/query/index.ts +0 -9
- package/src/query/testing.ts +0 -64
- package/src/query/types.ts +0 -23
- package/src/query/util.ts +0 -25
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { invariant } from '@dxos/invariant';
|
|
6
|
+
|
|
7
|
+
import { KindId } from '../types';
|
|
8
|
+
|
|
9
|
+
import { queueOwnerNotification } from './change-context';
|
|
10
|
+
import { defineHiddenProperty } from './define-hidden-property';
|
|
11
|
+
import { getProxyTarget, isProxy, isValidProxyTarget } from './proxy-utils';
|
|
12
|
+
import { EventId } from './symbols';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Symbol to store the owning ECHO object reference on nested JS objects (records).
|
|
16
|
+
* Every nested record is attributed to exactly one ECHO object.
|
|
17
|
+
* This achieves:
|
|
18
|
+
* - No cycles in the object graph (cyclical Refs are still allowed).
|
|
19
|
+
* - No multiple inbound pointers to one record.
|
|
20
|
+
* - Centralized reactivity for entire ECHO object.
|
|
21
|
+
*/
|
|
22
|
+
const EchoOwner = Symbol.for('@dxos/echo/Owner');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get the raw target from a value, unwrapping proxy if needed.
|
|
26
|
+
*/
|
|
27
|
+
export const getRawTarget = (value: any): any => {
|
|
28
|
+
return isProxy(value) ? getProxyTarget(value) : value;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the ECHO object that owns this nested record.
|
|
33
|
+
*
|
|
34
|
+
* The owner is always the raw target object (not a proxy) of the root ECHO object.
|
|
35
|
+
* For example, if you have `echoObject.nested.deep`, both `nested` and `deep`
|
|
36
|
+
* will have their owner set to the raw target of `echoObject`.
|
|
37
|
+
*
|
|
38
|
+
* @param value - The nested record to check (can be a proxy or raw target).
|
|
39
|
+
* @returns The raw target of the owning root ECHO object, or undefined if not owned.
|
|
40
|
+
*/
|
|
41
|
+
export const getOwner = (value: object | null | undefined): object | undefined => {
|
|
42
|
+
return (value as any)?.[EchoOwner];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Set the owner of a meta object to its parent.
|
|
47
|
+
* This allows meta mutations to respect the parent's change context.
|
|
48
|
+
* @internal
|
|
49
|
+
*/
|
|
50
|
+
export const setMetaOwner = (metaTarget: object, parent: object): void => {
|
|
51
|
+
defineHiddenProperty(metaTarget, EchoOwner, parent);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Set the ECHO object owner on a value and all its nested records.
|
|
56
|
+
* All nested JS objects point directly to the root ECHO object.
|
|
57
|
+
*
|
|
58
|
+
* @param value - The value to set ownership on (can be a proxy or raw target).
|
|
59
|
+
* @param owner - The raw target of the root ECHO object that will own this value.
|
|
60
|
+
* @param options.visited - Set of already-visited objects to avoid infinite loops.
|
|
61
|
+
* @param options.depth - Current recursion depth (unused, kept for debugging).
|
|
62
|
+
* @param options.allowedPreviousOwner - When reassigning a root ECHO object, its nested structures
|
|
63
|
+
* are allowed to have this as their previous owner without triggering the invariant.
|
|
64
|
+
*/
|
|
65
|
+
export const setOwnerRecursive = (
|
|
66
|
+
value: any,
|
|
67
|
+
owner: object,
|
|
68
|
+
options: {
|
|
69
|
+
visited?: Set<object>;
|
|
70
|
+
depth?: number;
|
|
71
|
+
allowedPreviousOwner?: object;
|
|
72
|
+
} = {},
|
|
73
|
+
): void => {
|
|
74
|
+
const { visited = new Set<object>(), depth = 0, allowedPreviousOwner } = options;
|
|
75
|
+
if (value == null || typeof value !== 'object') {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const actualValue = getRawTarget(value);
|
|
80
|
+
if (visited.has(actualValue)) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
visited.add(actualValue);
|
|
84
|
+
|
|
85
|
+
// Check that we're not stealing a nested record owned by a different ECHO object.
|
|
86
|
+
// Root ECHO objects (those with EventId) can be reassigned - they maintain their own
|
|
87
|
+
// identity and choosing to embed them in another object is a valid operation.
|
|
88
|
+
// When reassigning a root, its nested records (owned by that root) are also allowed.
|
|
89
|
+
const existingOwner = getOwner(actualValue);
|
|
90
|
+
const isRootEchoObject = EventId in actualValue;
|
|
91
|
+
|
|
92
|
+
// Track if this is a root being assigned - its nested structures are allowed to transfer.
|
|
93
|
+
let newAllowedPreviousOwner = allowedPreviousOwner;
|
|
94
|
+
if (isRootEchoObject && depth === 0) {
|
|
95
|
+
// This is the top-level root being assigned; allow its nested structures to transfer.
|
|
96
|
+
newAllowedPreviousOwner = actualValue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!isRootEchoObject) {
|
|
100
|
+
const ownershipAllowed =
|
|
101
|
+
existingOwner == null || existingOwner === owner || existingOwner === newAllowedPreviousOwner;
|
|
102
|
+
invariant(
|
|
103
|
+
ownershipAllowed,
|
|
104
|
+
'Cannot reassign ownership of a nested record to a different ECHO object. Use deep copy first.',
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Set owner directly to the root ECHO object.
|
|
109
|
+
defineHiddenProperty(actualValue, EchoOwner, owner);
|
|
110
|
+
|
|
111
|
+
// Recursively set owner on nested objects and array elements.
|
|
112
|
+
const recursiveOptions = {
|
|
113
|
+
visited,
|
|
114
|
+
depth: depth + 1,
|
|
115
|
+
allowedPreviousOwner: newAllowedPreviousOwner,
|
|
116
|
+
};
|
|
117
|
+
if (Array.isArray(actualValue)) {
|
|
118
|
+
for (const item of actualValue) {
|
|
119
|
+
if (isValidProxyTarget(item) || isProxy(item)) {
|
|
120
|
+
setOwnerRecursive(item, owner, recursiveOptions);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
for (const key in actualValue) {
|
|
125
|
+
if (Object.prototype.hasOwnProperty.call(actualValue, key)) {
|
|
126
|
+
const nested = actualValue[key];
|
|
127
|
+
if (isValidProxyTarget(nested) || isProxy(nested)) {
|
|
128
|
+
setOwnerRecursive(nested, owner, recursiveOptions);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Traverse an object graph, calling the visitor on each object.
|
|
137
|
+
* Handles proxy unwrapping and cycle detection.
|
|
138
|
+
*
|
|
139
|
+
* @param value - The value to traverse (can be a proxy or raw target).
|
|
140
|
+
* @param visitor - Called for each object. Return true to stop traversal (early exit).
|
|
141
|
+
* @returns true if the visitor returns true for any object.
|
|
142
|
+
*/
|
|
143
|
+
export const traverseObjectGraph = (
|
|
144
|
+
value: any,
|
|
145
|
+
visitor: (actualValue: any) => boolean,
|
|
146
|
+
visited = new Set<object>(),
|
|
147
|
+
): boolean => {
|
|
148
|
+
if (value == null || typeof value !== 'object') {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const actualValue = getRawTarget(value);
|
|
153
|
+
|
|
154
|
+
if (visited.has(actualValue)) {
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
visited.add(actualValue);
|
|
158
|
+
|
|
159
|
+
if (visitor(actualValue)) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (Array.isArray(actualValue)) {
|
|
164
|
+
for (const item of actualValue) {
|
|
165
|
+
if (traverseObjectGraph(item, visitor, visited)) {
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
for (const key in actualValue) {
|
|
171
|
+
if (Object.prototype.hasOwnProperty.call(actualValue, key)) {
|
|
172
|
+
if (traverseObjectGraph(actualValue[key], visitor, visited)) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return false;
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Check if a value would create a cycle when assigned to a target ECHO object.
|
|
184
|
+
* Returns true if the value (or any nested object) IS the target root.
|
|
185
|
+
*/
|
|
186
|
+
export const wouldCreateCycle = (targetRoot: object, value: any): boolean =>
|
|
187
|
+
traverseObjectGraph(value, (v) => v === targetRoot);
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Check if a value or any of its nested objects has an owner different from the target.
|
|
191
|
+
* Used to determine if deep copy is needed during init.
|
|
192
|
+
*/
|
|
193
|
+
export const hasForeignOwner = (value: any, target: object): boolean =>
|
|
194
|
+
traverseObjectGraph(value, (v) => {
|
|
195
|
+
const owner = getOwner(v);
|
|
196
|
+
if (owner != null && owner !== target) {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
// Root ECHO objects (with EventId) have their nested structures owned by them.
|
|
200
|
+
if (EventId in v && v !== target) {
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
203
|
+
return false;
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Maximum depth for owner chain traversal.
|
|
208
|
+
* This is a defensive measure against malformed circular ownership.
|
|
209
|
+
* Primary cycle detection is handled by wouldCreateCycle() before assignment.
|
|
210
|
+
*/
|
|
211
|
+
const MAX_OWNER_DEPTH = 100;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Get the root ECHO object for a target.
|
|
215
|
+
* Follows the owner chain to find the ultimate root.
|
|
216
|
+
* An object may have EventId (from being created standalone) but if it now
|
|
217
|
+
* has an owner, it's nested and we should use its owner's root instead.
|
|
218
|
+
* @internal
|
|
219
|
+
*/
|
|
220
|
+
export const getEchoRoot = (target: object, depth = 0): object => {
|
|
221
|
+
invariant(depth < MAX_OWNER_DEPTH, 'Owner chain too deep - possible circular ownership');
|
|
222
|
+
|
|
223
|
+
// Root ECHO objects (those created with Obj.make or Relation.make) have KindId set.
|
|
224
|
+
// They maintain their own change context identity even when nested inside another object.
|
|
225
|
+
// Nested helper objects like ObjectMeta don't have KindId and should follow their owner.
|
|
226
|
+
if (KindId in target) {
|
|
227
|
+
return target;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// For non-root objects (nested records, ObjectMeta, etc.), follow the owner chain.
|
|
231
|
+
const owner = getOwner(target);
|
|
232
|
+
if (owner) {
|
|
233
|
+
return getEchoRoot(owner, depth + 1);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// No owner means this is an unowned object (e.g., during initialization).
|
|
237
|
+
return target;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Notify all owners in the ownership chain.
|
|
242
|
+
* When a nested object changes, its parent should also be notified.
|
|
243
|
+
* This handles the case where a root ECHO object is nested inside another object.
|
|
244
|
+
*/
|
|
245
|
+
export const notifyOwnerChain = (target: object): void => {
|
|
246
|
+
const owner = getOwner(target);
|
|
247
|
+
if (owner) {
|
|
248
|
+
// Queue notification for the owner's root (which has EventId).
|
|
249
|
+
queueOwnerNotification(getEchoRoot(owner));
|
|
250
|
+
// Continue up the chain (owner might also be nested).
|
|
251
|
+
notifyOwnerChain(owner);
|
|
252
|
+
}
|
|
253
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reactive object proxy.
|
|
7
|
+
*/
|
|
8
|
+
export interface ReactiveHandler<T extends object> extends ProxyHandler<T> {
|
|
9
|
+
/**
|
|
10
|
+
* Target to Proxy mapping.
|
|
11
|
+
*/
|
|
12
|
+
readonly _proxyMap: WeakMap<object, any>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Called when a proxy is created for this target.
|
|
16
|
+
*/
|
|
17
|
+
init(target: T): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* For debug-dumping the data of the object.
|
|
22
|
+
*/
|
|
23
|
+
export const objectData = Symbol.for('@dxos/live-object/objectData');
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { invariant } from '@dxos/invariant';
|
|
6
|
+
|
|
7
|
+
import { type ReactiveHandler } from './proxy-types';
|
|
8
|
+
|
|
9
|
+
export const symbolIsProxy = Symbol.for('@dxos/schema/Proxy');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal api.
|
|
13
|
+
*/
|
|
14
|
+
export const isProxy = (value: unknown) => !!(value as any)?.[symbolIsProxy];
|
|
15
|
+
|
|
16
|
+
export const isValidProxyTarget = (value: any): value is object => {
|
|
17
|
+
if (value == null || value[symbolIsProxy]) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(value)) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated
|
|
29
|
+
*/
|
|
30
|
+
export const getProxySlot = <T extends object>(proxy: any): ProxyHandlerSlot<T> => {
|
|
31
|
+
const value = (proxy as any)[symbolIsProxy];
|
|
32
|
+
invariant(value instanceof ProxyHandlerSlot);
|
|
33
|
+
return value;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const getProxyTarget = <T extends object>(proxy: any): T => {
|
|
37
|
+
return getProxySlot<T>(proxy).target;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export const getProxyHandler = <T extends object>(proxy: any): ReactiveHandler<T> => {
|
|
41
|
+
return getProxySlot<T>(proxy).handler;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Unsafe method to override id for debugging/testing and migration purposes.
|
|
46
|
+
* @deprecated
|
|
47
|
+
*/
|
|
48
|
+
export const dangerouslySetProxyId = <T>(obj: T, id: string) => {
|
|
49
|
+
(getProxySlot(obj).target as any).id = id;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Create a reactive proxy object.
|
|
54
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
|
|
55
|
+
*
|
|
56
|
+
* @param target Object or array. Passing in array will enable array methods.
|
|
57
|
+
* @param handler ReactiveHandler instance.
|
|
58
|
+
*/
|
|
59
|
+
// TODO(burdon): Document.
|
|
60
|
+
// TODO(burdon): Tests for low-level functions.
|
|
61
|
+
export const createProxy = <T extends object>(target: T, handler: ReactiveHandler<T>): T => {
|
|
62
|
+
const existingProxy = handler._proxyMap.get(target);
|
|
63
|
+
if (existingProxy) {
|
|
64
|
+
return existingProxy;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// TODO(dmaretskyi): In the future this should be mutable to allow replacing the handler on-the-fly while maintaining the proxy identity.
|
|
68
|
+
const proxy = new Proxy(target, new ProxyHandlerSlot<T>(target, handler));
|
|
69
|
+
handler.init(target);
|
|
70
|
+
|
|
71
|
+
// TODO(dmaretskyi): Check if this will actually work; maybe a global WeakMap is better?
|
|
72
|
+
handler._proxyMap.set(target, proxy);
|
|
73
|
+
return proxy;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Passed as the handler to the Proxy constructor.
|
|
78
|
+
* Maintains a mutable slot for the actual handler.
|
|
79
|
+
*/
|
|
80
|
+
class ProxyHandlerSlot<T extends object> implements ProxyHandler<T> {
|
|
81
|
+
/**
|
|
82
|
+
* @param target Original object.
|
|
83
|
+
* @param _handler Handles intercepted operations.
|
|
84
|
+
*/
|
|
85
|
+
constructor(
|
|
86
|
+
readonly target: T,
|
|
87
|
+
private _handler: ReactiveHandler<T>,
|
|
88
|
+
) {}
|
|
89
|
+
|
|
90
|
+
get handler() {
|
|
91
|
+
invariant(this._handler);
|
|
92
|
+
return this._handler;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// TODO(burdon): Requires comment.
|
|
96
|
+
setHandler(handler: ReactiveHandler<T>): void {
|
|
97
|
+
this._handler = handler;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get value.
|
|
102
|
+
*/
|
|
103
|
+
get(target: T, prop: string | symbol, receiver: any): any {
|
|
104
|
+
if (prop === symbolIsProxy) {
|
|
105
|
+
return this;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!this._handler || !this._handler.get) {
|
|
109
|
+
return Reflect.get(target, prop, receiver);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return this._handler.get(target, prop, receiver);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
static {
|
|
116
|
+
const TRAPS: (keyof ProxyHandler<any>)[] = [
|
|
117
|
+
'apply',
|
|
118
|
+
'construct',
|
|
119
|
+
'defineProperty',
|
|
120
|
+
'deleteProperty',
|
|
121
|
+
'get',
|
|
122
|
+
'getOwnPropertyDescriptor',
|
|
123
|
+
'getPrototypeOf',
|
|
124
|
+
'has',
|
|
125
|
+
'isExtensible',
|
|
126
|
+
'ownKeys',
|
|
127
|
+
'preventExtensions',
|
|
128
|
+
'set',
|
|
129
|
+
'setPrototypeOf',
|
|
130
|
+
];
|
|
131
|
+
|
|
132
|
+
for (const trap of TRAPS) {
|
|
133
|
+
if (trap === 'get') {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
Object.defineProperty(this.prototype, trap, {
|
|
138
|
+
enumerable: false,
|
|
139
|
+
value: function (this: ProxyHandlerSlot<any>, ...args: any[]) {
|
|
140
|
+
// log.info('trap', { trap, args });
|
|
141
|
+
if (!this._handler || !this._handler[trap]) {
|
|
142
|
+
return (Reflect[trap] as Function)(...args);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return (this._handler[trap] as Function).apply(this._handler, args);
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { isInChangeContext } from './change-context';
|
|
6
|
+
import { createArrayMethodError } from './errors';
|
|
7
|
+
import { batchEvents } from './event-batch';
|
|
8
|
+
import { getEchoRoot } from './ownership';
|
|
9
|
+
import { getProxyTarget, isProxy } from './proxy-utils';
|
|
10
|
+
import { ChangeId, EventId } from './symbols';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Check if array mutation is allowed (inside a change context).
|
|
14
|
+
* Throws a descriptive error if not.
|
|
15
|
+
*/
|
|
16
|
+
const checkArrayMutationAllowed = (arr: any, method: string): void => {
|
|
17
|
+
// Get the raw target - if arr is a proxy, get its underlying target.
|
|
18
|
+
const target = isProxy(arr) ? getProxyTarget(arr) : arr;
|
|
19
|
+
|
|
20
|
+
// Find the root ECHO object.
|
|
21
|
+
const echoRoot = getEchoRoot(target);
|
|
22
|
+
|
|
23
|
+
// Check if initialized (has ChangeId marker or EventId).
|
|
24
|
+
const isInitialized = (echoRoot as any)[ChangeId] === true || EventId in echoRoot;
|
|
25
|
+
if (!isInitialized) {
|
|
26
|
+
// Array is still being initialized, allow mutations.
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!isInChangeContext(echoRoot)) {
|
|
31
|
+
throw createArrayMethodError(method);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Extends the native array to make sure that arrays methods are correctly reactive.
|
|
37
|
+
* Enforces that mutations only happen within Obj.change() context.
|
|
38
|
+
*/
|
|
39
|
+
export class ReactiveArray<T> extends Array<T> {
|
|
40
|
+
static override get [Symbol.species]() {
|
|
41
|
+
return Array;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
static {
|
|
45
|
+
/**
|
|
46
|
+
* These methods will trigger proxy traps like `set` and `defineProperty` and emit event notifications.
|
|
47
|
+
* We wrap them in a batch to avoid unnecessary event notifications.
|
|
48
|
+
* Change context is checked before allowing mutations.
|
|
49
|
+
*
|
|
50
|
+
* Note: When called on a proxy, `this` will be the proxy, so array mutations
|
|
51
|
+
* go through the proxy's set trap which handles event emission.
|
|
52
|
+
*/
|
|
53
|
+
const BATCHED_METHODS = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] as const;
|
|
54
|
+
|
|
55
|
+
for (const method of BATCHED_METHODS) {
|
|
56
|
+
Object.defineProperty(this.prototype, method, {
|
|
57
|
+
enumerable: false,
|
|
58
|
+
value: function (this: ReactiveArray<any>, ...args: any[]) {
|
|
59
|
+
// Check change context before allowing mutation.
|
|
60
|
+
checkArrayMutationAllowed(this, method);
|
|
61
|
+
|
|
62
|
+
let result!: any;
|
|
63
|
+
batchEvents(() => {
|
|
64
|
+
result = Array.prototype[method].apply(this, args);
|
|
65
|
+
});
|
|
66
|
+
return result;
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type RefTypeId } from '../ref/ref';
|
|
6
|
+
|
|
7
|
+
import { getProxyTarget } from './proxy-utils';
|
|
8
|
+
import { ChangeId, EventId } from './symbols';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to changes on a reactive object.
|
|
12
|
+
* @param obj - The reactive object to subscribe to.
|
|
13
|
+
* @param callback - Called when the object changes.
|
|
14
|
+
* @returns Unsubscribe function.
|
|
15
|
+
*/
|
|
16
|
+
// TODO(wittjosiah): Consider throwing if obj doesn't have EventId instead of returning no-op.
|
|
17
|
+
export const subscribe = (obj: unknown, callback: () => void): (() => void) => {
|
|
18
|
+
const target = getProxyTarget(obj as any);
|
|
19
|
+
if (target && EventId in target) {
|
|
20
|
+
return (target as any)[EventId].on(callback);
|
|
21
|
+
}
|
|
22
|
+
return () => {};
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Deeply removes readonly modifiers from all properties of T.
|
|
27
|
+
* Inside Obj.change, all properties are fully mutable regardless of schema definition.
|
|
28
|
+
* Ref types are preserved as-is since they are value-like objects that are replaced, not mutated.
|
|
29
|
+
* Primitive types (including branded primitives) are preserved as-is.
|
|
30
|
+
*/
|
|
31
|
+
export type Mutable<T> = T extends string | number | boolean | bigint | symbol | null | undefined
|
|
32
|
+
? T // Primitives (including branded primitives like JsonPath) stay as-is.
|
|
33
|
+
: T extends { [RefTypeId]: any }
|
|
34
|
+
? T // Keep Ref types as-is (they're value-like, not mutated in place).
|
|
35
|
+
: T extends object
|
|
36
|
+
? T extends readonly (infer U)[]
|
|
37
|
+
? Mutable<U>[]
|
|
38
|
+
: { -readonly [K in keyof T]: Mutable<T[K]> }
|
|
39
|
+
: T;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Callback type for the change function.
|
|
43
|
+
*/
|
|
44
|
+
export type ChangeCallback<T> = (mutableObj: Mutable<T>) => void;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Perform mutations on a reactive object within a change context.
|
|
48
|
+
*
|
|
49
|
+
* If the object has a change handler (via ChangeId), it will be called with the callback.
|
|
50
|
+
* This allows handlers to implement features like:
|
|
51
|
+
* - Readonly enforcement (mutations only allowed within change context)
|
|
52
|
+
* - Batched notifications (single notification for all mutations in the callback)
|
|
53
|
+
* - Transaction semantics
|
|
54
|
+
*
|
|
55
|
+
* If the object doesn't have a change handler, the callback is called directly.
|
|
56
|
+
*
|
|
57
|
+
* @param obj - The reactive object to mutate.
|
|
58
|
+
* @param callback - Callback that receives a mutable view of the object.
|
|
59
|
+
*/
|
|
60
|
+
export const change = <T>(obj: T, callback: ChangeCallback<T>): void => {
|
|
61
|
+
// Check proxy first (allows handler to intercept), then fall back to target.
|
|
62
|
+
// This order is important for EchoReactiveHandler which handles ChangeId in the proxy trap.
|
|
63
|
+
const changeFn = (obj as any)[ChangeId];
|
|
64
|
+
if (changeFn) {
|
|
65
|
+
changeFn(callback);
|
|
66
|
+
} else {
|
|
67
|
+
callback(obj as Mutable<T>);
|
|
68
|
+
}
|
|
69
|
+
};
|