@dxos/echo 0.8.4-main.21d9917 → 0.8.4-main.2244d791bb
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/{browser → neutral}/Annotation.mjs +7 -5
- package/dist/lib/neutral/Database.mjs +45 -0
- package/dist/lib/{browser → neutral}/Entity.mjs +8 -9
- package/dist/lib/{browser → neutral}/Err.mjs +4 -3
- package/dist/lib/{browser → neutral}/Filter.mjs +9 -10
- package/dist/lib/{browser → neutral}/Format.mjs +4 -5
- package/dist/lib/neutral/JsonSchema.mjs +19 -0
- package/dist/lib/{browser → neutral}/Key.mjs +2 -3
- package/dist/lib/{node-esm → neutral}/Obj.mjs +23 -10
- package/dist/lib/{browser → neutral}/Order.mjs +2 -3
- package/dist/lib/neutral/Query.mjs +26 -0
- package/dist/lib/neutral/QueryResult.mjs +2 -0
- package/dist/lib/neutral/Ref.mjs +22 -0
- package/dist/lib/{browser → neutral}/Relation.mjs +9 -10
- package/dist/lib/neutral/SchemaRegistry.mjs +2 -0
- package/dist/lib/neutral/Tag.mjs +25 -0
- package/dist/lib/{browser → neutral}/Type.mjs +8 -9
- package/dist/lib/{browser/chunk-W5D2GWAW.mjs → neutral/chunk-36GDMODZ.mjs} +5 -6
- package/dist/lib/{browser/chunk-W5D2GWAW.mjs.map → neutral/chunk-36GDMODZ.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-ZHXZGIXD.mjs → neutral/chunk-4L6DOFXP.mjs} +1 -3
- package/dist/lib/{browser/chunk-ZHXZGIXD.mjs.map → neutral/chunk-4L6DOFXP.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-NOPVNWPT.mjs → neutral/chunk-5MGEVDUW.mjs} +73 -8
- package/dist/lib/neutral/chunk-5MGEVDUW.mjs.map +7 -0
- package/dist/lib/{browser/chunk-FRDT7RA4.mjs → neutral/chunk-7WE7SBA2.mjs} +4 -5
- package/dist/lib/neutral/chunk-7WE7SBA2.mjs.map +7 -0
- package/dist/lib/{browser/chunk-INHXFXY5.mjs → neutral/chunk-ANHVGJI4.mjs} +2 -3
- package/dist/lib/{node-esm/chunk-AJEMYSIR.mjs.map → neutral/chunk-ANHVGJI4.mjs.map} +1 -1
- package/dist/lib/neutral/chunk-B4FLOYG7.mjs +130 -0
- package/dist/lib/neutral/chunk-B4FLOYG7.mjs.map +7 -0
- package/dist/lib/{browser/chunk-U2J7TA7K.mjs → neutral/chunk-BHR4XVJK.mjs} +5 -6
- package/dist/lib/{node-esm/chunk-DMR7OAFK.mjs.map → neutral/chunk-BHR4XVJK.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-ZAGAOZVY.mjs → neutral/chunk-CH3HX4Z5.mjs} +4 -5
- package/dist/lib/{node-esm/chunk-QYR67VBV.mjs.map → neutral/chunk-CH3HX4Z5.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-CGS2ULMK.mjs → neutral/chunk-J5LGTIGS.mjs} +1 -2
- package/dist/lib/{browser/chunk-YKTSSMDS.mjs → neutral/chunk-KB7RIVLK.mjs} +1 -3
- package/dist/lib/neutral/chunk-KB7RIVLK.mjs.map +7 -0
- package/dist/lib/{browser/chunk-2SBB7OWV.mjs → neutral/chunk-KSFI2POM.mjs} +11 -10
- package/dist/lib/neutral/chunk-KSFI2POM.mjs.map +7 -0
- package/dist/lib/{browser/chunk-6L5HHUVU.mjs → neutral/chunk-LKQBTRJX.mjs} +18 -5
- package/dist/lib/neutral/chunk-LKQBTRJX.mjs.map +7 -0
- package/dist/lib/{browser/chunk-CJ5YELTO.mjs → neutral/chunk-MXQJZCPY.mjs} +15 -3
- package/dist/lib/neutral/chunk-MXQJZCPY.mjs.map +7 -0
- package/dist/lib/{browser/chunk-PQZW3S6L.mjs → neutral/chunk-N2QNHMT5.mjs} +3 -4
- package/dist/lib/{node-esm/chunk-Y75VU7LB.mjs.map → neutral/chunk-N2QNHMT5.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-IXVWLTG7.mjs → neutral/chunk-OMUPQMLR.mjs} +1 -3
- package/dist/lib/{browser/chunk-IXVWLTG7.mjs.map → neutral/chunk-OMUPQMLR.mjs.map} +1 -1
- package/dist/lib/{node-esm/chunk-YOLH5KS4.mjs → neutral/chunk-RIMHNJ3E.mjs} +17 -4
- package/dist/lib/neutral/chunk-RIMHNJ3E.mjs.map +7 -0
- package/dist/lib/{browser/chunk-JMKVF2YQ.mjs → neutral/chunk-ROKO4RKJ.mjs} +2 -3
- package/dist/lib/{node-esm/chunk-K37NA7PO.mjs.map → neutral/chunk-ROKO4RKJ.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-KQQGVHFN.mjs → neutral/chunk-SHLKRGRB.mjs} +5 -6
- package/dist/lib/{node-esm/chunk-NRN3ZW2T.mjs.map → neutral/chunk-SHLKRGRB.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-22JMFST2.mjs → neutral/chunk-UW4T7PAL.mjs} +3 -4
- package/dist/lib/{browser/chunk-22JMFST2.mjs.map → neutral/chunk-UW4T7PAL.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-XDIUHAAX.mjs → neutral/chunk-WSPASOLU.mjs} +3 -4
- package/dist/lib/{node-esm/chunk-QLI2EIJ2.mjs.map → neutral/chunk-WSPASOLU.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-MYCCGG2T.mjs → neutral/chunk-X2MPMYYN.mjs} +1 -3
- package/dist/lib/{node-esm/chunk-MOWUEW5P.mjs.map → neutral/chunk-X2MPMYYN.mjs.map} +1 -1
- package/dist/lib/{browser/chunk-FPOISFQK.mjs → neutral/chunk-XAJMXQ4H.mjs} +7 -4
- package/dist/lib/neutral/chunk-XAJMXQ4H.mjs.map +7 -0
- package/dist/lib/{browser/chunk-BJPE6CIC.mjs → neutral/chunk-ZUG3PNXU.mjs} +146 -83
- package/dist/lib/neutral/chunk-ZUG3PNXU.mjs.map +7 -0
- package/dist/lib/{browser → neutral}/index.mjs +23 -24
- package/dist/lib/{browser → neutral}/internal/index.mjs +13 -8
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/{browser → neutral}/testing/index.mjs +23 -24
- package/dist/lib/{node-esm → neutral}/testing/index.mjs.map +1 -1
- package/dist/types/src/Annotation.d.ts +22 -0
- package/dist/types/src/Annotation.d.ts.map +1 -1
- package/dist/types/src/Database.d.ts +72 -50
- package/dist/types/src/Database.d.ts.map +1 -1
- package/dist/types/src/Err.d.ts +43 -0
- package/dist/types/src/Err.d.ts.map +1 -1
- package/dist/types/src/Obj.d.ts +84 -4
- package/dist/types/src/Obj.d.ts.map +1 -1
- package/dist/types/src/Query.d.ts +10 -0
- package/dist/types/src/Query.d.ts.map +1 -1
- package/dist/types/src/Relation.d.ts +2 -2
- package/dist/types/src/Relation.d.ts.map +1 -1
- package/dist/types/src/hierarchy.test.d.ts +2 -0
- package/dist/types/src/hierarchy.test.d.ts.map +1 -0
- package/dist/types/src/internal/annotations/annotations.d.ts +13 -6
- package/dist/types/src/internal/annotations/annotations.d.ts.map +1 -1
- package/dist/types/src/internal/annotations/util.d.ts +12 -0
- package/dist/types/src/internal/annotations/util.d.ts.map +1 -1
- package/dist/types/src/internal/api/meta.d.ts +2 -2
- package/dist/types/src/internal/api/meta.d.ts.map +1 -1
- package/dist/types/src/internal/entities/model.d.ts +3 -1
- package/dist/types/src/internal/entities/model.d.ts.map +1 -1
- package/dist/types/src/internal/entities/object.d.ts.map +1 -1
- package/dist/types/src/internal/entities/relation.d.ts.map +1 -1
- package/dist/types/src/internal/formats/select.d.ts +2 -2
- package/dist/types/src/internal/formats/select.d.ts.map +1 -1
- package/dist/types/src/internal/json-schema/json-schema-normalize.d.ts.map +1 -1
- package/dist/types/src/internal/json-schema/json-schema-type.d.ts +130 -29
- package/dist/types/src/internal/json-schema/json-schema-type.d.ts.map +1 -1
- package/dist/types/src/internal/json-schema/json-schema.d.ts +2 -1
- package/dist/types/src/internal/json-schema/json-schema.d.ts.map +1 -1
- package/dist/types/src/internal/object/json-serializer.d.ts.map +1 -1
- package/dist/types/src/internal/object/snapshot.d.ts.map +1 -1
- package/dist/types/src/internal/proxy/make-object.d.ts.map +1 -1
- package/dist/types/src/internal/proxy/typed-handler.d.ts +2 -1
- package/dist/types/src/internal/proxy/typed-handler.d.ts.map +1 -1
- package/dist/types/src/internal/ref/ref.d.ts +16 -2
- package/dist/types/src/internal/ref/ref.d.ts.map +1 -1
- package/dist/types/src/internal/schema/compose.d.ts +2 -1
- package/dist/types/src/internal/schema/compose.d.ts.map +1 -1
- package/dist/types/src/internal/schema/echo-schema.d.ts +2 -1
- package/dist/types/src/internal/schema/echo-schema.d.ts.map +1 -1
- package/dist/types/src/internal/schema/persistent-schema.d.ts.map +1 -1
- package/dist/types/src/internal/types/meta.d.ts +5 -5
- package/dist/types/src/internal/types/meta.d.ts.map +1 -1
- package/dist/types/src/internal/types/typename.d.ts +8 -0
- package/dist/types/src/internal/types/typename.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +34 -113
- package/src/Annotation.ts +27 -0
- package/src/Database.ts +141 -124
- package/src/Err.ts +20 -0
- package/src/Obj.test.ts +56 -0
- package/src/Obj.ts +140 -11
- package/src/Query.ts +28 -0
- package/src/Relation.ts +5 -3
- package/src/hierarchy.test.ts +33 -0
- package/src/internal/annotations/annotations.ts +28 -1
- package/src/internal/annotations/util.ts +13 -0
- package/src/internal/api/meta.ts +3 -3
- package/src/internal/entities/model.ts +4 -0
- package/src/internal/entities/object.ts +1 -3
- package/src/internal/entities/relation.ts +1 -3
- package/src/internal/formats/format.test.ts +1 -1
- package/src/internal/formats/select.ts +1 -1
- package/src/internal/formats/types.ts +1 -1
- package/src/internal/json-schema/json-schema-normalize.ts +4 -2
- package/src/internal/json-schema/json-schema-type.ts +30 -31
- package/src/internal/json-schema/json-schema.ts +10 -6
- package/src/internal/object/json-serializer.test.ts +26 -2
- package/src/internal/object/json-serializer.ts +25 -9
- package/src/internal/object/schema-validator.test.ts +3 -7
- package/src/internal/object/set-value.test.ts +9 -9
- package/src/internal/object/snapshot.ts +29 -6
- package/src/internal/proxy/make-object.ts +12 -2
- package/src/internal/proxy/typed-handler.test.ts +8 -12
- package/src/internal/proxy/typed-handler.ts +9 -1
- package/src/internal/proxy/typed-object.test.ts +11 -11
- package/src/internal/ref/ref.test.ts +1 -1
- package/src/internal/ref/ref.ts +26 -2
- package/src/internal/schema/compose.ts +13 -8
- package/src/internal/schema/echo-schema.ts +3 -2
- package/src/internal/schema/persistent-schema.ts +2 -1
- package/src/internal/types/meta.ts +12 -13
- package/src/internal/types/typename.ts +10 -0
- package/dist/lib/browser/Database.mjs +0 -18
- package/dist/lib/browser/JsonSchema.mjs +0 -20
- package/dist/lib/browser/Obj.mjs +0 -91
- package/dist/lib/browser/Query.mjs +0 -27
- package/dist/lib/browser/QueryResult.mjs +0 -3
- package/dist/lib/browser/Ref.mjs +0 -23
- package/dist/lib/browser/SchemaRegistry.mjs +0 -3
- package/dist/lib/browser/Tag.mjs +0 -26
- package/dist/lib/browser/chunk-2SBB7OWV.mjs.map +0 -7
- package/dist/lib/browser/chunk-6L5HHUVU.mjs.map +0 -7
- package/dist/lib/browser/chunk-7STIBCP7.mjs +0 -133
- package/dist/lib/browser/chunk-7STIBCP7.mjs.map +0 -7
- package/dist/lib/browser/chunk-BJPE6CIC.mjs.map +0 -7
- package/dist/lib/browser/chunk-CJ5YELTO.mjs.map +0 -7
- package/dist/lib/browser/chunk-FPOISFQK.mjs.map +0 -7
- package/dist/lib/browser/chunk-FRDT7RA4.mjs.map +0 -7
- package/dist/lib/browser/chunk-INHXFXY5.mjs.map +0 -7
- package/dist/lib/browser/chunk-JMKVF2YQ.mjs.map +0 -7
- package/dist/lib/browser/chunk-KQQGVHFN.mjs.map +0 -7
- package/dist/lib/browser/chunk-MYCCGG2T.mjs.map +0 -7
- package/dist/lib/browser/chunk-NOPVNWPT.mjs.map +0 -7
- package/dist/lib/browser/chunk-PQZW3S6L.mjs.map +0 -7
- package/dist/lib/browser/chunk-RK4Z4JUZ.mjs +0 -283
- package/dist/lib/browser/chunk-RK4Z4JUZ.mjs.map +0 -7
- package/dist/lib/browser/chunk-U2J7TA7K.mjs.map +0 -7
- package/dist/lib/browser/chunk-XDIUHAAX.mjs.map +0 -7
- package/dist/lib/browser/chunk-YKTSSMDS.mjs.map +0 -7
- package/dist/lib/browser/chunk-ZAGAOZVY.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/testing/index.mjs.map +0 -7
- package/dist/lib/node-esm/Annotation.mjs +0 -33
- package/dist/lib/node-esm/Annotation.mjs.map +0 -7
- package/dist/lib/node-esm/Database.mjs +0 -18
- package/dist/lib/node-esm/Database.mjs.map +0 -7
- package/dist/lib/node-esm/Entity.mjs +0 -52
- package/dist/lib/node-esm/Entity.mjs.map +0 -7
- package/dist/lib/node-esm/Err.mjs +0 -11
- package/dist/lib/node-esm/Err.mjs.map +0 -7
- package/dist/lib/node-esm/Filter.mjs +0 -62
- package/dist/lib/node-esm/Filter.mjs.map +0 -7
- package/dist/lib/node-esm/Format.mjs +0 -67
- package/dist/lib/node-esm/Format.mjs.map +0 -7
- package/dist/lib/node-esm/JsonSchema.mjs +0 -20
- package/dist/lib/node-esm/JsonSchema.mjs.map +0 -7
- package/dist/lib/node-esm/Key.mjs +0 -13
- package/dist/lib/node-esm/Key.mjs.map +0 -7
- package/dist/lib/node-esm/Obj.mjs.map +0 -7
- package/dist/lib/node-esm/Order.mjs +0 -13
- package/dist/lib/node-esm/Order.mjs.map +0 -7
- package/dist/lib/node-esm/Query.mjs +0 -27
- package/dist/lib/node-esm/Query.mjs.map +0 -7
- package/dist/lib/node-esm/QueryResult.mjs +0 -3
- package/dist/lib/node-esm/QueryResult.mjs.map +0 -7
- package/dist/lib/node-esm/Ref.mjs +0 -23
- package/dist/lib/node-esm/Ref.mjs.map +0 -7
- package/dist/lib/node-esm/Relation.mjs +0 -85
- package/dist/lib/node-esm/Relation.mjs.map +0 -7
- package/dist/lib/node-esm/SchemaRegistry.mjs +0 -3
- package/dist/lib/node-esm/SchemaRegistry.mjs.map +0 -7
- package/dist/lib/node-esm/Tag.mjs +0 -26
- package/dist/lib/node-esm/Tag.mjs.map +0 -7
- package/dist/lib/node-esm/Type.mjs +0 -48
- package/dist/lib/node-esm/Type.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-4PNXQA64.mjs +0 -250
- package/dist/lib/node-esm/chunk-4PNXQA64.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-5OBN7GZW.mjs +0 -158
- package/dist/lib/node-esm/chunk-5OBN7GZW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-AJEMYSIR.mjs +0 -22
- package/dist/lib/node-esm/chunk-ANLVLWME.mjs +0 -98
- package/dist/lib/node-esm/chunk-ANLVLWME.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-DMR7OAFK.mjs +0 -57
- package/dist/lib/node-esm/chunk-FWTSPIFF.mjs +0 -133
- package/dist/lib/node-esm/chunk-FWTSPIFF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs +0 -11
- package/dist/lib/node-esm/chunk-HSLMI22Q.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ILMLLM4R.mjs +0 -204
- package/dist/lib/node-esm/chunk-ILMLLM4R.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-K37NA7PO.mjs +0 -43
- package/dist/lib/node-esm/chunk-LT3H4JOX.mjs +0 -3847
- package/dist/lib/node-esm/chunk-LT3H4JOX.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MOLNWFNL.mjs +0 -9
- package/dist/lib/node-esm/chunk-MOLNWFNL.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-MOWUEW5P.mjs +0 -15
- package/dist/lib/node-esm/chunk-NBWL7UCZ.mjs +0 -40
- package/dist/lib/node-esm/chunk-NBWL7UCZ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-NRN3ZW2T.mjs +0 -143
- package/dist/lib/node-esm/chunk-QLI2EIJ2.mjs +0 -41
- package/dist/lib/node-esm/chunk-QYR67VBV.mjs +0 -288
- package/dist/lib/node-esm/chunk-UKGVOINP.mjs +0 -9
- package/dist/lib/node-esm/chunk-UKGVOINP.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-W6QIEBTQ.mjs +0 -403
- package/dist/lib/node-esm/chunk-W6QIEBTQ.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-XHJRMQZD.mjs +0 -69
- package/dist/lib/node-esm/chunk-XHJRMQZD.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-Y75VU7LB.mjs +0 -74
- package/dist/lib/node-esm/chunk-YOLH5KS4.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-YQ2NWGL5.mjs +0 -39
- package/dist/lib/node-esm/chunk-YQ2NWGL5.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ZBB67AKD.mjs +0 -24
- package/dist/lib/node-esm/chunk-ZBB67AKD.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -85
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/internal/index.mjs +0 -457
- package/dist/lib/node-esm/internal/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -367
- /package/dist/lib/{browser → neutral}/Annotation.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Database.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Entity.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Err.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Filter.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Format.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/JsonSchema.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Key.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Obj.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Order.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Query.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/QueryResult.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Ref.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Relation.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/SchemaRegistry.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Tag.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/Type.mjs.map +0 -0
- /package/dist/lib/{browser/chunk-CGS2ULMK.mjs.map → neutral/chunk-J5LGTIGS.mjs.map} +0 -0
- /package/dist/lib/{browser → neutral}/index.mjs.map +0 -0
- /package/dist/lib/{browser → neutral}/internal/index.mjs.map +0 -0
|
@@ -7,6 +7,7 @@ import * as Schema from 'effect/Schema';
|
|
|
7
7
|
import { JsonPath, type JsonProp } from '@dxos/effect';
|
|
8
8
|
|
|
9
9
|
import { FormatAnnotation, TypeFormat } from '../formats';
|
|
10
|
+
import { type Mutable } from '../proxy';
|
|
10
11
|
import { EntityKindSchema } from '../types';
|
|
11
12
|
|
|
12
13
|
//
|
|
@@ -18,7 +19,7 @@ const SimpleTypes = Schema.Literal('array', 'boolean', 'integer', 'null', 'numbe
|
|
|
18
19
|
|
|
19
20
|
const NonNegativeInteger = Schema.Number.pipe(Schema.greaterThanOrEqualTo(0));
|
|
20
21
|
|
|
21
|
-
const StringArray = Schema.Array(Schema.String)
|
|
22
|
+
const StringArray = Schema.Array(Schema.String);
|
|
22
23
|
|
|
23
24
|
const JsonSchemaOrBoolean = Schema.Union(
|
|
24
25
|
Schema.suspend(() => JsonSchemaType),
|
|
@@ -48,7 +49,7 @@ export const JsonSchemaEchoAnnotations = Schema.Struct({
|
|
|
48
49
|
Schema.Record({
|
|
49
50
|
key: Schema.String,
|
|
50
51
|
value: Schema.Any,
|
|
51
|
-
})
|
|
52
|
+
}),
|
|
52
53
|
),
|
|
53
54
|
|
|
54
55
|
/**
|
|
@@ -62,7 +63,7 @@ export const JsonSchemaEchoAnnotations = Schema.Struct({
|
|
|
62
63
|
|
|
63
64
|
// Not used.
|
|
64
65
|
schemaId: Schema.optional(Schema.String),
|
|
65
|
-
})
|
|
66
|
+
}),
|
|
66
67
|
),
|
|
67
68
|
|
|
68
69
|
/**
|
|
@@ -72,9 +73,9 @@ export const JsonSchemaEchoAnnotations = Schema.Struct({
|
|
|
72
73
|
Schema.Record({
|
|
73
74
|
key: Schema.String,
|
|
74
75
|
value: Schema.Any,
|
|
75
|
-
})
|
|
76
|
+
}),
|
|
76
77
|
),
|
|
77
|
-
})
|
|
78
|
+
});
|
|
78
79
|
export type JsonSchemaEchoAnnotations = Schema.Schema.Type<typeof JsonSchemaEchoAnnotations>;
|
|
79
80
|
|
|
80
81
|
/**
|
|
@@ -257,23 +258,21 @@ const _JsonSchemaType = Schema.Struct({
|
|
|
257
258
|
Schema.Record({
|
|
258
259
|
key: Schema.String,
|
|
259
260
|
value: Schema.suspend(() => JsonSchemaType),
|
|
260
|
-
})
|
|
261
|
+
}),
|
|
261
262
|
),
|
|
262
263
|
patternProperties: Schema.optional(
|
|
263
264
|
Schema.Record({
|
|
264
265
|
key: Schema.String,
|
|
265
266
|
value: Schema.suspend(() => JsonSchemaType),
|
|
266
|
-
})
|
|
267
|
+
}),
|
|
267
268
|
),
|
|
268
269
|
propertyNames: Schema.optional(Schema.suspend(() => JsonSchemaType)),
|
|
269
270
|
|
|
270
271
|
definitions: Schema.optional(
|
|
271
|
-
Schema.
|
|
272
|
-
Schema.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}),
|
|
276
|
-
),
|
|
272
|
+
Schema.Record({
|
|
273
|
+
key: Schema.String,
|
|
274
|
+
value: Schema.suspend(() => JsonSchemaType),
|
|
275
|
+
}),
|
|
277
276
|
),
|
|
278
277
|
dependencies: Schema.optional(
|
|
279
278
|
Schema.Record({
|
|
@@ -296,12 +295,10 @@ const _JsonSchemaType = Schema.Struct({
|
|
|
296
295
|
oneOf: Schema.optional(Schema.Array(Schema.suspend(() => JsonSchemaType))),
|
|
297
296
|
not: Schema.optional(Schema.suspend(() => JsonSchemaType)),
|
|
298
297
|
$defs: Schema.optional(
|
|
299
|
-
Schema.
|
|
300
|
-
Schema.
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}),
|
|
304
|
-
),
|
|
298
|
+
Schema.Record({
|
|
299
|
+
key: Schema.String,
|
|
300
|
+
value: Schema.suspend(() => JsonSchemaType),
|
|
301
|
+
}),
|
|
305
302
|
),
|
|
306
303
|
|
|
307
304
|
//
|
|
@@ -311,25 +308,23 @@ const _JsonSchemaType = Schema.Struct({
|
|
|
311
308
|
currency: Schema.optional(Schema.String),
|
|
312
309
|
|
|
313
310
|
reference: Schema.optional(
|
|
314
|
-
Schema.
|
|
315
|
-
Schema.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}),
|
|
320
|
-
),
|
|
311
|
+
Schema.Struct({
|
|
312
|
+
schema: Schema.suspend(() => JsonSchemaType),
|
|
313
|
+
schemaVersion: Schema.optional(Schema.String),
|
|
314
|
+
schemaObject: Schema.optional(Schema.String),
|
|
315
|
+
}),
|
|
321
316
|
),
|
|
322
317
|
|
|
323
318
|
/**
|
|
324
319
|
* ECHO-specific annotations.
|
|
325
320
|
*/
|
|
326
321
|
// TODO(dmaretskyi): Since we are adding a lot of new extensions to the JSON Schema, it is safer to namespace them here.
|
|
327
|
-
annotations: Schema.optional(
|
|
322
|
+
annotations: Schema.optional(JsonSchemaEchoAnnotations),
|
|
328
323
|
|
|
329
324
|
/**
|
|
330
325
|
* @deprecated Use `annotations` instead.
|
|
331
326
|
*/
|
|
332
|
-
echo: Schema.optional(
|
|
327
|
+
echo: Schema.optional(JsonSchemaEchoAnnotations),
|
|
333
328
|
}).annotations({ identifier: 'jsonSchema', description: 'JSON Schema' });
|
|
334
329
|
|
|
335
330
|
export const JsonSchemaFields = Object.keys(_JsonSchemaType.fields);
|
|
@@ -338,9 +333,9 @@ export const JsonSchemaFields = Object.keys(_JsonSchemaType.fields);
|
|
|
338
333
|
* https://json-schema.org/draft-07/schema
|
|
339
334
|
*/
|
|
340
335
|
// TODO(burdon): Reconcile with @effect/Schema/JSONSchema
|
|
341
|
-
export interface JsonSchemaType extends Schema.Schema.Type<
|
|
336
|
+
export interface JsonSchemaType extends Schema.Schema.Type<typeof _JsonSchemaType> {}
|
|
342
337
|
|
|
343
|
-
export const JsonSchemaType: Schema.Schema<JsonSchemaType> = _JsonSchemaType
|
|
338
|
+
export const JsonSchemaType: Schema.Schema<JsonSchemaType> = _JsonSchemaType;
|
|
344
339
|
|
|
345
340
|
// TODO(burdon): Factor out JSON schema utils.
|
|
346
341
|
|
|
@@ -349,7 +344,11 @@ export const getSchemaProperty = (schema: JsonSchemaType, property: JsonProp): J
|
|
|
349
344
|
};
|
|
350
345
|
|
|
351
346
|
// TODO(burdon): Properties should be ordered.
|
|
352
|
-
export const setSchemaProperty = (
|
|
347
|
+
export const setSchemaProperty = (
|
|
348
|
+
schema: Mutable<JsonSchemaType>,
|
|
349
|
+
property: JsonProp,
|
|
350
|
+
value: Mutable<JsonSchemaType>,
|
|
351
|
+
) => {
|
|
353
352
|
schema.properties ??= {};
|
|
354
353
|
schema.properties[property] = value;
|
|
355
354
|
return schema;
|
|
@@ -80,7 +80,11 @@ export type JsonSchemaOptions = {
|
|
|
80
80
|
*/
|
|
81
81
|
// TODO(burdon): Reconcile with possibly extending @effect/Schema/JSONSchema
|
|
82
82
|
// We add additional propertyOrder (but the object properties ARE ordered); and type "string" for literals.
|
|
83
|
-
|
|
83
|
+
// TODO(wittjosiah): This is mutable because its a pojo, perhaps should be left as readonly at type level though?
|
|
84
|
+
export const toJsonSchema = (
|
|
85
|
+
schema: Schema.Schema.All,
|
|
86
|
+
options: JsonSchemaOptions = {},
|
|
87
|
+
): Types.DeepMutable<JsonSchemaType> => {
|
|
84
88
|
assertArgument(Schema.isSchema(schema), 'schema');
|
|
85
89
|
let jsonSchema = _toJsonSchemaAST(schema.ast);
|
|
86
90
|
if (options.strict) {
|
|
@@ -103,11 +107,11 @@ export const toJsonSchema = (schema: Schema.Schema.All, options: JsonSchemaOptio
|
|
|
103
107
|
return jsonSchema;
|
|
104
108
|
};
|
|
105
109
|
|
|
106
|
-
const _toJsonSchemaAST = (ast: SchemaAST.AST): JsonSchemaType => {
|
|
110
|
+
const _toJsonSchemaAST = (ast: SchemaAST.AST): Types.DeepMutable<JsonSchemaType> => {
|
|
107
111
|
const withRefinements = withEchoRefinements(ast, '#');
|
|
108
112
|
const jsonSchema = JSONSchema.fromAST(withRefinements, {
|
|
109
113
|
definitions: {},
|
|
110
|
-
}) as JsonSchemaType
|
|
114
|
+
}) as Types.DeepMutable<JsonSchemaType>;
|
|
111
115
|
|
|
112
116
|
return normalizeJsonSchema(jsonSchema);
|
|
113
117
|
};
|
|
@@ -326,7 +330,7 @@ const objectToEffectSchema = (root: JsonSchemaType, defs: JsonSchemaType['$defs'
|
|
|
326
330
|
}
|
|
327
331
|
|
|
328
332
|
if (immutableIdField) {
|
|
329
|
-
schema = Schema.extend(
|
|
333
|
+
schema = Schema.extend(schema, Schema.Struct({ id: immutableIdField }));
|
|
330
334
|
}
|
|
331
335
|
|
|
332
336
|
const annotations = jsonSchemaFieldsToAnnotations(root);
|
|
@@ -377,7 +381,7 @@ const refToEffectSchema = (root: any): Schema.Schema.AnyNoContext => {
|
|
|
377
381
|
const annotations_toJsonSchemaFields = (annotations: SchemaAST.Annotations): Record<symbol, any> => {
|
|
378
382
|
const schemaFields: Record<string, any> = {};
|
|
379
383
|
|
|
380
|
-
const echoAnnotations: JsonSchemaEchoAnnotations = {};
|
|
384
|
+
const echoAnnotations: Types.Mutable<JsonSchemaEchoAnnotations> = {};
|
|
381
385
|
for (const [key, annotationId] of Object.entries(EchoAnnotations)) {
|
|
382
386
|
if (annotations[annotationId] != null) {
|
|
383
387
|
echoAnnotations[key as keyof JsonSchemaEchoAnnotations] = annotations[annotationId] as any;
|
|
@@ -495,7 +499,7 @@ const addJsonSchemaFields = (ast: SchemaAST.AST, schema: JsonSchemaType): Schema
|
|
|
495
499
|
* Fixes field order.
|
|
496
500
|
* Sets `$schema` prop.
|
|
497
501
|
*/
|
|
498
|
-
const normalizeJsonSchema = (jsonSchema: JsonSchemaType): JsonSchemaType => {
|
|
502
|
+
const normalizeJsonSchema = (jsonSchema: Types.DeepMutable<JsonSchemaType>): Types.DeepMutable<JsonSchemaType> => {
|
|
499
503
|
if (jsonSchema.properties && 'id' in jsonSchema.properties) {
|
|
500
504
|
jsonSchema.properties = orderKeys(jsonSchema.properties, ['id']); // Put id first.
|
|
501
505
|
}
|
|
@@ -9,10 +9,11 @@ import { DXN } from '@dxos/keys';
|
|
|
9
9
|
import * as Obj from '../../Obj';
|
|
10
10
|
import { TestSchema } from '../../testing';
|
|
11
11
|
import { getSchemaDXN, getSchemaTypename, getTypeDXN, getTypename } from '../annotations';
|
|
12
|
+
import { getMetaChecked } from '../api';
|
|
12
13
|
import { RelationSourceId, RelationTargetId, getObjectDXN } from '../entities';
|
|
13
14
|
import { makeObject } from '../proxy';
|
|
14
15
|
import { Ref, StaticRefResolver } from '../ref';
|
|
15
|
-
import { ATTR_TYPE, EntityKind, KindId, MetaId, TypeId,
|
|
16
|
+
import { ATTR_TYPE, EntityKind, KindId, MetaId, TypeId, getSchema } from '../types';
|
|
16
17
|
|
|
17
18
|
import { createObject } from './create-object';
|
|
18
19
|
import { objectFromJSON, objectToJSON } from './json-serializer';
|
|
@@ -21,7 +22,7 @@ describe('Object JSON serializer', () => {
|
|
|
21
22
|
test('should serialize and deserialize object', async () => {
|
|
22
23
|
const contact = makeObject(TestSchema.Person, { name: 'Alice' });
|
|
23
24
|
Obj.change(contact, (c) => {
|
|
24
|
-
|
|
25
|
+
getMetaChecked(c).keys.push({ id: '12345', source: 'example.com' });
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
const task = createObject(TestSchema.Task, {
|
|
@@ -94,4 +95,27 @@ describe('Object JSON serializer', () => {
|
|
|
94
95
|
expect(getObjectDXN(contactFromJson)).toEqual(getObjectDXN(contact));
|
|
95
96
|
expect(getTypeDXN(contactFromJson)).toEqual(getSchemaDXN(TestSchema.Person));
|
|
96
97
|
});
|
|
98
|
+
|
|
99
|
+
test('deserializes expando without leaking internal json keys', async () => {
|
|
100
|
+
const expando = Obj.make(TestSchema.Expando, { message: 'local-only' });
|
|
101
|
+
const expandoJson = objectToJSON(expando);
|
|
102
|
+
|
|
103
|
+
const refResolver = new StaticRefResolver().addSchema(TestSchema.Expando);
|
|
104
|
+
const expandoFromJson = (await objectFromJSON(expandoJson, { refResolver })) as TestSchema.Expando;
|
|
105
|
+
|
|
106
|
+
expect(expandoFromJson.id).toBe(expando.id);
|
|
107
|
+
expect(expandoFromJson.message).toBe('local-only');
|
|
108
|
+
expect((expandoFromJson as any)[ATTR_TYPE]).toBeUndefined();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test('deserializes expando without schema resolver and without leaking internal json keys', async () => {
|
|
112
|
+
const expando = Obj.make(TestSchema.Expando, { message: 'local-only' });
|
|
113
|
+
const expandoJson = objectToJSON(expando);
|
|
114
|
+
|
|
115
|
+
const expandoFromJson = (await objectFromJSON(expandoJson)) as TestSchema.Expando;
|
|
116
|
+
|
|
117
|
+
expect(expandoFromJson.id).toBe(expando.id);
|
|
118
|
+
expect(expandoFromJson.message).toBe('local-only');
|
|
119
|
+
expect((expandoFromJson as any)[ATTR_TYPE]).toBeUndefined();
|
|
120
|
+
});
|
|
97
121
|
});
|
|
@@ -29,12 +29,14 @@ import { attachTypedJsonSerializer, defineHiddenProperty, typedJsonSerializer }
|
|
|
29
29
|
import { Ref, type RefResolver, refFromEncodedReference, setRefResolver } from '../ref';
|
|
30
30
|
import {
|
|
31
31
|
ATTR_META,
|
|
32
|
+
ATTR_PARENT,
|
|
32
33
|
ATTR_TYPE,
|
|
33
34
|
type AnyEntity,
|
|
34
35
|
EntityKind,
|
|
35
36
|
KindId,
|
|
36
37
|
MetaId,
|
|
37
38
|
ObjectMetaSchema,
|
|
39
|
+
ParentId,
|
|
38
40
|
setSchema,
|
|
39
41
|
} from '../types';
|
|
40
42
|
|
|
@@ -81,15 +83,16 @@ export const objectFromJSON = async (
|
|
|
81
83
|
const type = DXN.parse(jsonData[ATTR_TYPE]);
|
|
82
84
|
const schema = await refResolver?.resolveSchema(type);
|
|
83
85
|
invariant(schema === undefined || Schema.isSchema(schema));
|
|
86
|
+
const decodedInput = stripInternalJsonKeys(jsonData);
|
|
84
87
|
|
|
85
88
|
let obj: any;
|
|
86
89
|
if (schema != null) {
|
|
87
|
-
obj = await schema.pipe(Schema.decodeUnknownPromise)(
|
|
90
|
+
obj = await schema.pipe(Schema.decodeUnknownPromise)(decodedInput);
|
|
88
91
|
if (refResolver) {
|
|
89
92
|
setRefResolverOnData(obj, refResolver);
|
|
90
93
|
}
|
|
91
94
|
} else {
|
|
92
|
-
obj = decodeGeneric(
|
|
95
|
+
obj = decodeGeneric(decodedInput, { refResolver });
|
|
93
96
|
}
|
|
94
97
|
|
|
95
98
|
invariant(ObjectId.isValid(obj.id), 'Invalid object id');
|
|
@@ -126,6 +129,12 @@ export const objectFromJSON = async (
|
|
|
126
129
|
});
|
|
127
130
|
}
|
|
128
131
|
|
|
132
|
+
if (jsonData[ATTR_PARENT]) {
|
|
133
|
+
const parentDxn = DXN.parse(jsonData[ATTR_PARENT]);
|
|
134
|
+
const parent = (await refResolver?.resolve(parentDxn)) as Obj.Unknown | undefined;
|
|
135
|
+
defineHiddenProperty(obj, ParentId, parent);
|
|
136
|
+
}
|
|
137
|
+
|
|
129
138
|
if (dxn) {
|
|
130
139
|
defineHiddenProperty(obj, SelfDXNId, dxn);
|
|
131
140
|
}
|
|
@@ -141,6 +150,18 @@ export const objectFromJSON = async (
|
|
|
141
150
|
};
|
|
142
151
|
|
|
143
152
|
const decodeGeneric = (jsonData: unknown, options: { refResolver?: RefResolver }) => {
|
|
153
|
+
const props = stripInternalJsonKeys(jsonData);
|
|
154
|
+
|
|
155
|
+
return deepMapValues(props, (value, visitor) => {
|
|
156
|
+
if (isEncodedReference(value)) {
|
|
157
|
+
return refFromEncodedReference(value, options.refResolver);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return visitor(value);
|
|
161
|
+
});
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const stripInternalJsonKeys = (jsonData: unknown) => {
|
|
144
165
|
const {
|
|
145
166
|
[ATTR_TYPE]: _type,
|
|
146
167
|
[ATTR_META]: _meta,
|
|
@@ -151,13 +172,7 @@ const decodeGeneric = (jsonData: unknown, options: { refResolver?: RefResolver }
|
|
|
151
172
|
...props
|
|
152
173
|
} = jsonData as any;
|
|
153
174
|
|
|
154
|
-
return
|
|
155
|
-
if (isEncodedReference(value)) {
|
|
156
|
-
return refFromEncodedReference(value, options.refResolver);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return visitor(value);
|
|
160
|
-
});
|
|
175
|
+
return props;
|
|
161
176
|
};
|
|
162
177
|
|
|
163
178
|
export const setRefResolverOnData = (obj: AnyEntity, refResolver: RefResolver) => {
|
|
@@ -182,6 +197,7 @@ export const objectStructureToJson = (objectId: string, structure: ObjectStructu
|
|
|
182
197
|
id: objectId,
|
|
183
198
|
[ATTR_TYPE]: (ObjectStructure.getTypeReference(structure)?.['/'] ?? '') as DXN.String,
|
|
184
199
|
[ATTR_DELETED]: ObjectStructure.isDeleted(structure),
|
|
200
|
+
[ATTR_PARENT]: ObjectStructure.getParent(structure)?.['/'] as DXN.String | undefined,
|
|
185
201
|
[ATTR_RELATION_SOURCE]: ObjectStructure.getRelationSource(structure)?.['/'] as DXN.String | undefined,
|
|
186
202
|
[ATTR_RELATION_TARGET]: ObjectStructure.getRelationTarget(structure)?.['/'] as DXN.String | undefined,
|
|
187
203
|
};
|
|
@@ -27,9 +27,7 @@ describe('schema-validator', () => {
|
|
|
27
27
|
const TestSchema: Schema.Schema.AnyNoContext = Schema.Struct({
|
|
28
28
|
name: Schema.String.annotations({ [annotationId]: annotationValue }),
|
|
29
29
|
parent: Schema.optional(Schema.suspend(() => TestSchema.annotations({ [annotationId]: annotationValue }))),
|
|
30
|
-
friends: Schema.suspend(() =>
|
|
31
|
-
Schema.mutable(Schema.Array(TestSchema.annotations({ [annotationId]: annotationValue }))),
|
|
32
|
-
),
|
|
30
|
+
friends: Schema.suspend(() => Schema.Array(TestSchema.annotations({ [annotationId]: annotationValue }))),
|
|
33
31
|
});
|
|
34
32
|
expect(SchemaValidator.hasTypeAnnotation(TestSchema, 'name', annotationId)).to.be.true;
|
|
35
33
|
expect(SchemaValidator.hasTypeAnnotation(TestSchema, 'parent', annotationId)).to.be.true;
|
|
@@ -41,7 +39,7 @@ describe('schema-validator', () => {
|
|
|
41
39
|
const Person: Schema.Schema.AnyNoContext = Schema.Struct({
|
|
42
40
|
name: Schema.String,
|
|
43
41
|
parent: Schema.optional(Schema.suspend(() => Person)),
|
|
44
|
-
friends: Schema.suspend(() => Schema.
|
|
42
|
+
friends: Schema.suspend(() => Schema.Array(Person)),
|
|
45
43
|
});
|
|
46
44
|
expect(SchemaValidator.hasTypeAnnotation(Person, 'name', annotationId)).to.be.false;
|
|
47
45
|
expect(SchemaValidator.hasTypeAnnotation(Person, 'parent', annotationId)).to.be.false;
|
|
@@ -87,9 +85,7 @@ describe('schema-validator', () => {
|
|
|
87
85
|
const annotationValue = 'bar';
|
|
88
86
|
const Person: Schema.Schema.AnyNoContext = Schema.Struct({
|
|
89
87
|
parent: Schema.optional(Schema.suspend(() => Person.annotations({ [annotationId]: annotationValue }))),
|
|
90
|
-
friends: Schema.suspend(() =>
|
|
91
|
-
Schema.mutable(Schema.Array(Person.annotations({ [annotationId]: annotationValue }))),
|
|
92
|
-
),
|
|
88
|
+
friends: Schema.suspend(() => Schema.Array(Person.annotations({ [annotationId]: annotationValue }))),
|
|
93
89
|
});
|
|
94
90
|
expect(SchemaValidator.getPropertySchema(Person, ['parent']).ast.annotations[annotationId]).to.eq(
|
|
95
91
|
annotationValue,
|
|
@@ -55,11 +55,11 @@ describe('Obj.setValue', () => {
|
|
|
55
55
|
test('sets multiple nested array elements', ({ expect }) => {
|
|
56
56
|
const Item = Schema.Struct({
|
|
57
57
|
value: Schema.optional(Schema.Number),
|
|
58
|
-
})
|
|
58
|
+
});
|
|
59
59
|
|
|
60
60
|
const Container = Schema.Struct({
|
|
61
61
|
name: Schema.String,
|
|
62
|
-
items: Schema.optional(Schema.
|
|
62
|
+
items: Schema.optional(Schema.Array(Item)),
|
|
63
63
|
}).pipe(
|
|
64
64
|
Type.object({
|
|
65
65
|
typename: 'test.com/Container',
|
|
@@ -150,7 +150,7 @@ describe('Obj.setValue', () => {
|
|
|
150
150
|
|
|
151
151
|
test('handles two-dimensional arrays', ({ expect }) => {
|
|
152
152
|
const Matrix = Schema.Struct({
|
|
153
|
-
values: Schema.optional(Schema.
|
|
153
|
+
values: Schema.optional(Schema.Array(Schema.Array(Schema.Number))),
|
|
154
154
|
}).pipe(
|
|
155
155
|
Type.object({
|
|
156
156
|
typename: 'test.com/Matrix',
|
|
@@ -200,11 +200,11 @@ describe('Obj.setValue', () => {
|
|
|
200
200
|
test('handles numeric keys as strings', ({ expect }) => {
|
|
201
201
|
const Item = Schema.Struct({
|
|
202
202
|
value: Schema.optional(Schema.Number),
|
|
203
|
-
})
|
|
203
|
+
});
|
|
204
204
|
|
|
205
205
|
const Container = Schema.Struct({
|
|
206
206
|
name: Schema.String,
|
|
207
|
-
items: Schema.optional(Schema.
|
|
207
|
+
items: Schema.optional(Schema.Array(Item)),
|
|
208
208
|
}).pipe(
|
|
209
209
|
Type.object({
|
|
210
210
|
typename: 'test.com/Container',
|
|
@@ -227,11 +227,11 @@ describe('Obj.setValue', () => {
|
|
|
227
227
|
id: Schema.String, // Required field.
|
|
228
228
|
title: Schema.optional(Schema.String),
|
|
229
229
|
completed: Schema.optional(Schema.Boolean),
|
|
230
|
-
})
|
|
230
|
+
});
|
|
231
231
|
|
|
232
232
|
const TodoList = Schema.Struct({
|
|
233
233
|
name: Schema.String,
|
|
234
|
-
tasks: Schema.optional(Schema.
|
|
234
|
+
tasks: Schema.optional(Schema.Array(Task)),
|
|
235
235
|
}).pipe(
|
|
236
236
|
Type.object({
|
|
237
237
|
typename: 'test.com/TodoList',
|
|
@@ -256,11 +256,11 @@ describe('Obj.setValue', () => {
|
|
|
256
256
|
count: Schema.Number, // Required - should default to 0
|
|
257
257
|
active: Schema.Boolean, // Required - should default to false
|
|
258
258
|
label: Schema.optional(Schema.String), // Optional - should not be initialized
|
|
259
|
-
})
|
|
259
|
+
});
|
|
260
260
|
|
|
261
261
|
const Container = Schema.Struct({
|
|
262
262
|
name: Schema.String,
|
|
263
|
-
items: Schema.optional(Schema.
|
|
263
|
+
items: Schema.optional(Schema.Array(Item)),
|
|
264
264
|
}).pipe(
|
|
265
265
|
Type.object({
|
|
266
266
|
typename: 'test.com/Container',
|
|
@@ -4,18 +4,35 @@
|
|
|
4
4
|
|
|
5
5
|
import { deepMapValues } from '@dxos/util';
|
|
6
6
|
|
|
7
|
+
import { ObjectDatabaseId } from '../entities/model';
|
|
7
8
|
import { RelationSourceDXNId, RelationSourceId, RelationTargetDXNId, RelationTargetId } from '../entities/relation';
|
|
8
9
|
import { KindId, SnapshotKindId } from '../types/entity';
|
|
9
10
|
import { MetaId } from '../types/meta';
|
|
10
11
|
import { SchemaId, TypeId } from '../types/typename';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
|
-
* Copy a Symbol-keyed property from source to target if it
|
|
14
|
+
* Copy a Symbol-keyed property from source to target if it has a defined value.
|
|
15
|
+
* Handles both plain objects (symbol in source) and proxies where get returns a value
|
|
16
|
+
* but has/in returns false (e.g. ObjectDatabaseId on echo-db proxies).
|
|
17
|
+
* Optional transform clones or mutates the value before assignment (e.g. for MetaId).
|
|
14
18
|
*/
|
|
15
|
-
const copySymbolProperty = (
|
|
16
|
-
|
|
19
|
+
const copySymbolProperty = (
|
|
20
|
+
source: any,
|
|
21
|
+
target: any,
|
|
22
|
+
symbol: symbol,
|
|
23
|
+
transform?: (value: unknown) => unknown,
|
|
24
|
+
): void => {
|
|
25
|
+
let value: unknown;
|
|
26
|
+
// Echo-db proxy getters (e.g. RelationSourceId, RelationTargetId) can throw in certain states.
|
|
27
|
+
try {
|
|
28
|
+
value = source[symbol];
|
|
29
|
+
} catch {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
if (value !== undefined) {
|
|
33
|
+
const finalValue = transform ? transform(value) : value;
|
|
17
34
|
Object.defineProperty(target, symbol, {
|
|
18
|
-
value:
|
|
35
|
+
value: finalValue,
|
|
19
36
|
writable: false,
|
|
20
37
|
enumerable: false,
|
|
21
38
|
configurable: false,
|
|
@@ -56,8 +73,14 @@ export const getSnapshot = <T extends object>(obj: T): T => {
|
|
|
56
73
|
copySymbolProperty(source, snapshot, TypeId);
|
|
57
74
|
copySymbolProperty(source, snapshot, SchemaId);
|
|
58
75
|
|
|
59
|
-
//
|
|
60
|
-
copySymbolProperty(source, snapshot,
|
|
76
|
+
// Database reference (required for Obj.getDatabase to work on snapshots).
|
|
77
|
+
copySymbolProperty(source, snapshot, ObjectDatabaseId);
|
|
78
|
+
|
|
79
|
+
// Metadata symbol. Copy arrays so the snapshot is not affected by mutations to the live meta's keys/tags.
|
|
80
|
+
copySymbolProperty(source, snapshot, MetaId, (meta: any) => ({
|
|
81
|
+
keys: [...(meta?.keys ?? [])],
|
|
82
|
+
tags: [...(meta?.tags ?? [])],
|
|
83
|
+
}));
|
|
61
84
|
|
|
62
85
|
// Relation endpoint symbols.
|
|
63
86
|
copySymbolProperty(source, snapshot, RelationSourceDXNId);
|
|
@@ -7,7 +7,7 @@ import type * as Schema from 'effect/Schema';
|
|
|
7
7
|
import { ObjectId } from '@dxos/keys';
|
|
8
8
|
|
|
9
9
|
import { getTypeAnnotation } from '../annotations';
|
|
10
|
-
import { type AnyProperties, KindId, MetaId, type ObjectMeta, ObjectMetaSchema } from '../types';
|
|
10
|
+
import { type AnyProperties, KindId, MetaId, type ObjectMeta, ObjectMetaSchema, ParentId } from '../types';
|
|
11
11
|
|
|
12
12
|
import { defineHiddenProperty } from './define-hidden-property';
|
|
13
13
|
import { attachTypedJsonSerializer } from './json-serializer';
|
|
@@ -35,7 +35,8 @@ export const makeObject: {
|
|
|
35
35
|
meta?: ObjectMeta,
|
|
36
36
|
): T;
|
|
37
37
|
} = <T extends AnyProperties>(schema: Schema.Schema<T, any>, obj: MakeObjectProps<T>, meta?: ObjectMeta): T => {
|
|
38
|
-
|
|
38
|
+
// Use Object.assign to copy symbol properties (like ParentId) that spread operator doesn't copy.
|
|
39
|
+
return createReactiveObject<T>(Object.assign({}, obj) as T, meta, schema);
|
|
39
40
|
};
|
|
40
41
|
|
|
41
42
|
const createReactiveObject = <T extends AnyProperties>(obj: T, meta?: ObjectMeta, schema?: Schema.Schema<T>): T => {
|
|
@@ -47,12 +48,21 @@ const createReactiveObject = <T extends AnyProperties>(obj: T, meta?: ObjectMeta
|
|
|
47
48
|
throw new Error('Schema is required for reactive objects. Use Atom for untyped reactive state.');
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
// Extract parent from props (can be set via [Obj.Parent]).
|
|
52
|
+
const parent = (obj as any)[ParentId];
|
|
53
|
+
if (parent !== undefined) {
|
|
54
|
+
delete (obj as any)[ParentId];
|
|
55
|
+
}
|
|
56
|
+
|
|
50
57
|
const annotation = getTypeAnnotation(schema);
|
|
51
58
|
if (annotation) {
|
|
52
59
|
setIdOnTarget(obj);
|
|
53
60
|
defineHiddenProperty(obj, KindId, annotation.kind);
|
|
54
61
|
}
|
|
55
62
|
initMeta(obj, meta);
|
|
63
|
+
if (parent !== undefined) {
|
|
64
|
+
defineHiddenProperty(obj, ParentId, parent);
|
|
65
|
+
}
|
|
56
66
|
prepareTypedTarget(obj, schema);
|
|
57
67
|
attachTypedJsonSerializer(obj);
|
|
58
68
|
const proxy = createProxy<T>(obj, TypedReactiveHandler.instance);
|
|
@@ -94,7 +94,7 @@ describe('complex schema validations', () => {
|
|
|
94
94
|
});
|
|
95
95
|
|
|
96
96
|
test('subscribe', () => {
|
|
97
|
-
const TestSchema = Schema.
|
|
97
|
+
const TestSchema = Schema.Struct({ field: Schema.String }).pipe(
|
|
98
98
|
EchoObjectSchema({ typename: 'Test', version: '0.1.0' }),
|
|
99
99
|
);
|
|
100
100
|
const object = makeObject(TestSchema, { field: 'value' });
|
|
@@ -117,12 +117,10 @@ describe('complex schema validations', () => {
|
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
describe('object structure restrictions', () => {
|
|
120
|
-
const NestedSchema = Schema.
|
|
121
|
-
Schema.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}),
|
|
125
|
-
);
|
|
120
|
+
const NestedSchema = Schema.Struct({
|
|
121
|
+
data: Schema.optional(Schema.Any),
|
|
122
|
+
nested: Schema.optional(Schema.Any),
|
|
123
|
+
});
|
|
126
124
|
|
|
127
125
|
test('prevents direct cycles', () => {
|
|
128
126
|
const obj = makeObject(NestedSchema, { data: null });
|
|
@@ -226,11 +224,9 @@ describe('object structure restrictions', () => {
|
|
|
226
224
|
});
|
|
227
225
|
|
|
228
226
|
test('array elements are owned by root ECHO object', () => {
|
|
229
|
-
const ArraySchema = Schema.
|
|
230
|
-
Schema.
|
|
231
|
-
|
|
232
|
-
}),
|
|
233
|
-
);
|
|
227
|
+
const ArraySchema = Schema.Struct({
|
|
228
|
+
items: Schema.Array(Schema.Any),
|
|
229
|
+
});
|
|
234
230
|
|
|
235
231
|
const obj1 = makeObject(ArraySchema, {
|
|
236
232
|
items: [{ id: 1 }, { id: 2 }, { id: 3 }],
|
|
@@ -13,7 +13,7 @@ import { invariant } from '@dxos/invariant';
|
|
|
13
13
|
|
|
14
14
|
import { getSchemaDXN } from '../annotations';
|
|
15
15
|
import { ObjectDeletedId } from '../entities';
|
|
16
|
-
import { SchemaId, TypeId } from '../types';
|
|
16
|
+
import { ParentId, SchemaId, TypeId } from '../types';
|
|
17
17
|
|
|
18
18
|
import { executeChange, isInChangeContext, queueNotification } from './change-context';
|
|
19
19
|
import { defineHiddenProperty } from './define-hidden-property';
|
|
@@ -47,6 +47,7 @@ type ProxyTarget = {
|
|
|
47
47
|
* Schema for the root.
|
|
48
48
|
*/
|
|
49
49
|
[SchemaId]: Schema.Schema.AnyNoContext;
|
|
50
|
+
[ParentId]?: any;
|
|
50
51
|
|
|
51
52
|
/**
|
|
52
53
|
* For modifications.
|
|
@@ -320,6 +321,10 @@ export class TypedReactiveHandler implements ReactiveHandler<ProxyTarget> {
|
|
|
320
321
|
): { echoRoot: object; preparedValue: any } {
|
|
321
322
|
const echoRoot = getEchoRoot(target);
|
|
322
323
|
|
|
324
|
+
if (prop === ParentId) {
|
|
325
|
+
return { echoRoot, preparedValue: value }; // Short-circuit for parent assignment.
|
|
326
|
+
}
|
|
327
|
+
|
|
323
328
|
// Check for cycles before assignment.
|
|
324
329
|
if (isValidProxyTarget(value) || isProxy(value)) {
|
|
325
330
|
if (wouldCreateCycle(echoRoot, value)) {
|
|
@@ -363,6 +368,9 @@ export class TypedReactiveHandler implements ReactiveHandler<ProxyTarget> {
|
|
|
363
368
|
}
|
|
364
369
|
|
|
365
370
|
private _validateValue(target: any, prop: string | symbol, value: any) {
|
|
371
|
+
if (prop === ParentId) {
|
|
372
|
+
return value;
|
|
373
|
+
}
|
|
366
374
|
const schema = SchemaValidator.getTargetPropertySchema(target, prop);
|
|
367
375
|
const _ = Schema.asserts(schema)(value);
|
|
368
376
|
if (isValidProxyTarget(value)) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import * as Schema from 'effect/Schema';
|
|
6
|
+
import type * as Types from 'effect/Types';
|
|
6
7
|
import { describe, expect, test } from 'vitest';
|
|
7
8
|
|
|
8
9
|
import { EchoObjectSchema } from '../entities';
|
|
@@ -61,15 +62,13 @@ describe('EchoObjectSchema class DSL', () => {
|
|
|
61
62
|
});
|
|
62
63
|
|
|
63
64
|
test('record', () => {
|
|
64
|
-
const schema = Schema.
|
|
65
|
-
Schema.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}),
|
|
72
|
-
);
|
|
65
|
+
const schema = Schema.Struct({
|
|
66
|
+
meta: Schema.optional(Schema.Any),
|
|
67
|
+
// NOTE: Schema.Record only supports shallow values.
|
|
68
|
+
// https://www.npmjs.com/package/@effect/schema#mutable-records
|
|
69
|
+
// meta: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
|
|
70
|
+
// meta: Schema.optional(Schema.object),
|
|
71
|
+
});
|
|
73
72
|
|
|
74
73
|
{
|
|
75
74
|
const object = makeObject(schema, {});
|
|
@@ -89,7 +88,8 @@ describe('EchoObjectSchema class DSL', () => {
|
|
|
89
88
|
|
|
90
89
|
{
|
|
91
90
|
// Plain object (not a reactive proxy) - doesn't need Obj.change.
|
|
92
|
-
|
|
91
|
+
// Note: Schema.Schema.Type generates readonly types, so we cast to mutable for plain objects.
|
|
92
|
+
type Test1 = Types.Mutable<Schema.Schema.Type<typeof schema>>;
|
|
93
93
|
|
|
94
94
|
const object: Test1 = {};
|
|
95
95
|
(object.meta ??= {}).test = 100;
|
|
@@ -98,7 +98,7 @@ describe('EchoObjectSchema class DSL', () => {
|
|
|
98
98
|
|
|
99
99
|
{
|
|
100
100
|
const Test2 = Schema.Struct({
|
|
101
|
-
meta: Schema.optional(Schema.
|
|
101
|
+
meta: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
|
|
102
102
|
}).pipe(
|
|
103
103
|
EchoObjectSchema({
|
|
104
104
|
typename: 'dxos.org/type/FunctionTrigger',
|