@astrale-os/kernel-dsl 1.0.0-beta.2 → 1.0.0-beta.20
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/v1/addressing/class-key.js +3 -3
- package/dist/v1/addressing/definition-ref.js +21 -37
- package/dist/v1/addressing/domain-dependency.d.ts +0 -6
- package/dist/v1/addressing/domain-dependency.js +1 -6
- package/dist/v1/addressing/key.d.ts +4 -1
- package/dist/v1/addressing/key.js +23 -3
- package/dist/v1/addressing/ordering.d.ts +2 -0
- package/dist/v1/addressing/ordering.js +4 -0
- package/dist/v1/addressing/patterns.d.ts +1 -1
- package/dist/v1/addressing/patterns.js +2 -6
- package/dist/v1/builder/callable/authoring.d.ts +11 -5
- package/dist/v1/builder/callable/authoring.js +2 -0
- package/dist/v1/builder/class/authoring.d.ts +94 -224
- package/dist/v1/builder/class/authoring.js +31 -33
- package/dist/v1/builder/class/handle.d.ts +27 -0
- package/dist/v1/builder/class/handle.js +1 -0
- package/dist/v1/builder/class/input.d.ts +159 -0
- package/dist/v1/builder/class/projection.d.ts +129 -0
- package/dist/v1/builder/class/projection.js +1 -0
- package/dist/v1/builder/class/property.d.ts +3 -9
- package/dist/v1/builder/class/property.js +1 -0
- package/dist/v1/builder/core/authoring.d.ts +1 -1
- package/dist/v1/builder/index.d.ts +5 -2
- package/dist/v1/builder/lowering/callable.js +1 -0
- package/dist/v1/builder/lowering/class.d.ts +9 -1
- package/dist/v1/builder/lowering/class.js +62 -17
- package/dist/v1/builder/lowering/policy.js +2 -0
- package/dist/v1/builder/lowering/property-keys.d.ts +27 -0
- package/dist/v1/builder/lowering/property-keys.js +108 -0
- package/dist/v1/builder/lowering/schema/compile.js +11 -6
- package/dist/v1/builder/lowering/schema/context.d.ts +9 -1
- package/dist/v1/builder/lowering/schema/context.js +36 -4
- package/dist/v1/builder/lowering/schema/lifetime.d.ts +11 -0
- package/dist/v1/builder/lowering/schema/lifetime.js +55 -0
- package/dist/v1/builder/lowering/value/path.d.ts +1 -1
- package/dist/v1/builder/lowering/value/path.js +2 -4
- package/dist/v1/builder/lowering/value/zod.js +1 -3
- package/dist/v1/builder/policy/authoring.d.ts +85 -5
- package/dist/v1/builder/policy/authoring.js +107 -68
- package/dist/v1/builder/schema/define.d.ts +0 -1
- package/dist/v1/builder/schema/diagnostic.d.ts +3 -2
- package/dist/v1/builder/schema/diagnostic.js +5 -10
- package/dist/v1/builder/schema/input.d.ts +1 -1
- package/dist/v1/builder/schema/projection/callable.d.ts +7 -5
- package/dist/v1/builder/schema/projection/class.d.ts +118 -15
- package/dist/v1/builder/schema/projection/core-input.d.ts +2 -1
- package/dist/v1/builder/schema/projection/identity.d.ts +2 -1
- package/dist/v1/builder/schema/projection/model.d.ts +4 -2
- package/dist/v1/builder/schema/projection/property.d.ts +1 -1
- package/dist/v1/builder/schema/projection/resolved-class.d.ts +2 -1
- package/dist/v1/builder/schema/projection.d.ts +1 -1
- package/dist/v1/builder/value/full-match.d.ts +0 -1
- package/dist/v1/builder/value/full-match.js +0 -1
- package/dist/v1/builder/value/path-metadata.d.ts +1 -1
- package/dist/v1/builder/value/path.d.ts +1 -1
- package/dist/v1/bundle/codec.js +11 -13
- package/dist/v1/bundle/diagnostic.d.ts +2 -0
- package/dist/v1/bundle/diagnostic.js +9 -7
- package/dist/v1/bundle/model.js +1 -1
- package/dist/v1/bundle/structure.js +4 -6
- package/dist/v1/compiled/admission/accept.js +4 -10
- package/dist/v1/compiled/admission/closure.js +2 -2
- package/dist/v1/compiled/admission/diagnostic.d.ts +1 -1
- package/dist/v1/compiled/admission/diagnostic.js +1 -3
- package/dist/v1/compiled/admission/revision.d.ts +9 -1
- package/dist/v1/compiled/admission/revision.js +6 -3
- package/dist/v1/compiled/admission/semantics/class.d.ts +4 -1
- package/dist/v1/compiled/admission/semantics/class.js +37 -12
- package/dist/v1/compiled/admission/semantics/core.d.ts +1 -24
- package/dist/v1/compiled/admission/semantics/core.js +56 -24
- package/dist/v1/compiled/admission/semantics/edge.d.ts +2 -4
- package/dist/v1/compiled/admission/semantics/edge.js +6 -65
- package/dist/v1/compiled/admission/semantics/policy/callable.js +0 -2
- package/dist/v1/compiled/admission/semantics/policy/contracts.d.ts +5 -20
- package/dist/v1/compiled/admission/semantics/policy/contracts.js +8 -21
- package/dist/v1/compiled/admission/semantics/policy/parity.js +40 -43
- package/dist/v1/compiled/admission/semantics/policy/pattern.d.ts +4 -1
- package/dist/v1/compiled/admission/semantics/policy/pattern.js +4 -16
- package/dist/v1/compiled/admission/semantics/policy/typing.d.ts +2 -24
- package/dist/v1/compiled/admission/semantics/policy/typing.js +19 -201
- package/dist/v1/compiled/admission/wire/callable.js +11 -1
- package/dist/v1/compiled/admission/wire/class.js +73 -21
- package/dist/v1/compiled/admission/wire/policy.d.ts +4 -3
- package/dist/v1/compiled/admission/wire/policy.js +8 -7
- package/dist/v1/compiled/compiler/compile.js +8 -8
- package/dist/v1/compiled/diagnostic.js +2 -3
- package/dist/v1/compiled/loader/load.d.ts +3 -2
- package/dist/v1/compiled/loader/load.js +11 -45
- package/dist/v1/domain/create.js +5 -6
- package/dist/v1/domain/model/domain.d.ts +2 -7
- package/dist/v1/domain/model/input.d.ts +2 -1
- package/dist/v1/domain/namespace/member-collection.js +4 -6
- package/dist/v1/domain/projection/classes.js +4 -1
- package/dist/v1/domain/validation/create.d.ts +6 -3
- package/dist/v1/domain/validation/create.js +21 -69
- package/dist/v1/index.d.ts +2 -1
- package/dist/v1/language/.spec/schemas/draft-2020-12.schema.json +462 -0
- package/dist/v1/language/.spec/schemas/value-schema.schema.json +22 -0
- package/dist/v1/language/callable/admission.d.ts +15 -0
- package/dist/v1/language/callable/admission.js +43 -0
- package/dist/v1/language/callable/model/callable.d.ts +10 -0
- package/dist/v1/language/callable/resolved.d.ts +16 -3
- package/dist/v1/language/class/inheritance.d.ts +36 -5
- package/dist/v1/language/class/inheritance.js +87 -1
- package/dist/v1/language/class/lifetime.d.ts +50 -11
- package/dist/v1/language/class/lifetime.js +89 -20
- package/dist/v1/language/class/model/class.d.ts +45 -9
- package/dist/v1/language/class/model/edge.d.ts +31 -2
- package/dist/v1/language/class/model/property.d.ts +16 -7
- package/dist/v1/language/class/property-values.js +2 -4
- package/dist/v1/language/class/unique.d.ts +143 -0
- package/dist/v1/language/class/unique.js +264 -0
- package/dist/v1/language/core/facts.d.ts +85 -0
- package/dist/v1/language/core/facts.js +116 -0
- package/dist/v1/language/definition/metadata.d.ts +5 -0
- package/dist/v1/language/definition/metadata.js +7 -0
- package/dist/v1/language/freeze.d.ts +5 -0
- package/dist/v1/{schema/canonical → language}/freeze.js +4 -0
- package/dist/v1/language/index.d.ts +7 -2
- package/dist/v1/language/index.js +5 -1
- package/dist/v1/language/limits.d.ts +64 -0
- package/dist/v1/language/limits.js +60 -0
- package/dist/v1/language/policy/analysis/branches.d.ts +8 -0
- package/dist/v1/language/policy/analysis/branches.js +2 -1
- package/dist/v1/language/policy/analysis/budgets.d.ts +45 -12
- package/dist/v1/language/policy/analysis/budgets.js +133 -19
- package/dist/v1/language/policy/analysis/checks.js +3 -2
- package/dist/v1/language/policy/analysis/typing.d.ts +51 -0
- package/dist/v1/language/policy/analysis/typing.js +455 -0
- package/dist/v1/language/policy/model/policy.d.ts +49 -9
- package/dist/v1/language/policy/model/policy.js +14 -0
- package/dist/v1/language/policy/normalization/normalize.d.ts +6 -6
- package/dist/v1/language/policy/normalization/normalize.js +22 -21
- package/dist/v1/language/validation/diagnostic.d.ts +15 -1
- package/dist/v1/language/validation/diagnostic.js +21 -0
- package/dist/v1/language/validation/rule.d.ts +45 -0
- package/dist/v1/language/validation/rule.js +109 -0
- package/dist/v1/language/value/compile/fields.js +4 -8
- package/dist/v1/language/value/evaluate/program.d.ts +2 -1
- package/dist/v1/language/value/evaluate/program.js +2 -2
- package/dist/v1/language/value/evaluate/validate.d.ts +2 -2
- package/dist/v1/language/value/evaluate/validate.js +6 -2
- package/dist/v1/language/value/evaluate/validity.optimization.d.ts +14 -0
- package/dist/v1/language/value/evaluate/validity.optimization.js +342 -0
- package/dist/v1/language/value/inclusion/arrays.d.ts +3 -0
- package/dist/v1/language/value/inclusion/arrays.js +85 -0
- package/dist/v1/language/value/inclusion/assertions.d.ts +18 -0
- package/dist/v1/language/value/inclusion/assertions.js +29 -0
- package/dist/v1/language/value/inclusion/combinators.d.ts +3 -0
- package/dist/v1/language/value/inclusion/combinators.js +67 -0
- package/dist/v1/language/value/inclusion/equivalence.d.ts +4 -0
- package/dist/v1/language/value/inclusion/equivalence.js +74 -0
- package/dist/v1/language/value/inclusion/evidence.d.ts +34 -0
- package/dist/v1/language/value/inclusion/evidence.js +138 -0
- package/dist/v1/language/value/inclusion/includes.d.ts +17 -0
- package/dist/v1/language/value/inclusion/includes.js +25 -719
- package/dist/v1/language/value/inclusion/keywords.d.ts +5 -0
- package/dist/v1/language/value/inclusion/keywords.js +31 -0
- package/dist/v1/language/value/inclusion/kinds.d.ts +17 -0
- package/dist/v1/language/value/inclusion/kinds.js +57 -0
- package/dist/v1/language/value/inclusion/location.d.ts +24 -0
- package/dist/v1/language/value/inclusion/location.js +63 -0
- package/dist/v1/language/value/inclusion/numbers.d.ts +3 -0
- package/dist/v1/language/value/inclusion/numbers.js +41 -0
- package/dist/v1/language/value/inclusion/objects.d.ts +3 -0
- package/dist/v1/language/value/inclusion/objects.js +190 -0
- package/dist/v1/language/value/inclusion/strings.d.ts +3 -0
- package/dist/v1/language/value/inclusion/strings.js +29 -0
- package/dist/v1/language/value/inclusion/types.d.ts +3 -0
- package/dist/v1/language/value/inclusion/types.js +11 -0
- package/dist/v1/language/value/ordering.d.ts +14 -0
- package/dist/v1/language/value/ordering.js +86 -9
- package/dist/v1/language/value/profile/canonicalize.js +6 -5
- package/dist/v1/language/value/profile/json.validate.d.ts +3 -1
- package/dist/v1/language/value/profile/json.validate.js +3 -2
- package/dist/v1/language/value/profile/meta-schema.compiled.js +4 -1
- package/dist/v1/language/value/profile/rules.js +17 -43
- package/dist/v1/language/value/profile/validate.js +1 -10
- package/dist/v1/language/value/profile/vocabulary.d.ts +35 -2
- package/dist/v1/language/value/profile/vocabulary.js +52 -5
- package/dist/v1/language/value/reference/closure.js +3 -4
- package/dist/v1/language/value/reference/recursion.js +2 -22
- package/dist/v1/language/value/reference/registry.index.d.ts +2 -0
- package/dist/v1/language/value/reference/registry.index.js +33 -16
- package/dist/v1/language/value/regexp/ast.js +6 -7
- package/dist/v1/language/value/regexp/compile.d.ts +5 -11
- package/dist/v1/language/value/regexp/compile.js +14 -12
- package/dist/v1/language/value/regexp/syntax.js +3 -2
- package/dist/v1/language/value/syntax/keywords.d.ts +3 -3
- package/dist/v1/language/value/syntax/keywords.js +4 -20
- package/dist/v1/language/value/syntax/node-path/validate.js +2 -3
- package/dist/v1/language/value/syntax/pointer.d.ts +2 -0
- package/dist/v1/language/value/syntax/pointer.js +5 -2
- package/dist/v1/language/value/syntax/walk.d.ts +2 -0
- package/dist/v1/language/value/syntax/walk.js +19 -20
- package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +2519 -0
- package/dist/v1/schema/admission/accept.js +3 -14
- package/dist/v1/schema/admission/callable.d.ts +21 -0
- package/dist/v1/schema/admission/callable.js +96 -0
- package/dist/v1/schema/analysis/class.js +33 -15
- package/dist/v1/schema/canonical/revision.js +4 -4
- package/dist/v1/schema/compatibility/api.d.ts +2 -1
- package/dist/v1/schema/compatibility/api.js +1 -0
- package/dist/v1/schema/compatibility/comparators/callables.js +1 -1
- package/dist/v1/schema/compatibility/comparators/classes.d.ts +9 -4
- package/dist/v1/schema/compatibility/comparators/classes.js +29 -8
- package/dist/v1/schema/compatibility/comparators/index.d.ts +1 -1
- package/dist/v1/schema/compatibility/comparators/members.js +1 -1
- package/dist/v1/schema/compatibility/comparators/values.js +2 -2
- package/dist/v1/schema/compatibility/coordinate.js +3 -3
- package/dist/v1/schema/compatibility/diff.js +2 -4
- package/dist/v1/schema/compatibility/extension.d.ts +11 -0
- package/dist/v1/schema/compatibility/extension.js +22 -0
- package/dist/v1/schema/compatibility/fields.d.ts +20 -4
- package/dist/v1/schema/compatibility/fields.js +13 -3
- package/dist/v1/schema/compatibility/meaning/footprint.js +16 -50
- package/dist/v1/schema/compatibility/model.d.ts +17 -5
- package/dist/v1/schema/compatibility/report.js +1 -1
- package/dist/v1/schema/compatibility/scope.d.ts +2 -0
- package/dist/v1/schema/compatibility/scope.js +4 -1
- package/dist/v1/schema/compatibility/structure/index.js +6 -4
- package/dist/v1/schema/compatibility/structure/requirements.js +57 -58
- package/dist/v1/schema/compatibility/versioning/index.js +1 -1
- package/dist/v1/schema/compatibility/versioning/surface.d.ts +1 -1
- package/dist/v1/schema/compatibility/versioning/surface.js +30 -2
- package/dist/v1/schema/compatibility/walk.d.ts +17 -0
- package/dist/v1/schema/compatibility/walk.js +41 -0
- package/dist/v1/schema/diagnostic.d.ts +17 -3
- package/dist/v1/schema/diagnostic.js +14 -11
- package/dist/v1/schema/index.d.ts +10 -1
- package/dist/v1/schema/index.js +4 -0
- package/dist/v1/schema/model/schema.d.ts +20 -1
- package/dist/v1/schema/model/schema.js +1 -1
- package/dist/v1/schema/normalization/domain.d.ts +2 -0
- package/dist/v1/schema/normalization/domain.js +29 -9
- package/dist/v1/schema/resolution/closure.js +2 -1
- package/dist/v1/schema/resolution/context.js +13 -13
- package/dist/v1/schema/resolution/domain.d.ts +5 -0
- package/dist/v1/schema/resolution/domain.js +2 -2
- package/dist/v1/schema/resolution/origins.js +3 -2
- package/dist/v1/schema/rule.d.ts +26 -0
- package/dist/v1/schema/rule.js +440 -0
- package/dist/v1/schema/validation/callable.d.ts +35 -2
- package/dist/v1/schema/validation/callable.js +65 -9
- package/dist/v1/schema/validation/class.d.ts +6 -2
- package/dist/v1/schema/validation/class.js +127 -104
- package/dist/v1/schema/validation/core.js +90 -168
- package/dist/v1/schema/validation/domain.js +4 -13
- package/dist/v1/schema/validation/domain.json-schema.d.ts +15 -0
- package/dist/v1/schema/validation/domain.json-schema.js +197 -0
- package/dist/v1/schema/validation/domain.route.d.ts +6 -1
- package/dist/v1/schema/validation/domain.route.js +18 -0
- package/dist/v1/schema/validation/domain.structure.d.ts +7780 -0
- package/dist/v1/schema/validation/domain.structure.js +398 -147
- package/dist/v1/schema/validation/json.json-schema.d.ts +40 -0
- package/dist/v1/schema/validation/json.json-schema.js +46 -0
- package/dist/v1/schema/validation/policy/budgets.d.ts +33 -2
- package/dist/v1/schema/validation/policy/budgets.js +87 -121
- package/dist/v1/schema/validation/policy/context.d.ts +6 -5
- package/dist/v1/schema/validation/policy/context.js +7 -10
- package/dist/v1/schema/validation/policy/objects.d.ts +1 -1
- package/dist/v1/schema/validation/policy/objects.js +6 -14
- package/dist/v1/schema/validation/policy/references.js +11 -27
- package/dist/v1/schema/validation/policy/typing.d.ts +3 -18
- package/dist/v1/schema/validation/policy/typing.js +22 -356
- package/dist/v1/schema/validation/policy/uses.d.ts +40 -3
- package/dist/v1/schema/validation/policy/uses.js +66 -89
- package/dist/v1/schema/validation/policy/validate.js +5 -5
- package/dist/v1/schema/validation/value-api.d.ts +22 -0
- package/dist/v1/schema/validation/value-api.js +34 -1
- package/dist/v1/schema/validation/value.d.ts +3 -0
- package/dist/v1/schema/validation/value.js +9 -17
- package/dist/v1/schema/validation/value.json-schema.d.ts +49 -0
- package/dist/v1/schema/validation/value.json-schema.js +137 -0
- package/dist/v1/schema/validation/view.js +4 -8
- package/dist/value/canonical.d.ts +6 -0
- package/dist/value/canonical.js +36 -0
- package/dist/value/decode.d.ts +7 -0
- package/dist/value/decode.js +159 -9
- package/dist/value/index.d.ts +1 -1
- package/dist/value/json.d.ts +2 -2
- package/dist/value/json.js +2 -2
- package/dist/value/types.d.ts +6 -0
- package/dist/value/unicode.d.ts +2 -1
- package/dist/value/unicode.js +2 -0
- package/package.json +8 -1
- package/dist/v1/builder/lowering/core-properties.d.ts +0 -10
- package/dist/v1/builder/lowering/core-properties.js +0 -76
- package/dist/v1/compiled/admission/semantics/cardinality.d.ts +0 -5
- package/dist/v1/compiled/admission/semantics/cardinality.js +0 -113
- package/dist/v1/compiled/admission/semantics/policy/equality.d.ts +0 -8
- package/dist/v1/compiled/admission/semantics/policy/equality.js +0 -27
- package/dist/v1/compiled/admission/semantics/policy/measure.d.ts +0 -9
- package/dist/v1/compiled/admission/semantics/policy/measure.js +0 -81
- package/dist/v1/compiled/admission/semantics/policy/variants.d.ts +0 -10
- package/dist/v1/compiled/admission/semantics/policy/variants.js +0 -65
- package/dist/v1/language/class/index.d.ts +0 -8
- package/dist/v1/language/class/index.js +0 -1
- package/dist/v1/language/core/index.d.ts +0 -2
- package/dist/v1/language/value/index.d.ts +0 -11
- package/dist/v1/language/value/index.js +0 -8
- package/dist/v1/schema/canonical/domain.d.ts +0 -3
- package/dist/v1/schema/canonical/domain.digest.d.ts +0 -3
- package/dist/v1/schema/canonical/domain.digest.js +0 -5
- package/dist/v1/schema/canonical/domain.js +0 -5
- package/dist/v1/schema/canonical/freeze.d.ts +0 -1
- /package/dist/v1/{language/core/index.js → builder/class/input.js} +0 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** Keywords the evaluator never asserts. */
|
|
2
|
+
export const INERT = new Set([
|
|
3
|
+
'$anchor',
|
|
4
|
+
'$comment',
|
|
5
|
+
'$defs',
|
|
6
|
+
'$id',
|
|
7
|
+
'$schema',
|
|
8
|
+
'contentEncoding',
|
|
9
|
+
'contentMediaType',
|
|
10
|
+
'contentSchema',
|
|
11
|
+
'default',
|
|
12
|
+
'deprecated',
|
|
13
|
+
'description',
|
|
14
|
+
'examples',
|
|
15
|
+
'format',
|
|
16
|
+
'readOnly',
|
|
17
|
+
'title',
|
|
18
|
+
'writeOnly',
|
|
19
|
+
'x-astrale-path',
|
|
20
|
+
]);
|
|
21
|
+
/** Whether a schema asserts nothing. */
|
|
22
|
+
export function isTop(schema) {
|
|
23
|
+
if (schema === true)
|
|
24
|
+
return true;
|
|
25
|
+
if (schema === false)
|
|
26
|
+
return false;
|
|
27
|
+
return Object.keys(schema).every((keyword) => INERT.has(keyword) ||
|
|
28
|
+
((keyword === 'then' || keyword === 'else') && !Object.hasOwn(schema, 'if')) ||
|
|
29
|
+
((keyword === 'minContains' || keyword === 'maxContains') &&
|
|
30
|
+
!Object.hasOwn(schema, 'contains')));
|
|
31
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
2
|
+
/** Bit masks over the JSON kinds a schema may admit; integers and fractions split `number`. */
|
|
3
|
+
export declare const NULL = 1;
|
|
4
|
+
export declare const BOOLEAN = 2;
|
|
5
|
+
export declare const OBJECT = 4;
|
|
6
|
+
export declare const ARRAY = 8;
|
|
7
|
+
export declare const STRING = 16;
|
|
8
|
+
export declare const INTEGER = 32;
|
|
9
|
+
export declare const FRACTION = 64;
|
|
10
|
+
export declare const NUMBER: number;
|
|
11
|
+
export declare const ANY: number;
|
|
12
|
+
/** The kinds a `type` keyword value admits; an absent `type` admits every kind. */
|
|
13
|
+
export declare function typeMask(type: JsonValue | undefined): number;
|
|
14
|
+
/** The single kind of one JSON value. */
|
|
15
|
+
export declare function kindOf(value: JsonValue): number;
|
|
16
|
+
/** The finite value set of a kind mask made only of null and booleans. */
|
|
17
|
+
export declare function finiteKinds(kinds: number): readonly JsonValue[] | undefined;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** Bit masks over the JSON kinds a schema may admit; integers and fractions split `number`. */
|
|
2
|
+
export const NULL = 1;
|
|
3
|
+
export const BOOLEAN = 2;
|
|
4
|
+
export const OBJECT = 4;
|
|
5
|
+
export const ARRAY = 8;
|
|
6
|
+
export const STRING = 16;
|
|
7
|
+
export const INTEGER = 32;
|
|
8
|
+
export const FRACTION = 64;
|
|
9
|
+
export const NUMBER = INTEGER | FRACTION;
|
|
10
|
+
export const ANY = NULL | BOOLEAN | OBJECT | ARRAY | STRING | NUMBER;
|
|
11
|
+
/** The kinds a `type` keyword value admits; an absent `type` admits every kind. */
|
|
12
|
+
export function typeMask(type) {
|
|
13
|
+
if (type === undefined)
|
|
14
|
+
return ANY;
|
|
15
|
+
const names = typeof type === 'string' ? [type] : Array.isArray(type) ? type : [];
|
|
16
|
+
return names.reduce((mask, name) => {
|
|
17
|
+
switch (name) {
|
|
18
|
+
case 'null':
|
|
19
|
+
return mask | NULL;
|
|
20
|
+
case 'boolean':
|
|
21
|
+
return mask | BOOLEAN;
|
|
22
|
+
case 'object':
|
|
23
|
+
return mask | OBJECT;
|
|
24
|
+
case 'array':
|
|
25
|
+
return mask | ARRAY;
|
|
26
|
+
case 'string':
|
|
27
|
+
return mask | STRING;
|
|
28
|
+
case 'integer':
|
|
29
|
+
return mask | INTEGER;
|
|
30
|
+
case 'number':
|
|
31
|
+
return mask | NUMBER;
|
|
32
|
+
default:
|
|
33
|
+
return mask;
|
|
34
|
+
}
|
|
35
|
+
}, 0);
|
|
36
|
+
}
|
|
37
|
+
/** The single kind of one JSON value. */
|
|
38
|
+
export function kindOf(value) {
|
|
39
|
+
if (value === null)
|
|
40
|
+
return NULL;
|
|
41
|
+
if (typeof value === 'boolean')
|
|
42
|
+
return BOOLEAN;
|
|
43
|
+
if (typeof value === 'string')
|
|
44
|
+
return STRING;
|
|
45
|
+
if (typeof value === 'number')
|
|
46
|
+
return Number.isInteger(value) ? INTEGER : FRACTION;
|
|
47
|
+
return Array.isArray(value) ? ARRAY : OBJECT;
|
|
48
|
+
}
|
|
49
|
+
/** The finite value set of a kind mask made only of null and booleans. */
|
|
50
|
+
export function finiteKinds(kinds) {
|
|
51
|
+
if ((kinds & ~(NULL | BOOLEAN)) !== 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
return [
|
|
54
|
+
...((kinds & NULL) !== 0 ? [null] : []),
|
|
55
|
+
...((kinds & BOOLEAN) !== 0 ? [true, false] : []),
|
|
56
|
+
];
|
|
57
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
2
|
+
import type { ValueProgram } from '../compile/model.js';
|
|
3
|
+
import type { JsonSchema } from '../model/schema.js';
|
|
4
|
+
/** One schema location inside one admitted program. */
|
|
5
|
+
export interface At {
|
|
6
|
+
readonly program: ValueProgram;
|
|
7
|
+
readonly index: number;
|
|
8
|
+
}
|
|
9
|
+
/** The location `index` of `program`. */
|
|
10
|
+
export declare function at(program: ValueProgram, index: number): At;
|
|
11
|
+
/** The schema written at a location. */
|
|
12
|
+
export declare function schemaOf(location: At): JsonSchema;
|
|
13
|
+
/** The subschema under `keyword`, or under its `member`, when the program compiled one. */
|
|
14
|
+
export declare function childOf(location: At, keyword: string, member?: string | number): At | undefined;
|
|
15
|
+
/** The subschemas of an array-valued `keyword`, in order. */
|
|
16
|
+
export declare function childrenOf(location: At, keyword: string): readonly At[];
|
|
17
|
+
/** The resolved `$ref` target of a location. */
|
|
18
|
+
export declare function referenceOf(location: At): At | undefined;
|
|
19
|
+
/** The location with its `$ref` targets and `allOf` branches; admission makes this acyclic. */
|
|
20
|
+
export declare function conjunctsOf(location: At): readonly At[];
|
|
21
|
+
/** Whether `name` matches `pattern` under the program's regular-expression dialect. */
|
|
22
|
+
export declare function matches(location: At, pattern: string, name: string): boolean;
|
|
23
|
+
/** Whether the V1 evaluator accepts `value` at a location. */
|
|
24
|
+
export declare function accepts(location: At, value: JsonValue): boolean;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { loadValueProgram } from '../compile/load.js';
|
|
2
|
+
import { evaluateInstance } from '../evaluate/evaluator.js';
|
|
3
|
+
import { isValueObject as isJsonObject } from '../model/object.js';
|
|
4
|
+
import { appendPointer } from '../syntax/pointer.js';
|
|
5
|
+
/** The location `index` of `program`. */
|
|
6
|
+
export function at(program, index) {
|
|
7
|
+
return { program, index };
|
|
8
|
+
}
|
|
9
|
+
/** The schema written at a location. */
|
|
10
|
+
export function schemaOf(location) {
|
|
11
|
+
const node = location.program.nodes[location.index];
|
|
12
|
+
if (node === undefined)
|
|
13
|
+
throw new TypeError('Value program location is absent.');
|
|
14
|
+
return node.schema;
|
|
15
|
+
}
|
|
16
|
+
/** The subschema under `keyword`, or under its `member`, when the program compiled one. */
|
|
17
|
+
export function childOf(location, keyword, member) {
|
|
18
|
+
const base = appendPointer('', keyword);
|
|
19
|
+
const key = (member === undefined ? base : appendPointer(base, member)).slice(1);
|
|
20
|
+
const index = location.program.nodes[location.index]?.children[key];
|
|
21
|
+
return index === undefined ? undefined : at(location.program, index);
|
|
22
|
+
}
|
|
23
|
+
/** The subschemas of an array-valued `keyword`, in order. */
|
|
24
|
+
export function childrenOf(location, keyword) {
|
|
25
|
+
const schema = schemaOf(location);
|
|
26
|
+
if (!isJsonObject(schema) || !Array.isArray(schema[keyword]))
|
|
27
|
+
return [];
|
|
28
|
+
return schema[keyword].flatMap((_, index) => {
|
|
29
|
+
const child = childOf(location, keyword, index);
|
|
30
|
+
return child === undefined ? [] : [child];
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/** The resolved `$ref` target of a location. */
|
|
34
|
+
export function referenceOf(location) {
|
|
35
|
+
const index = location.program.nodes[location.index]?.reference;
|
|
36
|
+
return index === undefined ? undefined : at(location.program, index);
|
|
37
|
+
}
|
|
38
|
+
/** The location with its `$ref` targets and `allOf` branches; admission makes this acyclic. */
|
|
39
|
+
export function conjunctsOf(location) {
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
const conjuncts = [];
|
|
42
|
+
const visit = (current) => {
|
|
43
|
+
if (seen.has(current.index))
|
|
44
|
+
return;
|
|
45
|
+
seen.add(current.index);
|
|
46
|
+
conjuncts.push(current);
|
|
47
|
+
const reference = referenceOf(current);
|
|
48
|
+
if (reference !== undefined)
|
|
49
|
+
visit(reference);
|
|
50
|
+
for (const branch of childrenOf(current, 'allOf'))
|
|
51
|
+
visit(branch);
|
|
52
|
+
};
|
|
53
|
+
visit(location);
|
|
54
|
+
return conjuncts;
|
|
55
|
+
}
|
|
56
|
+
/** Whether `name` matches `pattern` under the program's regular-expression dialect. */
|
|
57
|
+
export function matches(location, pattern, name) {
|
|
58
|
+
return loadValueProgram(location.program).matcher(pattern).test(name);
|
|
59
|
+
}
|
|
60
|
+
/** Whether the V1 evaluator accepts `value` at a location. */
|
|
61
|
+
export function accepts(location, value) {
|
|
62
|
+
return evaluateInstance(loadValueProgram(location.program, location.index), value).valid;
|
|
63
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { isMultipleOf } from '../evaluate/assertions.js';
|
|
2
|
+
import { has } from './evidence.js';
|
|
3
|
+
import { FRACTION, NUMBER } from './kinds.js';
|
|
4
|
+
/** Rules for the numeric bound and divisibility keywords. */
|
|
5
|
+
export const numberRules = new Map([
|
|
6
|
+
[
|
|
7
|
+
'maximum',
|
|
8
|
+
({ value, evidence }) => typeof value !== 'number' ||
|
|
9
|
+
!has(evidence, NUMBER) ||
|
|
10
|
+
(evidence.upper !== undefined && evidence.upper.value <= value),
|
|
11
|
+
],
|
|
12
|
+
[
|
|
13
|
+
'exclusiveMaximum',
|
|
14
|
+
({ value, evidence }) => typeof value !== 'number' ||
|
|
15
|
+
!has(evidence, NUMBER) ||
|
|
16
|
+
(evidence.upper !== undefined &&
|
|
17
|
+
(evidence.upper.value < value ||
|
|
18
|
+
(evidence.upper.exclusive && evidence.upper.value <= value))),
|
|
19
|
+
],
|
|
20
|
+
[
|
|
21
|
+
'minimum',
|
|
22
|
+
({ value, evidence }) => typeof value !== 'number' ||
|
|
23
|
+
!has(evidence, NUMBER) ||
|
|
24
|
+
(evidence.lower !== undefined && evidence.lower.value >= value),
|
|
25
|
+
],
|
|
26
|
+
[
|
|
27
|
+
'exclusiveMinimum',
|
|
28
|
+
({ value, evidence }) => typeof value !== 'number' ||
|
|
29
|
+
!has(evidence, NUMBER) ||
|
|
30
|
+
(evidence.lower !== undefined &&
|
|
31
|
+
(evidence.lower.value > value ||
|
|
32
|
+
(evidence.lower.exclusive && evidence.lower.value >= value))),
|
|
33
|
+
],
|
|
34
|
+
[
|
|
35
|
+
'multipleOf',
|
|
36
|
+
({ value, evidence }) => typeof value !== 'number' ||
|
|
37
|
+
!has(evidence, NUMBER) ||
|
|
38
|
+
evidence.multipleOf.some((divisor) => isMultipleOf(divisor, value)) ||
|
|
39
|
+
(!has(evidence, FRACTION) && isMultipleOf(1, value)),
|
|
40
|
+
],
|
|
41
|
+
]);
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { isValueObject as isJsonObject } from '../model/object.js';
|
|
2
|
+
import { has } from './evidence.js';
|
|
3
|
+
import { isTop } from './keywords.js';
|
|
4
|
+
import { OBJECT } from './kinds.js';
|
|
5
|
+
import { accepts, childOf, matches, schemaOf } from './location.js';
|
|
6
|
+
/** Rules for the object size, required-name and property-subschema keywords. */
|
|
7
|
+
export const objectRules = new Map([
|
|
8
|
+
[
|
|
9
|
+
'maxProperties',
|
|
10
|
+
({ inclusion, value, evidence }) => typeof value !== 'number' ||
|
|
11
|
+
!has(evidence, OBJECT) ||
|
|
12
|
+
(evidence.maxProperties !== undefined && evidence.maxProperties <= value) ||
|
|
13
|
+
evidence.conjuncts.some((conjunct) => {
|
|
14
|
+
const names = closedNames(inclusion, conjunct);
|
|
15
|
+
return names !== undefined && names.length <= value;
|
|
16
|
+
}),
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
'minProperties',
|
|
20
|
+
({ value, evidence }) => typeof value !== 'number' ||
|
|
21
|
+
!has(evidence, OBJECT) ||
|
|
22
|
+
value <= 0 ||
|
|
23
|
+
evidence.minProperties >= value ||
|
|
24
|
+
evidence.required.size >= value,
|
|
25
|
+
],
|
|
26
|
+
[
|
|
27
|
+
'required',
|
|
28
|
+
({ value, evidence }) => !Array.isArray(value) ||
|
|
29
|
+
!has(evidence, OBJECT) ||
|
|
30
|
+
value.every((name) => typeof name !== 'string' || evidence.required.has(name)),
|
|
31
|
+
],
|
|
32
|
+
['dependentRequired', dependentRequiredHolds],
|
|
33
|
+
['properties', propertiesHold],
|
|
34
|
+
['patternProperties', patternPropertiesHold],
|
|
35
|
+
['additionalProperties', additionalHolds],
|
|
36
|
+
['propertyNames', propertyNamesHold],
|
|
37
|
+
['dependentSchemas', dependentSchemasHold],
|
|
38
|
+
]);
|
|
39
|
+
function dependentRequiredHolds({ inclusion, value, evidence }) {
|
|
40
|
+
return (!isJsonObject(value) ||
|
|
41
|
+
!has(evidence, OBJECT) ||
|
|
42
|
+
Object.entries(value).every(([name, dependencies]) => !Array.isArray(dependencies) ||
|
|
43
|
+
forbids(inclusion, evidence, name) ||
|
|
44
|
+
dependencies.every((dependency) => typeof dependency !== 'string' ||
|
|
45
|
+
evidence.required.has(dependency) ||
|
|
46
|
+
evidence.dependentRequired.get(name)?.has(dependency) === true)));
|
|
47
|
+
}
|
|
48
|
+
function propertiesHold({ inclusion, value, outer, evidence }) {
|
|
49
|
+
return (!isJsonObject(value) ||
|
|
50
|
+
!has(evidence, OBJECT) ||
|
|
51
|
+
Object.keys(value).every((name) => {
|
|
52
|
+
const member = childOf(outer, 'properties', name);
|
|
53
|
+
return (member === undefined ||
|
|
54
|
+
isTop(schemaOf(member)) ||
|
|
55
|
+
memberConstraints(evidence, name).some((constraint) => inclusion.includes(member, constraint)));
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
function patternPropertiesHold({ inclusion, value, outer, evidence }) {
|
|
59
|
+
return (!isJsonObject(value) ||
|
|
60
|
+
!has(evidence, OBJECT) ||
|
|
61
|
+
Object.keys(value).every((pattern) => {
|
|
62
|
+
const member = childOf(outer, 'patternProperties', pattern);
|
|
63
|
+
if (member === undefined || isTop(schemaOf(member)))
|
|
64
|
+
return true;
|
|
65
|
+
return evidence.conjuncts.some((conjunct) => {
|
|
66
|
+
const same = childOf(conjunct, 'patternProperties', pattern);
|
|
67
|
+
if (same !== undefined && inclusion.includes(member, same))
|
|
68
|
+
return true;
|
|
69
|
+
return coversNames(inclusion, conjunct, member, (name) => matches(outer, pattern, name));
|
|
70
|
+
});
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
function additionalHolds({ inclusion, outer, schema, evidence }) {
|
|
74
|
+
const additional = childOf(outer, 'additionalProperties');
|
|
75
|
+
if (additional === undefined || isTop(schemaOf(additional)) || !has(evidence, OBJECT)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
const declared = isJsonObject(schema.properties) ? schema.properties : {};
|
|
79
|
+
const patterns = isJsonObject(schema.patternProperties)
|
|
80
|
+
? Object.keys(schema.patternProperties)
|
|
81
|
+
: [];
|
|
82
|
+
const isAdditional = (name) => !Object.hasOwn(declared, name) && !patterns.some((pattern) => matches(outer, pattern, name));
|
|
83
|
+
return evidence.conjuncts.some((conjunct) => coversNames(inclusion, conjunct, additional, isAdditional, patterns));
|
|
84
|
+
}
|
|
85
|
+
function propertyNamesHold({ inclusion, outer, evidence }) {
|
|
86
|
+
const names = childOf(outer, 'propertyNames');
|
|
87
|
+
if (names === undefined || isTop(schemaOf(names)) || !has(evidence, OBJECT))
|
|
88
|
+
return true;
|
|
89
|
+
return evidence.conjuncts.some((conjunct) => {
|
|
90
|
+
const own = childOf(conjunct, 'propertyNames');
|
|
91
|
+
if (own !== undefined && inclusion.includes(names, own))
|
|
92
|
+
return true;
|
|
93
|
+
const closed = closedNames(inclusion, conjunct);
|
|
94
|
+
return closed !== undefined && closed.every((name) => accepts(names, name));
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function dependentSchemasHold({ inclusion, value, outer, evidence }) {
|
|
98
|
+
return (!isJsonObject(value) ||
|
|
99
|
+
!has(evidence, OBJECT) ||
|
|
100
|
+
Object.keys(value).every((name) => {
|
|
101
|
+
const dependent = childOf(outer, 'dependentSchemas', name);
|
|
102
|
+
if (dependent === undefined || isTop(schemaOf(dependent)))
|
|
103
|
+
return true;
|
|
104
|
+
if (forbids(inclusion, evidence, name))
|
|
105
|
+
return true;
|
|
106
|
+
if (evidence.conjuncts.some((conjunct) => inclusion.includes(dependent, conjunct))) {
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
return evidence.conjuncts.some((conjunct) => {
|
|
110
|
+
const own = childOf(conjunct, 'dependentSchemas', name);
|
|
111
|
+
return own !== undefined && inclusion.includes(dependent, own);
|
|
112
|
+
});
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Whether every property of a `conjunct`-admitted object whose name is `relevant` satisfies
|
|
117
|
+
* `required`. Names matching a pattern in `settled` are never relevant.
|
|
118
|
+
*/
|
|
119
|
+
function coversNames(inclusion, conjunct, required, relevant, settled = []) {
|
|
120
|
+
const schema = schemaOf(conjunct);
|
|
121
|
+
if (!isJsonObject(schema))
|
|
122
|
+
return false;
|
|
123
|
+
const rest = childOf(conjunct, 'additionalProperties');
|
|
124
|
+
if (rest === undefined || !inclusion.includes(required, rest))
|
|
125
|
+
return false;
|
|
126
|
+
const declared = isJsonObject(schema.properties) ? Object.keys(schema.properties) : [];
|
|
127
|
+
const patterns = isJsonObject(schema.patternProperties)
|
|
128
|
+
? Object.keys(schema.patternProperties)
|
|
129
|
+
: [];
|
|
130
|
+
return (declared.every((name) => {
|
|
131
|
+
if (!relevant(name))
|
|
132
|
+
return true;
|
|
133
|
+
const member = childOf(conjunct, 'properties', name);
|
|
134
|
+
return member !== undefined && inclusion.includes(required, member);
|
|
135
|
+
}) &&
|
|
136
|
+
patterns.every((pattern) => {
|
|
137
|
+
if (settled.includes(pattern))
|
|
138
|
+
return true;
|
|
139
|
+
const member = childOf(conjunct, 'patternProperties', pattern);
|
|
140
|
+
return member !== undefined && inclusion.includes(required, member);
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
/** Locations one conjunct applies to the value of property `name`. */
|
|
144
|
+
function memberConstraints(evidence, name) {
|
|
145
|
+
return evidence.conjuncts.flatMap((conjunct) => {
|
|
146
|
+
const schema = schemaOf(conjunct);
|
|
147
|
+
if (!isJsonObject(schema))
|
|
148
|
+
return [];
|
|
149
|
+
const constraints = [];
|
|
150
|
+
const declared = isJsonObject(schema.properties) && Object.hasOwn(schema.properties, name);
|
|
151
|
+
if (declared) {
|
|
152
|
+
const member = childOf(conjunct, 'properties', name);
|
|
153
|
+
if (member !== undefined)
|
|
154
|
+
constraints.push(member);
|
|
155
|
+
}
|
|
156
|
+
let matched = false;
|
|
157
|
+
if (isJsonObject(schema.patternProperties)) {
|
|
158
|
+
for (const pattern of Object.keys(schema.patternProperties)) {
|
|
159
|
+
if (!matches(conjunct, pattern, name))
|
|
160
|
+
continue;
|
|
161
|
+
matched = true;
|
|
162
|
+
const member = childOf(conjunct, 'patternProperties', pattern);
|
|
163
|
+
if (member !== undefined)
|
|
164
|
+
constraints.push(member);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!declared && !matched) {
|
|
168
|
+
const rest = childOf(conjunct, 'additionalProperties');
|
|
169
|
+
if (rest !== undefined)
|
|
170
|
+
constraints.push(rest);
|
|
171
|
+
}
|
|
172
|
+
return constraints;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
/** Whether an admitted object never has property `name`. */
|
|
176
|
+
function forbids(inclusion, evidence, name) {
|
|
177
|
+
return memberConstraints(evidence, name).some((constraint) => inclusion.evidence(constraint).kinds === 0);
|
|
178
|
+
}
|
|
179
|
+
/** The complete property-name set of a closed object conjunct, when it is one. */
|
|
180
|
+
function closedNames(inclusion, conjunct) {
|
|
181
|
+
const schema = schemaOf(conjunct);
|
|
182
|
+
if (!isJsonObject(schema))
|
|
183
|
+
return undefined;
|
|
184
|
+
if (isJsonObject(schema.patternProperties) && Object.keys(schema.patternProperties).length > 0)
|
|
185
|
+
return undefined;
|
|
186
|
+
const rest = childOf(conjunct, 'additionalProperties');
|
|
187
|
+
if (rest === undefined || inclusion.evidence(rest).kinds !== 0)
|
|
188
|
+
return undefined;
|
|
189
|
+
return isJsonObject(schema.properties) ? Object.keys(schema.properties) : [];
|
|
190
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { has } from './evidence.js';
|
|
2
|
+
import { STRING } from './kinds.js';
|
|
3
|
+
/** Rules for the string length and pattern keywords. */
|
|
4
|
+
export const stringRules = new Map([
|
|
5
|
+
[
|
|
6
|
+
'maxLength',
|
|
7
|
+
({ value, evidence }) => typeof value !== 'number' ||
|
|
8
|
+
!has(evidence, STRING) ||
|
|
9
|
+
(evidence.maxLength !== undefined && evidence.maxLength <= value),
|
|
10
|
+
],
|
|
11
|
+
[
|
|
12
|
+
'minLength',
|
|
13
|
+
({ value, evidence }) => typeof value !== 'number' ||
|
|
14
|
+
!has(evidence, STRING) ||
|
|
15
|
+
value <= 0 ||
|
|
16
|
+
evidence.minLength >= value,
|
|
17
|
+
],
|
|
18
|
+
[
|
|
19
|
+
'pattern',
|
|
20
|
+
({ value, evidence }) => typeof value !== 'string' ||
|
|
21
|
+
!has(evidence, STRING) ||
|
|
22
|
+
evidence.patterns.has(value) ||
|
|
23
|
+
evidence.fullMatches.has(value),
|
|
24
|
+
],
|
|
25
|
+
[
|
|
26
|
+
'fullMatch',
|
|
27
|
+
({ value, evidence }) => typeof value !== 'string' || !has(evidence, STRING) || evidence.fullMatches.has(value),
|
|
28
|
+
],
|
|
29
|
+
]);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { typeMask } from './kinds.js';
|
|
2
|
+
/** Rules for the keywords that restrict the kind or the exact value. */
|
|
3
|
+
export const typeRules = new Map([
|
|
4
|
+
['type', ({ value, evidence }) => (evidence.kinds & ~typeMask(value)) === 0],
|
|
5
|
+
['const', () => false],
|
|
6
|
+
['enum', () => false],
|
|
7
|
+
[
|
|
8
|
+
'x-astrale-value-kind',
|
|
9
|
+
({ value, evidence }) => typeof value === 'string' && evidence.valueKinds.has(value),
|
|
10
|
+
],
|
|
11
|
+
]);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ValueKind } from '@astrale-os/kernel-dsl/value';
|
|
1
2
|
import type { ValueProgram } from './compile/model.js';
|
|
2
3
|
import type { CompiledInstanceSchema } from './evaluate/types.js';
|
|
3
4
|
import type { JsonSchema } from './model/schema.js';
|
|
@@ -13,3 +14,16 @@ export declare function classifyOrdering(contract: JsonSchema, registry: ValueSc
|
|
|
13
14
|
/** Recompute the scalar ordering classification from one admitted portable value program. */
|
|
14
15
|
export declare function classifyValueProgramOrdering(program: ValueProgram): OrderingKind | undefined;
|
|
15
16
|
export declare function classifyPreparedOrdering(compiled: CompiledInstanceSchema): OrderingKind | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Every value kind `classifyValueKind` returns, frozen, in the order `ValueKind` names them. A kind
|
|
19
|
+
* added to `ValueKind` does not compile until it is listed here.
|
|
20
|
+
*/
|
|
21
|
+
export declare const VALUE_KINDS: readonly ValueKind[];
|
|
22
|
+
/**
|
|
23
|
+
* The value kind of a property schema (page "Fondations Query v7", § Atoms et value kinds, Y4):
|
|
24
|
+
* the one kind its non-null values admit, `null` being a point of every kind. An annotated
|
|
25
|
+
* instant is `instant`, a finite set of strings `enum`, an array of unique items `set`; an
|
|
26
|
+
* ambiguous union, an object, an array with repetitions or a domain without values is `json`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function classifyValueKind(contract: JsonSchema, registry: ValueSchemaRegistry): ValueKind;
|
|
29
|
+
export declare function classifyPreparedValueKind(compiled: CompiledInstanceSchema): ValueKind;
|
|
@@ -25,7 +25,8 @@ export function classifyValueProgramOrdering(program) {
|
|
|
25
25
|
return classifyPreparedOrdering(loadValueProgram(program));
|
|
26
26
|
}
|
|
27
27
|
export function classifyPreparedOrdering(compiled) {
|
|
28
|
-
|
|
28
|
+
// An enum orders as the strings it lists; a set is an array.
|
|
29
|
+
const candidates = new Set([...new OrderingClassifier(compiled).classify(compiled.root)].map((kind) => kind === 'enum' ? 'string' : kind === 'set' ? 'array' : kind));
|
|
29
30
|
if (candidates.size !== 1)
|
|
30
31
|
return undefined;
|
|
31
32
|
const [kind] = candidates;
|
|
@@ -33,6 +34,51 @@ export function classifyPreparedOrdering(compiled) {
|
|
|
33
34
|
? kind
|
|
34
35
|
: undefined;
|
|
35
36
|
}
|
|
37
|
+
const valueKinds = {
|
|
38
|
+
text: true,
|
|
39
|
+
number: true,
|
|
40
|
+
instant: true,
|
|
41
|
+
enum: true,
|
|
42
|
+
boolean: true,
|
|
43
|
+
set: true,
|
|
44
|
+
json: true,
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Every value kind `classifyValueKind` returns, frozen, in the order `ValueKind` names them. A kind
|
|
48
|
+
* added to `ValueKind` does not compile until it is listed here.
|
|
49
|
+
*/
|
|
50
|
+
export const VALUE_KINDS = Object.freeze(Object.keys(valueKinds));
|
|
51
|
+
/**
|
|
52
|
+
* The value kind of a property schema (page "Fondations Query v7", § Atoms et value kinds, Y4):
|
|
53
|
+
* the one kind its non-null values admit, `null` being a point of every kind. An annotated
|
|
54
|
+
* instant is `instant`, a finite set of strings `enum`, an array of unique items `set`; an
|
|
55
|
+
* ambiguous union, an object, an array with repetitions or a domain without values is `json`.
|
|
56
|
+
*/
|
|
57
|
+
export function classifyValueKind(contract, registry) {
|
|
58
|
+
const prepared = prepareValueSchema(contract, registry, '');
|
|
59
|
+
if (!prepared.compiled)
|
|
60
|
+
return 'json';
|
|
61
|
+
return classifyPreparedValueKind(prepared.value);
|
|
62
|
+
}
|
|
63
|
+
export function classifyPreparedValueKind(compiled) {
|
|
64
|
+
const candidates = new Set(new OrderingClassifier(compiled).classify(compiled.root));
|
|
65
|
+
candidates.delete('null');
|
|
66
|
+
if (candidates.size !== 1)
|
|
67
|
+
return 'json';
|
|
68
|
+
const [kind] = candidates;
|
|
69
|
+
switch (kind) {
|
|
70
|
+
case 'string':
|
|
71
|
+
return 'text';
|
|
72
|
+
case 'number':
|
|
73
|
+
case 'boolean':
|
|
74
|
+
case 'instant':
|
|
75
|
+
case 'enum':
|
|
76
|
+
case 'set':
|
|
77
|
+
return kind;
|
|
78
|
+
default:
|
|
79
|
+
return 'json';
|
|
80
|
+
}
|
|
81
|
+
}
|
|
36
82
|
class OrderingClassifier {
|
|
37
83
|
compiled;
|
|
38
84
|
active = new Set();
|
|
@@ -72,6 +118,9 @@ class OrderingClassifier {
|
|
|
72
118
|
const declaredTypes = typesOf(schema.type);
|
|
73
119
|
if (declaredTypes !== undefined)
|
|
74
120
|
candidates = intersect(candidates, declaredTypes);
|
|
121
|
+
if (schema.uniqueItems === true) {
|
|
122
|
+
candidates = new Set([...candidates].map((kind) => (kind === 'array' ? 'set' : kind)));
|
|
123
|
+
}
|
|
75
124
|
if (Object.hasOwn(schema, 'const')) {
|
|
76
125
|
candidates = intersect(candidates, new Set([kindOfValue(schema.const)]));
|
|
77
126
|
}
|
|
@@ -130,6 +179,7 @@ function typeSet(types) {
|
|
|
130
179
|
}
|
|
131
180
|
return result;
|
|
132
181
|
}
|
|
182
|
+
/** The kind of one listed value: a listed string belongs to a finite set of strings. */
|
|
133
183
|
function kindOfValue(value) {
|
|
134
184
|
if (value === null)
|
|
135
185
|
return 'null';
|
|
@@ -141,18 +191,35 @@ function kindOfValue(value) {
|
|
|
141
191
|
return 'boolean';
|
|
142
192
|
if (typeof value === 'number')
|
|
143
193
|
return 'number';
|
|
144
|
-
return '
|
|
194
|
+
return 'enum';
|
|
145
195
|
}
|
|
196
|
+
/** The kind both admit: equal kinds, or the narrower of a kind and one of its subsets. */
|
|
197
|
+
function meet(left, right) {
|
|
198
|
+
if (left === right)
|
|
199
|
+
return left;
|
|
200
|
+
for (const [narrow, wide] of NARROWER) {
|
|
201
|
+
if ((left === narrow && right === wide) || (left === wide && right === narrow))
|
|
202
|
+
return narrow;
|
|
203
|
+
}
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* The narrower kind of two that meet: a subset and its kind, and an enum of instants, which
|
|
208
|
+
* orders as instants.
|
|
209
|
+
*/
|
|
210
|
+
const NARROWER = [
|
|
211
|
+
['instant', 'string'],
|
|
212
|
+
['enum', 'string'],
|
|
213
|
+
['instant', 'enum'],
|
|
214
|
+
['set', 'array'],
|
|
215
|
+
];
|
|
146
216
|
function intersect(left, right) {
|
|
147
217
|
const result = new Set();
|
|
148
218
|
for (const leftKind of left) {
|
|
149
219
|
for (const rightKind of right) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
(leftKind === 'instant' && rightKind === 'string')) {
|
|
154
|
-
result.add('instant');
|
|
155
|
-
}
|
|
220
|
+
const kind = meet(leftKind, rightKind);
|
|
221
|
+
if (kind !== undefined)
|
|
222
|
+
result.add(kind);
|
|
156
223
|
}
|
|
157
224
|
}
|
|
158
225
|
return normalize(result);
|
|
@@ -160,9 +227,19 @@ function intersect(left, right) {
|
|
|
160
227
|
function union(left, right) {
|
|
161
228
|
return normalize(new Set([...left, ...right]));
|
|
162
229
|
}
|
|
230
|
+
/** A kind absorbs its subsets; strings of two subsets are strings. */
|
|
163
231
|
function normalize(input) {
|
|
164
|
-
if (input.has('
|
|
232
|
+
if (input.has('enum') && input.has('instant')) {
|
|
233
|
+
input.delete('enum');
|
|
165
234
|
input.delete('instant');
|
|
235
|
+
input.add('string');
|
|
236
|
+
}
|
|
237
|
+
if (input.has('string')) {
|
|
238
|
+
input.delete('instant');
|
|
239
|
+
input.delete('enum');
|
|
240
|
+
}
|
|
241
|
+
if (input.has('array'))
|
|
242
|
+
input.delete('set');
|
|
166
243
|
return input;
|
|
167
244
|
}
|
|
168
245
|
function schemaList(input) {
|