@astrale-os/kernel-dsl 0.2.0-beta.9 → 1.0.0-beta.2
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 +5 -4
- package/dist/v1/addressing/method-key.js +3 -2
- package/dist/v1/addressing/patterns.d.ts +0 -5
- package/dist/v1/addressing/patterns.js +5 -5
- package/dist/v1/builder/callable/authoring.d.ts +87 -41
- package/dist/v1/builder/callable/authoring.js +29 -14
- package/dist/v1/builder/class/authoring.d.ts +311 -36
- package/dist/v1/builder/class/authoring.js +3 -4
- package/dist/v1/builder/class/property.d.ts +16 -8
- package/dist/v1/builder/class/property.js +21 -5
- package/dist/v1/builder/core/authoring.d.ts +28 -13
- package/dist/v1/builder/core/authoring.js +22 -2
- package/dist/v1/builder/index.d.ts +9 -8
- package/dist/v1/builder/index.js +2 -2
- package/dist/v1/builder/lowering/callable.d.ts +3 -1
- package/dist/v1/builder/lowering/callable.js +43 -51
- package/dist/v1/builder/lowering/class.d.ts +7 -0
- package/dist/v1/builder/lowering/class.js +79 -29
- package/dist/v1/builder/lowering/core.d.ts +3 -2
- package/dist/v1/builder/lowering/core.js +9 -6
- package/dist/v1/builder/lowering/policy.d.ts +8 -1
- package/dist/v1/builder/lowering/policy.js +35 -0
- package/dist/v1/builder/lowering/schema/capture.js +9 -4
- package/dist/v1/builder/lowering/schema/compile.d.ts +2 -2
- package/dist/v1/builder/lowering/schema/compile.js +15 -2
- package/dist/v1/builder/lowering/schema/context.d.ts +11 -8
- package/dist/v1/builder/lowering/schema/context.js +59 -29
- package/dist/v1/builder/lowering/schema/ownership.d.ts +3 -3
- package/dist/v1/builder/lowering/schema/ownership.js +2 -1
- package/dist/v1/builder/lowering/value/compile.js +14 -4
- package/dist/v1/builder/lowering/value/zod.d.ts +7 -6
- package/dist/v1/builder/lowering/value/zod.js +16 -7
- package/dist/v1/builder/lowering/view.d.ts +4 -3
- package/dist/v1/builder/lowering/view.js +2 -14
- package/dist/v1/builder/policy/authoring.d.ts +23 -6
- package/dist/v1/builder/policy/authoring.js +35 -8
- package/dist/v1/builder/schema/define.d.ts +8 -5
- package/dist/v1/builder/schema/diagnostic.d.ts +1 -1
- package/dist/v1/builder/schema/handle.d.ts +2 -1
- package/dist/v1/builder/schema/input.d.ts +9 -1
- package/dist/v1/builder/schema/projection/callable.d.ts +51 -0
- package/dist/v1/builder/schema/projection/callable.js +1 -0
- package/dist/v1/builder/schema/projection/class.d.ts +89 -0
- package/dist/v1/builder/schema/projection/class.js +1 -0
- package/dist/v1/builder/schema/projection/core-input.d.ts +32 -0
- package/dist/v1/builder/schema/projection/core-input.js +1 -0
- package/dist/v1/builder/schema/projection/domain.d.ts +59 -0
- package/dist/v1/builder/schema/projection/domain.js +1 -0
- package/dist/v1/builder/schema/projection/identity.d.ts +39 -0
- package/dist/v1/builder/schema/projection/identity.js +1 -0
- package/dist/v1/builder/schema/projection/model.d.ts +59 -0
- package/dist/v1/builder/schema/projection/model.js +1 -0
- package/dist/v1/builder/schema/projection/property.d.ts +32 -0
- package/dist/v1/builder/schema/projection/property.js +1 -0
- package/dist/v1/builder/schema/projection/resolved-class.d.ts +76 -0
- package/dist/v1/builder/schema/projection/resolved-class.js +1 -0
- package/dist/v1/builder/schema/projection.d.ts +4 -258
- package/dist/v1/builder/value/full-match.js +6 -3
- package/dist/v1/builder/value/path.d.ts +2 -2
- package/dist/v1/builder/value/path.js +2 -2
- package/dist/v1/builder/view/authoring.d.ts +6 -14
- package/dist/v1/builder/view/authoring.js +13 -5
- package/dist/v1/bundle/structure.d.ts +2 -1
- package/dist/v1/compiled/admission/accept.d.ts +1 -1
- package/dist/v1/compiled/admission/accept.js +11 -814
- package/dist/v1/compiled/admission/closure.d.ts +3 -0
- package/dist/v1/compiled/admission/closure.js +173 -0
- package/dist/v1/compiled/admission/diagnostic.d.ts +7 -0
- package/dist/v1/compiled/admission/diagnostic.js +13 -0
- package/dist/v1/compiled/admission/revision.js +9 -1
- package/dist/v1/compiled/admission/semantics/cardinality.d.ts +5 -0
- package/dist/v1/compiled/admission/semantics/cardinality.js +113 -0
- package/dist/v1/compiled/admission/semantics/class.d.ts +5 -0
- package/dist/v1/compiled/admission/semantics/class.js +125 -0
- package/dist/v1/compiled/admission/semantics/context.d.ts +60 -0
- package/dist/v1/compiled/admission/semantics/context.js +81 -0
- package/dist/v1/compiled/admission/semantics/core.d.ts +35 -0
- package/dist/v1/compiled/admission/semantics/core.js +123 -0
- package/dist/v1/compiled/admission/semantics/edge.d.ts +8 -0
- package/dist/v1/compiled/admission/semantics/edge.js +89 -0
- package/dist/v1/compiled/admission/semantics/policy/admit.d.ts +4 -0
- package/dist/v1/compiled/admission/semantics/policy/admit.js +63 -0
- package/dist/v1/compiled/admission/semantics/policy/callable.d.ts +5 -0
- package/dist/v1/compiled/admission/semantics/policy/callable.js +72 -0
- package/dist/v1/compiled/admission/semantics/policy/contracts.d.ts +24 -0
- package/dist/v1/compiled/admission/semantics/policy/contracts.js +45 -0
- package/dist/v1/compiled/admission/semantics/policy/equality.d.ts +8 -0
- package/dist/v1/compiled/admission/semantics/policy/equality.js +27 -0
- package/dist/v1/compiled/admission/semantics/policy/measure.d.ts +9 -0
- package/dist/v1/compiled/admission/semantics/policy/measure.js +81 -0
- package/dist/v1/compiled/admission/semantics/policy/parity.d.ts +4 -0
- package/dist/v1/compiled/admission/semantics/policy/parity.js +138 -0
- package/dist/v1/compiled/admission/semantics/policy/pattern.d.ts +8 -0
- package/dist/v1/compiled/admission/semantics/policy/pattern.js +78 -0
- package/dist/v1/compiled/admission/semantics/policy/typing.d.ts +28 -0
- package/dist/v1/compiled/admission/semantics/policy/typing.js +206 -0
- package/dist/v1/compiled/admission/semantics/policy/variants.d.ts +10 -0
- package/dist/v1/compiled/admission/semantics/policy/variants.js +65 -0
- package/dist/v1/compiled/admission/semantics.d.ts +1 -1
- package/dist/v1/compiled/admission/semantics.js +8 -1078
- package/dist/v1/compiled/admission/wire/callable.d.ts +6 -0
- package/dist/v1/compiled/admission/wire/callable.js +90 -0
- package/dist/v1/compiled/admission/wire/class.d.ts +4 -0
- package/dist/v1/compiled/admission/wire/class.js +133 -0
- package/dist/v1/compiled/admission/wire/core.d.ts +5 -0
- package/dist/v1/compiled/admission/wire/core.js +32 -0
- package/dist/v1/compiled/admission/wire/dependency.d.ts +14 -0
- package/dist/v1/compiled/admission/wire/dependency.js +43 -0
- package/dist/v1/compiled/admission/wire/entry.d.ts +5 -0
- package/dist/v1/compiled/admission/wire/entry.js +100 -0
- package/dist/v1/compiled/admission/wire/pattern.d.ts +12 -0
- package/dist/v1/compiled/admission/wire/pattern.js +111 -0
- package/dist/v1/compiled/admission/wire/policy.d.ts +10 -0
- package/dist/v1/compiled/admission/wire/policy.js +189 -0
- package/dist/v1/compiled/admission/wire/program.d.ts +4 -0
- package/dist/v1/compiled/admission/wire/program.js +52 -0
- package/dist/v1/compiled/admission/wire/property.d.ts +4 -0
- package/dist/v1/compiled/admission/wire/property.js +74 -0
- package/dist/v1/compiled/admission/wire/reference.d.ts +8 -0
- package/dist/v1/compiled/admission/wire/reference.js +33 -0
- package/dist/v1/compiled/admission/wire/value.d.ts +29 -0
- package/dist/v1/compiled/admission/wire/value.js +98 -0
- package/dist/v1/compiled/admission/wire/view.d.ts +3 -0
- package/dist/v1/compiled/admission/wire/view.js +17 -0
- package/dist/v1/compiled/compiler/compile.js +43 -11
- package/dist/v1/compiled/generated.d.ts +10 -0
- package/dist/v1/compiled/generated.js +15 -0
- package/dist/v1/compiled/loader/load.d.ts +5 -0
- package/dist/v1/compiled/loader/load.js +18 -4
- package/dist/v1/compiled/model/schema.d.ts +16 -7
- package/dist/v1/domain/create.js +18 -28
- package/dist/v1/domain/model/domain.d.ts +0 -1
- package/dist/v1/domain/model/input.d.ts +3 -0
- package/dist/v1/domain/projection/classes.js +13 -41
- package/dist/v1/domain/validation/create.d.ts +2 -0
- package/dist/v1/domain/validation/create.js +12 -0
- package/dist/v1/index.d.ts +5 -2
- package/dist/v1/index.js +3 -1
- package/dist/v1/language/callable/composition.d.ts +27 -0
- package/dist/v1/language/callable/composition.js +52 -0
- package/dist/v1/language/callable/executable.d.ts +6 -0
- package/dist/v1/language/callable/executable.js +4 -0
- package/dist/v1/language/callable/model/callable.d.ts +34 -9
- package/dist/v1/language/callable/model/callable.js +3 -1
- package/dist/v1/language/callable/resolved.d.ts +23 -8
- package/dist/v1/language/class/facets.d.ts +5 -2
- package/dist/v1/language/class/inheritance.d.ts +12 -0
- package/dist/v1/language/class/inheritance.js +25 -0
- package/dist/v1/language/class/lifetime.d.ts +17 -0
- package/dist/v1/language/class/lifetime.js +26 -0
- package/dist/v1/language/class/model/class.d.ts +8 -4
- package/dist/v1/language/class/model/property.d.ts +4 -2
- package/dist/v1/language/class/property-values.js +5 -2
- package/dist/v1/language/class/resolved.d.ts +25 -15
- package/dist/v1/language/core/model/core.d.ts +7 -1
- package/dist/v1/language/core/resolved.d.ts +6 -2
- package/dist/v1/language/definition/metadata.d.ts +8 -0
- package/dist/v1/language/definition/metadata.js +12 -0
- package/dist/v1/language/index.d.ts +9 -0
- package/dist/v1/language/index.js +6 -0
- package/dist/v1/language/policy/analysis/branches.d.ts +4 -3
- package/dist/v1/language/policy/analysis/branches.js +54 -12
- package/dist/v1/language/policy/analysis/budgets.js +1 -1
- package/dist/v1/language/policy/analysis/checks.d.ts +28 -0
- package/dist/v1/language/policy/analysis/checks.js +80 -0
- package/dist/v1/language/policy/index.d.ts +1 -0
- package/dist/v1/language/policy/index.js +1 -0
- package/dist/v1/language/policy/model/policy.d.ts +71 -5
- package/dist/v1/language/policy/model/policy.js +52 -1
- package/dist/v1/language/policy/normalization/normalize.d.ts +12 -1
- package/dist/v1/language/policy/normalization/normalize.js +178 -44
- package/dist/v1/language/state/index.d.ts +3 -0
- package/dist/v1/language/state/index.js +1 -0
- package/dist/v1/language/state/machine.d.ts +28 -0
- package/dist/v1/language/state/machine.js +40 -0
- package/dist/v1/language/state/topology.d.ts +14 -0
- package/dist/v1/language/state/topology.js +69 -0
- package/dist/v1/language/value/compile/fields.d.ts +1 -1
- package/dist/v1/language/value/compile/load.d.ts +4 -2
- package/dist/v1/language/value/compile/load.js +22 -1
- package/dist/v1/language/value/compile/model.d.ts +16 -1
- package/dist/v1/language/value/evaluate/assertions.d.ts +2 -0
- package/dist/v1/language/value/evaluate/assertions.js +13 -0
- package/dist/v1/language/value/evaluate/evaluator.js +111 -28
- package/dist/v1/language/value/evaluate/preparation.d.ts +10 -1
- package/dist/v1/language/value/evaluate/preparation.js +17 -26
- package/dist/v1/language/value/evaluate/program.js +1 -1
- package/dist/v1/language/value/evaluate/validate.d.ts +6 -2
- package/dist/v1/language/value/evaluate/validate.js +18 -11
- package/dist/v1/language/value/inclusion/includes.d.ts +9 -0
- package/dist/v1/language/value/inclusion/includes.js +777 -0
- package/dist/v1/language/value/model/blob.d.ts +31 -0
- package/dist/v1/language/value/model/blob.js +90 -0
- package/dist/v1/language/value/model/instant.d.ts +0 -2
- package/dist/v1/language/value/model/instant.js +1 -1
- package/dist/v1/language/value/model/schema.d.ts +2 -1
- package/dist/v1/language/value/model/schema.js +1 -5
- package/dist/v1/language/value/ordering.d.ts +2 -0
- package/dist/v1/language/value/ordering.js +4 -8
- package/dist/v1/language/value/profile/json.validate.js +1 -1
- package/dist/v1/language/value/profile/rules.js +48 -12
- package/dist/v1/language/value/reference/registry.d.ts +2 -1
- package/dist/v1/language/value/regexp/ast.js +3 -64
- package/dist/v1/language/value/regexp/compile.d.ts +32 -3
- package/dist/v1/language/value/regexp/compile.js +117 -21
- package/dist/v1/language/value/regexp/matcher.d.ts +6 -3
- package/dist/v1/language/value/regexp/matcher.js +185 -28
- package/dist/v1/language/value/regexp/syntax.d.ts +12 -1
- package/dist/v1/language/value/regexp/syntax.js +14 -2
- package/dist/v1/language/value/regexp/unicode.d.ts +32 -0
- package/dist/v1/language/value/regexp/unicode.js +139 -0
- package/dist/v1/language/value/syntax/full-match.js +8 -5
- package/dist/v1/language/value/syntax/node-path/validate.js +4 -6
- package/dist/v1/language/view/model/view.d.ts +2 -4
- package/dist/v1/language/view/resolved.d.ts +2 -2
- package/dist/v1/schema/analysis/class.d.ts +36 -6
- package/dist/v1/schema/analysis/class.js +31 -66
- package/dist/v1/schema/canonical/revision.d.ts +2 -1
- package/dist/v1/schema/canonical/revision.js +4 -1
- package/dist/v1/schema/compatibility/api.d.ts +3 -0
- package/dist/v1/schema/compatibility/api.js +2 -0
- package/dist/v1/schema/compatibility/comparators/callables.d.ts +39 -0
- package/dist/v1/schema/compatibility/comparators/callables.js +101 -0
- package/dist/v1/schema/compatibility/comparators/classes.d.ts +47 -0
- package/dist/v1/schema/compatibility/comparators/classes.js +160 -0
- package/dist/v1/schema/compatibility/comparators/index.d.ts +12 -0
- package/dist/v1/schema/compatibility/comparators/index.js +53 -0
- package/dist/v1/schema/compatibility/comparators/members.d.ts +4 -0
- package/dist/v1/schema/compatibility/comparators/members.js +49 -0
- package/dist/v1/schema/compatibility/comparators/values.d.ts +5 -0
- package/dist/v1/schema/compatibility/comparators/values.js +37 -0
- package/dist/v1/schema/compatibility/compare.d.ts +5 -0
- package/dist/v1/schema/compatibility/compare.js +23 -0
- package/dist/v1/schema/compatibility/context.d.ts +19 -0
- package/dist/v1/schema/compatibility/context.js +49 -0
- package/dist/v1/schema/compatibility/coordinate.d.ts +40 -0
- package/dist/v1/schema/compatibility/coordinate.js +38 -0
- package/dist/v1/schema/compatibility/diff.d.ts +12 -0
- package/dist/v1/schema/compatibility/diff.js +39 -0
- package/dist/v1/schema/compatibility/evidence.d.ts +16 -0
- package/dist/v1/schema/compatibility/evidence.js +62 -0
- package/dist/v1/schema/compatibility/fields.d.ts +194 -0
- package/dist/v1/schema/compatibility/fields.js +94 -0
- package/dist/v1/schema/compatibility/index.d.ts +6 -0
- package/dist/v1/schema/compatibility/index.js +3 -0
- package/dist/v1/schema/compatibility/meaning/footprint.d.ts +7 -0
- package/dist/v1/schema/compatibility/meaning/footprint.js +59 -0
- package/dist/v1/schema/compatibility/meaning/index.d.ts +3 -0
- package/dist/v1/schema/compatibility/meaning/index.js +16 -0
- package/dist/v1/schema/compatibility/model.d.ts +131 -0
- package/dist/v1/schema/compatibility/model.js +1 -0
- package/dist/v1/schema/compatibility/references.d.ts +38 -0
- package/dist/v1/schema/compatibility/references.js +149 -0
- package/dist/v1/schema/compatibility/report.d.ts +27 -0
- package/dist/v1/schema/compatibility/report.js +100 -0
- package/dist/v1/schema/compatibility/scope.d.ts +36 -0
- package/dist/v1/schema/compatibility/scope.js +110 -0
- package/dist/v1/schema/compatibility/structure/index.d.ts +3 -0
- package/dist/v1/schema/compatibility/structure/index.js +40 -0
- package/dist/v1/schema/compatibility/structure/inheritance.d.ts +7 -0
- package/dist/v1/schema/compatibility/structure/inheritance.js +68 -0
- package/dist/v1/schema/compatibility/structure/requirements.d.ts +7 -0
- package/dist/v1/schema/compatibility/structure/requirements.js +97 -0
- package/dist/v1/schema/compatibility/subjects.d.ts +39 -0
- package/dist/v1/schema/compatibility/subjects.js +143 -0
- package/dist/v1/schema/compatibility/versioning/index.d.ts +8 -0
- package/dist/v1/schema/compatibility/versioning/index.js +50 -0
- package/dist/v1/schema/compatibility/versioning/members.d.ts +12 -0
- package/dist/v1/schema/compatibility/versioning/members.js +47 -0
- package/dist/v1/schema/compatibility/versioning/surface.d.ts +11 -0
- package/dist/v1/schema/compatibility/versioning/surface.js +61 -0
- package/dist/v1/schema/diagnostic.d.ts +1 -1
- package/dist/v1/schema/fingerprint.d.ts +2 -0
- package/dist/v1/schema/fingerprint.js +1 -0
- package/dist/v1/schema/index.d.ts +4 -3
- package/dist/v1/schema/index.js +2 -1
- package/dist/v1/schema/model/schema.d.ts +8 -11
- package/dist/v1/schema/normalization/domain.js +57 -13
- package/dist/v1/schema/resolution/definition.index.d.ts +1 -2
- package/dist/v1/schema/resolution/definition.index.js +0 -3
- package/dist/v1/schema/resolution/domain.d.ts +4 -1
- package/dist/v1/schema/resolution/domain.js +26 -18
- package/dist/v1/schema/resolution/origins.d.ts +28 -5
- package/dist/v1/schema/resolution/origins.js +68 -31
- package/dist/v1/schema/validation/callable.d.ts +4 -0
- package/dist/v1/schema/validation/callable.js +39 -0
- package/dist/v1/schema/validation/class.d.ts +10 -0
- package/dist/v1/schema/validation/class.js +79 -60
- package/dist/v1/schema/validation/core.js +83 -55
- package/dist/v1/schema/validation/domain.d.ts +2 -1
- package/dist/v1/schema/validation/domain.js +2 -0
- package/dist/v1/schema/validation/domain.structure.d.ts +0 -2
- package/dist/v1/schema/validation/domain.structure.js +214 -171
- package/dist/v1/schema/validation/policy/budgets.js +36 -6
- package/dist/v1/schema/validation/policy/context.js +5 -16
- package/dist/v1/schema/validation/policy/objects.d.ts +4 -0
- package/dist/v1/schema/validation/policy/objects.js +71 -0
- package/dist/v1/schema/validation/policy/references.js +16 -4
- package/dist/v1/schema/validation/policy/typing.d.ts +2 -0
- package/dist/v1/schema/validation/policy/typing.js +55 -7
- package/dist/v1/schema/validation/policy/uses.js +73 -99
- package/dist/v1/schema/validation/value-api.d.ts +2 -0
- package/dist/v1/schema/validation/value-api.js +1 -0
- package/dist/v1/schema/validation/value.js +7 -1
- package/dist/v1/schema/validation/view.js +14 -9
- package/dist/value/accept.d.ts +9 -0
- package/dist/value/accept.js +106 -7
- package/dist/value/canonical.d.ts +3 -0
- package/dist/value/canonical.js +22 -6
- package/dist/value/immutable.optimization.d.ts +15 -0
- package/dist/value/immutable.optimization.js +34 -0
- package/dist/value/index.d.ts +1 -1
- package/dist/value/index.js +1 -1
- package/dist/value/json.d.ts +1 -1
- package/dist/value/json.js +1 -1
- package/dist/value/limits.d.ts +1 -1
- package/dist/value/unicode.d.ts +4 -1
- package/dist/value/unicode.js +28 -0
- package/package.json +19 -3
- package/dist/v1/schema/compatibility/meaning.d.ts +0 -29
- package/dist/v1/schema/compatibility/meaning.js +0 -605
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type ZodType } from 'zod';
|
|
2
|
+
import type { EventName, StateName, StateTopology, TransitionRelation, ValidTransitionRelation } from './topology.js';
|
|
3
|
+
export interface StateMachine<Transitions extends TransitionRelation = any, Initial extends StateName<Transitions> = StateName<Transitions>> extends StateTopology<Transitions, Initial> {
|
|
4
|
+
readonly stateSchema: ZodType<StateName<Transitions>, StateName<Transitions>>;
|
|
5
|
+
readonly eventSchema: ZodType<EventName<Transitions>, EventName<Transitions>>;
|
|
6
|
+
eventsFrom<State extends StateName<Transitions>>(state: State): readonly EventName<Transitions, State>[];
|
|
7
|
+
decide<State extends StateName<Transitions>, Event extends EventName<Transitions>>(state: State, event: Event): TransitionDecision<Transitions, State, Event>;
|
|
8
|
+
}
|
|
9
|
+
export type StateOf<Machine extends StateMachine> = StateName<Machine['transitions']>;
|
|
10
|
+
export type EventOf<Machine extends StateMachine, State extends StateOf<Machine> = StateOf<Machine>> = EventName<Machine['transitions'], State>;
|
|
11
|
+
type TransitionDecision<Transitions extends TransitionRelation, State extends StateName<Transitions>, Event extends EventName<Transitions>> = State extends StateName<Transitions> ? Event extends EventName<Transitions> ? Event extends keyof Transitions[State] ? Readonly<{
|
|
12
|
+
kind: 'allowed';
|
|
13
|
+
transition: Readonly<{
|
|
14
|
+
from: State;
|
|
15
|
+
event: Event;
|
|
16
|
+
to: Transitions[State][Event];
|
|
17
|
+
}>;
|
|
18
|
+
}> : Readonly<{
|
|
19
|
+
kind: 'rejected';
|
|
20
|
+
code: 'STATE_TRANSITION_ILLEGAL';
|
|
21
|
+
from: State;
|
|
22
|
+
event: Event;
|
|
23
|
+
}> : never : never;
|
|
24
|
+
export declare function stateMachine<const Transitions extends TransitionRelation, const Initial extends StateName<Transitions>>(input: Readonly<{
|
|
25
|
+
initial: Initial;
|
|
26
|
+
transitions: Transitions & ValidTransitionRelation<Transitions>;
|
|
27
|
+
}>): StateMachine<Transitions, Initial>;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { enum as enumeration } from 'zod';
|
|
2
|
+
import { compareUnicode } from '../../addressing/ordering.js';
|
|
3
|
+
import { admitStateTopology } from './topology.js';
|
|
4
|
+
export function stateMachine(input) {
|
|
5
|
+
const topology = admitStateTopology(input);
|
|
6
|
+
const states = Object.keys(topology.transitions);
|
|
7
|
+
const events = [
|
|
8
|
+
...new Set(states.flatMap((state) => Object.keys(topology.transitions[state]))),
|
|
9
|
+
].sort(compareUnicode);
|
|
10
|
+
const stateSet = new Set(states);
|
|
11
|
+
const eventSet = new Set(events);
|
|
12
|
+
return Object.freeze({
|
|
13
|
+
...topology,
|
|
14
|
+
stateSchema: enumeration(states),
|
|
15
|
+
eventSchema: enumeration(events),
|
|
16
|
+
eventsFrom(state) {
|
|
17
|
+
admitState(state, stateSet);
|
|
18
|
+
return Object.freeze(Object.keys(topology.transitions[state]));
|
|
19
|
+
},
|
|
20
|
+
decide(state, event) {
|
|
21
|
+
admitState(state, stateSet);
|
|
22
|
+
admitEvent(event, eventSet);
|
|
23
|
+
const target = topology.transitions[state][event];
|
|
24
|
+
return target === undefined
|
|
25
|
+
? Object.freeze({ kind: 'rejected', code: 'STATE_TRANSITION_ILLEGAL', from: state, event })
|
|
26
|
+
: Object.freeze({
|
|
27
|
+
kind: 'allowed',
|
|
28
|
+
transition: Object.freeze({ from: state, event, to: target }),
|
|
29
|
+
});
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function admitState(state, states) {
|
|
34
|
+
if (!states.has(state))
|
|
35
|
+
throw new TypeError(`State machine does not declare state "${state}".`);
|
|
36
|
+
}
|
|
37
|
+
function admitEvent(event, events) {
|
|
38
|
+
if (!events.has(event))
|
|
39
|
+
throw new TypeError(`State machine does not declare event "${event}".`);
|
|
40
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type TransitionRelation = Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
2
|
+
export type StateName<Transitions extends TransitionRelation> = Extract<keyof Transitions, string>;
|
|
3
|
+
export type EventName<Transitions extends TransitionRelation, State extends StateName<Transitions> = StateName<Transitions>> = State extends StateName<Transitions> ? Extract<keyof Transitions[State], string> : never;
|
|
4
|
+
export type ValidTransitionRelation<Transitions extends TransitionRelation> = Readonly<{
|
|
5
|
+
[State in StateName<Transitions>]: Readonly<{
|
|
6
|
+
[Event in keyof Transitions[State]]: string extends StateName<Transitions> ? string : Exclude<StateName<Transitions>, State>;
|
|
7
|
+
}>;
|
|
8
|
+
}>;
|
|
9
|
+
export interface StateTopology<Transitions extends TransitionRelation = TransitionRelation, Initial extends StateName<Transitions> = StateName<Transitions>> {
|
|
10
|
+
readonly initial: Initial;
|
|
11
|
+
readonly transitions: Transitions;
|
|
12
|
+
}
|
|
13
|
+
export type StateTopologyOf<Value> = Value extends StateTopology<infer Transitions, infer Initial> ? StateTopology<Transitions, Initial> : undefined;
|
|
14
|
+
export declare function admitStateTopology(input: unknown): StateTopology;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { compareUnicode } from '../../addressing/ordering.js';
|
|
2
|
+
export function admitStateTopology(input) {
|
|
3
|
+
if (!isRecord(input) || !exactKeys(input, ['initial', 'transitions']))
|
|
4
|
+
invalidInput();
|
|
5
|
+
const initial = input.initial;
|
|
6
|
+
const authored = input.transitions;
|
|
7
|
+
if (typeof initial !== 'string' || initial.length === 0 || !isRecord(authored))
|
|
8
|
+
invalidInput();
|
|
9
|
+
const states = Object.keys(authored);
|
|
10
|
+
if (states.length === 0)
|
|
11
|
+
throw new TypeError('State topology requires at least one state.');
|
|
12
|
+
if (states.includes(''))
|
|
13
|
+
throw new TypeError('State topology has an empty state name.');
|
|
14
|
+
if (!states.includes(initial)) {
|
|
15
|
+
throw new TypeError(`State topology initial state "${initial}" is not declared.`);
|
|
16
|
+
}
|
|
17
|
+
const declared = new Set(states);
|
|
18
|
+
const transitions = Object.create(null);
|
|
19
|
+
let transitionCount = 0;
|
|
20
|
+
for (const state of states.sort(compareUnicode)) {
|
|
21
|
+
const outgoing = authored[state];
|
|
22
|
+
if (!isRecord(outgoing))
|
|
23
|
+
throw new TypeError(`State "${state}" transitions must be an object.`);
|
|
24
|
+
const accepted = Object.create(null);
|
|
25
|
+
for (const event of Object.keys(outgoing).sort(compareUnicode)) {
|
|
26
|
+
const target = outgoing[event];
|
|
27
|
+
if (event.length === 0)
|
|
28
|
+
throw new TypeError(`State "${state}" has an empty event name.`);
|
|
29
|
+
if (typeof target !== 'string' || !declared.has(target)) {
|
|
30
|
+
throw new TypeError(`State "${state}" event "${event}" targets undeclared state "${String(target)}".`);
|
|
31
|
+
}
|
|
32
|
+
if (target === state) {
|
|
33
|
+
throw new TypeError(`State "${state}" event "${event}" is a self-transition.`);
|
|
34
|
+
}
|
|
35
|
+
accepted[event] = target;
|
|
36
|
+
transitionCount += 1;
|
|
37
|
+
}
|
|
38
|
+
transitions[state] = Object.freeze(accepted);
|
|
39
|
+
}
|
|
40
|
+
if (transitionCount === 0)
|
|
41
|
+
throw new TypeError('State topology requires at least one transition.');
|
|
42
|
+
const reachable = new Set([initial]);
|
|
43
|
+
const pending = [initial];
|
|
44
|
+
while (pending.length > 0) {
|
|
45
|
+
const state = pending.shift();
|
|
46
|
+
for (const target of Object.values(transitions[state])) {
|
|
47
|
+
if (reachable.has(target))
|
|
48
|
+
continue;
|
|
49
|
+
reachable.add(target);
|
|
50
|
+
pending.push(target);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const state of states) {
|
|
54
|
+
if (!reachable.has(state)) {
|
|
55
|
+
throw new TypeError(`State "${state}" is unreachable from initial "${initial}".`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return Object.freeze({ initial, transitions: Object.freeze(transitions) });
|
|
59
|
+
}
|
|
60
|
+
function invalidInput() {
|
|
61
|
+
throw new TypeError('State topology must contain exactly initial and transitions.');
|
|
62
|
+
}
|
|
63
|
+
function isRecord(input) {
|
|
64
|
+
return input !== null && typeof input === 'object' && !Array.isArray(input);
|
|
65
|
+
}
|
|
66
|
+
function exactKeys(input, expected) {
|
|
67
|
+
const keys = Reflect.ownKeys(input);
|
|
68
|
+
return keys.length === expected.length && keys.every((key) => expected.includes(String(key)));
|
|
69
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { ValueProgram } from './model.js';
|
|
2
2
|
/** Exact field constraints available for independent scalar route validation. */
|
|
3
|
-
export declare function inputFieldNodes(program: ValueProgram): Readonly<Record<string, readonly number[]>> | undefined;
|
|
3
|
+
export declare function inputFieldNodes(program: Pick<ValueProgram, 'root' | 'nodes'>): Readonly<Record<string, readonly number[]>> | undefined;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { CompiledInstanceSchema } from '../evaluate/types.js';
|
|
2
|
-
import type { ValueProgram } from './model.js';
|
|
2
|
+
import type { IndexedValueProgram, ValueProgram } from './model.js';
|
|
3
|
+
/** Bind already-admitted entry-local indices without copying any pattern machine. */
|
|
4
|
+
export declare function bindValueProgram(program: IndexedValueProgram, patterns: ValueProgram['patterns']): ValueProgram;
|
|
3
5
|
/** Bind admitted portable indices to the canonical instance evaluator. */
|
|
4
|
-
export declare function loadValueProgram(program: ValueProgram): CompiledInstanceSchema;
|
|
6
|
+
export declare function loadValueProgram(program: ValueProgram, rootIndex?: number): CompiledInstanceSchema;
|
|
@@ -1,7 +1,28 @@
|
|
|
1
1
|
import { loadPattern } from '../regexp/matcher.js';
|
|
2
2
|
import { appendPointer } from '../syntax/pointer.js';
|
|
3
|
+
const loadedPrograms = new WeakMap();
|
|
4
|
+
/** Bind already-admitted entry-local indices without copying any pattern machine. */
|
|
5
|
+
export function bindValueProgram(program, patterns) {
|
|
6
|
+
return Object.freeze({
|
|
7
|
+
...program,
|
|
8
|
+
patterns: Object.freeze(program.patterns.map((index) => patterns[index])),
|
|
9
|
+
});
|
|
10
|
+
}
|
|
3
11
|
/** Bind admitted portable indices to the canonical instance evaluator. */
|
|
4
|
-
export function loadValueProgram(program) {
|
|
12
|
+
export function loadValueProgram(program, rootIndex = program.root) {
|
|
13
|
+
let compiled = loadedPrograms.get(program);
|
|
14
|
+
if (compiled === undefined) {
|
|
15
|
+
compiled = createValueProgram(program);
|
|
16
|
+
loadedPrograms.set(program, compiled);
|
|
17
|
+
}
|
|
18
|
+
if (rootIndex === program.root)
|
|
19
|
+
return compiled;
|
|
20
|
+
const root = compiled.locations.get(String(rootIndex));
|
|
21
|
+
if (root === undefined)
|
|
22
|
+
throw new TypeError('Compiled value program root is absent.');
|
|
23
|
+
return { ...compiled, root };
|
|
24
|
+
}
|
|
25
|
+
function createValueProgram(program) {
|
|
5
26
|
const resource = { key: 'compiled', schema: true, ownerOrigin: 'compiled' };
|
|
6
27
|
const locations = program.nodes.map((node, index) => ({
|
|
7
28
|
key: String(index),
|
|
@@ -14,6 +14,17 @@ export interface Pattern {
|
|
|
14
14
|
readonly start: number;
|
|
15
15
|
readonly states: readonly PatternState[];
|
|
16
16
|
}
|
|
17
|
+
/** A scalar run counts Unicode scalars, never UTF-16 code units. */
|
|
18
|
+
export interface ScalarRun {
|
|
19
|
+
readonly ranges: readonly ScalarRange[];
|
|
20
|
+
readonly minimum: number;
|
|
21
|
+
readonly maximum?: number;
|
|
22
|
+
}
|
|
23
|
+
/** All runs except the last have an exact finite length. */
|
|
24
|
+
export interface RunPattern {
|
|
25
|
+
readonly source: string;
|
|
26
|
+
readonly runs: readonly ScalarRun[];
|
|
27
|
+
}
|
|
17
28
|
export interface ValueNode {
|
|
18
29
|
readonly schema: JsonValue;
|
|
19
30
|
readonly children: Readonly<Record<string, number>>;
|
|
@@ -23,5 +34,9 @@ export interface ValueNode {
|
|
|
23
34
|
export interface ValueProgram {
|
|
24
35
|
readonly root: number;
|
|
25
36
|
readonly nodes: readonly ValueNode[];
|
|
26
|
-
readonly patterns: readonly Pattern[];
|
|
37
|
+
readonly patterns: readonly (Pattern | RunPattern)[];
|
|
38
|
+
}
|
|
39
|
+
/** Pattern indices belong to the enclosing compiled entry's table. */
|
|
40
|
+
export interface IndexedValueProgram extends Omit<ValueProgram, 'patterns'> {
|
|
41
|
+
readonly patterns: readonly number[];
|
|
27
42
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
export declare function matchesType(input: string, value: JsonValue): boolean;
|
|
3
3
|
export declare function jsonEqual(left: JsonValue, right: JsonValue): boolean;
|
|
4
|
+
/** Count Unicode code points with string-iterator semantics, without a character array. */
|
|
5
|
+
export declare function codePointLength(input: string): number;
|
|
4
6
|
export declare function unique(values: readonly JsonValue[]): boolean;
|
|
5
7
|
/** Compare finite JSON numbers as their exact ECMAScript decimal spellings. */
|
|
6
8
|
export declare function isMultipleOf(value: number, divisor: number): boolean;
|
|
@@ -21,6 +21,19 @@ export function matchesType(input, value) {
|
|
|
21
21
|
export function jsonEqual(left, right) {
|
|
22
22
|
return equal(left, right);
|
|
23
23
|
}
|
|
24
|
+
/** Count Unicode code points with string-iterator semantics, without a character array. */
|
|
25
|
+
export function codePointLength(input) {
|
|
26
|
+
let length = 0;
|
|
27
|
+
for (let index = 0; index < input.length; index++, length++) {
|
|
28
|
+
const first = input.charCodeAt(index);
|
|
29
|
+
if (first >= 0xd800 && first <= 0xdbff && index + 1 < input.length) {
|
|
30
|
+
const second = input.charCodeAt(index + 1);
|
|
31
|
+
if (second >= 0xdc00 && second <= 0xdfff)
|
|
32
|
+
index++;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return length;
|
|
36
|
+
}
|
|
24
37
|
export function unique(values) {
|
|
25
38
|
const seen = new Set();
|
|
26
39
|
for (const value of values) {
|
|
@@ -1,36 +1,61 @@
|
|
|
1
|
+
import { isCanonicalBlobRef } from '../model/blob.js';
|
|
1
2
|
import { isInstantValue } from '../model/instant.js';
|
|
2
3
|
import { isValueObject as isJsonObject } from '../model/object.js';
|
|
3
4
|
import { appendPointer } from '../syntax/pointer.js';
|
|
4
|
-
import { isMultipleOf, jsonEqual, matchesType, unique } from './assertions.js';
|
|
5
|
+
import { codePointLength, isMultipleOf, jsonEqual, matchesType, unique } from './assertions.js';
|
|
5
6
|
import { evaluation, mergeAnnotations } from './types.js';
|
|
6
7
|
/** Evaluate complete Draft 2020-12 acceptance without dynamic source generation. */
|
|
7
8
|
export function evaluateInstance(compiled, input) {
|
|
8
9
|
return new InstanceEvaluator(compiled).evaluate(compiled.root, input, '');
|
|
9
10
|
}
|
|
11
|
+
/** Bound per-call retention independently of the size or diversity of an input value. */
|
|
12
|
+
const MAX_CACHED_PATTERN_RESULTS = 256;
|
|
13
|
+
const MAX_CACHED_PATTERN_INPUT_LENGTH = 128;
|
|
10
14
|
class InstanceEvaluator {
|
|
11
15
|
compiled;
|
|
12
|
-
|
|
16
|
+
resultCache = new Map();
|
|
17
|
+
searchResults = new Map();
|
|
18
|
+
fullMatchResults = new Map();
|
|
19
|
+
patternResultCount = 0;
|
|
13
20
|
constructor(compiled) {
|
|
14
21
|
this.compiled = compiled;
|
|
15
22
|
}
|
|
16
23
|
evaluate(location, input, instancePointer) {
|
|
17
|
-
const
|
|
18
|
-
|
|
24
|
+
const key = `${location.key}\u0000${instancePointer}`;
|
|
25
|
+
// propertyNames evaluates the name at the property's value pointer. Cache by the actual
|
|
26
|
+
// input too, retaining one result per repeated sub-problem within this call. A null entry
|
|
27
|
+
// marks an active evaluation; completed results and their annotations are only read.
|
|
28
|
+
let byLocation = this.resultCache.get(input);
|
|
29
|
+
const memoized = byLocation?.get(key);
|
|
30
|
+
if (memoized === null) {
|
|
19
31
|
throw new TypeError('Admitted value schema entered a non-productive evaluation cycle.');
|
|
20
32
|
}
|
|
21
|
-
|
|
33
|
+
if (memoized !== undefined)
|
|
34
|
+
return memoized;
|
|
35
|
+
if (byLocation === undefined) {
|
|
36
|
+
byLocation = new Map();
|
|
37
|
+
this.resultCache.set(input, byLocation);
|
|
38
|
+
}
|
|
39
|
+
byLocation.set(key, null);
|
|
22
40
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
let result;
|
|
42
|
+
if (location.schema === true) {
|
|
43
|
+
result = evaluation();
|
|
44
|
+
}
|
|
45
|
+
else if (location.schema === false) {
|
|
46
|
+
result = evaluation([
|
|
27
47
|
invalid(instancePointer, 'false', 'The false schema rejects every value.'),
|
|
28
48
|
]);
|
|
29
49
|
}
|
|
30
|
-
|
|
50
|
+
else {
|
|
51
|
+
result = this.evaluateObjectSchema(location, location.schema, input, instancePointer);
|
|
52
|
+
}
|
|
53
|
+
byLocation.set(key, result);
|
|
54
|
+
return result;
|
|
31
55
|
}
|
|
32
|
-
|
|
33
|
-
|
|
56
|
+
catch (error) {
|
|
57
|
+
byLocation.delete(key);
|
|
58
|
+
throw error;
|
|
34
59
|
}
|
|
35
60
|
}
|
|
36
61
|
evaluateObjectSchema(location, schema, input, instancePointer) {
|
|
@@ -79,14 +104,20 @@ class InstanceEvaluator {
|
|
|
79
104
|
});
|
|
80
105
|
const anyOf = schemaList(schema.anyOf);
|
|
81
106
|
if (anyOf !== undefined) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
107
|
+
let admitted = 0;
|
|
108
|
+
anyOf.forEach((_, index) => {
|
|
109
|
+
const branch = this.child(location, 'anyOf', index);
|
|
110
|
+
if (contradictsRequiredConst(branch.schema, input))
|
|
111
|
+
return;
|
|
112
|
+
const result = this.evaluate(branch, input, instancePointer);
|
|
113
|
+
if (!result.valid)
|
|
114
|
+
return;
|
|
115
|
+
admitted += 1;
|
|
116
|
+
mergeAnnotations(properties, items, result);
|
|
117
|
+
});
|
|
118
|
+
if (admitted === 0) {
|
|
85
119
|
failures.push(invalid(instancePointer, 'anyOf', 'Value satisfies no anyOf branch.'));
|
|
86
120
|
}
|
|
87
|
-
else {
|
|
88
|
-
admitted.forEach((result) => mergeAnnotations(properties, items, result));
|
|
89
|
-
}
|
|
90
121
|
}
|
|
91
122
|
const oneOf = schemaList(schema.oneOf);
|
|
92
123
|
if (oneOf !== undefined) {
|
|
@@ -119,6 +150,9 @@ class InstanceEvaluator {
|
|
|
119
150
|
if (schema['x-astrale-value-kind'] === 'instant' && !isInstantValue(input)) {
|
|
120
151
|
failures.push(invalid(instancePointer, 'x-astrale-value-kind', 'Value is not an exact UTC millisecond Instant.'));
|
|
121
152
|
}
|
|
153
|
+
if (schema['x-astrale-value-kind'] === 'blob' && !isCanonicalBlobRef(input)) {
|
|
154
|
+
failures.push(invalid(instancePointer, 'x-astrale-value-kind', 'Value is not a canonical blob Ref.'));
|
|
155
|
+
}
|
|
122
156
|
if (typeof schema.type === 'string') {
|
|
123
157
|
if (!matchesType(schema.type, input)) {
|
|
124
158
|
failures.push(invalid(instancePointer, 'type', `Value is not of type ${schema.type}.`));
|
|
@@ -154,18 +188,23 @@ class InstanceEvaluator {
|
|
|
154
188
|
}
|
|
155
189
|
}
|
|
156
190
|
evaluateStringAssertions(schema, input, instancePointer, failures) {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
191
|
+
// For n UTF-16 units, ceil(n / 2) <= code points <= n. Count only if those
|
|
192
|
+
// bounds cannot already prove both length assertions; other assertions still run.
|
|
193
|
+
if ((typeof schema.maxLength === 'number' && input.length > schema.maxLength) ||
|
|
194
|
+
(typeof schema.minLength === 'number' && Math.ceil(input.length / 2) < schema.minLength)) {
|
|
195
|
+
const length = codePointLength(input);
|
|
196
|
+
if (typeof schema.maxLength === 'number' && length > schema.maxLength) {
|
|
197
|
+
failures.push(invalid(instancePointer, 'maxLength', 'String exceeds maxLength.'));
|
|
198
|
+
}
|
|
199
|
+
if (typeof schema.minLength === 'number' && length < schema.minLength) {
|
|
200
|
+
failures.push(invalid(instancePointer, 'minLength', 'String is below minLength.'));
|
|
201
|
+
}
|
|
163
202
|
}
|
|
164
|
-
if (typeof schema.pattern === 'string' && !this.
|
|
203
|
+
if (typeof schema.pattern === 'string' && !this.matchesPattern(schema.pattern, input, false)) {
|
|
165
204
|
failures.push(invalid(instancePointer, 'pattern', 'String does not match pattern.'));
|
|
166
205
|
}
|
|
167
206
|
if (typeof schema.fullMatch === 'string' &&
|
|
168
|
-
!this.
|
|
207
|
+
!this.matchesPattern(schema.fullMatch, input, true)) {
|
|
169
208
|
failures.push(invalid(instancePointer, 'fullMatch', 'String does not match fullMatch.'));
|
|
170
209
|
}
|
|
171
210
|
}
|
|
@@ -284,9 +323,8 @@ class InstanceEvaluator {
|
|
|
284
323
|
const patternMatched = new Set();
|
|
285
324
|
let patternsValid = true;
|
|
286
325
|
for (const pattern of Object.keys(patterns)) {
|
|
287
|
-
const matcher = this.compiled.matcher(pattern);
|
|
288
326
|
for (const name of names) {
|
|
289
|
-
if (!
|
|
327
|
+
if (!this.matchesPattern(pattern, name, false))
|
|
290
328
|
continue;
|
|
291
329
|
patternMatched.add(name);
|
|
292
330
|
const result = this.evaluate(this.child(location, 'patternProperties', pattern), input[name], appendPointer(instancePointer, name));
|
|
@@ -349,6 +387,51 @@ class InstanceEvaluator {
|
|
|
349
387
|
child(parent, keyword, member) {
|
|
350
388
|
return this.compiled.child(parent, keyword, member);
|
|
351
389
|
}
|
|
390
|
+
matchesPattern(source, input, whole) {
|
|
391
|
+
const cacheable = input.length <= MAX_CACHED_PATTERN_INPUT_LENGTH;
|
|
392
|
+
const cache = whole ? this.fullMatchResults : this.searchResults;
|
|
393
|
+
if (cacheable) {
|
|
394
|
+
const found = cache.get(source)?.get(input);
|
|
395
|
+
if (found !== undefined)
|
|
396
|
+
return found;
|
|
397
|
+
}
|
|
398
|
+
const matcher = this.compiled.matcher(source);
|
|
399
|
+
const result = whole ? matcher.matches(input) : matcher.test(input);
|
|
400
|
+
if (cacheable && this.patternResultCount < MAX_CACHED_PATTERN_RESULTS) {
|
|
401
|
+
let values = cache.get(source);
|
|
402
|
+
if (values === undefined) {
|
|
403
|
+
values = new Map();
|
|
404
|
+
cache.set(source, values);
|
|
405
|
+
}
|
|
406
|
+
values.set(input, result);
|
|
407
|
+
this.patternResultCount++;
|
|
408
|
+
}
|
|
409
|
+
return result;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/** A required property with a different primitive const makes the entire branch impossible. */
|
|
413
|
+
function contradictsRequiredConst(schema, input) {
|
|
414
|
+
if (!isJsonObject(schema) || !isJsonObject(input) || !Array.isArray(schema.required)) {
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
if (!isJsonObject(schema.properties))
|
|
418
|
+
return false;
|
|
419
|
+
for (const key of schema.required) {
|
|
420
|
+
if (typeof key !== 'string' || !Object.hasOwn(input, key))
|
|
421
|
+
continue;
|
|
422
|
+
const property = schema.properties[key];
|
|
423
|
+
if (!isJsonObject(property) || !Object.hasOwn(property, 'const'))
|
|
424
|
+
continue;
|
|
425
|
+
const expected = property.const;
|
|
426
|
+
if ((expected === null ||
|
|
427
|
+
typeof expected === 'string' ||
|
|
428
|
+
typeof expected === 'number' ||
|
|
429
|
+
typeof expected === 'boolean') &&
|
|
430
|
+
input[key] !== expected) {
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return false;
|
|
352
435
|
}
|
|
353
436
|
function schemaList(input) {
|
|
354
437
|
return Array.isArray(input) ? input : undefined;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { JsonSchema } from '../model/schema.js';
|
|
2
2
|
import type { ValueSchemaRegistry } from '../reference/registry.js';
|
|
3
|
+
import type { IRegexpMatcher } from '../regexp/matcher.js';
|
|
3
4
|
import type { CompiledInstanceSchema } from './types.js';
|
|
5
|
+
import { compilePattern } from '../regexp/compile.js';
|
|
4
6
|
export type InstanceSchemaCompilation = {
|
|
5
7
|
readonly compiled: true;
|
|
6
8
|
readonly value: CompiledInstanceSchema;
|
|
@@ -8,6 +10,13 @@ export type InstanceSchemaCompilation = {
|
|
|
8
10
|
readonly compiled: false;
|
|
9
11
|
readonly message: string;
|
|
10
12
|
};
|
|
13
|
+
/** Owned by one compilation or source-resolution closure, not retained process-globally. */
|
|
14
|
+
interface PreparedPattern {
|
|
15
|
+
readonly pattern: ReturnType<typeof compilePattern>;
|
|
16
|
+
readonly matcher: IRegexpMatcher;
|
|
17
|
+
}
|
|
18
|
+
export type PatternPool = Map<string, PreparedPattern>;
|
|
11
19
|
/** Prepare reference and linear-regexp state without generating executable source. */
|
|
12
|
-
export declare function prepareValueSchema(schema: JsonSchema, registry: ValueSchemaRegistry, pointer: string): InstanceSchemaCompilation;
|
|
20
|
+
export declare function prepareValueSchema(schema: JsonSchema, registry: ValueSchemaRegistry, pointer: string, pool?: PatternPool): InstanceSchemaCompilation;
|
|
13
21
|
export declare function childKey(pointer: string): string;
|
|
22
|
+
export {};
|
|
@@ -6,7 +6,7 @@ import { loadPattern } from '../regexp/matcher.js';
|
|
|
6
6
|
import { appendPointer } from '../syntax/pointer.js';
|
|
7
7
|
import { walkSchema } from '../syntax/walk.js';
|
|
8
8
|
/** Prepare reference and linear-regexp state without generating executable source. */
|
|
9
|
-
export function prepareValueSchema(schema, registry, pointer) {
|
|
9
|
+
export function prepareValueSchema(schema, registry, pointer, pool = new Map()) {
|
|
10
10
|
const indexed = indexRegistry(registry, pointer);
|
|
11
11
|
if (indexed.diagnostics.length > 0)
|
|
12
12
|
return failed(indexed.diagnostics);
|
|
@@ -26,11 +26,11 @@ export function prepareValueSchema(schema, registry, pointer) {
|
|
|
26
26
|
});
|
|
27
27
|
if (!isJsonObject(location.schema))
|
|
28
28
|
return;
|
|
29
|
-
rememberMatcher(matchers, location.schema.pattern);
|
|
30
|
-
rememberMatcher(matchers, location.schema.fullMatch);
|
|
29
|
+
rememberMatcher(matchers, pool, location.schema.pattern);
|
|
30
|
+
rememberMatcher(matchers, pool, location.schema.fullMatch);
|
|
31
31
|
if (isJsonObject(location.schema.patternProperties)) {
|
|
32
32
|
for (const pattern of Object.keys(location.schema.patternProperties)) {
|
|
33
|
-
rememberMatcher(matchers, pattern);
|
|
33
|
+
rememberMatcher(matchers, pool, pattern);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
});
|
|
@@ -40,13 +40,14 @@ export function prepareValueSchema(schema, registry, pointer) {
|
|
|
40
40
|
if (root === undefined) {
|
|
41
41
|
return { compiled: false, message: 'The root value schema is absent from its closure.' };
|
|
42
42
|
}
|
|
43
|
+
let program;
|
|
43
44
|
return {
|
|
44
45
|
compiled: true,
|
|
45
46
|
value: {
|
|
46
47
|
root,
|
|
47
48
|
locations,
|
|
48
49
|
resolutions: closure.resolutions,
|
|
49
|
-
program: () => portableProgram(root, locations, closure.resolutions),
|
|
50
|
+
program: () => (program ??= portableProgram(root, locations, closure.resolutions, matchers)),
|
|
50
51
|
child(location, keyword, member) {
|
|
51
52
|
const keywordPointer = appendPointer(location.pointer, keyword);
|
|
52
53
|
const pointer = member === undefined ? keywordPointer : appendPointer(keywordPointer, member);
|
|
@@ -67,15 +68,14 @@ export function prepareValueSchema(schema, registry, pointer) {
|
|
|
67
68
|
if (matcher === undefined) {
|
|
68
69
|
throw new TypeError('Value schema uses an unprepared I-Regexp source.');
|
|
69
70
|
}
|
|
70
|
-
return matcher;
|
|
71
|
+
return matcher.matcher;
|
|
71
72
|
},
|
|
72
73
|
},
|
|
73
74
|
};
|
|
74
75
|
}
|
|
75
|
-
function portableProgram(root, locations, resolutions) {
|
|
76
|
+
function portableProgram(root, locations, resolutions, patterns) {
|
|
76
77
|
const ordered = [...locations.values()];
|
|
77
78
|
const indices = new Map(ordered.map((location, index) => [location.key, index]));
|
|
78
|
-
const patterns = new Map();
|
|
79
79
|
const nodes = ordered.map((location) => {
|
|
80
80
|
const children = {};
|
|
81
81
|
walkSchema(location.schema, (child) => {
|
|
@@ -85,7 +85,6 @@ function portableProgram(root, locations, resolutions) {
|
|
|
85
85
|
if (target !== undefined)
|
|
86
86
|
children[childKey(child.pointer)] = target;
|
|
87
87
|
});
|
|
88
|
-
rememberPattern(patterns, location.schema);
|
|
89
88
|
const resolved = resolutions.get(location.key);
|
|
90
89
|
const reference = resolved === undefined
|
|
91
90
|
? undefined
|
|
@@ -102,29 +101,21 @@ function portableProgram(root, locations, resolutions) {
|
|
|
102
101
|
return Object.freeze({
|
|
103
102
|
root: indices.get(root.key),
|
|
104
103
|
nodes: Object.freeze(nodes),
|
|
105
|
-
patterns: Object.freeze([...patterns.values()]),
|
|
104
|
+
patterns: Object.freeze([...patterns.values()].map(({ pattern }) => pattern)),
|
|
106
105
|
});
|
|
107
106
|
}
|
|
108
|
-
function rememberPattern(patterns, schema) {
|
|
109
|
-
if (!isJsonObject(schema))
|
|
110
|
-
return;
|
|
111
|
-
for (const source of [schema.pattern, schema.fullMatch]) {
|
|
112
|
-
if (typeof source === 'string' && !patterns.has(source))
|
|
113
|
-
patterns.set(source, compilePattern(source));
|
|
114
|
-
}
|
|
115
|
-
if (isJsonObject(schema.patternProperties)) {
|
|
116
|
-
for (const source of Object.keys(schema.patternProperties)) {
|
|
117
|
-
if (!patterns.has(source))
|
|
118
|
-
patterns.set(source, compilePattern(source));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
107
|
export function childKey(pointer) {
|
|
123
108
|
return pointer.slice(1);
|
|
124
109
|
}
|
|
125
|
-
function rememberMatcher(matchers, input) {
|
|
110
|
+
function rememberMatcher(matchers, pool, input) {
|
|
126
111
|
if (typeof input === 'string' && !matchers.has(input)) {
|
|
127
|
-
|
|
112
|
+
let prepared = pool.get(input);
|
|
113
|
+
if (prepared === undefined) {
|
|
114
|
+
const pattern = compilePattern(input);
|
|
115
|
+
prepared = { pattern, matcher: loadPattern(pattern) };
|
|
116
|
+
pool.set(input, prepared);
|
|
117
|
+
}
|
|
118
|
+
matchers.set(input, prepared);
|
|
128
119
|
}
|
|
129
120
|
}
|
|
130
121
|
function failed(diagnostics) {
|
|
@@ -6,7 +6,7 @@ export function validateValueProgram(program, input, roots = [program.root]) {
|
|
|
6
6
|
if (canonical.length > 0)
|
|
7
7
|
return canonical;
|
|
8
8
|
return roots.flatMap((root) => {
|
|
9
|
-
const result = evaluateInstance(loadValueProgram(
|
|
9
|
+
const result = evaluateInstance(loadValueProgram(program, root), input);
|
|
10
10
|
return result.failures.map((failure) => ({
|
|
11
11
|
code: 'VALUE_SCHEMA_INSTANCE_INVALID',
|
|
12
12
|
pointer: failure.instancePointer,
|
|
@@ -2,11 +2,15 @@ import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
|
|
|
2
2
|
import type { Diagnostic } from '../../validation/diagnostic.js';
|
|
3
3
|
import type { JsonSchema } from '../model/schema.js';
|
|
4
4
|
import type { ValueSchemaRegistry } from '../reference/registry.js';
|
|
5
|
+
import type { CompiledInstanceSchema } from './types.js';
|
|
6
|
+
import { type PatternPool } from './preparation.js';
|
|
5
7
|
/**
|
|
6
8
|
* Validate one complete portable value with the admitted V1 Draft 2020-12 profile.
|
|
7
9
|
*
|
|
8
10
|
* Evaluation is an interpreter over the exact local resource closure. It never performs network
|
|
9
|
-
* resolution or dynamic source generation
|
|
11
|
+
* resolution or dynamic source generation; patterns use the portable executor.
|
|
10
12
|
* `format` remains annotation-only; the graph NodePath codec performs its separate locator check.
|
|
11
13
|
*/
|
|
12
|
-
export declare function validateJsonInstance(schema: JsonSchema, value: JsonValue, registry: ValueSchemaRegistry, pointer?: string): Diagnostic[];
|
|
14
|
+
export declare function validateJsonInstance(schema: JsonSchema, value: JsonValue, registry: ValueSchemaRegistry, pointer?: string, patterns?: PatternPool): Diagnostic[];
|
|
15
|
+
/** Validate with the exact preparation already retained by a source-resolved Property or Callable. */
|
|
16
|
+
export declare function validatePreparedInstance(compiled: CompiledInstanceSchema, value: unknown): Diagnostic[];
|