@dxos/echo 0.8.4-main.5ea62a8 → 0.8.4-main.72ec0f3
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/chunk-BIDAASFK.mjs +3727 -0
- package/dist/lib/browser/chunk-BIDAASFK.mjs.map +7 -0
- package/dist/lib/browser/chunk-ZDLCWGEW.mjs +410 -0
- package/dist/lib/browser/chunk-ZDLCWGEW.mjs.map +7 -0
- package/dist/lib/browser/chunk-ZFRJKT4A.mjs +585 -0
- package/dist/lib/browser/chunk-ZFRJKT4A.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +17 -4
- package/dist/lib/browser/internal/index.mjs +336 -0
- package/dist/lib/browser/internal/index.mjs.map +7 -0
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/query/index.mjs +13 -0
- package/dist/lib/browser/query/index.mjs.map +7 -0
- package/dist/lib/browser/testing/index.mjs +231 -34
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node-esm/chunk-3SVRRCUU.mjs +3727 -0
- package/dist/lib/node-esm/chunk-3SVRRCUU.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-CGDHRZWH.mjs +585 -0
- package/dist/lib/node-esm/chunk-CGDHRZWH.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-HWS6VBQC.mjs +410 -0
- package/dist/lib/node-esm/chunk-HWS6VBQC.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +17 -4
- package/dist/lib/node-esm/internal/index.mjs +336 -0
- package/dist/lib/node-esm/internal/index.mjs.map +7 -0
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/query/index.mjs +13 -0
- package/dist/lib/node-esm/query/index.mjs.map +7 -0
- package/dist/lib/node-esm/testing/index.mjs +231 -34
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/Obj.d.ts +70 -8
- package/dist/types/src/Obj.d.ts.map +1 -1
- package/dist/types/src/Ref.d.ts +1 -1
- package/dist/types/src/Ref.d.ts.map +1 -1
- package/dist/types/src/Relation.d.ts +5 -4
- package/dist/types/src/Relation.d.ts.map +1 -1
- package/dist/types/src/Tag.d.ts +17 -0
- package/dist/types/src/Tag.d.ts.map +1 -0
- package/dist/types/src/Type.d.ts +16 -17
- package/dist/types/src/Type.d.ts.map +1 -1
- package/dist/types/src/errors.d.ts +68 -0
- package/dist/types/src/errors.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +4 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/internal/ast/annotation-helper.d.ts +8 -0
- package/dist/types/src/internal/ast/annotation-helper.d.ts.map +1 -0
- package/dist/types/src/internal/ast/annotations.d.ts +131 -0
- package/dist/types/src/internal/ast/annotations.d.ts.map +1 -0
- package/dist/types/src/internal/ast/annotations.test.d.ts +2 -0
- package/dist/types/src/internal/ast/annotations.test.d.ts.map +1 -0
- package/dist/types/src/internal/ast/entity-kind.d.ts +10 -0
- package/dist/types/src/internal/ast/entity-kind.d.ts.map +1 -0
- package/dist/types/src/internal/ast/index.d.ts +5 -0
- package/dist/types/src/internal/ast/index.d.ts.map +1 -0
- package/dist/types/src/internal/ast/types.d.ts +6 -0
- package/dist/types/src/internal/ast/types.d.ts.map +1 -0
- package/dist/types/src/internal/formats/date.d.ts +63 -0
- package/dist/types/src/internal/formats/date.d.ts.map +1 -0
- package/dist/types/src/internal/formats/date.test.d.ts +2 -0
- package/dist/types/src/internal/formats/date.test.d.ts.map +1 -0
- package/dist/types/src/internal/formats/format.d.ts +30 -0
- package/dist/types/src/internal/formats/format.d.ts.map +1 -0
- package/dist/types/src/internal/formats/format.test.d.ts +2 -0
- package/dist/types/src/internal/formats/format.test.d.ts.map +1 -0
- package/dist/types/src/internal/formats/index.d.ts +8 -0
- package/dist/types/src/internal/formats/index.d.ts.map +1 -0
- package/dist/types/src/internal/formats/number.d.ts +31 -0
- package/dist/types/src/internal/formats/number.d.ts.map +1 -0
- package/dist/types/src/internal/formats/object.d.ts +35 -0
- package/dist/types/src/internal/formats/object.d.ts.map +1 -0
- package/dist/types/src/internal/formats/select.d.ts +11 -0
- package/dist/types/src/internal/formats/select.d.ts.map +1 -0
- package/dist/types/src/internal/formats/string.d.ts +38 -0
- package/dist/types/src/internal/formats/string.d.ts.map +1 -0
- package/dist/types/src/internal/formats/types.d.ts +68 -0
- package/dist/types/src/internal/formats/types.d.ts.map +1 -0
- package/dist/types/src/internal/index.d.ts +15 -0
- package/dist/types/src/internal/index.d.ts.map +1 -0
- package/dist/types/src/internal/json/annotations.d.ts +19 -0
- package/dist/types/src/internal/json/annotations.d.ts.map +1 -0
- package/dist/types/src/internal/json/effect-schema.test.d.ts +2 -0
- package/dist/types/src/internal/json/effect-schema.test.d.ts.map +1 -0
- package/dist/types/src/internal/json/index.d.ts +2 -0
- package/dist/types/src/internal/json/index.d.ts.map +1 -0
- package/dist/types/src/internal/json/json-schema.d.ts +28 -0
- package/dist/types/src/internal/json/json-schema.d.ts.map +1 -0
- package/dist/types/src/internal/json/json-schema.test.d.ts +2 -0
- package/dist/types/src/internal/json/json-schema.test.d.ts.map +1 -0
- package/dist/types/src/internal/json-schema/index.d.ts +3 -0
- package/dist/types/src/internal/json-schema/index.d.ts.map +1 -0
- package/dist/types/src/internal/json-schema/json-schema-normalize.d.ts +7 -0
- package/dist/types/src/internal/json-schema/json-schema-normalize.d.ts.map +1 -0
- package/dist/types/src/internal/json-schema/json-schema-type.d.ts +250 -0
- package/dist/types/src/internal/json-schema/json-schema-type.d.ts.map +1 -0
- package/dist/types/src/internal/object/accessors.d.ts +37 -0
- package/dist/types/src/internal/object/accessors.d.ts.map +1 -0
- package/dist/types/src/internal/object/common.d.ts +18 -0
- package/dist/types/src/internal/object/common.d.ts.map +1 -0
- package/dist/types/src/internal/object/create.d.ts +40 -0
- package/dist/types/src/internal/object/create.d.ts.map +1 -0
- package/dist/types/src/internal/object/create.test.d.ts +2 -0
- package/dist/types/src/internal/object/create.test.d.ts.map +1 -0
- package/dist/types/src/internal/object/deleted.d.ts +6 -0
- package/dist/types/src/internal/object/deleted.d.ts.map +1 -0
- package/dist/types/src/internal/object/entity.d.ts +33 -0
- package/dist/types/src/internal/object/entity.d.ts.map +1 -0
- package/dist/types/src/internal/object/expando.d.ts +14 -0
- package/dist/types/src/internal/object/expando.d.ts.map +1 -0
- package/dist/types/src/internal/object/ids.d.ts +6 -0
- package/dist/types/src/internal/object/ids.d.ts.map +1 -0
- package/dist/types/src/internal/object/index.d.ts +16 -0
- package/dist/types/src/internal/object/index.d.ts.map +1 -0
- package/dist/types/src/internal/object/inspect.d.ts +2 -0
- package/dist/types/src/internal/object/inspect.d.ts.map +1 -0
- package/dist/types/src/internal/object/json-serializer.d.ts +32 -0
- package/dist/types/src/internal/object/json-serializer.d.ts.map +1 -0
- package/dist/types/src/internal/object/json-serializer.test.d.ts +2 -0
- package/dist/types/src/internal/object/json-serializer.test.d.ts.map +1 -0
- package/dist/types/src/internal/object/meta.d.ts +31 -0
- package/dist/types/src/internal/object/meta.d.ts.map +1 -0
- package/dist/types/src/internal/object/model.d.ts +117 -0
- package/dist/types/src/internal/object/model.d.ts.map +1 -0
- package/dist/types/src/internal/object/relation.d.ts +17 -0
- package/dist/types/src/internal/object/relation.d.ts.map +1 -0
- package/dist/types/src/internal/object/schema-validator.d.ts +15 -0
- package/dist/types/src/internal/object/schema-validator.d.ts.map +1 -0
- package/dist/types/src/internal/object/schema-validator.test.d.ts +2 -0
- package/dist/types/src/internal/object/schema-validator.test.d.ts.map +1 -0
- package/dist/types/src/internal/object/typed-object.d.ts +31 -0
- package/dist/types/src/internal/object/typed-object.d.ts.map +1 -0
- package/dist/types/src/internal/object/typed-object.test.d.ts +2 -0
- package/dist/types/src/internal/object/typed-object.test.d.ts.map +1 -0
- package/dist/types/src/internal/object/typename.d.ts +15 -0
- package/dist/types/src/internal/object/typename.d.ts.map +1 -0
- package/dist/types/src/internal/object/version.d.ts +14 -0
- package/dist/types/src/internal/object/version.d.ts.map +1 -0
- package/dist/types/src/internal/projection/compose.d.ts +6 -0
- package/dist/types/src/internal/projection/compose.d.ts.map +1 -0
- package/dist/types/src/internal/projection/compose.test.d.ts +2 -0
- package/dist/types/src/internal/projection/compose.test.d.ts.map +1 -0
- package/dist/types/src/internal/projection/index.d.ts +2 -0
- package/dist/types/src/internal/projection/index.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/handler.test.d.ts +2 -0
- package/dist/types/src/internal/proxy/handler.test.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/reactive-object.d.ts +15 -0
- package/dist/types/src/internal/proxy/reactive-object.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/schema.test.d.ts +2 -0
- package/dist/types/src/internal/proxy/schema.test.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/typed-handler.d.ts +44 -0
- package/dist/types/src/internal/proxy/typed-handler.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/typed-handler.test.d.ts +2 -0
- package/dist/types/src/internal/proxy/typed-handler.test.d.ts.map +1 -0
- package/dist/types/src/internal/proxy/typed-object.test.d.ts +2 -0
- package/dist/types/src/internal/proxy/typed-object.test.d.ts.map +1 -0
- package/dist/types/src/internal/query/index.d.ts +2 -0
- package/dist/types/src/internal/query/index.d.ts.map +1 -0
- package/dist/types/src/internal/query/query.d.ts +17 -0
- package/dist/types/src/internal/query/query.d.ts.map +1 -0
- package/dist/types/src/internal/ref/index.d.ts +3 -0
- package/dist/types/src/internal/ref/index.d.ts.map +1 -0
- package/dist/types/src/internal/ref/ref-array.d.ts +21 -0
- package/dist/types/src/internal/ref/ref-array.d.ts.map +1 -0
- package/dist/types/src/internal/ref/ref.d.ts +206 -0
- package/dist/types/src/internal/ref/ref.d.ts.map +1 -0
- package/dist/types/src/internal/ref/ref.test.d.ts +2 -0
- package/dist/types/src/internal/ref/ref.test.d.ts.map +1 -0
- package/dist/types/src/internal/schema/echo-schema.d.ts +168 -0
- package/dist/types/src/internal/schema/echo-schema.d.ts.map +1 -0
- package/dist/types/src/internal/schema/index.d.ts +7 -0
- package/dist/types/src/internal/schema/index.d.ts.map +1 -0
- package/dist/types/src/internal/schema/manipulation.d.ts +10 -0
- package/dist/types/src/internal/schema/manipulation.d.ts.map +1 -0
- package/dist/types/src/internal/schema/runtime-schema-registry.d.ts +18 -0
- package/dist/types/src/internal/schema/runtime-schema-registry.d.ts.map +1 -0
- package/dist/types/src/internal/schema/snapshot.d.ts +6 -0
- package/dist/types/src/internal/schema/snapshot.d.ts.map +1 -0
- package/dist/types/src/internal/schema/stored-schema.d.ts +13 -0
- package/dist/types/src/internal/schema/stored-schema.d.ts.map +1 -0
- package/dist/types/src/internal/testing/index.d.ts +3 -0
- package/dist/types/src/internal/testing/index.d.ts.map +1 -0
- package/dist/types/src/internal/testing/types.d.ts +381 -0
- package/dist/types/src/internal/testing/types.d.ts.map +1 -0
- package/dist/types/src/internal/testing/utils.d.ts +10 -0
- package/dist/types/src/internal/testing/utils.d.ts.map +1 -0
- package/dist/types/src/internal/types/index.d.ts +3 -0
- package/dist/types/src/internal/types/index.d.ts.map +1 -0
- package/dist/types/src/internal/types/types.d.ts +79 -0
- package/dist/types/src/internal/types/types.d.ts.map +1 -0
- package/dist/types/src/internal/types/types.test.d.ts +2 -0
- package/dist/types/src/internal/types/types.test.d.ts.map +1 -0
- package/dist/types/src/internal/types/util.d.ts +5 -0
- package/dist/types/src/internal/types/util.d.ts.map +1 -0
- package/dist/types/src/query/index.d.ts +1 -1
- package/dist/types/src/query/index.d.ts.map +1 -1
- package/dist/types/src/query/{dsl.d.ts → query.d.ts} +52 -22
- package/dist/types/src/query/query.d.ts.map +1 -0
- package/dist/types/src/query/query.test.d.ts +2 -0
- package/dist/types/src/query/query.test.d.ts.map +1 -0
- package/dist/types/src/testing/echo-schema.d.ts +7 -0
- package/dist/types/src/testing/echo-schema.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/types.d.ts +170 -55
- package/dist/types/src/testing/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -17
- package/src/Obj.ts +195 -16
- package/src/Ref.ts +1 -2
- package/src/Relation.ts +16 -5
- package/src/Tag.ts +39 -0
- package/src/Type.ts +29 -30
- package/src/errors.ts +18 -0
- package/src/index.ts +5 -1
- package/src/internal/ast/annotation-helper.ts +22 -0
- package/src/internal/ast/annotations.test.ts +98 -0
- package/src/internal/ast/annotations.ts +226 -0
- package/src/internal/ast/entity-kind.ts +15 -0
- package/src/internal/ast/index.ts +8 -0
- package/src/internal/ast/types.ts +17 -0
- package/src/internal/formats/date.test.ts +56 -0
- package/src/internal/formats/date.ts +217 -0
- package/src/internal/formats/format.test.ts +77 -0
- package/src/internal/formats/format.ts +52 -0
- package/src/internal/formats/index.ts +12 -0
- package/src/internal/formats/number.ts +89 -0
- package/src/internal/formats/object.ts +80 -0
- package/src/internal/formats/select.ts +16 -0
- package/src/internal/formats/string.ts +76 -0
- package/src/internal/formats/types.ts +175 -0
- package/src/internal/index.ts +22 -0
- package/src/internal/json/annotations.ts +50 -0
- package/src/internal/json/effect-schema.test.ts +143 -0
- package/src/internal/json/index.ts +5 -0
- package/src/internal/json/json-schema.test.ts +849 -0
- package/src/internal/json/json-schema.ts +519 -0
- package/src/internal/json-schema/index.ts +6 -0
- package/src/internal/json-schema/json-schema-normalize.ts +109 -0
- package/src/internal/json-schema/json-schema-type.ts +403 -0
- package/src/internal/object/accessors.ts +153 -0
- package/src/internal/object/common.ts +76 -0
- package/src/internal/object/create.test.ts +118 -0
- package/src/internal/object/create.ts +96 -0
- package/src/internal/object/deleted.ts +19 -0
- package/src/internal/object/entity.ts +248 -0
- package/src/internal/object/expando.ts +21 -0
- package/src/internal/object/ids.ts +12 -0
- package/src/internal/object/index.ts +19 -0
- package/src/internal/object/inspect.ts +48 -0
- package/src/internal/object/json-serializer.test.ts +99 -0
- package/src/internal/object/json-serializer.ts +225 -0
- package/src/internal/object/meta.ts +61 -0
- package/src/internal/object/model.ts +170 -0
- package/src/internal/object/relation.ts +24 -0
- package/src/internal/object/schema-validator.test.ts +186 -0
- package/src/internal/object/schema-validator.ts +241 -0
- package/src/internal/object/typed-object.test.ts +34 -0
- package/src/internal/object/typed-object.ts +88 -0
- package/src/internal/object/typename.ts +61 -0
- package/src/internal/object/version.ts +22 -0
- package/src/internal/projection/compose.test.ts +43 -0
- package/src/internal/projection/compose.ts +36 -0
- package/src/internal/projection/index.ts +5 -0
- package/src/internal/proxy/handler.test.ts +163 -0
- package/src/internal/proxy/reactive-object.ts +108 -0
- package/src/internal/proxy/schema.test.ts +136 -0
- package/src/internal/proxy/typed-handler.test.ts +102 -0
- package/src/internal/proxy/typed-handler.ts +228 -0
- package/src/internal/proxy/typed-object.test.ts +100 -0
- package/src/internal/query/index.ts +5 -0
- package/src/internal/query/query.ts +23 -0
- package/src/internal/ref/index.ts +6 -0
- package/src/internal/ref/ref-array.ts +39 -0
- package/src/internal/ref/ref.test.ts +100 -0
- package/src/internal/ref/ref.ts +521 -0
- package/src/internal/schema/echo-schema.ts +383 -0
- package/src/internal/schema/index.ts +10 -0
- package/src/internal/schema/manipulation.ts +92 -0
- package/src/internal/schema/runtime-schema-registry.ts +78 -0
- package/src/internal/schema/snapshot.ts +25 -0
- package/src/internal/schema/stored-schema.ts +26 -0
- package/src/internal/testing/index.ts +6 -0
- package/src/internal/testing/types.ts +144 -0
- package/src/internal/testing/utils.ts +54 -0
- package/src/internal/types/index.ts +6 -0
- package/src/internal/types/types.test.ts +48 -0
- package/src/internal/types/types.ts +176 -0
- package/src/internal/types/util.ts +9 -0
- package/src/query/index.ts +2 -1
- package/src/query/query.test.ts +401 -0
- package/src/query/{dsl.ts → query.ts} +188 -45
- package/src/test/api.test.ts +16 -9
- package/src/testing/echo-schema.ts +39 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/types.ts +40 -23
- package/dist/lib/browser/chunk-2KNTYMIW.mjs +0 -697
- package/dist/lib/browser/chunk-2KNTYMIW.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-EXNNC62J.mjs +0 -697
- package/dist/lib/node-esm/chunk-EXNNC62J.mjs.map +0 -7
- package/dist/types/src/experimental/database.d.ts +0 -8
- package/dist/types/src/experimental/database.d.ts.map +0 -1
- package/dist/types/src/experimental/index.d.ts +0 -1
- package/dist/types/src/experimental/index.d.ts.map +0 -1
- package/dist/types/src/experimental/queue.d.ts +0 -8
- package/dist/types/src/experimental/queue.d.ts.map +0 -1
- package/dist/types/src/experimental/space.d.ts +0 -8
- package/dist/types/src/experimental/space.d.ts.map +0 -1
- package/dist/types/src/query/dsl.d.ts.map +0 -1
- package/dist/types/src/query/dsl.test.d.ts +0 -2
- package/dist/types/src/query/dsl.test.d.ts.map +0 -1
- package/src/experimental/database.ts +0 -11
- package/src/experimental/index.ts +0 -7
- package/src/experimental/queue.ts +0 -11
- package/src/experimental/space.ts +0 -11
- package/src/query/dsl.test.ts +0 -324
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Array from 'effect/Array';
|
|
6
|
+
import * as Function from 'effect/Function';
|
|
7
|
+
import * as JSONSchema from 'effect/JSONSchema';
|
|
8
|
+
import * as Option from 'effect/Option';
|
|
9
|
+
import * as Schema from 'effect/Schema';
|
|
10
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
11
|
+
import type * as Types from 'effect/Types';
|
|
12
|
+
|
|
13
|
+
import { raise } from '@dxos/debug';
|
|
14
|
+
import { mapAst } from '@dxos/effect';
|
|
15
|
+
import { assertArgument, invariant } from '@dxos/invariant';
|
|
16
|
+
import { DXN, ObjectId } from '@dxos/keys';
|
|
17
|
+
import { log } from '@dxos/log';
|
|
18
|
+
import { clearUndefined, orderKeys, removeProperties } from '@dxos/util';
|
|
19
|
+
|
|
20
|
+
import { type TypeAnnotation, TypeAnnotationId, TypeIdentifierAnnotationId } from '../ast';
|
|
21
|
+
import { EntityKind, EntityKindSchema } from '../ast';
|
|
22
|
+
import {
|
|
23
|
+
ECHO_ANNOTATIONS_NS_DEPRECATED_KEY,
|
|
24
|
+
ECHO_ANNOTATIONS_NS_KEY,
|
|
25
|
+
type JsonSchemaEchoAnnotations,
|
|
26
|
+
type JsonSchemaType,
|
|
27
|
+
getNormalizedEchoAnnotations,
|
|
28
|
+
} from '../json-schema';
|
|
29
|
+
import { Expando, makeTypeJsonSchemaAnnotation } from '../object';
|
|
30
|
+
import { type JsonSchemaReferenceInfo, Ref, createEchoReferenceSchema } from '../ref';
|
|
31
|
+
|
|
32
|
+
import { CustomAnnotations, DecodedAnnotations, EchoAnnotations } from './annotations';
|
|
33
|
+
|
|
34
|
+
// TODO(burdon): Are these values stored (can they be changed?)
|
|
35
|
+
export enum PropType {
|
|
36
|
+
NONE = 0,
|
|
37
|
+
STRING = 1, // TODO(burdon): vs TEXT?
|
|
38
|
+
NUMBER = 2,
|
|
39
|
+
BOOLEAN = 3,
|
|
40
|
+
DATE = 4,
|
|
41
|
+
REF = 5,
|
|
42
|
+
RECORD = 6,
|
|
43
|
+
ENUM = 7,
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// TODO(burdon): Reconcile with @dxos/schema.
|
|
47
|
+
export const toPropType = (type?: PropType): string => {
|
|
48
|
+
switch (type) {
|
|
49
|
+
case PropType.STRING:
|
|
50
|
+
return 'string';
|
|
51
|
+
case PropType.NUMBER:
|
|
52
|
+
return 'number';
|
|
53
|
+
case PropType.BOOLEAN:
|
|
54
|
+
return 'boolean';
|
|
55
|
+
case PropType.DATE:
|
|
56
|
+
return 'date';
|
|
57
|
+
case PropType.REF:
|
|
58
|
+
return 'ref';
|
|
59
|
+
case PropType.RECORD:
|
|
60
|
+
return 'object';
|
|
61
|
+
default:
|
|
62
|
+
throw new Error(`Invalid type: ${type}`);
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const JSON_SCHEMA_URL = 'http://json-schema.org/draft-07/schema#';
|
|
67
|
+
|
|
68
|
+
export type JsonSchemaOptions = {
|
|
69
|
+
strict?: boolean;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Convert effect schema to JSON Schema.
|
|
74
|
+
* @param schema
|
|
75
|
+
*/
|
|
76
|
+
export const toJsonSchema = (schema: Schema.Schema.All, options: JsonSchemaOptions = {}): JsonSchemaType => {
|
|
77
|
+
assertArgument(Schema.isSchema(schema), 'schema');
|
|
78
|
+
let jsonSchema = _toJsonSchemaAST(schema.ast);
|
|
79
|
+
if (options.strict) {
|
|
80
|
+
// TOOD(burdon): Workaround to ensure JSON schema is valid (for agv parsing).
|
|
81
|
+
jsonSchema = removeProperties(jsonSchema, (key, value) => {
|
|
82
|
+
if (key === '$id' && value === '/schemas/any') {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
if (key === '$ref' && value === '#/$defs/dependency') {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
if (key === '$ref' && value === '#/$defs/jsonSchema') {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return false;
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return jsonSchema;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const _toJsonSchemaAST = (ast: SchemaAST.AST): JsonSchemaType => {
|
|
100
|
+
const withRefinements = withEchoRefinements(ast, '#');
|
|
101
|
+
const jsonSchema = JSONSchema.fromAST(withRefinements, {
|
|
102
|
+
definitions: {},
|
|
103
|
+
}) as JsonSchemaType;
|
|
104
|
+
|
|
105
|
+
return normalizeJsonSchema(jsonSchema);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const withEchoRefinements = (
|
|
109
|
+
ast: SchemaAST.AST,
|
|
110
|
+
path: string | undefined,
|
|
111
|
+
suspendCache = new Map<SchemaAST.AST, string>(),
|
|
112
|
+
): SchemaAST.AST => {
|
|
113
|
+
if (path) {
|
|
114
|
+
suspendCache.set(ast, path);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let recursiveResult: SchemaAST.AST;
|
|
118
|
+
if (SchemaAST.isSuspend(ast)) {
|
|
119
|
+
// Precompute JSON schema for suspended AST since effect serializer does not support it.
|
|
120
|
+
const suspendedAst = ast.f();
|
|
121
|
+
const cachedPath = suspendCache.get(suspendedAst);
|
|
122
|
+
if (cachedPath) {
|
|
123
|
+
recursiveResult = new SchemaAST.Suspend(() => withEchoRefinements(suspendedAst, path, suspendCache), {
|
|
124
|
+
[SchemaAST.JSONSchemaAnnotationId]: {
|
|
125
|
+
$ref: cachedPath,
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
} else {
|
|
129
|
+
const jsonSchema = _toJsonSchemaAST(suspendedAst);
|
|
130
|
+
recursiveResult = new SchemaAST.Suspend(() => withEchoRefinements(suspendedAst, path, suspendCache), {
|
|
131
|
+
[SchemaAST.JSONSchemaAnnotationId]: jsonSchema,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
} else if (SchemaAST.isTypeLiteral(ast)) {
|
|
135
|
+
// Add property order annotations
|
|
136
|
+
recursiveResult = mapAst(ast, (ast, key) =>
|
|
137
|
+
withEchoRefinements(ast, path && typeof key === 'string' ? `${path}/${key}` : undefined, suspendCache),
|
|
138
|
+
);
|
|
139
|
+
recursiveResult = addJsonSchemaFields(recursiveResult, {
|
|
140
|
+
propertyOrder: [...ast.propertySignatures.map((p) => p.name)] as string[],
|
|
141
|
+
});
|
|
142
|
+
} else if (SchemaAST.isUndefinedKeyword(ast)) {
|
|
143
|
+
// Ignore undefined keyword that appears in the optional fields.
|
|
144
|
+
return ast;
|
|
145
|
+
} else {
|
|
146
|
+
recursiveResult = mapAst(ast, (ast, key) =>
|
|
147
|
+
withEchoRefinements(
|
|
148
|
+
ast,
|
|
149
|
+
path && (typeof key === 'string' || typeof key === 'number') ? `${path}/${key}` : undefined,
|
|
150
|
+
suspendCache,
|
|
151
|
+
),
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const annotationFields = annotations_toJsonSchemaFields(ast.annotations);
|
|
156
|
+
if (Object.keys(annotationFields).length === 0) {
|
|
157
|
+
return recursiveResult;
|
|
158
|
+
} else {
|
|
159
|
+
return addJsonSchemaFields(recursiveResult, annotationFields);
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Convert JSON schema to effect schema.
|
|
165
|
+
* @param root
|
|
166
|
+
* @param definitions
|
|
167
|
+
*/
|
|
168
|
+
export const toEffectSchema = (root: JsonSchemaType, _defs?: JsonSchemaType['$defs']): Schema.Schema.AnyNoContext => {
|
|
169
|
+
const defs = root.$defs ? { ..._defs, ...root.$defs } : (_defs ?? {});
|
|
170
|
+
if ('type' in root && root.type === 'object') {
|
|
171
|
+
return objectToEffectSchema(root, defs);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
let result: Schema.Schema.AnyNoContext = Schema.Unknown;
|
|
175
|
+
if ('$ref' in root) {
|
|
176
|
+
switch (root.$ref) {
|
|
177
|
+
case '/schemas/echo/ref': {
|
|
178
|
+
result = refToEffectSchema(root);
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} else if ('$id' in root) {
|
|
183
|
+
switch (root.$id as string) {
|
|
184
|
+
case '/schemas/any': {
|
|
185
|
+
result = anyToEffectSchema(root as JSONSchema.JsonSchema7Any);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
case '/schemas/unknown': {
|
|
189
|
+
result = Schema.Unknown;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
case '/schemas/{}':
|
|
193
|
+
case '/schemas/object': {
|
|
194
|
+
result = Schema.Object;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
// Custom ECHO object reference.
|
|
198
|
+
case '/schemas/echo/ref': {
|
|
199
|
+
result = refToEffectSchema(root);
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} else if ('enum' in root) {
|
|
204
|
+
result = Schema.Union(...root.enum!.map((e) => Schema.Literal(e)));
|
|
205
|
+
} else if ('oneOf' in root) {
|
|
206
|
+
result = Schema.Union(...root.oneOf!.map((v) => toEffectSchema(v, defs)));
|
|
207
|
+
} else if ('anyOf' in root) {
|
|
208
|
+
result = Schema.Union(...root.anyOf!.map((v) => toEffectSchema(v, defs)));
|
|
209
|
+
} else if ('allOf' in root) {
|
|
210
|
+
if (root.allOf!.length === 1) {
|
|
211
|
+
result = toEffectSchema(root.allOf![0], defs);
|
|
212
|
+
} else {
|
|
213
|
+
log.warn('allOf with multiple schemas is not supported');
|
|
214
|
+
result = Schema.Unknown;
|
|
215
|
+
}
|
|
216
|
+
} else if ('type' in root) {
|
|
217
|
+
switch (root.type) {
|
|
218
|
+
case 'string': {
|
|
219
|
+
result = Schema.String;
|
|
220
|
+
if (root.pattern) {
|
|
221
|
+
result = result.pipe(Schema.pattern(new RegExp(root.pattern)));
|
|
222
|
+
}
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
case 'number': {
|
|
226
|
+
result = Schema.Number;
|
|
227
|
+
break;
|
|
228
|
+
}
|
|
229
|
+
case 'integer': {
|
|
230
|
+
result = Schema.Number.pipe(Schema.int());
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
case 'boolean': {
|
|
234
|
+
result = Schema.Boolean;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
case 'array': {
|
|
238
|
+
if (Array.isArray(root.items)) {
|
|
239
|
+
const [required, optional] = Function.pipe(
|
|
240
|
+
root.items,
|
|
241
|
+
Array.map((v) => toEffectSchema(v as JsonSchemaType, defs)),
|
|
242
|
+
Array.splitAt(root.minItems ?? root.items.length),
|
|
243
|
+
);
|
|
244
|
+
result = Schema.Tuple(...required, ...optional.map(Schema.optionalElement));
|
|
245
|
+
} else {
|
|
246
|
+
invariant(root.items);
|
|
247
|
+
const items = root.items;
|
|
248
|
+
result = Array.isArray(items)
|
|
249
|
+
? Schema.Tuple(...items.map((v) => toEffectSchema(v as JsonSchemaType, defs)))
|
|
250
|
+
: Schema.Array(toEffectSchema(items as JsonSchemaType, defs));
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
case 'null': {
|
|
255
|
+
result = Schema.Null;
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
} else if ('$ref' in root) {
|
|
260
|
+
const refSegments = root.$ref!.split('/');
|
|
261
|
+
const jsonSchema = defs[refSegments[refSegments.length - 1]];
|
|
262
|
+
invariant(jsonSchema, `missing definition for ${root.$ref}`);
|
|
263
|
+
result = toEffectSchema(jsonSchema, defs).pipe(
|
|
264
|
+
Schema.annotations({ identifier: refSegments[refSegments.length - 1] }),
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const annotations = jsonSchemaFieldsToAnnotations(root);
|
|
269
|
+
|
|
270
|
+
// log.info('toEffectSchema', { root, annotations });
|
|
271
|
+
result = result.annotations(annotations);
|
|
272
|
+
|
|
273
|
+
return result;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const objectToEffectSchema = (root: JsonSchemaType, defs: JsonSchemaType['$defs']): Schema.Schema.AnyNoContext => {
|
|
277
|
+
invariant('type' in root && root.type === 'object', `not an object: ${root}`);
|
|
278
|
+
|
|
279
|
+
const echoRefinement: JsonSchemaEchoAnnotations = (root as any)[ECHO_ANNOTATIONS_NS_DEPRECATED_KEY];
|
|
280
|
+
const isEchoObject =
|
|
281
|
+
echoRefinement != null || ('$id' in root && typeof root.$id === 'string' && root.$id.startsWith('dxn:'));
|
|
282
|
+
|
|
283
|
+
let fields: Schema.Struct.Fields = {};
|
|
284
|
+
const propertyList = Object.entries(root.properties ?? {});
|
|
285
|
+
let immutableIdField: Schema.Schema.AnyNoContext | undefined;
|
|
286
|
+
for (const [key, value] of propertyList) {
|
|
287
|
+
if (isEchoObject && key === 'id') {
|
|
288
|
+
immutableIdField = toEffectSchema(value, defs);
|
|
289
|
+
} else {
|
|
290
|
+
// TODO(burdon): Mutable cast.
|
|
291
|
+
(fields as any)[key] = root.required?.includes(key)
|
|
292
|
+
? toEffectSchema(value, defs)
|
|
293
|
+
: Schema.optional(toEffectSchema(value, defs));
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (root.propertyOrder) {
|
|
298
|
+
fields = orderKeys(fields, root.propertyOrder as any);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
let schema: Schema.Schema<any, any, unknown>;
|
|
302
|
+
if (root.patternProperties) {
|
|
303
|
+
invariant(propertyList.length === 0, 'pattern properties mixed with regular properties are not supported');
|
|
304
|
+
invariant(
|
|
305
|
+
Object.keys(root.patternProperties).length === 1 && Object.keys(root.patternProperties)[0] === '',
|
|
306
|
+
'only one pattern property is supported',
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
schema = Schema.Record({ key: Schema.String, value: toEffectSchema(root.patternProperties[''], defs) });
|
|
310
|
+
} else if (typeof root.additionalProperties !== 'object') {
|
|
311
|
+
schema = Schema.Struct(fields);
|
|
312
|
+
} else {
|
|
313
|
+
const indexValue = toEffectSchema(root.additionalProperties, defs);
|
|
314
|
+
if (propertyList.length > 0) {
|
|
315
|
+
schema = Schema.Struct(fields, { key: Schema.String, value: indexValue });
|
|
316
|
+
} else {
|
|
317
|
+
schema = Schema.Record({ key: Schema.String, value: indexValue });
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (immutableIdField) {
|
|
322
|
+
schema = Schema.extend(Schema.mutable(schema), Schema.Struct({ id: immutableIdField }));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const annotations = jsonSchemaFieldsToAnnotations(root);
|
|
326
|
+
return schema.annotations(annotations) as any;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
const anyToEffectSchema = (root: JSONSchema.JsonSchema7Any): Schema.Schema.AnyNoContext => {
|
|
330
|
+
const echoRefinement: JsonSchemaEchoAnnotations = (root as any)[ECHO_ANNOTATIONS_NS_DEPRECATED_KEY];
|
|
331
|
+
// TODO(dmaretskyi): Is this branch still taken?
|
|
332
|
+
if ((echoRefinement as any)?.reference != null) {
|
|
333
|
+
const echoId = root.$id.startsWith('dxn:echo:') ? root.$id : undefined;
|
|
334
|
+
return createEchoReferenceSchema(
|
|
335
|
+
echoId,
|
|
336
|
+
(echoRefinement as any).reference.typename,
|
|
337
|
+
(echoRefinement as any).reference.version,
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
return Schema.Any;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// TODO(dmaretskyi): Types.
|
|
345
|
+
const refToEffectSchema = (root: any): Schema.Schema.AnyNoContext => {
|
|
346
|
+
if (!('reference' in root)) {
|
|
347
|
+
return Ref(Expando);
|
|
348
|
+
}
|
|
349
|
+
const reference: JsonSchemaReferenceInfo = root.reference;
|
|
350
|
+
if (typeof reference !== 'object') {
|
|
351
|
+
throw new Error('Invalid reference field in ref schema');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const targetSchemaDXN = DXN.parse(reference.schema.$ref);
|
|
355
|
+
invariant(targetSchemaDXN.kind === DXN.kind.TYPE);
|
|
356
|
+
|
|
357
|
+
return createEchoReferenceSchema(
|
|
358
|
+
targetSchemaDXN.toString(),
|
|
359
|
+
targetSchemaDXN.kind === DXN.kind.TYPE ? targetSchemaDXN.parts[0] : undefined,
|
|
360
|
+
reference.schemaVersion,
|
|
361
|
+
);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
//
|
|
365
|
+
// Annotations
|
|
366
|
+
//
|
|
367
|
+
|
|
368
|
+
const annotations_toJsonSchemaFields = (annotations: SchemaAST.Annotations): Record<symbol, any> => {
|
|
369
|
+
const schemaFields: Record<string, any> = {};
|
|
370
|
+
|
|
371
|
+
const echoAnnotations: JsonSchemaEchoAnnotations = {};
|
|
372
|
+
for (const [key, annotationId] of Object.entries(EchoAnnotations)) {
|
|
373
|
+
if (annotations[annotationId] != null) {
|
|
374
|
+
echoAnnotations[key as keyof JsonSchemaEchoAnnotations] = annotations[annotationId] as any;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (Object.keys(echoAnnotations).length > 0) {
|
|
378
|
+
// TODO(dmaretskyi): use new namespace.
|
|
379
|
+
schemaFields[ECHO_ANNOTATIONS_NS_KEY] = echoAnnotations;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const echoIdentifier = annotations[TypeIdentifierAnnotationId];
|
|
383
|
+
if (echoIdentifier) {
|
|
384
|
+
schemaFields[ECHO_ANNOTATIONS_NS_KEY] ??= {};
|
|
385
|
+
schemaFields[ECHO_ANNOTATIONS_NS_KEY].schemaId = echoIdentifier;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Custom (at end).
|
|
389
|
+
for (const [key, annotationId] of Object.entries(CustomAnnotations)) {
|
|
390
|
+
const value = annotations[annotationId];
|
|
391
|
+
if (value != null) {
|
|
392
|
+
schemaFields[key] = value;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
return schemaFields;
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
const decodeTypeIdentifierAnnotation = (schema: JsonSchemaType): string | undefined => {
|
|
400
|
+
// Limit to dxn:echo: URIs.
|
|
401
|
+
if (schema.$id && schema.$id.startsWith('dxn:echo:')) {
|
|
402
|
+
return schema.$id;
|
|
403
|
+
} else if (schema.$id && schema.$id.startsWith('dxn:type:') && schema?.echo?.type?.schemaId) {
|
|
404
|
+
const id = schema?.echo?.type?.schemaId;
|
|
405
|
+
if (ObjectId.isValid(id)) {
|
|
406
|
+
return DXN.fromLocalObjectId(id).toString();
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return undefined;
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const decodeTypeAnnotation = (schema: JsonSchemaType): TypeAnnotation | undefined => {
|
|
413
|
+
if (schema.typename) {
|
|
414
|
+
const annotation: Types.Mutable<TypeAnnotation> = {
|
|
415
|
+
// TODO(dmaretskyi): Decoding default.
|
|
416
|
+
kind: schema.entityKind ? Schema.decodeSync(EntityKindSchema)(schema.entityKind) : EntityKind.Object,
|
|
417
|
+
typename: schema.typename,
|
|
418
|
+
version: schema.version ?? '0.1.0',
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
if (annotation.kind === EntityKind.Relation) {
|
|
422
|
+
const source = schema.relationSource?.$ref ?? raise(new Error('Relation source not set'));
|
|
423
|
+
const target = schema.relationTarget?.$ref ?? raise(new Error('Relation target not set'));
|
|
424
|
+
annotation.sourceSchema = DXN.parse(source).toString();
|
|
425
|
+
annotation.targetSchema = DXN.parse(target).toString();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return annotation;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Decode legacy schema.
|
|
432
|
+
if (!schema.typename && schema?.echo?.type) {
|
|
433
|
+
return {
|
|
434
|
+
kind: EntityKind.Object,
|
|
435
|
+
typename: schema.echo.type.typename,
|
|
436
|
+
version: schema.echo.type.version,
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return undefined;
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const jsonSchemaFieldsToAnnotations = (schema: JsonSchemaType): SchemaAST.Annotations => {
|
|
444
|
+
const annotations: Types.Mutable<Schema.Annotations.Schema<any>> = {};
|
|
445
|
+
|
|
446
|
+
const echoAnnotations: JsonSchemaEchoAnnotations = getNormalizedEchoAnnotations(schema) ?? {};
|
|
447
|
+
if (echoAnnotations) {
|
|
448
|
+
for (const [key, annotationId] of Object.entries(EchoAnnotations)) {
|
|
449
|
+
if (echoAnnotations[key as keyof JsonSchemaEchoAnnotations]) {
|
|
450
|
+
annotations[annotationId] = echoAnnotations[key as keyof JsonSchemaEchoAnnotations];
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
annotations[TypeIdentifierAnnotationId] = decodeTypeIdentifierAnnotation(schema);
|
|
456
|
+
const typeAnnotation = decodeTypeAnnotation(schema);
|
|
457
|
+
if (typeAnnotation) {
|
|
458
|
+
annotations[TypeAnnotationId] = typeAnnotation;
|
|
459
|
+
annotations[SchemaAST.JSONSchemaAnnotationId] = makeTypeJsonSchemaAnnotation({
|
|
460
|
+
kind: typeAnnotation.kind,
|
|
461
|
+
typename: typeAnnotation.typename,
|
|
462
|
+
version: typeAnnotation.version,
|
|
463
|
+
relationSource: typeAnnotation.sourceSchema,
|
|
464
|
+
relationTarget: typeAnnotation.targetSchema,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Custom (at end).
|
|
469
|
+
for (const [key, annotationId] of Object.entries({ ...CustomAnnotations, ...DecodedAnnotations })) {
|
|
470
|
+
if (key in schema) {
|
|
471
|
+
annotations[annotationId] = (schema as any)[key];
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
return clearUndefined(annotations);
|
|
476
|
+
};
|
|
477
|
+
|
|
478
|
+
const makeAnnotatedRefinement = (ast: SchemaAST.AST, annotations: SchemaAST.Annotations): SchemaAST.Refinement => {
|
|
479
|
+
return new SchemaAST.Refinement(ast, () => Option.none(), annotations);
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const addJsonSchemaFields = (ast: SchemaAST.AST, schema: JsonSchemaType): SchemaAST.AST =>
|
|
483
|
+
makeAnnotatedRefinement(ast, { [SchemaAST.JSONSchemaAnnotationId]: schema });
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Fixes field order.
|
|
487
|
+
* Sets `$schema` prop.
|
|
488
|
+
*/
|
|
489
|
+
const normalizeJsonSchema = (jsonSchema: JsonSchemaType): JsonSchemaType => {
|
|
490
|
+
if (jsonSchema.properties && 'id' in jsonSchema.properties) {
|
|
491
|
+
jsonSchema.properties = orderKeys(jsonSchema.properties, ['id']); // Put id first.
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// TODO(dmaretskyi): Makes sure undefined is not left on optional fields for the resulting object.
|
|
495
|
+
jsonSchema.$schema = JSON_SCHEMA_URL;
|
|
496
|
+
jsonSchema = orderKeys(jsonSchema, [
|
|
497
|
+
'$schema',
|
|
498
|
+
'$id',
|
|
499
|
+
|
|
500
|
+
'entityKind',
|
|
501
|
+
'typename',
|
|
502
|
+
'version',
|
|
503
|
+
'relationTarget',
|
|
504
|
+
'relationSource',
|
|
505
|
+
|
|
506
|
+
'type',
|
|
507
|
+
'enum',
|
|
508
|
+
|
|
509
|
+
'properties',
|
|
510
|
+
'required',
|
|
511
|
+
'propertyOrder', // Custom.
|
|
512
|
+
'items',
|
|
513
|
+
'additionalProperties',
|
|
514
|
+
|
|
515
|
+
'anyOf',
|
|
516
|
+
'oneOf',
|
|
517
|
+
]);
|
|
518
|
+
return jsonSchema;
|
|
519
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { JsonSchemaFields, type JsonSchemaType } from './json-schema-type';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Normalize schema to to draft-07 format.
|
|
9
|
+
* Note: the input type does not necessarily match the {@link JsonSchemaType} type.
|
|
10
|
+
*/
|
|
11
|
+
export const normalizeSchema = (schema: JsonSchemaType): JsonSchemaType => {
|
|
12
|
+
const copy = structuredClone(schema);
|
|
13
|
+
go(copy);
|
|
14
|
+
return copy;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const go = (schema: JsonSchemaType) => {
|
|
18
|
+
if (typeof schema !== 'object' || schema === null) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if ((schema as any).exclusiveMaximum === true) {
|
|
23
|
+
schema.exclusiveMaximum = schema.maximum;
|
|
24
|
+
delete (schema as any).exclusiveMaximum;
|
|
25
|
+
} else if ((schema as any).exclusiveMaximum === false) {
|
|
26
|
+
delete (schema as any).exclusiveMaximum;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if ((schema as any).exclusiveMinimum === true) {
|
|
30
|
+
schema.exclusiveMinimum = schema.minimum;
|
|
31
|
+
delete (schema as any).exclusiveMinimum;
|
|
32
|
+
} else if ((schema as any).exclusiveMinimum === false) {
|
|
33
|
+
delete (schema as any).exclusiveMinimum;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Delete all properties that are not in the JsonSchemaFields.
|
|
37
|
+
for (const key of Object.keys(schema)) {
|
|
38
|
+
if (!JsonSchemaFields.includes(key)) {
|
|
39
|
+
delete (schema as any)[key];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Recursively normalize the schema.
|
|
44
|
+
// Recursively normalize the schema.
|
|
45
|
+
if (schema.properties) {
|
|
46
|
+
goOnRecord(schema.properties);
|
|
47
|
+
}
|
|
48
|
+
if (schema.patternProperties) {
|
|
49
|
+
goOnRecord(schema.patternProperties);
|
|
50
|
+
}
|
|
51
|
+
if (schema.propertyNames) {
|
|
52
|
+
go(schema.propertyNames);
|
|
53
|
+
}
|
|
54
|
+
if (schema.definitions) {
|
|
55
|
+
goOnRecord(schema.definitions);
|
|
56
|
+
}
|
|
57
|
+
if (schema.items) {
|
|
58
|
+
maybeGoOnArray(schema.items);
|
|
59
|
+
}
|
|
60
|
+
if (schema.additionalItems) {
|
|
61
|
+
maybeGoOnArray(schema.additionalItems);
|
|
62
|
+
}
|
|
63
|
+
if (schema.contains) {
|
|
64
|
+
go(schema.contains);
|
|
65
|
+
}
|
|
66
|
+
if (schema.if) {
|
|
67
|
+
go(schema.if);
|
|
68
|
+
}
|
|
69
|
+
if (schema.then) {
|
|
70
|
+
go(schema.then);
|
|
71
|
+
}
|
|
72
|
+
if (schema.else) {
|
|
73
|
+
go(schema.else);
|
|
74
|
+
}
|
|
75
|
+
if (schema.allOf) {
|
|
76
|
+
maybeGoOnArray(schema.allOf);
|
|
77
|
+
}
|
|
78
|
+
if (schema.anyOf) {
|
|
79
|
+
maybeGoOnArray(schema.anyOf);
|
|
80
|
+
}
|
|
81
|
+
if (schema.oneOf) {
|
|
82
|
+
maybeGoOnArray(schema.oneOf);
|
|
83
|
+
}
|
|
84
|
+
if (schema.not) {
|
|
85
|
+
go(schema.not);
|
|
86
|
+
}
|
|
87
|
+
if (schema.$defs) {
|
|
88
|
+
goOnRecord(schema.$defs);
|
|
89
|
+
}
|
|
90
|
+
if (schema.reference) {
|
|
91
|
+
go(schema.reference.schema);
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const maybeGoOnArray = (value: any) => {
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
for (const item of value) {
|
|
98
|
+
go(item);
|
|
99
|
+
}
|
|
100
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
101
|
+
go(value);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const goOnRecord = (record: Record<string, any>) => {
|
|
106
|
+
for (const key of Object.keys(record)) {
|
|
107
|
+
go(record[key]);
|
|
108
|
+
}
|
|
109
|
+
};
|