@astrale-os/kernel-dsl 0.2.0-beta.36 → 0.2.0-beta.38
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/builder/core/authoring.d.ts +7 -2
- package/dist/v1/builder/lowering/schema/context.d.ts +9 -4
- package/dist/v1/builder/lowering/schema/context.js +20 -20
- package/dist/v1/builder/policy/authoring.d.ts +6 -6
- package/dist/v1/builder/policy/authoring.js +3 -1
- package/dist/v1/compiled/admission/accept.js +97 -18
- package/dist/v1/compiled/admission/semantics.js +77 -24
- package/dist/v1/compiled/compiler/compile.js +23 -7
- package/dist/v1/compiled/loader/load.d.ts +11 -2
- package/dist/v1/compiled/loader/load.js +4 -1
- package/dist/v1/compiled/model/schema.d.ts +5 -4
- package/dist/v1/domain/create.js +17 -9
- package/dist/v1/index.d.ts +1 -1
- package/dist/v1/language/class/property-values.js +5 -2
- 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/index.d.ts +1 -1
- package/dist/v1/language/index.js +1 -1
- package/dist/v1/language/policy/index.d.ts +1 -1
- package/dist/v1/language/policy/index.js +1 -1
- package/dist/v1/language/policy/model/policy.d.ts +1 -0
- package/dist/v1/language/policy/model/policy.js +17 -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 +12 -7
- 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/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/regexp/compile.d.ts +3 -3
- package/dist/v1/language/value/regexp/compile.js +56 -11
- package/dist/v1/language/value/regexp/matcher.d.ts +3 -3
- package/dist/v1/language/value/regexp/matcher.js +75 -3
- package/dist/v1/schema/compatibility/footprint.js +6 -5
- package/dist/v1/schema/normalization/domain.js +15 -1
- package/dist/v1/schema/resolution/domain.js +17 -10
- package/dist/v1/schema/resolution/origins.d.ts +3 -0
- package/dist/v1/schema/resolution/origins.js +12 -4
- package/dist/v1/schema/validation/core.js +78 -26
- package/dist/v1/schema/validation/domain.structure.js +7 -2
- package/dist/value/accept.d.ts +9 -0
- package/dist/value/accept.js +78 -5
- package/dist/value/canonical.js +2 -2
- package/dist/value/immutable.optimization.d.ts +12 -0
- package/dist/value/immutable.optimization.js +28 -0
- package/dist/value/index.d.ts +1 -1
- package/dist/value/index.js +1 -1
- package/dist/value/unicode.d.ts +1 -1
- package/dist/value/unicode.js +1 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Object as JsonObject } from '@astrale-os/kernel-dsl/value';
|
|
2
|
-
import type { CoreRef } from '../../addressing/index.js';
|
|
3
2
|
import type { DomainEndpointRef } from '../../language/core/model/core.js';
|
|
3
|
+
import type { PolicyObjectInput } from '../callable/authoring.js';
|
|
4
4
|
import type { EdgeClass, NodeClass } from '../class/authoring.js';
|
|
5
5
|
import type { AUTHORING } from '../schema/handle.js';
|
|
6
6
|
import type { Lazy } from '../schema/lazy.js';
|
|
@@ -18,7 +18,12 @@ interface CoreEdgeConfiguration {
|
|
|
18
18
|
export interface CoreNode {
|
|
19
19
|
readonly [AUTHORING]: 'core-node';
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* The owning Domain, or any local Definition of it: a Class, Function, Method, Policy, View, or
|
|
23
|
+
* Core Node, authored as a handle, a lazy handle, or its canonical ref. A lazy handle lets a Core
|
|
24
|
+
* Edge name a declaration that only exists later in a module cycle.
|
|
25
|
+
*/
|
|
26
|
+
export type CoreEndpoint = PolicyObjectInput | DomainEndpointRef;
|
|
22
27
|
export interface CoreEdge {
|
|
23
28
|
readonly [AUTHORING]: 'core-edge';
|
|
24
29
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ClassRef, DefinitionRef, FunctionRef, PolicyRef, ViewRef } from '../../../addressing/index.js';
|
|
2
2
|
import type { DomainDependency } from '../../../addressing/index.js';
|
|
3
3
|
import type { ClassKind } from '../../../language/class/model/class.js';
|
|
4
4
|
import type { CoreEndpointRef } from '../../../language/core/model/core.js';
|
|
@@ -7,7 +7,7 @@ import type { ValueSchemaRegistry } from '../../../language/value/reference/regi
|
|
|
7
7
|
import type { DomainSchema, DomainSchemaIR } from '../../../schema/model/schema.js';
|
|
8
8
|
import type { Function, PolicyObjectInput } from '../../callable/authoring.js';
|
|
9
9
|
import type { Class, ClassReferenceInput } from '../../class/authoring.js';
|
|
10
|
-
import type { CoreNode } from '../../core/authoring.js';
|
|
10
|
+
import type { CoreEndpoint, CoreNode } from '../../core/authoring.js';
|
|
11
11
|
import type { Policy } from '../../policy/authoring.js';
|
|
12
12
|
import type { View } from '../../view/authoring.js';
|
|
13
13
|
import { type Lazy } from '../../schema/lazy.js';
|
|
@@ -19,7 +19,6 @@ export declare class BuilderContext {
|
|
|
19
19
|
readonly localDefinitions: Map<string, DefinitionRef<import("../../../addressing/definition-ref.js").Definition.Kind, string>>;
|
|
20
20
|
readonly localClassKinds: Map<string, ClassKind>;
|
|
21
21
|
readonly policies: Map<object, PolicyRef>;
|
|
22
|
-
readonly coreNodes: Map<CoreNode, CoreRef>;
|
|
23
22
|
readonly policyObjects: Map<object, PolicyObjectRef>;
|
|
24
23
|
readonly policyObjectRefs: Set<string>;
|
|
25
24
|
readonly usedForeignOrigins: Set<string>;
|
|
@@ -34,7 +33,13 @@ export declare class BuilderContext {
|
|
|
34
33
|
resolveEdgeClass(input: ClassReferenceInput, pointer: string): ClassRef;
|
|
35
34
|
resolvePolicy(input: Lazy<unknown>, pointer: string): PolicyRef;
|
|
36
35
|
resolvePolicyObject(input: PolicyObjectInput | PolicyObjectRef, pointer: string): PolicyObjectRef;
|
|
37
|
-
|
|
36
|
+
/** A Core fact belongs to the Domain that declares it, so a dependency endpoint never resolves. */
|
|
37
|
+
resolveCoreEndpoint(input: CoreEndpoint, pointer: string): CoreEndpointRef;
|
|
38
|
+
/**
|
|
39
|
+
* The shared lookup from one already-resolved authoring value to its ref; each caller owns its
|
|
40
|
+
* own admission rule and diagnostic.
|
|
41
|
+
*/
|
|
42
|
+
private policyObjectRef;
|
|
38
43
|
dependencies(): Readonly<Record<string, DomainDependency>>;
|
|
39
44
|
valueRegistry(): ValueSchemaRegistry;
|
|
40
45
|
noteDomainEndpointReferences(schema: DomainSchemaIR): void;
|
|
@@ -15,7 +15,6 @@ export class BuilderContext {
|
|
|
15
15
|
localDefinitions = new Map();
|
|
16
16
|
localClassKinds = new Map();
|
|
17
17
|
policies = new Map();
|
|
18
|
-
coreNodes = new Map();
|
|
19
18
|
policyObjects = new Map();
|
|
20
19
|
policyObjectRefs = new Set();
|
|
21
20
|
usedForeignOrigins = new Set();
|
|
@@ -59,7 +58,6 @@ export class BuilderContext {
|
|
|
59
58
|
captureCoreNode(value, slug, pointer) {
|
|
60
59
|
const ref = { origin: this.origin, kind: 'core', name: slug };
|
|
61
60
|
this.ownership.captureDefinition(value, ref, pointer);
|
|
62
|
-
this.coreNodes.set(value, ref);
|
|
63
61
|
this.capturePolicyObject(value, ref);
|
|
64
62
|
}
|
|
65
63
|
captureFunction(value, ref, pointer) {
|
|
@@ -110,12 +108,7 @@ export class BuilderContext {
|
|
|
110
108
|
return { ...ref };
|
|
111
109
|
}
|
|
112
110
|
resolvePolicyObject(input, pointer) {
|
|
113
|
-
const
|
|
114
|
-
const ref = isPolicyObjectRef(value)
|
|
115
|
-
? value
|
|
116
|
-
: typeof value === 'object' && value !== null
|
|
117
|
-
? (this.policyObjects.get(value) ?? this.ownership.definition(value))
|
|
118
|
-
: undefined;
|
|
111
|
+
const ref = this.policyObjectRef(resolveLazy(input, pointer));
|
|
119
112
|
const origin = ref === undefined ? undefined : policyObjectOrigin(ref);
|
|
120
113
|
const dependency = origin === undefined ? undefined : this.#dependencies.get(origin)?.schema;
|
|
121
114
|
const admitted = ref !== undefined &&
|
|
@@ -130,15 +123,30 @@ export class BuilderContext {
|
|
|
130
123
|
this.usedForeignOrigins.add(admittedOrigin);
|
|
131
124
|
return { ...ref };
|
|
132
125
|
}
|
|
133
|
-
|
|
126
|
+
/** A Core fact belongs to the Domain that declares it, so a dependency endpoint never resolves. */
|
|
127
|
+
resolveCoreEndpoint(input, pointer) {
|
|
128
|
+
const value = resolveLazy(input, pointer);
|
|
134
129
|
if (isDomainEndpointRef(value))
|
|
135
130
|
return { kind: 'domain' };
|
|
136
|
-
const ref =
|
|
137
|
-
if (ref === undefined ||
|
|
138
|
-
|
|
131
|
+
const ref = this.policyObjectRef(value);
|
|
132
|
+
if (ref === undefined ||
|
|
133
|
+
policyObjectOrigin(ref) !== this.origin ||
|
|
134
|
+
!this.policyObjectRefs.has(policyObjectKey(ref))) {
|
|
135
|
+
fail('BUILDER_CORE_REF', pointer, 'A Core Edge endpoint must name one registered local Definition, Function, Method, Policy, View, or Core Node.');
|
|
139
136
|
}
|
|
140
137
|
return { ...ref };
|
|
141
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* The shared lookup from one already-resolved authoring value to its ref; each caller owns its
|
|
141
|
+
* own admission rule and diagnostic.
|
|
142
|
+
*/
|
|
143
|
+
policyObjectRef(value) {
|
|
144
|
+
if (isPolicyObjectRef(value))
|
|
145
|
+
return value;
|
|
146
|
+
return typeof value === 'object' && value !== null
|
|
147
|
+
? (this.policyObjects.get(value) ?? this.ownership.definition(value))
|
|
148
|
+
: undefined;
|
|
149
|
+
}
|
|
142
150
|
dependencies() {
|
|
143
151
|
return Object.fromEntries([...this.#dependencies.values()]
|
|
144
152
|
.sort((left, right) => compareUnicode(left.alias, right.alias))
|
|
@@ -226,14 +234,6 @@ function isDefinitionRef(value) {
|
|
|
226
234
|
typeof candidate.name === 'string' &&
|
|
227
235
|
candidate.kind === 'class');
|
|
228
236
|
}
|
|
229
|
-
function isCoreRef(value) {
|
|
230
|
-
if (typeof value !== 'object' || value === null)
|
|
231
|
-
return false;
|
|
232
|
-
const candidate = value;
|
|
233
|
-
return (typeof candidate.origin === 'string' &&
|
|
234
|
-
candidate.kind === 'core' &&
|
|
235
|
-
typeof candidate.name === 'string');
|
|
236
|
-
}
|
|
237
237
|
function isPolicyObjectRef(value) {
|
|
238
238
|
if (typeof value !== 'object' || value === null)
|
|
239
239
|
return false;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { ClassRef } from '../../addressing/index.js';
|
|
2
2
|
import type { PolicyCheckExpression, PolicyPatternExpression, PolicyPatternTerm as CanonicalPolicyPatternTerm, PolicyObjectRef } from '../../language/policy/model/policy.js';
|
|
3
3
|
import type { PolicyObjectInput } from '../callable/authoring.js';
|
|
4
|
-
import type {
|
|
4
|
+
import type { EdgeClassReferenceInput, NodeClassReferenceInput } from '../class/authoring.js';
|
|
5
5
|
import type { AuthoringValue } from '../schema/handle.js';
|
|
6
6
|
import type { Lazy } from '../schema/lazy.js';
|
|
7
7
|
export interface PolicyEdgeInput {
|
|
8
8
|
readonly source: CanonicalPolicyPatternTerm;
|
|
9
|
-
readonly class:
|
|
9
|
+
readonly class: EdgeClassReferenceInput;
|
|
10
10
|
readonly target: CanonicalPolicyPatternTerm;
|
|
11
11
|
readonly repeat?: {
|
|
12
12
|
readonly min: number;
|
|
@@ -15,8 +15,8 @@ export interface PolicyEdgeInput {
|
|
|
15
15
|
}
|
|
16
16
|
export interface PolicyNodeBinder {
|
|
17
17
|
(): CanonicalPolicyPatternTerm;
|
|
18
|
-
(definition:
|
|
19
|
-
readonly exact: (definition:
|
|
18
|
+
(definition: NodeClassReferenceInput): CanonicalPolicyPatternTerm;
|
|
19
|
+
readonly exact: (definition: NodeClassReferenceInput) => CanonicalPolicyPatternTerm;
|
|
20
20
|
}
|
|
21
21
|
export interface PolicyExistsContext {
|
|
22
22
|
readonly node: PolicyNodeBinder;
|
|
@@ -54,8 +54,8 @@ export declare function policyDeclaration(value: Policy): PolicyDeclaration;
|
|
|
54
54
|
export declare const policy: PolicyBuilder;
|
|
55
55
|
export declare function createPatternContext(options: {
|
|
56
56
|
readonly resolveObject: (value: PolicyObjectInput, pointer: string) => PolicyObjectRef;
|
|
57
|
-
readonly resolveEdgeClass: (value:
|
|
58
|
-
readonly resolveNodeClass: (value:
|
|
57
|
+
readonly resolveEdgeClass: (value: EdgeClassReferenceInput, pointer: string) => ClassRef;
|
|
58
|
+
readonly resolveNodeClass: (value: NodeClassReferenceInput, pointer: string) => ClassRef;
|
|
59
59
|
readonly pointer: string;
|
|
60
60
|
}): PolicyPatternContext;
|
|
61
61
|
export type { PolicyCheckExpression };
|
|
@@ -71,7 +71,9 @@ export function createPatternContext(options) {
|
|
|
71
71
|
if (args.length > 1 || (args.length === 1 && args[0] === undefined)) {
|
|
72
72
|
fail('BUILDER_POLICY', `${options.pointer}/match/exists/nodes`, 'node(...) accepts either no arguments or one Node Class.');
|
|
73
73
|
}
|
|
74
|
-
return args.length === 0
|
|
74
|
+
return args.length === 0
|
|
75
|
+
? bind('any')
|
|
76
|
+
: bind('satisfies', args[0]);
|
|
75
77
|
};
|
|
76
78
|
Object.defineProperty(node, 'exact', {
|
|
77
79
|
enumerable: true,
|
|
@@ -5,6 +5,8 @@ import { DEPENDENCY_ALIAS_PATTERN, DOMAIN_ORIGIN_PATTERN, DOMAIN_REVISION_PATTER
|
|
|
5
5
|
import { MEDIA_TYPE_PATTERN } from '../../language/class/model/data.js';
|
|
6
6
|
import { PropertyValues } from '../../language/class/property-values.js';
|
|
7
7
|
import { admitStateTopology } from '../../language/state/topology.js';
|
|
8
|
+
import { bindValueProgram } from '../../language/value/compile/load.js';
|
|
9
|
+
import { isValueObject } from '../../language/value/model/object.js';
|
|
8
10
|
import { classifyValueProgramOrdering } from '../../language/value/ordering.js';
|
|
9
11
|
import { invalidCompiled } from '../diagnostic.js';
|
|
10
12
|
import { revisionOfCompiledEntry } from './revision.js';
|
|
@@ -19,10 +21,11 @@ export function acceptCompiledSchema(input) {
|
|
|
19
21
|
exact(root, ['format', 'version', 'root', 'closure'], 'CompiledSchema', '');
|
|
20
22
|
if (root.format !== 'astrale.dsl.compiled-schema')
|
|
21
23
|
invalid('format', undefined, '/format');
|
|
22
|
-
if (root.version !== 1)
|
|
24
|
+
if (root.version !== 1 && root.version !== 2)
|
|
23
25
|
invalid('version', undefined, '/version');
|
|
26
|
+
const version = root.version;
|
|
24
27
|
const rootPin = dependency(root.root, 'root', '/root');
|
|
25
|
-
const closure = array(root.closure, 'closure', '/closure').map((value, index) => entry(value, index, `/closure/${index}
|
|
28
|
+
const closure = array(root.closure, 'closure', '/closure').map((value, index) => entry(value, index, `/closure/${index}`, version));
|
|
26
29
|
if (closure.length > dependencyLimits.closure) {
|
|
27
30
|
invalid('dependency closure limit', 'COMPILED_DEPENDENCY_LIMITS', '/closure');
|
|
28
31
|
}
|
|
@@ -64,7 +67,7 @@ export function acceptCompiledSchema(input) {
|
|
|
64
67
|
}
|
|
65
68
|
const candidate = {
|
|
66
69
|
format: 'astrale.dsl.compiled-schema',
|
|
67
|
-
version:
|
|
70
|
+
version: 2,
|
|
68
71
|
root: rootPin,
|
|
69
72
|
closure,
|
|
70
73
|
};
|
|
@@ -72,9 +75,17 @@ export function acceptCompiledSchema(input) {
|
|
|
72
75
|
validateCompiledSemantics(candidate);
|
|
73
76
|
return deepFreeze(candidate);
|
|
74
77
|
}
|
|
75
|
-
function entry(input, entryIndex, path) {
|
|
78
|
+
function entry(input, entryIndex, path, version) {
|
|
76
79
|
const value = object(input, `closure[${entryIndex}]`, path);
|
|
77
|
-
exact(value, [
|
|
80
|
+
exact(value, [
|
|
81
|
+
'origin',
|
|
82
|
+
'revision',
|
|
83
|
+
'dependencies',
|
|
84
|
+
'definitions',
|
|
85
|
+
'facts',
|
|
86
|
+
'values',
|
|
87
|
+
...(version === 2 ? ['patterns'] : []),
|
|
88
|
+
], 'entry', path);
|
|
78
89
|
const origin = originValue(value.origin, 'entry origin', `${path}/origin`);
|
|
79
90
|
const revision = revisionValue(value.revision, 'entry revision', `${path}/revision`);
|
|
80
91
|
const dependenciesPath = `${path}/dependencies`;
|
|
@@ -86,14 +97,19 @@ function entry(input, entryIndex, path) {
|
|
|
86
97
|
dependencyOrigins.add(dependency.origin);
|
|
87
98
|
}
|
|
88
99
|
const valuesPath = `${path}/values`;
|
|
89
|
-
const
|
|
100
|
+
const patternsPath = `${path}/patterns`;
|
|
101
|
+
const patterns = version === 2
|
|
102
|
+
? array(value.patterns, 'entry patterns', patternsPath).map((item, index) => pattern(item, index, `${patternsPath}/${index}`, true))
|
|
103
|
+
: [];
|
|
104
|
+
const values = array(value.values, 'values', valuesPath).map((item, index) => program(item, index, `${valuesPath}/${index}`, patterns, version));
|
|
105
|
+
const bound = values.map((value) => bindValueProgram(value, patterns));
|
|
90
106
|
const definitionsPath = `${path}/definitions`;
|
|
91
107
|
const definitions = object(value.definitions, 'definitions', definitionsPath);
|
|
92
108
|
exact(definitions, ['classes', 'functions', 'policies', 'views', 'core'], 'definitions', definitionsPath);
|
|
93
109
|
const classesPath = `${definitionsPath}/classes`;
|
|
94
|
-
const classes = array(definitions.classes, 'classes', classesPath).map((item, index) => compiledClass(item,
|
|
110
|
+
const classes = array(definitions.classes, 'classes', classesPath).map((item, index) => compiledClass(item, bound, `${classesPath}/${index}`));
|
|
95
111
|
const functionsPath = `${definitionsPath}/functions`;
|
|
96
|
-
const functions = array(definitions.functions, 'functions', functionsPath).map((item, index) => compiledFunction(item,
|
|
112
|
+
const functions = array(definitions.functions, 'functions', functionsPath).map((item, index) => compiledFunction(item, bound, `${functionsPath}/${index}`));
|
|
97
113
|
const factsPath = `${path}/facts`;
|
|
98
114
|
const facts = object(value.facts, 'facts', factsPath);
|
|
99
115
|
exact(facts, ['core'], 'facts', factsPath);
|
|
@@ -116,6 +132,7 @@ function entry(input, entryIndex, path) {
|
|
|
116
132
|
core: array(facts.core, 'core facts', coreFactsPath).map((item, index) => compiledCoreEdge(item, `${coreFactsPath}/${index}`)),
|
|
117
133
|
},
|
|
118
134
|
values,
|
|
135
|
+
patterns,
|
|
119
136
|
};
|
|
120
137
|
const collections = [
|
|
121
138
|
['classes', accepted.definitions.classes],
|
|
@@ -554,18 +571,20 @@ function coreEndpoint(input, label, path) {
|
|
|
554
571
|
if (value.kind === 'domain')
|
|
555
572
|
exact(value, ['kind'], label, path);
|
|
556
573
|
else
|
|
557
|
-
|
|
574
|
+
projectedMemberRef(value, label, path);
|
|
558
575
|
}
|
|
559
576
|
function policyObjectRef(input, path) {
|
|
560
|
-
|
|
577
|
+
projectedMemberRef(object(input, 'Policy object ref', path), 'Policy object ref', path);
|
|
578
|
+
}
|
|
579
|
+
function projectedMemberRef(ref, label, path) {
|
|
561
580
|
if (ref.kind !== 'method') {
|
|
562
|
-
definitionRef(
|
|
581
|
+
definitionRef(ref, undefined, label, path);
|
|
563
582
|
return;
|
|
564
583
|
}
|
|
565
|
-
exact(ref, ['kind', 'owner', 'name'],
|
|
566
|
-
definitionRef(ref.owner, 'class',
|
|
584
|
+
exact(ref, ['kind', 'owner', 'name'], `${label} Method`, path);
|
|
585
|
+
definitionRef(ref.owner, 'class', `${label} Method owner`, `${path}/owner`);
|
|
567
586
|
if (typeof ref.name !== 'string' || !MEMBER_NAME_PATTERN.test(ref.name))
|
|
568
|
-
invalid(
|
|
587
|
+
invalid(`${label} Method name`, undefined, `${path}/name`);
|
|
569
588
|
}
|
|
570
589
|
function definitionRef(input, kind, label, path) {
|
|
571
590
|
if (!isDefinitionRef(input) || (kind !== undefined && input.kind !== kind))
|
|
@@ -752,16 +771,35 @@ function walk(input, path, visit) {
|
|
|
752
771
|
for (const [key, value] of Object.entries(input))
|
|
753
772
|
walk(value, pointer(path, key), visit);
|
|
754
773
|
}
|
|
755
|
-
function program(input, programIndex, path) {
|
|
774
|
+
function program(input, programIndex, path, table, version) {
|
|
756
775
|
const value = object(input, `values[${programIndex}]`, path);
|
|
757
776
|
exact(value, ['root', 'nodes', 'patterns'], 'ValueProgram', path);
|
|
758
777
|
const nodesPath = `${path}/nodes`;
|
|
759
778
|
const nodes = array(value.nodes, 'ValueProgram nodes', nodesPath).map((node, index) => valueNode(node, index, `${nodesPath}/${index}`));
|
|
760
779
|
const root = index(value.root, nodes.length, 'ValueProgram root', `${path}/root`);
|
|
761
780
|
const patternsPath = `${path}/patterns`;
|
|
762
|
-
const patterns = array(value.patterns, 'ValueProgram patterns', patternsPath).map((item,
|
|
763
|
-
|
|
781
|
+
const patterns = array(value.patterns, 'ValueProgram patterns', patternsPath).map((item, position) => {
|
|
782
|
+
const patternPath = `${patternsPath}/${position}`;
|
|
783
|
+
if (version === 2)
|
|
784
|
+
return index(item, table.length, 'ValueProgram pattern', patternPath);
|
|
785
|
+
// Legacy programs can carry different machines with the same source label. Preserve each.
|
|
786
|
+
return table.push(pattern(item, position, patternPath, false)) - 1;
|
|
787
|
+
});
|
|
788
|
+
const sources = patterns.map((index) => table[index].source);
|
|
789
|
+
unique(sources, 'patterns', patternsPath);
|
|
790
|
+
const available = new Set(sources);
|
|
764
791
|
for (const [nodeIndex, node] of nodes.entries()) {
|
|
792
|
+
if (isValueObject(node.schema)) {
|
|
793
|
+
const required = [node.schema.pattern, node.schema.fullMatch];
|
|
794
|
+
if (isValueObject(node.schema.patternProperties)) {
|
|
795
|
+
required.push(...Object.keys(node.schema.patternProperties));
|
|
796
|
+
}
|
|
797
|
+
for (const source of required) {
|
|
798
|
+
if (typeof source === 'string' && !available.has(source)) {
|
|
799
|
+
invalid('ValueProgram pattern binding', undefined, patternsPath);
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
765
803
|
for (const [name, child] of Object.entries(node.children))
|
|
766
804
|
index(child, nodes.length, 'ValueNode child', pointer(`${nodesPath}/${nodeIndex}/children`, name));
|
|
767
805
|
if (node.reference !== undefined)
|
|
@@ -785,8 +823,10 @@ function valueNode(input, nodeIndex, path) {
|
|
|
785
823
|
: { reference: integer(value.reference, 'reference', `${path}/reference`) }),
|
|
786
824
|
};
|
|
787
825
|
}
|
|
788
|
-
function pattern(input, patternIndex, path) {
|
|
826
|
+
function pattern(input, patternIndex, path, counted) {
|
|
789
827
|
const value = object(input, `patterns[${patternIndex}]`, path);
|
|
828
|
+
if (counted && Object.hasOwn(value, 'runs'))
|
|
829
|
+
return runPattern(value, path);
|
|
790
830
|
exact(value, ['source', 'start', 'states'], 'Pattern', path);
|
|
791
831
|
if (typeof value.source !== 'string')
|
|
792
832
|
invalid('Pattern source', undefined, `${path}/source`);
|
|
@@ -831,6 +871,45 @@ function pattern(input, patternIndex, path) {
|
|
|
831
871
|
}
|
|
832
872
|
return { source: value.source, start, states };
|
|
833
873
|
}
|
|
874
|
+
function runPattern(value, path) {
|
|
875
|
+
exact(value, ['source', 'runs'], 'RunPattern', path);
|
|
876
|
+
if (typeof value.source !== 'string')
|
|
877
|
+
invalid('Pattern source', undefined, `${path}/source`);
|
|
878
|
+
const entries = array(value.runs, 'scalar runs', `${path}/runs`);
|
|
879
|
+
if (entries.length === 0 || entries.length > 4096) {
|
|
880
|
+
invalid('scalar run count', undefined, `${path}/runs`);
|
|
881
|
+
}
|
|
882
|
+
const runs = entries.map((entry, position) => {
|
|
883
|
+
const runPath = `${path}/runs/${position}`;
|
|
884
|
+
const run = object(entry, 'scalar run', runPath);
|
|
885
|
+
exact(run, ['ranges', 'minimum'], 'scalar run', runPath, ['maximum']);
|
|
886
|
+
const minimum = boundedInteger(run.minimum, 0, 1000, 'run minimum', `${runPath}/minimum`);
|
|
887
|
+
const maximum = run.maximum === undefined
|
|
888
|
+
? undefined
|
|
889
|
+
: boundedInteger(run.maximum, minimum, 1000, 'run maximum', `${runPath}/maximum`);
|
|
890
|
+
if (position !== entries.length - 1 && minimum !== maximum) {
|
|
891
|
+
invalid('exact scalar prefix', undefined, `${runPath}/maximum`);
|
|
892
|
+
}
|
|
893
|
+
let previous = -1;
|
|
894
|
+
const ranges = array(run.ranges, 'scalar ranges', `${runPath}/ranges`).map((entry, index) => {
|
|
895
|
+
const rangePath = `${runPath}/ranges/${index}`;
|
|
896
|
+
const range = array(entry, 'scalar range', rangePath);
|
|
897
|
+
if (range.length !== 2)
|
|
898
|
+
invalid('scalar range', undefined, rangePath);
|
|
899
|
+
const minimum = scalar(range[0], 'range minimum', `${rangePath}/0`);
|
|
900
|
+
const maximum = scalar(range[1], 'range maximum', `${rangePath}/1`);
|
|
901
|
+
if (minimum > maximum ||
|
|
902
|
+
minimum <= previous ||
|
|
903
|
+
(minimum >= 0xd800 && minimum <= 0xdfff) ||
|
|
904
|
+
(maximum >= 0xd800 && maximum <= 0xdfff))
|
|
905
|
+
invalid('ordered scalar range', undefined, rangePath);
|
|
906
|
+
previous = maximum;
|
|
907
|
+
return [minimum, maximum];
|
|
908
|
+
});
|
|
909
|
+
return { ranges, minimum, ...(maximum === undefined ? {} : { maximum }) };
|
|
910
|
+
});
|
|
911
|
+
return { source: value.source, runs };
|
|
912
|
+
}
|
|
834
913
|
function dependency(input, label, path) {
|
|
835
914
|
const value = object(input, label, path);
|
|
836
915
|
exact(value, ['origin', 'revision'], label, path);
|
|
@@ -3,10 +3,11 @@ import { Key, PropertyKey as PropertyKeyValue } from '../../addressing/index.js'
|
|
|
3
3
|
import { compareUnicode } from '../../addressing/ordering.js';
|
|
4
4
|
import { composeMethods } from '../../language/callable/composition.js';
|
|
5
5
|
import { isMethodExecutable } from '../../language/callable/executable.js';
|
|
6
|
-
import { policyObjectOrigin, policyVariableClass } from '../../language/index.js';
|
|
6
|
+
import { policyObjectKey, policyObjectOrigin, policyVariableClass, projectedMemberClass, } from '../../language/index.js';
|
|
7
7
|
import { branchesOf, branchProduct, capBranches, isConnected, targetMode, termKey, } from '../../language/policy/analysis/branches.js';
|
|
8
8
|
import { BRANCH_OVERFLOW, MAX_BRANCHES, MAX_CHECK_DEPTH, MAX_CHECK_LEAVES, MAX_DOMAINS, MAX_EDGES, MAX_PATTERN_DEPTH, MAX_REPEAT, MAX_VARIABLES, measureChecks, } from '../../language/policy/analysis/budgets.js';
|
|
9
9
|
import { normalizePolicyExpression } from '../../language/policy/normalization/normalize.js';
|
|
10
|
+
import { bindValueProgram } from '../../language/value/compile/load.js';
|
|
10
11
|
import { validateValueProgram } from '../../language/value/evaluate/program.js';
|
|
11
12
|
import { GRAPH_NODE_PATH_SCHEMA_ID } from '../../language/value/syntax/node-path/schema.js';
|
|
12
13
|
import { invalidCompiled } from '../diagnostic.js';
|
|
@@ -140,7 +141,7 @@ function outputKey(entry, output) {
|
|
|
140
141
|
return output.mode === 'binary' ? 'binary' : `${output.mode}:${programKey(entry, output.value)}`;
|
|
141
142
|
}
|
|
142
143
|
function programKey(entry, value) {
|
|
143
|
-
return json.serialize(entry.values[value]);
|
|
144
|
+
return json.serialize(bindValueProgram(entry.values[value], entry.patterns));
|
|
144
145
|
}
|
|
145
146
|
function validateClassFacets(context) {
|
|
146
147
|
for (const located of context.classes.values()) {
|
|
@@ -984,8 +985,8 @@ function validateCore(candidate, context) {
|
|
|
984
985
|
if (analysis === undefined)
|
|
985
986
|
fail(`${path}/class`, 'Core Edge Class analysis is absent.');
|
|
986
987
|
validateCoreProperties(value.properties, analysis, `${path}/properties`);
|
|
987
|
-
const source = resolveCoreEndpoint(value.source, nodes, `${path}/source`);
|
|
988
|
-
const target = resolveCoreEndpoint(value.target, nodes, `${path}/target`);
|
|
988
|
+
const source = resolveCoreEndpoint(context, entry, value.source, nodes, `${path}/source`);
|
|
989
|
+
const target = resolveCoreEndpoint(context, entry, value.target, nodes, `${path}/target`);
|
|
989
990
|
const declaration = classValue.value;
|
|
990
991
|
const satisfies = declaration.orientation === 'directed'
|
|
991
992
|
? endpointSatisfies(context, source, declaration.endpoints.source.accepts) &&
|
|
@@ -1019,21 +1020,44 @@ function validateCoreProperties(values, analysis, path) {
|
|
|
1019
1020
|
}
|
|
1020
1021
|
if (property.declaration.auto !== undefined)
|
|
1021
1022
|
fail(`${path}/${escapePointer(property.key)}`, 'Core properties cannot author an automatic Property.');
|
|
1022
|
-
if (validateValueProgram(property.entry.values[property.declaration.value], values[property.key])
|
|
1023
|
-
.length > 0)
|
|
1023
|
+
if (validateValueProgram(bindValueProgram(property.entry.values[property.declaration.value], property.entry.patterns), values[property.key]).length > 0)
|
|
1024
1024
|
fail(`${path}/${escapePointer(property.key)}`, 'Core Property value does not satisfy its exact contract.');
|
|
1025
1025
|
}
|
|
1026
1026
|
}
|
|
1027
|
-
function resolveCoreEndpoint(ref, nodes, path) {
|
|
1027
|
+
function resolveCoreEndpoint(context, entry, ref, nodes, path) {
|
|
1028
1028
|
if (ref.kind === 'domain')
|
|
1029
1029
|
return { key: 'domain' };
|
|
1030
|
-
const
|
|
1031
|
-
if (
|
|
1032
|
-
|
|
1033
|
-
|
|
1030
|
+
const undeclared = () => fail(path, 'Core endpoint does not name a declared local Definition.');
|
|
1031
|
+
if (policyObjectOrigin(ref) !== entry.origin)
|
|
1032
|
+
return undeclared();
|
|
1033
|
+
if (ref.kind === 'core')
|
|
1034
|
+
return nodes.get(Key.of(ref)) ?? undeclared();
|
|
1035
|
+
if (!declaresProjectedMember(context, entry, ref))
|
|
1036
|
+
return undeclared();
|
|
1037
|
+
return { key: policyObjectKey(ref), class: projectedMemberClass(ref), pointer: path };
|
|
1038
|
+
}
|
|
1039
|
+
/** Only a projected declaration can carry a Core fact; an abstract Method is never projected. */
|
|
1040
|
+
function declaresProjectedMember(context, entry, ref) {
|
|
1041
|
+
if (ref.kind === 'method') {
|
|
1042
|
+
const owner = context.classes.get(Key.of(ref.owner))?.value;
|
|
1043
|
+
const method = owner?.kind === 'node' ? owner.methods.find((value) => value.name === ref.name) : undefined;
|
|
1044
|
+
return method !== undefined && isMethodExecutable(owner, method);
|
|
1045
|
+
}
|
|
1046
|
+
const declarations = ref.kind === 'class'
|
|
1047
|
+
? entry.definitions.classes
|
|
1048
|
+
: ref.kind === 'function'
|
|
1049
|
+
? entry.definitions.functions
|
|
1050
|
+
: ref.kind === 'policy'
|
|
1051
|
+
? entry.definitions.policies
|
|
1052
|
+
: entry.definitions.views;
|
|
1053
|
+
return declarations.some((declaration) => declaration.ref.name === ref.name);
|
|
1034
1054
|
}
|
|
1035
1055
|
function endpointSatisfies(context, endpoint, accepts) {
|
|
1036
|
-
|
|
1056
|
+
if ('value' in endpoint)
|
|
1057
|
+
return classSatisfiesAny(context, endpoint.value.class, accepts);
|
|
1058
|
+
if ('class' in endpoint)
|
|
1059
|
+
return classSatisfiesAny(context, endpoint.class, accepts);
|
|
1060
|
+
return true;
|
|
1037
1061
|
}
|
|
1038
1062
|
function validateCoreEdgeSet(context, entry, entryIndex, nodes, edges) {
|
|
1039
1063
|
const seen = new Set();
|
|
@@ -1064,27 +1088,56 @@ function validateCoreEdgeSet(context, entry, entryIndex, nodes, edges) {
|
|
|
1064
1088
|
}
|
|
1065
1089
|
}
|
|
1066
1090
|
function validateCardinality(context, declaration, edges, nodes) {
|
|
1067
|
-
for (const
|
|
1091
|
+
for (const subject of cardinalitySubjects(edges, nodes)) {
|
|
1068
1092
|
if (declaration.orientation === 'directed') {
|
|
1069
|
-
if (classSatisfiesAny(context,
|
|
1070
|
-
checkCount(declaration.endpoints.source.outgoing, edges.filter(({ source }) => source.key ===
|
|
1071
|
-
if (classSatisfiesAny(context,
|
|
1072
|
-
checkCount(declaration.endpoints.target.incoming, edges.filter(({ target }) => target.key ===
|
|
1093
|
+
if (classSatisfiesAny(context, subject.class, declaration.endpoints.source.accepts))
|
|
1094
|
+
checkCount(declaration.endpoints.source.outgoing, edges.filter(({ source }) => source.key === subject.key).length, subject.pointer, subject.minimum);
|
|
1095
|
+
if (classSatisfiesAny(context, subject.class, declaration.endpoints.target.accepts))
|
|
1096
|
+
checkCount(declaration.endpoints.target.incoming, edges.filter(({ target }) => target.key === subject.key).length, subject.pointer, subject.minimum);
|
|
1073
1097
|
}
|
|
1074
1098
|
else {
|
|
1075
|
-
const degree = edges.filter(({ source, target }) => source.key ===
|
|
1099
|
+
const degree = edges.filter(({ source, target }) => source.key === subject.key || target.key === subject.key).length;
|
|
1076
1100
|
const [first, second] = declaration.endpoints;
|
|
1077
|
-
if (classSatisfiesAny(context,
|
|
1078
|
-
checkCount(first.incident, degree,
|
|
1101
|
+
if (classSatisfiesAny(context, subject.class, first.accepts))
|
|
1102
|
+
checkCount(first.incident, degree, subject.pointer, subject.minimum);
|
|
1079
1103
|
if (!sameIncidentEndpoint(first, second) &&
|
|
1080
|
-
classSatisfiesAny(context,
|
|
1081
|
-
checkCount(second.incident, degree,
|
|
1104
|
+
classSatisfiesAny(context, subject.class, second.accepts))
|
|
1105
|
+
checkCount(second.incident, degree, subject.pointer, subject.minimum);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Every declared Core Node proves its own complete degree, so both bounds hold. A Definition
|
|
1111
|
+
* endpoint is only named by the Edges that reach it: the Domain may also create ordinary Edges of
|
|
1112
|
+
* the same Class against it, so a minimum is unprovable here and only the maximum is enforced.
|
|
1113
|
+
*/
|
|
1114
|
+
function cardinalitySubjects(edges, nodes) {
|
|
1115
|
+
const subjects = new Map();
|
|
1116
|
+
for (const node of nodes.values()) {
|
|
1117
|
+
subjects.set(node.key, {
|
|
1118
|
+
key: node.key,
|
|
1119
|
+
class: node.value.class,
|
|
1120
|
+
pointer: node.pointer,
|
|
1121
|
+
minimum: true,
|
|
1122
|
+
});
|
|
1123
|
+
}
|
|
1124
|
+
for (const edge of edges) {
|
|
1125
|
+
for (const endpoint of [edge.source, edge.target]) {
|
|
1126
|
+
if ('value' in endpoint || !('class' in endpoint) || subjects.has(endpoint.key))
|
|
1127
|
+
continue;
|
|
1128
|
+
subjects.set(endpoint.key, {
|
|
1129
|
+
key: endpoint.key,
|
|
1130
|
+
class: endpoint.class,
|
|
1131
|
+
pointer: endpoint.pointer,
|
|
1132
|
+
minimum: false,
|
|
1133
|
+
});
|
|
1082
1134
|
}
|
|
1083
1135
|
}
|
|
1136
|
+
return Object.freeze([...subjects.values()]);
|
|
1084
1137
|
}
|
|
1085
|
-
function checkCount(count, degree, path) {
|
|
1138
|
+
function checkCount(count, degree, path, enforceMinimum) {
|
|
1086
1139
|
const [minimum, maximum] = countBounds(count);
|
|
1087
|
-
if (degree
|
|
1140
|
+
if (degree > maximum || (degree < minimum && enforceMinimum))
|
|
1088
1141
|
fail(path, `Core degree ${degree} violates Edge count ${count}.`);
|
|
1089
1142
|
}
|
|
1090
1143
|
function countBounds(value) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { inputFieldNodes } from '../../language/value/compile/fields.js';
|
|
2
2
|
import { prepareValueSchema } from '../../language/value/evaluate/preparation.js';
|
|
3
|
-
import {
|
|
3
|
+
import { classifyPreparedOrdering } from '../../language/value/ordering.js';
|
|
4
4
|
import { revisionOfDomainSchema } from '../../schema/canonical/revision.js';
|
|
5
5
|
import { lookupForDomainClosure, requireDomainClosure } from '../../schema/resolution/closure.js';
|
|
6
6
|
import { createDomainValueSchemaRegistry } from '../../schema/resolution/value.registry.js';
|
|
@@ -16,7 +16,7 @@ export function compile(schema) {
|
|
|
16
16
|
const closure = Object.freeze(sources.map((source) => compileEntry(source, lookup)));
|
|
17
17
|
return Object.freeze({
|
|
18
18
|
format: 'astrale.dsl.compiled-schema',
|
|
19
|
-
version:
|
|
19
|
+
version: 2,
|
|
20
20
|
root: Object.freeze({ origin: schema.origin, revision: revisionOfDomainSchema(schema) }),
|
|
21
21
|
closure,
|
|
22
22
|
});
|
|
@@ -24,16 +24,31 @@ export function compile(schema) {
|
|
|
24
24
|
function compileEntry(schema, lookup) {
|
|
25
25
|
const registry = createDomainValueSchemaRegistry(schema, lookup);
|
|
26
26
|
const values = [];
|
|
27
|
-
const
|
|
28
|
-
|
|
27
|
+
const pool = new Map();
|
|
28
|
+
const patternIndices = new Map();
|
|
29
|
+
const prepare = (contract, pointer) => {
|
|
30
|
+
const result = prepareValueSchema(contract, registry, pointer, pool);
|
|
29
31
|
if (!result.compiled)
|
|
30
32
|
throw new TypeError(`Cannot compile ValueSchema: ${result.message}`);
|
|
31
|
-
return
|
|
33
|
+
return result.value;
|
|
32
34
|
};
|
|
35
|
+
const addProgram = (program) => {
|
|
36
|
+
const patterns = program.patterns.map((pattern) => {
|
|
37
|
+
let index = patternIndices.get(pattern);
|
|
38
|
+
if (index === undefined) {
|
|
39
|
+
index = patternIndices.size;
|
|
40
|
+
patternIndices.set(pattern, index);
|
|
41
|
+
}
|
|
42
|
+
return index;
|
|
43
|
+
});
|
|
44
|
+
return values.push(Object.freeze({ ...program, patterns: Object.freeze(patterns) })) - 1;
|
|
45
|
+
};
|
|
46
|
+
const add = (contract, pointer) => addProgram(prepare(contract, pointer).program());
|
|
33
47
|
const properties = (definition, pointer) => Object.entries(definition.properties)
|
|
34
48
|
.sort(([left], [right]) => compare(left, right))
|
|
35
49
|
.map(([name, property]) => {
|
|
36
|
-
const
|
|
50
|
+
const prepared = prepare(property.schema, `${pointer}/properties/${name}/schema`);
|
|
51
|
+
const ordering = classifyPreparedOrdering(prepared);
|
|
37
52
|
return {
|
|
38
53
|
name,
|
|
39
54
|
required: property.required,
|
|
@@ -41,7 +56,7 @@ function compileEntry(schema, lookup) {
|
|
|
41
56
|
...(property.description === undefined ? {} : { description: property.description }),
|
|
42
57
|
...(property.state === undefined ? {} : { state: property.state }),
|
|
43
58
|
...(ordering === undefined ? {} : { ordering }),
|
|
44
|
-
value:
|
|
59
|
+
value: addProgram(prepared.program()),
|
|
45
60
|
};
|
|
46
61
|
});
|
|
47
62
|
const output = (value, pointer) => value.mode === 'binary'
|
|
@@ -106,6 +121,7 @@ function compileEntry(schema, lookup) {
|
|
|
106
121
|
}),
|
|
107
122
|
facts: Object.freeze({ core: schema.core.edges }),
|
|
108
123
|
values: Object.freeze(values),
|
|
124
|
+
patterns: Object.freeze([...patternIndices.keys()]),
|
|
109
125
|
});
|
|
110
126
|
}
|
|
111
127
|
function compare(left, right) {
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ValueProgram } from '../../language/value/compile/model.js';
|
|
2
|
+
import type { CompiledSchema, CompiledSchemaEntry, DomainOfCompiled } from '../model/schema.js';
|
|
3
|
+
type LegacyEntry = Omit<CompiledSchemaEntry, 'values' | 'patterns'> & {
|
|
4
|
+
readonly values: readonly ValueProgram[];
|
|
5
|
+
};
|
|
6
|
+
type LegacyCompiledSchema = Omit<CompiledSchema, 'version' | 'closure'> & {
|
|
7
|
+
readonly version: 1;
|
|
8
|
+
readonly closure: readonly LegacyEntry[];
|
|
9
|
+
};
|
|
2
10
|
/** Load admitted compiled data through the one resolved Domain constructor. */
|
|
3
11
|
export declare function load<const Input extends CompiledSchema>(input: Input): DomainOfCompiled<Input>;
|
|
4
12
|
export declare function load(input: unknown): import('../../domain/model/domain.js').Domain;
|
|
@@ -6,4 +14,5 @@ export declare function load(input: unknown): import('../../domain/model/domain.
|
|
|
6
14
|
* Realize compiled material whose exact embedded bytes were already proven by generated build code.
|
|
7
15
|
* External bytes and caller-provided values must continue through {@link load}.
|
|
8
16
|
*/
|
|
9
|
-
export declare function realize(compiled: CompiledSchema): import('../../domain/model/domain.js').Domain;
|
|
17
|
+
export declare function realize(compiled: CompiledSchema | LegacyCompiledSchema): import('../../domain/model/domain.js').Domain;
|
|
18
|
+
export {};
|