@dxos/echo 0.8.4-main.e098934 → 0.8.4-main.ead640a
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-HKFCK2GL.mjs +175 -0
- package/dist/lib/browser/chunk-HKFCK2GL.mjs.map +7 -0
- package/dist/lib/browser/{chunk-EIXXFUN5.mjs → chunk-MAAYELT7.mjs} +477 -486
- package/dist/lib/browser/chunk-MAAYELT7.mjs.map +7 -0
- package/dist/lib/browser/chunk-MB6MMNFP.mjs +3857 -0
- package/dist/lib/browser/chunk-MB6MMNFP.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +12 -7
- package/dist/lib/browser/internal/index.mjs +332 -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 +15 -0
- package/dist/lib/browser/query/index.mjs.map +7 -0
- package/dist/lib/browser/testing/index.mjs +260 -32
- package/dist/lib/browser/testing/index.mjs.map +4 -4
- package/dist/lib/node-esm/{chunk-TCY7IVTS.mjs → chunk-5NWDGIBT.mjs} +477 -486
- package/dist/lib/node-esm/chunk-5NWDGIBT.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-AUAH4E2J.mjs +175 -0
- package/dist/lib/node-esm/chunk-AUAH4E2J.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-BQRA4VLX.mjs +3857 -0
- package/dist/lib/node-esm/chunk-BQRA4VLX.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +12 -7
- package/dist/lib/node-esm/internal/index.mjs +332 -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 +15 -0
- package/dist/lib/node-esm/query/index.mjs.map +7 -0
- package/dist/lib/node-esm/testing/index.mjs +260 -32
- package/dist/lib/node-esm/testing/index.mjs.map +4 -4
- package/dist/types/src/Obj.d.ts +22 -5
- 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/Type.d.ts +16 -17
- package/dist/types/src/Type.d.ts.map +1 -1
- package/dist/types/src/errors.d.ts +8 -8
- package/dist/types/src/index.d.ts +2 -2
- 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 +125 -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 +455 -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 +2 -1
- package/dist/types/src/query/index.d.ts.map +1 -1
- package/dist/types/src/query/{dsl.d.ts → query.d.ts} +28 -23
- 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/query/tag.d.ts +17 -0
- package/dist/types/src/query/tag.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 +18 -18
- package/dist/types/src/testing/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +29 -18
- package/src/Obj.ts +77 -12
- package/src/Ref.ts +1 -2
- package/src/Relation.ts +15 -5
- package/src/Type.ts +29 -30
- package/src/index.ts +2 -2
- package/src/internal/ast/annotation-helper.ts +22 -0
- package/src/internal/ast/annotations.test.ts +98 -0
- package/src/internal/ast/annotations.ts +219 -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 +857 -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 +211 -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} +82 -45
- package/src/query/tag.ts +37 -0
- package/src/test/api.test.ts +9 -9
- package/src/testing/echo-schema.ts +39 -0
- package/src/testing/index.ts +2 -0
- package/src/testing/types.ts +1 -1
- package/dist/lib/browser/chunk-EIXXFUN5.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-TCY7IVTS.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 -362
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Function from 'effect/Function';
|
|
6
|
+
import * as Option from 'effect/Option';
|
|
7
|
+
import * as Schema from 'effect/Schema';
|
|
8
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
9
|
+
|
|
10
|
+
import { assertArgument } from '@dxos/invariant';
|
|
11
|
+
import { DXN } from '@dxos/keys';
|
|
12
|
+
import { type Primitive } from '@dxos/util';
|
|
13
|
+
|
|
14
|
+
import { createAnnotationHelper } from './annotation-helper';
|
|
15
|
+
import { EntityKind } from './entity-kind';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* If property is optional returns the nested property, otherwise returns the property.
|
|
19
|
+
*/
|
|
20
|
+
// TODO(wittjosiah): Is there a way to do this as a generic?
|
|
21
|
+
export const unwrapOptional = (property: SchemaAST.PropertySignature) => {
|
|
22
|
+
if (!property.isOptional || !SchemaAST.isUnion(property.type)) {
|
|
23
|
+
return property;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return property.type.types[0];
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* ECHO identifier (for a stored schema).
|
|
31
|
+
* Must be a `dxn:echo:` URI.
|
|
32
|
+
*/
|
|
33
|
+
export const TypeIdentifierAnnotationId = Symbol.for('@dxos/schema/annotation/TypeIdentifier');
|
|
34
|
+
|
|
35
|
+
export const getTypeIdentifierAnnotation = (schema: Schema.Schema.All) =>
|
|
36
|
+
Function.flow(
|
|
37
|
+
SchemaAST.getAnnotation<string>(TypeIdentifierAnnotationId),
|
|
38
|
+
Option.getOrElse(() => undefined),
|
|
39
|
+
)(schema.ast);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* ECHO type.
|
|
43
|
+
*/
|
|
44
|
+
export const TypeAnnotationId = Symbol.for('@dxos/schema/annotation/Type');
|
|
45
|
+
|
|
46
|
+
// TODO(burdon): Create echo-schema Format types.
|
|
47
|
+
// TODO(burdon): Reconcile with "short" DXN.
|
|
48
|
+
export const Typename = Schema.String.pipe(Schema.pattern(/^[a-zA-Z]\w+\.[a-zA-Z]\w{1,}\/[\w/_-]+$/));
|
|
49
|
+
export const SchemaVersion = Schema.String.pipe(Schema.pattern(/^\d+.\d+.\d+$/));
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Payload stored under {@link TypeAnnotationId}.
|
|
53
|
+
*/
|
|
54
|
+
// TODO(dmaretskyi): Rename getTypeAnnotation to represent commonality between objects and relations (e.g. `entity`).
|
|
55
|
+
export const TypeAnnotation = Schema.Struct({
|
|
56
|
+
kind: Schema.Enums(EntityKind),
|
|
57
|
+
typename: Typename,
|
|
58
|
+
version: SchemaVersion,
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* If this is a relation, the schema of the source object.
|
|
62
|
+
* Must be present if entity kind is {@link EntityKind.Relation}.
|
|
63
|
+
*/
|
|
64
|
+
sourceSchema: Schema.optional(DXN.Schema),
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* If this is a relation, the schema of the target object.
|
|
68
|
+
* Must be present if entity kind is {@link EntityKind.Relation}.
|
|
69
|
+
*/
|
|
70
|
+
targetSchema: Schema.optional(DXN.Schema),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export interface TypeAnnotation extends Schema.Schema.Type<typeof TypeAnnotation> {}
|
|
74
|
+
|
|
75
|
+
export type TypeMeta = Pick<TypeAnnotation, 'typename' | 'version'>;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @returns {@link TypeAnnotation} from a schema.
|
|
79
|
+
* Schema must have been created with {@link TypedObject} or {@link TypedLink} or manually assigned an appropriate annotation.
|
|
80
|
+
*/
|
|
81
|
+
export const getTypeAnnotation = (schema: Schema.Schema.All): TypeAnnotation | undefined => {
|
|
82
|
+
assertArgument(schema != null && schema.ast != null, 'schema', 'invalid schema');
|
|
83
|
+
return Function.flow(
|
|
84
|
+
SchemaAST.getAnnotation<TypeAnnotation>(TypeAnnotationId),
|
|
85
|
+
Option.getOrElse(() => undefined),
|
|
86
|
+
)(schema.ast);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @returns {@link EntityKind} from a schema.
|
|
91
|
+
*/
|
|
92
|
+
export const getEntityKind = (schema: Schema.Schema.All): EntityKind | undefined => getTypeAnnotation(schema)?.kind;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @deprecated Use {@link Type.getTypename} instead.
|
|
96
|
+
* @returns Schema typename (without dxn: prefix or version number).
|
|
97
|
+
*/
|
|
98
|
+
export const getSchemaTypename = (schema: Schema.Schema.All): string | undefined => getTypeAnnotation(schema)?.typename;
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* @deprecated Use {@link Type.getVersion} instead.
|
|
102
|
+
* @returns Schema version in semver format.
|
|
103
|
+
*/
|
|
104
|
+
export const getSchemaVersion = (schema: Schema.Schema.All): string | undefined => getTypeAnnotation(schema)?.version;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* PropertyMeta (metadata for dynamic schema properties).
|
|
108
|
+
* For user-defined annotations.
|
|
109
|
+
*/
|
|
110
|
+
export const PropertyMetaAnnotationId = Symbol.for('@dxos/schema/annotation/PropertyMeta');
|
|
111
|
+
|
|
112
|
+
export type PropertyMetaValue = Primitive | Record<string, Primitive> | Primitive[];
|
|
113
|
+
|
|
114
|
+
export type PropertyMetaAnnotation = {
|
|
115
|
+
[name: string]: PropertyMetaValue;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
export const PropertyMeta = (name: string, value: PropertyMetaValue) => {
|
|
119
|
+
return <A, I, R>(self: Schema.Schema<A, I, R>): Schema.Schema<A, I, R> => {
|
|
120
|
+
const existingMeta = self.ast.annotations[PropertyMetaAnnotationId] as PropertyMetaAnnotation;
|
|
121
|
+
return self.annotations({
|
|
122
|
+
[PropertyMetaAnnotationId]: {
|
|
123
|
+
...existingMeta,
|
|
124
|
+
[name]: value,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const getPropertyMetaAnnotation = <T>(prop: SchemaAST.PropertySignature, name: string) =>
|
|
131
|
+
Function.pipe(
|
|
132
|
+
SchemaAST.getAnnotation<PropertyMetaAnnotation>(PropertyMetaAnnotationId)(prop.type),
|
|
133
|
+
Option.map((meta) => meta[name] as T),
|
|
134
|
+
Option.getOrElse(() => undefined),
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Schema reference.
|
|
139
|
+
*/
|
|
140
|
+
export const ReferenceAnnotationId = Symbol.for('@dxos/schema/annotation/Reference');
|
|
141
|
+
|
|
142
|
+
export type ReferenceAnnotationValue = TypeAnnotation;
|
|
143
|
+
|
|
144
|
+
export const getReferenceAnnotation = (schema: Schema.Schema.AnyNoContext) =>
|
|
145
|
+
Function.pipe(
|
|
146
|
+
SchemaAST.getAnnotation<ReferenceAnnotationValue>(ReferenceAnnotationId)(schema.ast),
|
|
147
|
+
Option.getOrElse(() => undefined),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* SchemaMeta.
|
|
152
|
+
*/
|
|
153
|
+
export const SchemaMetaSymbol = Symbol.for('@dxos/schema/SchemaMeta');
|
|
154
|
+
|
|
155
|
+
export type SchemaMeta = TypeMeta & { id: string };
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Identifies a schema as a view.
|
|
159
|
+
*/
|
|
160
|
+
export const ViewAnnotationId = Symbol.for('@dxos/schema/annotation/View');
|
|
161
|
+
export const ViewAnnotation = createAnnotationHelper<boolean>(ViewAnnotationId);
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Identifies label property or JSON path expression.
|
|
165
|
+
* Either a string or an array of strings representing field accessors each matched in priority order.
|
|
166
|
+
*/
|
|
167
|
+
export const LabelAnnotationId = Symbol.for('@dxos/schema/annotation/Label');
|
|
168
|
+
export const LabelAnnotation = createAnnotationHelper<string[]>(LabelAnnotationId);
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Identifies description property or JSON path expression.
|
|
172
|
+
* A string representing field accessor.
|
|
173
|
+
*/
|
|
174
|
+
export const DescriptionAnnotationId = Symbol.for('@dxos/schema/annotation/Description');
|
|
175
|
+
export const DescriptionAnnotation = createAnnotationHelper<string>(DescriptionAnnotationId);
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Default field to be used on referenced schema to lookup the value.
|
|
179
|
+
*/
|
|
180
|
+
export const FieldLookupAnnotationId = Symbol.for('@dxos/schema/annotation/FieldLookup');
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Generate test data.
|
|
184
|
+
*/
|
|
185
|
+
export const GeneratorAnnotationId = Symbol.for('@dxos/schema/annotation/Generator');
|
|
186
|
+
|
|
187
|
+
export type GeneratorAnnotationValue =
|
|
188
|
+
| string
|
|
189
|
+
| {
|
|
190
|
+
generator: string;
|
|
191
|
+
args?: any[];
|
|
192
|
+
probability?: number;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export const GeneratorAnnotation = createAnnotationHelper<GeneratorAnnotationValue>(GeneratorAnnotationId);
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* @returns DXN of the schema.
|
|
199
|
+
*
|
|
200
|
+
* For non-stored schema returns `dxn:type:`.
|
|
201
|
+
* For stored schema returns `dxn:echo:`.
|
|
202
|
+
* @deprecated Use `Type.getDXN`.
|
|
203
|
+
*/
|
|
204
|
+
export const getSchemaDXN = (schema: Schema.Schema.All): DXN | undefined => {
|
|
205
|
+
assertArgument(Schema.isSchema(schema), 'schema', 'invalid schema');
|
|
206
|
+
|
|
207
|
+
const id = getTypeIdentifierAnnotation(schema);
|
|
208
|
+
if (id) {
|
|
209
|
+
return DXN.parse(id);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// TODO(dmaretskyi): Add support for dynamic schema.
|
|
213
|
+
const objectAnnotation = getTypeAnnotation(schema);
|
|
214
|
+
if (!objectAnnotation) {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return DXN.fromTypenameAndVersion(objectAnnotation.typename, objectAnnotation.version);
|
|
219
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Kinds of entities stored in ECHO: objects and relations.
|
|
9
|
+
*/
|
|
10
|
+
export enum EntityKind {
|
|
11
|
+
Object = 'object',
|
|
12
|
+
Relation = 'relation',
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const EntityKindSchema = Schema.Enums(EntityKind);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { PropertyMeta } from './annotations';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export const FIELD_PATH_ANNOTATION = 'path';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Sets the path for the field.
|
|
14
|
+
* @param path Data source path in the json path format. This is the field path in the source object.
|
|
15
|
+
*/
|
|
16
|
+
// TODO(burdon): Field, vs. path vs. property.
|
|
17
|
+
export const FieldPath = (path: string) => PropertyMeta(FIELD_PATH_ANNOTATION, path);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
import { describe, expect, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { log } from '@dxos/log';
|
|
9
|
+
|
|
10
|
+
import { toJsonSchema } from '../json';
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
DateOnly,
|
|
14
|
+
DateTime,
|
|
15
|
+
type SimpleDate,
|
|
16
|
+
type SimpleDateTime,
|
|
17
|
+
type SimpleTime,
|
|
18
|
+
TimeOnly,
|
|
19
|
+
toSimpleDate,
|
|
20
|
+
toSimpleTime,
|
|
21
|
+
} from './date';
|
|
22
|
+
|
|
23
|
+
describe.skip('date', () => {
|
|
24
|
+
test('basic', () => {
|
|
25
|
+
const date = new Date('2024-12-31T23:59:59Z');
|
|
26
|
+
expect(toSimpleDate(date)).to.deep.eq({ year: 2024, month: 12, day: 31 });
|
|
27
|
+
expect(toSimpleTime(date)).to.deep.eq({ hours: 23, minutes: 59, seconds: 59 });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test('Date', ({ expect }) => {
|
|
31
|
+
const jsonSchema = toJsonSchema(DateOnly);
|
|
32
|
+
log('schema', { jsonSchema });
|
|
33
|
+
const v1: SimpleDate = { year: 1999, month: 12, day: 31 };
|
|
34
|
+
const str = Schema.encodeUnknownSync(DateOnly)(v1);
|
|
35
|
+
const v2 = Schema.decodeUnknownSync(DateOnly)(str);
|
|
36
|
+
expect(v1).to.deep.eq(v2);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('Time', ({ expect }) => {
|
|
40
|
+
const jsonSchema = toJsonSchema(TimeOnly);
|
|
41
|
+
log('schema', { jsonSchema });
|
|
42
|
+
const v1: SimpleTime = { hours: 23, minutes: 59, seconds: 59 };
|
|
43
|
+
const str = Schema.encodeUnknownSync(TimeOnly)(v1);
|
|
44
|
+
const v2 = Schema.decodeUnknownSync(TimeOnly)(str);
|
|
45
|
+
expect(v1).to.deep.eq(v2);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('DateTime', ({ expect }) => {
|
|
49
|
+
const jsonSchema = toJsonSchema(DateTime);
|
|
50
|
+
log('schema', { jsonSchema });
|
|
51
|
+
const v1: SimpleDateTime = { year: 1999, month: 12, day: 31, hours: 23, minutes: 59, seconds: 59 };
|
|
52
|
+
const str = Schema.encodeUnknownSync(DateTime)(v1);
|
|
53
|
+
const v2 = Schema.decodeUnknownSync(DateTime)(str);
|
|
54
|
+
expect(v1).to.deep.eq(v2);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
import * as SchemaAST from 'effect/SchemaAST';
|
|
7
|
+
|
|
8
|
+
import { FormatAnnotation, FormatEnum } from './types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Datetime values should be stored as ISO strings or unix numbers (ms) in UTC.
|
|
12
|
+
*
|
|
13
|
+
* NOTE: HyperFormula uses Excel's time format (null date 1900/01/01)
|
|
14
|
+
* It can be configured to use a different parser via `parseDateTime`.
|
|
15
|
+
* https://hyperformula.handsontable.com/guide/date-and-time-handling.html#date-and-time-handling
|
|
16
|
+
* https://github.com/handsontable/hyperformula/blob/master/src/DateTimeHelper.ts
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// TODO(burdon): Annotations not present in JSON.
|
|
20
|
+
// TODO(burdon): Timezone.
|
|
21
|
+
// TODO(burdon): Format for timestamp (Unix UTC or ISO 8601)?
|
|
22
|
+
// TODO(burdon): Refs
|
|
23
|
+
// - https://www.npmjs.com/package/numfmt
|
|
24
|
+
// - https://date-fns.org/docs/Getting-Started
|
|
25
|
+
// - https://github.com/date-fns/tz
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Simple date compatible with HF.
|
|
29
|
+
*/
|
|
30
|
+
export const SimpleDate = Schema.Struct({
|
|
31
|
+
year: Schema.Number.pipe(Schema.between(1900, 9999)),
|
|
32
|
+
month: Schema.Number.pipe(Schema.between(1, 12)),
|
|
33
|
+
day: Schema.Number.pipe(Schema.between(1, 31)),
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
export type SimpleDate = Schema.Schema.Type<typeof SimpleDate>;
|
|
37
|
+
|
|
38
|
+
export const toSimpleDate = (date: Date): SimpleDate => ({
|
|
39
|
+
year: date.getUTCFullYear(),
|
|
40
|
+
month: date.getUTCMonth() + 1,
|
|
41
|
+
day: date.getUTCDate(),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Simple time compatible with HF.
|
|
46
|
+
*/
|
|
47
|
+
export const SimpleTime = Schema.Struct({
|
|
48
|
+
hours: Schema.Number.pipe(Schema.between(0, 23)),
|
|
49
|
+
minutes: Schema.Number.pipe(Schema.between(0, 59)),
|
|
50
|
+
seconds: Schema.Number.pipe(Schema.between(0, 59)),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export type SimpleTime = Schema.Schema.Type<typeof SimpleTime>;
|
|
54
|
+
|
|
55
|
+
export const toSimpleTime = (date: Date): SimpleTime => ({
|
|
56
|
+
hours: date.getUTCHours(),
|
|
57
|
+
minutes: date.getUTCSeconds(),
|
|
58
|
+
seconds: date.getUTCSeconds(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Simple date-time compatible with HF.
|
|
63
|
+
*/
|
|
64
|
+
export const SimpleDateTime = Schema.extend(SimpleDate, SimpleTime);
|
|
65
|
+
|
|
66
|
+
export type SimpleDateTime = Schema.Schema.Type<typeof SimpleDateTime>;
|
|
67
|
+
|
|
68
|
+
export const toSimpleDateTime = (date: Date): SimpleDateTime => ({
|
|
69
|
+
...toSimpleDate(date),
|
|
70
|
+
...toSimpleTime(date),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* https://effect.website/docs/guides/schema/transformations#date-transformations
|
|
75
|
+
*/
|
|
76
|
+
|
|
77
|
+
// TODO(burdon): Consider if transformations should be supported with Automerge.
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Format: 2018-11-13
|
|
81
|
+
*/
|
|
82
|
+
export const DateOnly = /* Schema.transformOrFail(Schema.String, SimpleDate, {
|
|
83
|
+
strict: true,
|
|
84
|
+
decode: (str, _, ast) => {
|
|
85
|
+
if (!isValidDateFormat(str)) {
|
|
86
|
+
return ParseResult.fail(new ParseResult.Type(ast, str, 'Expected YYYY-MM-DD format'));
|
|
87
|
+
}
|
|
88
|
+
if (!isValidDate(str)) {
|
|
89
|
+
return ParseResult.fail(new ParseResult.Type(ast, str, 'Invalid date'));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const [year, month, day] = str.split('-').map(Number);
|
|
93
|
+
return ParseResult.succeed({ year, month, day });
|
|
94
|
+
},
|
|
95
|
+
encode: (date) => {
|
|
96
|
+
return ParseResult.succeed(
|
|
97
|
+
[
|
|
98
|
+
date.year.toString().padStart(4, '0'),
|
|
99
|
+
date.month.toString().padStart(2, '0'),
|
|
100
|
+
date.day.toString().padStart(2, '0'),
|
|
101
|
+
].join('-'),
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
}) */ Schema.String.pipe(
|
|
105
|
+
FormatAnnotation.set(FormatEnum.Date),
|
|
106
|
+
Schema.annotations({
|
|
107
|
+
title: 'Date',
|
|
108
|
+
description: 'Valid date in ISO format',
|
|
109
|
+
}),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Format: 20:20:39+00:00
|
|
114
|
+
*/
|
|
115
|
+
export const TimeOnly = /* Schema.transformOrFail(Schema.String, SimpleTime, {
|
|
116
|
+
strict: true,
|
|
117
|
+
decode: (str, _, ast) => {
|
|
118
|
+
if (!isValidTimeFormat(str)) {
|
|
119
|
+
return ParseResult.fail(new ParseResult.Type(ast, str, 'Expected HH:mm:ss format'));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const [hours, minutes, seconds] = str.split(':').map(Number);
|
|
123
|
+
return ParseResult.succeed({ hours, minutes, seconds });
|
|
124
|
+
},
|
|
125
|
+
encode: (time) => {
|
|
126
|
+
return ParseResult.succeed(
|
|
127
|
+
[
|
|
128
|
+
time.hours.toString().padStart(2, '0'),
|
|
129
|
+
time.minutes.toString().padStart(2, '0'),
|
|
130
|
+
time.seconds.toString().padStart(2, '0'),
|
|
131
|
+
].join(':'),
|
|
132
|
+
);
|
|
133
|
+
},
|
|
134
|
+
}) */ Schema.String.pipe(
|
|
135
|
+
FormatAnnotation.set(FormatEnum.Time),
|
|
136
|
+
Schema.annotations({
|
|
137
|
+
title: 'Time',
|
|
138
|
+
description: 'Valid time in ISO format',
|
|
139
|
+
}),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Format: 2018-11-13T20:20:39+00:00
|
|
144
|
+
*/
|
|
145
|
+
export const DateTime = /* Schema.transformOrFail(Schema.String, SimpleDateTime, {
|
|
146
|
+
strict: false,
|
|
147
|
+
decode: (str, _, ast) => {
|
|
148
|
+
const [date, time] = str.split('T');
|
|
149
|
+
if (!isValidDateFormat(date)) {
|
|
150
|
+
return ParseResult.fail(new ParseResult.Type(ast, date, 'Expected YYYY-MM-DD format'));
|
|
151
|
+
}
|
|
152
|
+
if (!isValidDate(date)) {
|
|
153
|
+
return ParseResult.fail(new ParseResult.Type(ast, date, 'Invalid date'));
|
|
154
|
+
}
|
|
155
|
+
if (!isValidTimeFormat(time)) {
|
|
156
|
+
return ParseResult.fail(new ParseResult.Type(ast, str, 'Expected HH:mm:ss format'));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const [year, month, day] = date.split('-').map(Number);
|
|
160
|
+
const [hours, minutes, seconds] = time.split(':').map(Number);
|
|
161
|
+
return ParseResult.succeed({ year, month, day, hours, minutes, seconds });
|
|
162
|
+
},
|
|
163
|
+
encode: (datetime) => {
|
|
164
|
+
return ParseResult.succeed(
|
|
165
|
+
[
|
|
166
|
+
[
|
|
167
|
+
datetime.year.toString().padStart(4, '0'),
|
|
168
|
+
datetime.month.toString().padStart(2, '0'),
|
|
169
|
+
datetime.day.toString().padStart(2, '0'),
|
|
170
|
+
].join('-'),
|
|
171
|
+
[
|
|
172
|
+
datetime.hours.toString().padStart(2, '0'),
|
|
173
|
+
datetime.minutes.toString().padStart(2, '0'),
|
|
174
|
+
datetime.seconds.toString().padStart(2, '0'),
|
|
175
|
+
].join(':'),
|
|
176
|
+
].join('T'),
|
|
177
|
+
);
|
|
178
|
+
},
|
|
179
|
+
}) */ Schema.String.pipe(
|
|
180
|
+
FormatAnnotation.set(FormatEnum.DateTime),
|
|
181
|
+
Schema.annotations({
|
|
182
|
+
title: 'DateTime',
|
|
183
|
+
description: 'Valid date and time in ISO format',
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* https://datatracker.ietf.org/doc/html/rfc3339#appendix-A
|
|
189
|
+
*/
|
|
190
|
+
// TODO(burdon): Define duration type.
|
|
191
|
+
export const Duration = Schema.String.pipe(
|
|
192
|
+
FormatAnnotation.set(FormatEnum.Duration),
|
|
193
|
+
Schema.annotations({
|
|
194
|
+
title: 'Duration',
|
|
195
|
+
description: 'Duration in ISO 8601 format',
|
|
196
|
+
[SchemaAST.ExamplesAnnotationId]: ['1h', '3D'],
|
|
197
|
+
}),
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
//
|
|
201
|
+
// Utils
|
|
202
|
+
//
|
|
203
|
+
|
|
204
|
+
// YYYY-MM-DD
|
|
205
|
+
const DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/;
|
|
206
|
+
|
|
207
|
+
const _isValidDateFormat = (str: string) => DATE_REGEX.test(str);
|
|
208
|
+
|
|
209
|
+
const _isValidDate = (str: string) => {
|
|
210
|
+
const date = new Date(str);
|
|
211
|
+
return !isNaN(date.getTime()) && date.toISOString().startsWith(str);
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// HH:mm:ss
|
|
215
|
+
const TIME_REGEX = /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)$/;
|
|
216
|
+
|
|
217
|
+
const _isValidTimeFormat = (str: string) => TIME_REGEX.test(str);
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
import { describe, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { toJsonSchema } from '../json';
|
|
9
|
+
|
|
10
|
+
import { Format } from './format';
|
|
11
|
+
import { FormatEnum, TypeEnum, getTypeEnum } from './types';
|
|
12
|
+
|
|
13
|
+
describe('formats', () => {
|
|
14
|
+
test('annotations', ({ expect }) => {
|
|
15
|
+
const TestSchema = Schema.Struct({
|
|
16
|
+
name: Schema.String,
|
|
17
|
+
email: Schema.optional(Format.Email),
|
|
18
|
+
salary: Schema.optional(Format.Currency({ decimals: 2, code: 'usd' })),
|
|
19
|
+
website: Schema.optional(Format.URL),
|
|
20
|
+
birthday: Schema.optional(Format.Date),
|
|
21
|
+
started: Schema.optional(Format.DateTime),
|
|
22
|
+
active: Schema.optional(Schema.Boolean),
|
|
23
|
+
}).pipe(Schema.mutable);
|
|
24
|
+
|
|
25
|
+
type TestType = Schema.Schema.Type<typeof TestSchema>;
|
|
26
|
+
|
|
27
|
+
const jsonSchema = toJsonSchema(TestSchema);
|
|
28
|
+
|
|
29
|
+
const data: TestType = {
|
|
30
|
+
name: 'Alice',
|
|
31
|
+
email: 'alice@example.com',
|
|
32
|
+
birthday: '1999-06-11',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const validate = Schema.validateSync(TestSchema);
|
|
36
|
+
validate(data);
|
|
37
|
+
|
|
38
|
+
{
|
|
39
|
+
const prop = jsonSchema.properties!['active' as const];
|
|
40
|
+
expect(getTypeEnum(prop)).to.eq(TypeEnum.Boolean);
|
|
41
|
+
expect(prop).includes({
|
|
42
|
+
type: TypeEnum.Boolean,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
{
|
|
47
|
+
const prop = jsonSchema.properties!['email' as const];
|
|
48
|
+
expect(getTypeEnum(prop)).to.eq(TypeEnum.String);
|
|
49
|
+
expect(prop).includes({
|
|
50
|
+
type: TypeEnum.String,
|
|
51
|
+
format: FormatEnum.Email,
|
|
52
|
+
title: 'Email',
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
{
|
|
57
|
+
const prop = jsonSchema.properties!['salary' as const];
|
|
58
|
+
expect(getTypeEnum(prop)).to.eq(TypeEnum.Number);
|
|
59
|
+
expect(prop).includes({
|
|
60
|
+
type: TypeEnum.Number,
|
|
61
|
+
format: FormatEnum.Currency,
|
|
62
|
+
title: 'Currency',
|
|
63
|
+
multipleOf: 0.01,
|
|
64
|
+
currency: 'USD',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
{
|
|
69
|
+
const prop = jsonSchema.properties!['birthday' as const];
|
|
70
|
+
expect(getTypeEnum(prop)).to.eq(TypeEnum.String);
|
|
71
|
+
expect(prop).includes({
|
|
72
|
+
type: TypeEnum.String,
|
|
73
|
+
format: FormatEnum.Date,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2024 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Schema from 'effect/Schema';
|
|
6
|
+
|
|
7
|
+
import * as Keys from '@dxos/keys';
|
|
8
|
+
|
|
9
|
+
import * as DateUtil from './date';
|
|
10
|
+
import * as NumberUtil from './number';
|
|
11
|
+
import * as ObjectUtil from './object';
|
|
12
|
+
import * as StringUtil from './string';
|
|
13
|
+
|
|
14
|
+
// TODO(burdon): Consider factoring out to separate `@dxos/json-schema`
|
|
15
|
+
// TODO(burdon): Media encoding.
|
|
16
|
+
// - https://json-schema.org/understanding-json-schema/reference/non_json_data
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Formats.
|
|
20
|
+
* https://json-schema.org/understanding-json-schema/reference/string#built-in-formats
|
|
21
|
+
* NOTE: A JSON Schema validator will ignore any format type that it does not understand.
|
|
22
|
+
*/
|
|
23
|
+
// TODO(burdon): Add fields for `examples`, `message`, etc.
|
|
24
|
+
export namespace Format {
|
|
25
|
+
// Strings
|
|
26
|
+
export const DXN = Keys.DXN.Schema;
|
|
27
|
+
export const Email = StringUtil.Email;
|
|
28
|
+
export const Formula = StringUtil.Formula;
|
|
29
|
+
export const Hostname = StringUtil.Hostname;
|
|
30
|
+
export const JSON = StringUtil.JSON;
|
|
31
|
+
export const Markdown = StringUtil.Markdown;
|
|
32
|
+
export const Regex = StringUtil.Regex;
|
|
33
|
+
export const URL = StringUtil.URL;
|
|
34
|
+
export const UUID = Schema.UUID;
|
|
35
|
+
|
|
36
|
+
// Numbers
|
|
37
|
+
// TODO(burdon): BigInt.
|
|
38
|
+
export const Currency = NumberUtil.Currency;
|
|
39
|
+
export const Integer = NumberUtil.Integer;
|
|
40
|
+
export const Percent = NumberUtil.Percent;
|
|
41
|
+
export const Timestamp = NumberUtil.Timestamp;
|
|
42
|
+
|
|
43
|
+
// Dates and times
|
|
44
|
+
export const DateTime = DateUtil.DateTime;
|
|
45
|
+
export const Date = DateUtil.DateOnly;
|
|
46
|
+
export const Time = DateUtil.TimeOnly;
|
|
47
|
+
export const Duration = DateUtil.Duration;
|
|
48
|
+
|
|
49
|
+
// Objects
|
|
50
|
+
export const GeoPoint = ObjectUtil.GeoPoint;
|
|
51
|
+
export type GeoPoint = ObjectUtil.GeoPoint;
|
|
52
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
export type { SimpleDate, SimpleDateTime, SimpleTime } from './date';
|
|
6
|
+
export { CurrencyAnnotationId, DecimalPrecision, Currency } from './number';
|
|
7
|
+
export { GeoPoint, GeoLocation } from './object';
|
|
8
|
+
|
|
9
|
+
export * from './format';
|
|
10
|
+
export * from './select';
|
|
11
|
+
export * from './string';
|
|
12
|
+
export * from './types';
|