@astrale-os/kernel-dsl 1.0.0-beta.11 → 1.0.0-beta.13
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/definition-ref.js +21 -37
- package/dist/v1/addressing/ordering.d.ts +2 -0
- package/dist/v1/addressing/ordering.js +4 -0
- package/dist/v1/addressing/patterns.d.ts +0 -1
- package/dist/v1/addressing/patterns.js +0 -5
- package/dist/v1/builder/callable/authoring.d.ts +1 -1
- package/dist/v1/builder/class/authoring.d.ts +6 -256
- package/dist/v1/builder/class/authoring.js +5 -28
- 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 +129 -0
- package/dist/v1/builder/class/projection.d.ts +129 -0
- package/dist/v1/builder/class/projection.js +1 -0
- package/dist/v1/builder/core/authoring.d.ts +1 -1
- package/dist/v1/builder/index.d.ts +3 -1
- package/dist/v1/builder/lowering/class.d.ts +1 -1
- package/dist/v1/builder/lowering/schema/context.d.ts +2 -1
- package/dist/v1/builder/lowering/schema/context.js +2 -4
- 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 +1 -1
- package/dist/v1/builder/schema/define.d.ts +0 -1
- package/dist/v1/builder/schema/diagnostic.d.ts +2 -1
- 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/class.d.ts +2 -1
- 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/property.d.ts +1 -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 +2 -4
- package/dist/v1/bundle/diagnostic.js +3 -7
- package/dist/v1/bundle/structure.js +2 -4
- 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 +4 -8
- package/dist/v1/compiled/admission/semantics/core.d.ts +1 -24
- package/dist/v1/compiled/admission/semantics/core.js +54 -34
- 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 +13 -22
- 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/compiler/compile.js +6 -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 +10 -44
- package/dist/v1/domain/create.js +3 -5
- package/dist/v1/domain/namespace/member-collection.js +4 -6
- package/dist/v1/domain/validation/create.d.ts +0 -1
- package/dist/v1/domain/validation/create.js +0 -35
- package/dist/v1/language/class/inheritance.d.ts +18 -0
- package/dist/v1/language/class/inheritance.js +73 -0
- package/dist/v1/language/class/property-values.js +2 -4
- package/dist/v1/language/core/facts.d.ts +72 -0
- package/dist/v1/language/core/facts.js +95 -0
- package/dist/v1/language/index.d.ts +1 -0
- package/dist/v1/language/index.js +1 -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 +13 -1
- package/dist/v1/language/policy/analysis/budgets.js +104 -0
- package/dist/v1/language/policy/analysis/typing.d.ts +48 -0
- package/dist/v1/language/policy/analysis/typing.js +338 -0
- package/dist/v1/language/validation/diagnostic.d.ts +15 -1
- package/dist/v1/language/validation/diagnostic.js +21 -0
- package/dist/v1/language/value/compile/fields.js +4 -8
- 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 +9 -0
- package/dist/v1/language/value/ordering.js +72 -9
- package/dist/v1/language/value/profile/rules.js +5 -7
- package/dist/v1/language/value/profile/validate.js +1 -10
- package/dist/v1/language/value/reference/recursion.js +2 -22
- 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/compatibility/diff.js +2 -4
- package/dist/v1/schema/compatibility/meaning/footprint.js +16 -50
- package/dist/v1/schema/compatibility/structure/requirements.js +26 -58
- package/dist/v1/schema/compatibility/walk.d.ts +17 -0
- package/dist/v1/schema/compatibility/walk.js +41 -0
- package/dist/v1/schema/diagnostic.js +3 -11
- package/dist/v1/schema/resolution/context.js +3 -5
- package/dist/v1/schema/validation/callable.js +3 -5
- package/dist/v1/schema/validation/class.js +10 -52
- package/dist/v1/schema/validation/core.js +65 -199
- package/dist/v1/schema/validation/domain.js +1 -10
- package/dist/v1/schema/validation/domain.structure.js +2 -4
- package/dist/v1/schema/validation/policy/budgets.js +5 -112
- package/dist/v1/schema/validation/policy/context.d.ts +5 -3
- package/dist/v1/schema/validation/policy/context.js +5 -3
- 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 +5 -6
- 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 +1 -1
- package/dist/v1/schema/validation/policy/uses.js +9 -22
- package/dist/v1/schema/validation/policy/validate.js +3 -2
- package/dist/v1/schema/validation/value-api.d.ts +9 -0
- package/dist/v1/schema/validation/value-api.js +23 -1
- package/dist/v1/schema/validation/value.js +5 -7
- package/dist/v1/schema/validation/view.js +4 -8
- package/dist/value/canonical.d.ts +3 -2
- package/dist/value/canonical.js +30 -6
- 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 +1 -1
- package/dist/value/json.js +1 -1
- 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 +1 -1
- package/dist/v1/compiled/admission/semantics/cardinality.d.ts +0 -5
- package/dist/v1/compiled/admission/semantics/cardinality.js +0 -128
- 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/{language/core/index.js → builder/class/input.js} +0 -0
|
@@ -4,6 +4,7 @@ import { blob, BLOB_VALUE_KIND } from '../model/blob.js';
|
|
|
4
4
|
import { isValueObject as isJsonObject } from '../model/object.js';
|
|
5
5
|
import { VALUE_SCHEMA_DIALECT } from '../model/schema.js';
|
|
6
6
|
import { refuseIRegexp, stateBudgetMessage } from '../regexp/syntax.js';
|
|
7
|
+
import { escapePointerToken } from '../syntax/pointer.js';
|
|
7
8
|
import { walkSchema } from '../syntax/walk.js';
|
|
8
9
|
import { FORBIDDEN_DYNAMIC_KEYWORDS, STANDARD_SCHEMA_KEYWORDS } from './vocabulary.js';
|
|
9
10
|
export function validateSchemaProfile(schema, context, pointer) {
|
|
@@ -14,10 +15,10 @@ export function validateSchemaProfile(schema, context, pointer) {
|
|
|
14
15
|
const currentPointer = `${pointer}${location.pointer}`;
|
|
15
16
|
for (const keyword of Object.keys(location.schema)) {
|
|
16
17
|
if (!STANDARD_SCHEMA_KEYWORDS.has(keyword)) {
|
|
17
|
-
diagnostics.push(diagnostic('VS_KEYWORD_CLOSED', `${currentPointer}/${
|
|
18
|
+
diagnostics.push(diagnostic('VS_KEYWORD_CLOSED', `${currentPointer}/${escapePointerToken(keyword)}`, `Keyword ${keyword} is outside the closed V1 Draft 2020-12 vocabulary.`));
|
|
18
19
|
}
|
|
19
20
|
if (FORBIDDEN_DYNAMIC_KEYWORDS.has(keyword)) {
|
|
20
|
-
diagnostics.push(diagnostic('VS_DYNAMIC_FORBIDDEN', `${currentPointer}/${
|
|
21
|
+
diagnostics.push(diagnostic('VS_DYNAMIC_FORBIDDEN', `${currentPointer}/${escapePointerToken(keyword)}`, `Keyword ${keyword} is forbidden by V1.`));
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
if (Object.hasOwn(location.schema, '$schema')) {
|
|
@@ -86,7 +87,7 @@ function validateSemanticSets(schema, pointer, diagnostics) {
|
|
|
86
87
|
if (isJsonObject(schema.dependentRequired)) {
|
|
87
88
|
for (const [name, dependencies] of Object.entries(schema.dependentRequired)) {
|
|
88
89
|
if (Array.isArray(dependencies)) {
|
|
89
|
-
rejectPrimitiveDuplicates(dependencies, `${pointer}/dependentRequired/${
|
|
90
|
+
rejectPrimitiveDuplicates(dependencies, `${pointer}/dependentRequired/${escapePointerToken(name)}`, 'dependentRequired', diagnostics);
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
}
|
|
@@ -103,7 +104,7 @@ function validateRegexes(schema, pointer, diagnostics) {
|
|
|
103
104
|
}
|
|
104
105
|
if (isJsonObject(schema.patternProperties)) {
|
|
105
106
|
for (const pattern of Object.keys(schema.patternProperties)) {
|
|
106
|
-
portableRegex(pattern, `${pointer}/patternProperties/${
|
|
107
|
+
portableRegex(pattern, `${pointer}/patternProperties/${escapePointerToken(pattern)}`, 'A patternProperties key', 'patternProperties keys must satisfy RFC 9485 I-Regexp syntax.', diagnostics);
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
}
|
|
@@ -140,6 +141,3 @@ function rejectCanonicalDuplicates(values, pointer, name, diagnostics) {
|
|
|
140
141
|
seen.add(key);
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
|
-
function escapeToken(value) {
|
|
144
|
-
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
145
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { orderAndDeduplicate } from '../../validation/diagnostic.js';
|
|
2
2
|
import { collectResourceClosure } from '../reference/closure.js';
|
|
3
3
|
import { validateProductiveRecursion } from '../reference/recursion.js';
|
|
4
4
|
import { hasNodeDefinition, indexRegistry } from '../reference/registry.index.js';
|
|
@@ -28,12 +28,3 @@ export function validateValueSchema(schema, context, registry, pointer = '') {
|
|
|
28
28
|
diagnostics.push(...validateProductiveRecursion(closure.resources, closure.resolutions));
|
|
29
29
|
return orderAndDeduplicate(diagnostics);
|
|
30
30
|
}
|
|
31
|
-
function orderAndDeduplicate(diagnostics) {
|
|
32
|
-
const unique = new Map();
|
|
33
|
-
for (const value of diagnostics) {
|
|
34
|
-
unique.set(`${value.pointer}\u0000${value.code}\u0000${value.ref ?? ''}\u0000${value.message}`, value);
|
|
35
|
-
}
|
|
36
|
-
return [...unique.values()].sort((left, right) => compareUnicode(left.pointer, right.pointer) ||
|
|
37
|
-
compareUnicode(left.code, right.code) ||
|
|
38
|
-
compareUnicode(left.message, right.message));
|
|
39
|
-
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { diagnostic } from '../../validation/diagnostic.js';
|
|
2
2
|
import { isValueObject as isJsonObject } from '../model/object.js';
|
|
3
|
-
import { SCHEMA_ARRAY_KEYWORDS, SCHEMA_MAP_KEYWORDS, SCHEMA_SINGLE_KEYWORDS, } from '../syntax/keywords.js';
|
|
4
3
|
import { appendPointer } from '../syntax/pointer.js';
|
|
5
|
-
import {
|
|
4
|
+
import { forEachSubschema, walkSchema } from '../syntax/walk.js';
|
|
6
5
|
import { locationKey } from './closure.js';
|
|
7
6
|
export const CONSUMING_SCHEMA_KEYWORDS = new Set([
|
|
8
7
|
'additionalProperties',
|
|
@@ -43,26 +42,7 @@ export function validateProductiveRecursion(resources, resolutions) {
|
|
|
43
42
|
for (const [keyword, value] of Object.entries(location.schema)) {
|
|
44
43
|
if (!NON_CONSUMING_SCHEMA_KEYWORDS.has(keyword))
|
|
45
44
|
continue;
|
|
46
|
-
|
|
47
|
-
if (SCHEMA_SINGLE_KEYWORDS.has(keyword) && isJsonSchema(value)) {
|
|
48
|
-
outgoing.add(locationKey(resource.key, keywordPointer));
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
51
|
-
if (SCHEMA_ARRAY_KEYWORDS.has(keyword) && Array.isArray(value)) {
|
|
52
|
-
value.forEach((entry, index) => {
|
|
53
|
-
if (isJsonSchema(entry)) {
|
|
54
|
-
outgoing.add(locationKey(resource.key, appendPointer(keywordPointer, index)));
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (SCHEMA_MAP_KEYWORDS.has(keyword) && isJsonObject(value)) {
|
|
60
|
-
for (const [name, entry] of Object.entries(value)) {
|
|
61
|
-
if (isJsonSchema(entry)) {
|
|
62
|
-
outgoing.add(locationKey(resource.key, appendPointer(keywordPointer, name)));
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
45
|
+
forEachSubschema(keyword, value, appendPointer(location.pointer, keyword), (_, at) => outgoing.add(locationKey(resource.key, at)));
|
|
66
46
|
}
|
|
67
47
|
});
|
|
68
48
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Escape one JSON Pointer reference token: `~` as `~0`, then `/` as `~1`. */
|
|
2
|
+
export declare function escapePointerToken(token: string): string;
|
|
1
3
|
export declare function appendPointer(pointer: string, token: string | number): string;
|
|
2
4
|
export declare function parseJsonPointerFragment(fragment: string): readonly string[] | undefined;
|
|
3
5
|
export declare function tokensToPointer(tokens: readonly string[]): string;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
/** Escape one JSON Pointer reference token: `~` as `~0`, then `/` as `~1`. */
|
|
2
|
+
export function escapePointerToken(token) {
|
|
3
|
+
return token.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
4
|
+
}
|
|
1
5
|
export function appendPointer(pointer, token) {
|
|
2
|
-
|
|
3
|
-
return `${pointer}/${encoded}`;
|
|
6
|
+
return `${pointer}/${escapePointerToken(String(token))}`;
|
|
4
7
|
}
|
|
5
8
|
export function parseJsonPointerFragment(fragment) {
|
|
6
9
|
if (!fragment.startsWith('#'))
|
|
@@ -6,5 +6,7 @@ export interface SchemaLocation {
|
|
|
6
6
|
readonly parentKeyword?: string;
|
|
7
7
|
}
|
|
8
8
|
export declare function walkSchema(schema: JsonSchema, visit: (location: SchemaLocation) => void, pointer?: string): void;
|
|
9
|
+
/** Visit each subschema `keyword` holds directly, with its pointer under `pointer`. */
|
|
10
|
+
export declare function forEachSubschema(keyword: string, value: unknown, pointer: string, visit: (schema: JsonSchema, pointer: string) => void): void;
|
|
9
11
|
export declare function collectSchemaPointers(schema: JsonSchema): ReadonlySet<string>;
|
|
10
12
|
export declare function isJsonSchema(value: unknown): value is JsonSchema;
|
|
@@ -14,26 +14,7 @@ export function walkSchema(schema, visit, pointer = '') {
|
|
|
14
14
|
if (!isJsonObject(current))
|
|
15
15
|
return;
|
|
16
16
|
for (const [keyword, value] of Object.entries(current)) {
|
|
17
|
-
|
|
18
|
-
if (SCHEMA_SINGLE_KEYWORDS.has(keyword) && isJsonSchema(value)) {
|
|
19
|
-
walk(value, keywordPointer, depth + 1, keyword);
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
if (SCHEMA_ARRAY_KEYWORDS.has(keyword) && Array.isArray(value)) {
|
|
23
|
-
value.forEach((entry, index) => {
|
|
24
|
-
if (isJsonSchema(entry)) {
|
|
25
|
-
walk(entry, appendPointer(keywordPointer, index), depth + 1, keyword);
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (SCHEMA_MAP_KEYWORDS.has(keyword) && isJsonObject(value)) {
|
|
31
|
-
for (const [name, entry] of Object.entries(value)) {
|
|
32
|
-
if (isJsonSchema(entry)) {
|
|
33
|
-
walk(entry, appendPointer(keywordPointer, name), depth + 1, keyword);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
17
|
+
forEachSubschema(keyword, value, appendPointer(currentPointer, keyword), (entry, at) => walk(entry, at, depth + 1, keyword));
|
|
37
18
|
}
|
|
38
19
|
}
|
|
39
20
|
finally {
|
|
@@ -43,6 +24,24 @@ export function walkSchema(schema, visit, pointer = '') {
|
|
|
43
24
|
}
|
|
44
25
|
walk(schema, pointer, 0);
|
|
45
26
|
}
|
|
27
|
+
/** Visit each subschema `keyword` holds directly, with its pointer under `pointer`. */
|
|
28
|
+
export function forEachSubschema(keyword, value, pointer, visit) {
|
|
29
|
+
if (SCHEMA_SINGLE_KEYWORDS.has(keyword) && isJsonSchema(value)) {
|
|
30
|
+
visit(value, pointer);
|
|
31
|
+
}
|
|
32
|
+
else if (SCHEMA_ARRAY_KEYWORDS.has(keyword) && Array.isArray(value)) {
|
|
33
|
+
value.forEach((entry, index) => {
|
|
34
|
+
if (isJsonSchema(entry))
|
|
35
|
+
visit(entry, appendPointer(pointer, index));
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else if (SCHEMA_MAP_KEYWORDS.has(keyword) && isJsonObject(value)) {
|
|
39
|
+
for (const [name, entry] of Object.entries(value)) {
|
|
40
|
+
if (isJsonSchema(entry))
|
|
41
|
+
visit(entry, appendPointer(pointer, name));
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
46
45
|
export function collectSchemaPointers(schema) {
|
|
47
46
|
const pointers = new Set();
|
|
48
47
|
walkSchema(schema, ({ pointer }) => pointers.add(pointer));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { compareUnicode } from '../../addressing/ordering.js';
|
|
2
2
|
import { isValueObject as isJsonObject } from '../../language/value/model/object.js';
|
|
3
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
3
4
|
/** Structural differences between two portable values, leaf by leaf, in JSON Pointer order. */
|
|
4
5
|
export function differences(before, after) {
|
|
5
6
|
const found = [];
|
|
@@ -17,7 +18,7 @@ function collect(before, after, path, found) {
|
|
|
17
18
|
if (isJsonObject(before) && isJsonObject(after)) {
|
|
18
19
|
const keys = [...new Set([...Object.keys(before), ...Object.keys(after)])].sort(compareUnicode);
|
|
19
20
|
for (const key of keys) {
|
|
20
|
-
collect(before[key], after[key], `${path}/${
|
|
21
|
+
collect(before[key], after[key], `${path}/${escapePointerToken(key)}`, found);
|
|
21
22
|
}
|
|
22
23
|
return;
|
|
23
24
|
}
|
|
@@ -34,6 +35,3 @@ function collect(before, after, path, found) {
|
|
|
34
35
|
...(after === undefined ? {} : { after }),
|
|
35
36
|
}));
|
|
36
37
|
}
|
|
37
|
-
function escape(segment) {
|
|
38
|
-
return segment.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
39
|
-
}
|
|
@@ -1,59 +1,25 @@
|
|
|
1
|
-
import { Key } from '../../../addressing/index.js';
|
|
2
|
-
import { compareAddedProperties, compareLostProperties } from '../comparators/classes.js';
|
|
3
|
-
import { compareFacet } from '../comparators/index.js';
|
|
4
|
-
import { contextInvalid } from '../context.js';
|
|
5
1
|
import { callableReferences, classReferences, inherits, memberReferences, valueReferences, } from '../references.js';
|
|
2
|
+
import { walkReferences } from '../walk.js';
|
|
6
3
|
/** Meaning follows complete callable contracts through the exact authored reference closure. */
|
|
7
4
|
export function compareFootprint({ context, report, }) {
|
|
8
|
-
|
|
9
|
-
const visited = new Set();
|
|
10
|
-
for (const reference of queue) {
|
|
11
|
-
report.note(reference);
|
|
12
|
-
const { coordinate, usage } = reference;
|
|
13
|
-
const visit = `${coordinate.key}:${usage.relation}`;
|
|
14
|
-
if (visited.has(visit))
|
|
15
|
-
continue;
|
|
16
|
-
visited.add(visit);
|
|
17
|
-
const pair = context.pair(coordinate);
|
|
18
|
-
const subject = pair.source;
|
|
19
|
-
if (subject === undefined)
|
|
20
|
-
throw contextInvalid(`Retained acceptance context has no meaning for ${coordinate.key}.`, coordinate.key);
|
|
21
|
-
let facet;
|
|
5
|
+
walkReferences({ context, report }, (subject, { usage }, reach) => {
|
|
22
6
|
switch (subject.kind) {
|
|
23
|
-
case 'class':
|
|
24
|
-
|
|
25
|
-
queue.push(...classReferences(subject, false));
|
|
7
|
+
case 'class':
|
|
8
|
+
reach(...classReferences(subject, false));
|
|
26
9
|
if (inherits(usage))
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
10
|
+
reach(...classReferences(subject, true));
|
|
11
|
+
return inherits(usage) ? 'class.inherit' : 'class.reference';
|
|
30
12
|
case 'property':
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
break;
|
|
13
|
+
reach(...valueReferences(subject));
|
|
14
|
+
return 'property';
|
|
34
15
|
case 'method':
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
: subject.coordinate.ref.kind;
|
|
43
|
-
queue.push(...memberReferences(subject));
|
|
44
|
-
}
|
|
16
|
+
reach(...callableReferences(subject));
|
|
17
|
+
return 'method.signature';
|
|
18
|
+
case 'member':
|
|
19
|
+
reach(...memberReferences(subject));
|
|
20
|
+
return subject.coordinate.ref.kind === 'function'
|
|
21
|
+
? 'function.signature'
|
|
22
|
+
: subject.coordinate.ref.kind;
|
|
45
23
|
}
|
|
46
|
-
|
|
47
|
-
continue;
|
|
48
|
-
report.record({
|
|
49
|
-
pair,
|
|
50
|
-
facet,
|
|
51
|
-
...compareFacet({ context, pair, facet }),
|
|
52
|
-
requirement: { kind: facet },
|
|
53
|
-
});
|
|
54
|
-
if (facet === 'class.inherit')
|
|
55
|
-
compareAddedProperties({ context, pair, report });
|
|
56
|
-
if (subject.kind === 'class')
|
|
57
|
-
compareLostProperties({ pair, report });
|
|
58
|
-
}
|
|
24
|
+
});
|
|
59
25
|
}
|
|
@@ -1,79 +1,47 @@
|
|
|
1
|
-
import { Key } from '../../../addressing/index.js';
|
|
2
1
|
import { inheritanceOf } from '../../../language/class/inheritance.js';
|
|
3
|
-
import {
|
|
2
|
+
import { inheritanceMeaning } from '../comparators/classes.js';
|
|
4
3
|
import { compareFacet } from '../comparators/index.js';
|
|
5
4
|
import { fingerprintMeaning } from '../comparators/values.js';
|
|
6
5
|
import { contextInvalid } from '../context.js';
|
|
7
6
|
import { methodCoordinate } from '../coordinate.js';
|
|
8
7
|
import { closedParents } from '../extension.js';
|
|
9
8
|
import { callableReferences, classReferences, inherits, memberReferences, usageOf, valueReferences, } from '../references.js';
|
|
9
|
+
import { walkReferences } from '../walk.js';
|
|
10
10
|
/** Structure follows data and abstract obligations; concrete IO never expands its requirements. */
|
|
11
11
|
export function compareRequirements({ context, report, }) {
|
|
12
|
-
|
|
13
|
-
const visited = new Set();
|
|
14
|
-
for (const reference of queue) {
|
|
15
|
-
report.note(reference);
|
|
16
|
-
const { coordinate, usage } = reference;
|
|
17
|
-
const visit = `${coordinate.key}:${usage.relation}`;
|
|
18
|
-
if (visited.has(visit))
|
|
19
|
-
continue;
|
|
20
|
-
visited.add(visit);
|
|
21
|
-
const pair = context.pair(coordinate);
|
|
22
|
-
const subject = pair.source;
|
|
23
|
-
if (subject === undefined)
|
|
24
|
-
throw contextInvalid(`Retained acceptance context has no meaning for ${coordinate.key}.`, coordinate.key);
|
|
25
|
-
let facet;
|
|
12
|
+
walkReferences({ context, report }, (subject, { coordinate, usage }, reach) => {
|
|
26
13
|
switch (subject.kind) {
|
|
27
|
-
case 'class':
|
|
28
|
-
|
|
29
|
-
queue.push(...classReferences(subject, false));
|
|
14
|
+
case 'class':
|
|
15
|
+
reach(...classReferences(subject, false));
|
|
30
16
|
if (inherits(usage)) {
|
|
31
|
-
|
|
17
|
+
reach(...classReferences(subject, true));
|
|
32
18
|
for (const method of subject.analysis.requirements.values())
|
|
33
|
-
|
|
19
|
+
reach({
|
|
34
20
|
coordinate: methodCoordinate(method.owner, method.name),
|
|
35
21
|
usage: usageOf(subject.schema, 'abstract', coordinate.key),
|
|
36
22
|
});
|
|
37
23
|
}
|
|
38
|
-
|
|
39
|
-
}
|
|
24
|
+
return inherits(usage) ? 'class.inherit' : 'class.reference';
|
|
40
25
|
case 'property':
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
? 'function.existence'
|
|
54
|
-
: subject.coordinate.ref.kind;
|
|
55
|
-
if (facet !== 'function.existence')
|
|
56
|
-
queue.push(...memberReferences(subject));
|
|
57
|
-
}
|
|
26
|
+
reach(...valueReferences(subject));
|
|
27
|
+
return 'property';
|
|
28
|
+
case 'method':
|
|
29
|
+
if (usage.relation !== 'abstract')
|
|
30
|
+
return 'method.existence';
|
|
31
|
+
reach(...callableReferences(subject));
|
|
32
|
+
return 'method.inheritance';
|
|
33
|
+
case 'member':
|
|
34
|
+
if (subject.coordinate.ref.kind === 'function')
|
|
35
|
+
return 'function.existence';
|
|
36
|
+
reach(...memberReferences(subject));
|
|
37
|
+
return subject.coordinate.ref.kind;
|
|
58
38
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
usage.relation === 'capability';
|
|
66
|
-
report.record({
|
|
67
|
-
pair,
|
|
68
|
-
facet,
|
|
69
|
-
...compareFacet({ context, pair, facet }),
|
|
70
|
-
...(required ? { requirement: { kind: facet } } : {}),
|
|
71
|
-
});
|
|
72
|
-
if (facet === 'class.inherit')
|
|
73
|
-
compareAddedProperties({ context, pair, report });
|
|
74
|
-
if (subject.kind === 'class')
|
|
75
|
-
compareLostProperties({ pair, report });
|
|
76
|
-
}
|
|
39
|
+
},
|
|
40
|
+
// Class membership does not establish an invocation dependency. Explicit member selection,
|
|
41
|
+
// a Method the dependent declares as a capability, and inherited abstract obligations do.
|
|
42
|
+
(facet, usage) => facet !== 'method.existence' ||
|
|
43
|
+
usage.relation === 'selection' ||
|
|
44
|
+
usage.relation === 'capability');
|
|
77
45
|
compareReliance({ context, report });
|
|
78
46
|
compareExtension({ context, report });
|
|
79
47
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Usage } from './model.js';
|
|
2
|
+
import type { Reference } from './references.js';
|
|
3
|
+
import type { Report } from './report.js';
|
|
4
|
+
import type { ComparisonContext } from './scope.js';
|
|
5
|
+
import type { ResolvedSubject } from './subjects.js';
|
|
6
|
+
import { compareFacet } from './comparators/index.js';
|
|
7
|
+
type WalkedFacet = Parameters<typeof compareFacet>[0]['facet'];
|
|
8
|
+
/**
|
|
9
|
+
* Follow the reference closure from the roots once per coordinate and usage relation. `expand`
|
|
10
|
+
* names the facet a reached subject is compared under and `reach`es what it references; a subject
|
|
11
|
+
* of the compared origin is then recorded, as a requirement when `required` says so.
|
|
12
|
+
*/
|
|
13
|
+
export declare function walkReferences({ context, report }: {
|
|
14
|
+
readonly context: ComparisonContext;
|
|
15
|
+
readonly report: Report;
|
|
16
|
+
}, expand: (subject: ResolvedSubject, reference: Reference, reach: (...references: readonly Reference[]) => void) => WalkedFacet, required?: (facet: WalkedFacet, usage: Usage) => boolean): void;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Key } from '../../addressing/index.js';
|
|
2
|
+
import { compareAddedProperties, compareLostProperties } from './comparators/classes.js';
|
|
3
|
+
import { compareFacet } from './comparators/index.js';
|
|
4
|
+
import { contextInvalid } from './context.js';
|
|
5
|
+
/**
|
|
6
|
+
* Follow the reference closure from the roots once per coordinate and usage relation. `expand`
|
|
7
|
+
* names the facet a reached subject is compared under and `reach`es what it references; a subject
|
|
8
|
+
* of the compared origin is then recorded, as a requirement when `required` says so.
|
|
9
|
+
*/
|
|
10
|
+
export function walkReferences({ context, report }, expand, required = () => true) {
|
|
11
|
+
const queue = [...context.roots];
|
|
12
|
+
const reach = (...references) => {
|
|
13
|
+
queue.push(...references);
|
|
14
|
+
};
|
|
15
|
+
const visited = new Set();
|
|
16
|
+
for (const reference of queue) {
|
|
17
|
+
report.note(reference);
|
|
18
|
+
const { coordinate, usage } = reference;
|
|
19
|
+
const visit = `${coordinate.key}:${usage.relation}`;
|
|
20
|
+
if (visited.has(visit))
|
|
21
|
+
continue;
|
|
22
|
+
visited.add(visit);
|
|
23
|
+
const pair = context.pair(coordinate);
|
|
24
|
+
const subject = pair.source;
|
|
25
|
+
if (subject === undefined)
|
|
26
|
+
throw contextInvalid(`Retained acceptance context has no meaning for ${coordinate.key}.`, coordinate.key);
|
|
27
|
+
const facet = expand(subject, reference, reach);
|
|
28
|
+
if (Key.origin(coordinate.key) !== context.source.origin)
|
|
29
|
+
continue;
|
|
30
|
+
report.record({
|
|
31
|
+
pair,
|
|
32
|
+
facet,
|
|
33
|
+
...compareFacet({ context, pair, facet }),
|
|
34
|
+
...(required(facet, usage) ? { requirement: { kind: facet } } : {}),
|
|
35
|
+
});
|
|
36
|
+
if (facet === 'class.inherit')
|
|
37
|
+
compareAddedProperties({ context, pair, report });
|
|
38
|
+
if (subject.kind === 'class')
|
|
39
|
+
compareLostProperties({ pair, report });
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,21 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatDiagnostics, orderDiagnostics } from '../language/validation/diagnostic.js';
|
|
2
2
|
export function diagnostic(code, pointer, message, ref) {
|
|
3
3
|
return Object.freeze({ code, pointer, message, ...(ref === undefined ? {} : { ref }) });
|
|
4
4
|
}
|
|
5
5
|
export class SchemaError extends Error {
|
|
6
6
|
diagnostics;
|
|
7
7
|
constructor(diagnostics) {
|
|
8
|
-
const ordered =
|
|
9
|
-
super(ordered
|
|
8
|
+
const ordered = orderDiagnostics(diagnostics);
|
|
9
|
+
super(formatDiagnostics(ordered));
|
|
10
10
|
this.name = 'SchemaError';
|
|
11
11
|
this.diagnostics = Object.freeze(ordered);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
function compareDiagnostic(left, right) {
|
|
15
|
-
return (compareUnicode(left.pointer, right.pointer) ||
|
|
16
|
-
compareUnicode(left.code, right.code) ||
|
|
17
|
-
compareUnicode(left.message, right.message));
|
|
18
|
-
}
|
|
19
|
-
function formatDiagnostic(value) {
|
|
20
|
-
return `${value.pointer || '/'} [${value.code}] ${value.message}`;
|
|
21
|
-
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { dependencyLimits } from '../../addressing/domain-dependency.js';
|
|
2
2
|
import { definitionRefIndex } from '../../addressing/ordering.js';
|
|
3
3
|
import { DOMAIN_REVISION_PATTERN } from '../../addressing/patterns.js';
|
|
4
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
4
5
|
import { revisionOfDomainSchema } from '../canonical/revision.js';
|
|
5
6
|
import { diagnostic } from '../diagnostic.js';
|
|
6
7
|
import { indexSchemaDefinitions } from './definition.index.js';
|
|
@@ -14,7 +15,7 @@ export function buildDomainResolutionContext(candidate, lookup) {
|
|
|
14
15
|
diagnostics.push(diagnostic('DV_DEPENDENCY_LIMITS', '/dependencies', `A Domain may declare at most ${dependencyLimits.direct} direct dependency aliases.`));
|
|
15
16
|
}
|
|
16
17
|
for (const [alias, dependency] of Object.entries(candidate.dependencies)) {
|
|
17
|
-
const pointer = `/dependencies/${
|
|
18
|
+
const pointer = `/dependencies/${escapePointerToken(alias)}`;
|
|
18
19
|
if (!DOMAIN_REVISION_PATTERN.test(dependency.revision))
|
|
19
20
|
continue;
|
|
20
21
|
if (dependency.origin === candidate.origin) {
|
|
@@ -94,7 +95,7 @@ function loadDependencyClosure(candidate, dependencies, lookup, diagnostics) {
|
|
|
94
95
|
continue;
|
|
95
96
|
}
|
|
96
97
|
const [alias, dependency] = edge;
|
|
97
|
-
const pointer = frames.length === 1 ? `/dependencies/${
|
|
98
|
+
const pointer = frames.length === 1 ? `/dependencies/${escapePointerToken(alias)}` : '/dependencies';
|
|
98
99
|
if (activeOrigins.has(dependency.origin)) {
|
|
99
100
|
diagnostics.push(diagnostic('DV_DEPENDENCY_ACYCLIC', pointer, `Dependency cycle reaches ${dependency.origin}.`));
|
|
100
101
|
continue;
|
|
@@ -153,6 +154,3 @@ function validateDependencyUse(candidate, directOrigins, diagnostics) {
|
|
|
153
154
|
}
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
|
-
function escapeToken(value) {
|
|
157
|
-
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
158
|
-
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { isAdmissionMaxAge, MAXIMUM_ADMISSION_AGE_SECONDS, } from '../../language/callable/admission.js';
|
|
2
2
|
import { isBinaryInput } from '../../language/callable/model/callable.js';
|
|
3
|
+
import { escapePointerToken } from '../../language/value/syntax/pointer.js';
|
|
3
4
|
import { diagnostic } from '../diagnostic.js';
|
|
4
5
|
/** Validate the execution modifier and admission age of every local Function and Node Method. */
|
|
5
6
|
export function validateCallables(schema) {
|
|
6
7
|
const diagnostics = [];
|
|
7
8
|
for (const [name, declaration] of Object.entries(schema.functions)) {
|
|
8
|
-
const pointer = `/functions/${
|
|
9
|
+
const pointer = `/functions/${escapePointerToken(name)}`;
|
|
9
10
|
validateExecution(declaration, pointer, diagnostics);
|
|
10
11
|
validateAdmission(declaration, pointer, diagnostics);
|
|
11
12
|
}
|
|
@@ -13,7 +14,7 @@ export function validateCallables(schema) {
|
|
|
13
14
|
if (declaration.kind !== 'node')
|
|
14
15
|
continue;
|
|
15
16
|
for (const [method, value] of Object.entries(declaration.methods)) {
|
|
16
|
-
const pointer = `/classes/${
|
|
17
|
+
const pointer = `/classes/${escapePointerToken(name)}/methods/${escapePointerToken(method)}`;
|
|
17
18
|
validateExecution(value, pointer, diagnostics);
|
|
18
19
|
validateAdmission(value, pointer, diagnostics);
|
|
19
20
|
}
|
|
@@ -49,6 +50,3 @@ function validateExecution(declaration, pointer, diagnostics) {
|
|
|
49
50
|
diagnostics.push(diagnostic('DV_CALLABLE_IO', `${pointer}/output/mode`, 'A durable callable requires a value output.'));
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
|
-
function escapePointer(value) {
|
|
53
|
-
return value.replaceAll('~', '~0').replaceAll('/', '~1');
|
|
54
|
-
}
|