@astrale-os/kernel-dsl 0.2.0-beta.37 → 0.2.0-beta.39
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/callable/composition.d.ts +13 -1
- package/dist/v1/language/callable/composition.js +13 -3
- 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 +78 -17
- 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/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/analysis/class.d.ts +21 -0
- package/dist/v1/schema/analysis/class.js +18 -9
- package/dist/v1/schema/compatibility/api.d.ts +2 -0
- package/dist/v1/schema/compatibility/api.js +1 -0
- package/dist/v1/schema/compatibility/comparators/callables.d.ts +28 -0
- package/dist/v1/schema/compatibility/comparators/callables.js +77 -0
- package/dist/v1/schema/compatibility/comparators/classes.d.ts +28 -0
- package/dist/v1/schema/compatibility/comparators/classes.js +96 -0
- package/dist/v1/schema/compatibility/comparators/index.d.ts +12 -0
- package/dist/v1/schema/compatibility/comparators/index.js +41 -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 -57
- package/dist/v1/schema/compatibility/compare.js +19 -162
- package/dist/v1/schema/compatibility/coordinate.d.ts +2 -7
- package/dist/v1/schema/compatibility/coordinate.js +2 -6
- package/dist/v1/schema/compatibility/evidence.d.ts +5 -0
- package/dist/v1/schema/compatibility/evidence.js +51 -0
- package/dist/v1/schema/compatibility/fields.d.ts +6 -0
- package/dist/v1/schema/compatibility/fields.js +5 -0
- package/dist/v1/schema/compatibility/index.d.ts +5 -4
- package/dist/v1/schema/compatibility/index.js +3 -2
- package/dist/v1/schema/compatibility/legacy/index.d.ts +58 -0
- package/dist/v1/schema/compatibility/legacy/index.js +145 -0
- package/dist/v1/schema/compatibility/meaning/footprint.d.ts +7 -0
- package/dist/v1/schema/compatibility/meaning/footprint.js +57 -0
- package/dist/v1/schema/compatibility/meaning/index.d.ts +3 -0
- package/dist/v1/schema/compatibility/meaning/index.js +14 -0
- package/dist/v1/schema/compatibility/model.d.ts +96 -0
- package/dist/v1/schema/compatibility/model.js +1 -0
- package/dist/v1/schema/compatibility/references.d.ts +30 -0
- package/dist/v1/schema/compatibility/references.js +122 -0
- package/dist/v1/schema/compatibility/report.d.ts +26 -0
- package/dist/v1/schema/compatibility/report.js +100 -0
- package/dist/v1/schema/compatibility/scope.d.ts +27 -0
- package/dist/v1/schema/compatibility/scope.js +78 -0
- package/dist/v1/schema/compatibility/structure/index.d.ts +3 -0
- package/dist/v1/schema/compatibility/structure/index.js +16 -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 +70 -0
- package/dist/v1/schema/compatibility/subjects.d.ts +34 -0
- package/dist/v1/schema/compatibility/subjects.js +86 -0
- package/dist/v1/schema/fingerprint.d.ts +2 -2
- package/dist/v1/schema/fingerprint.js +1 -1
- package/dist/v1/schema/index.d.ts +3 -0
- package/dist/v1/schema/index.js +2 -0
- 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/package.json +2 -2
- package/dist/v1/schema/compatibility/footprint.d.ts +0 -25
- package/dist/v1/schema/compatibility/footprint.js +0 -263
- package/dist/v1/schema/compatibility/meaning.d.ts +0 -44
- package/dist/v1/schema/compatibility/meaning.js +0 -234
|
@@ -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[];
|
|
@@ -1,31 +1,41 @@
|
|
|
1
1
|
import { diagnostic } from '../../validation/diagnostic.js';
|
|
2
2
|
import { validateCanonicalJsonDomain } from '../profile/json.validate.js';
|
|
3
3
|
import { evaluateInstance } from './evaluator.js';
|
|
4
|
-
import { prepareValueSchema } from './preparation.js';
|
|
4
|
+
import { prepareValueSchema, } from './preparation.js';
|
|
5
5
|
const validatorCache = new WeakMap();
|
|
6
6
|
/**
|
|
7
7
|
* Validate one complete portable value with the admitted V1 Draft 2020-12 profile.
|
|
8
8
|
*
|
|
9
9
|
* Evaluation is an interpreter over the exact local resource closure. It never performs network
|
|
10
|
-
* resolution or dynamic source generation
|
|
10
|
+
* resolution or dynamic source generation; patterns use the portable executor.
|
|
11
11
|
* `format` remains annotation-only; the graph NodePath codec performs its separate locator check.
|
|
12
12
|
*/
|
|
13
|
-
export function validateJsonInstance(schema, value, registry, pointer = '') {
|
|
13
|
+
export function validateJsonInstance(schema, value, registry, pointer = '', patterns) {
|
|
14
14
|
const diagnostics = [...validateCanonicalJsonDomain(value, pointer)];
|
|
15
15
|
if (diagnostics.length > 0)
|
|
16
16
|
return diagnostics;
|
|
17
|
-
const prepared = preparedInstanceValidator(schema, registry, pointer);
|
|
18
|
-
if (prepared.
|
|
17
|
+
const prepared = preparedInstanceValidator(schema, registry, pointer, patterns);
|
|
18
|
+
if (!prepared.compiled) {
|
|
19
19
|
return [
|
|
20
20
|
diagnostic('VS_REFERENCE_RESOLUTION', pointer, `Value validation failed closed while preparing exact schema resources: ${prepared.message}.`),
|
|
21
21
|
];
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
return instanceDiagnostics(prepared.value, value, pointer);
|
|
24
|
+
}
|
|
25
|
+
/** Validate with the exact preparation already retained by a source-resolved Property or Callable. */
|
|
26
|
+
export function validatePreparedInstance(compiled, value) {
|
|
27
|
+
const diagnostics = [...validateCanonicalJsonDomain(value, '')];
|
|
28
|
+
return diagnostics.length > 0
|
|
29
|
+
? diagnostics
|
|
30
|
+
: instanceDiagnostics(compiled, value, '');
|
|
31
|
+
}
|
|
32
|
+
function instanceDiagnostics(compiled, value, pointer) {
|
|
33
|
+
const result = evaluateInstance(compiled, value);
|
|
24
34
|
if (result.valid)
|
|
25
35
|
return [];
|
|
26
36
|
return result.failures.map((failure) => diagnostic('VALUE_SCHEMA_INSTANCE_INVALID', `${pointer}${failure.instancePointer}`, `Value does not satisfy its complete V1 schema at ${failure.keyword}: ${failure.message}`));
|
|
27
37
|
}
|
|
28
|
-
function preparedInstanceValidator(schema, registry, pointer) {
|
|
38
|
+
function preparedInstanceValidator(schema, registry, pointer, patterns) {
|
|
29
39
|
let bySchema = validatorCache.get(registry);
|
|
30
40
|
if (bySchema === undefined) {
|
|
31
41
|
bySchema = new Map();
|
|
@@ -34,10 +44,7 @@ function preparedInstanceValidator(schema, registry, pointer) {
|
|
|
34
44
|
const cached = bySchema.get(schema);
|
|
35
45
|
if (cached !== undefined)
|
|
36
46
|
return cached;
|
|
37
|
-
const
|
|
38
|
-
const prepared = compilation.compiled
|
|
39
|
-
? { kind: 'validator', compiled: compilation.value }
|
|
40
|
-
: { kind: 'error', message: compilation.message };
|
|
47
|
+
const prepared = prepareValueSchema(schema, registry, pointer, patterns);
|
|
41
48
|
bySchema.set(schema, prepared);
|
|
42
49
|
return prepared;
|
|
43
50
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ValueProgram } from './compile/model.js';
|
|
2
|
+
import type { CompiledInstanceSchema } from './evaluate/types.js';
|
|
2
3
|
import type { JsonSchema } from './model/schema.js';
|
|
3
4
|
import type { ValueSchemaRegistry } from './reference/registry.js';
|
|
4
5
|
export type OrderingKind = 'boolean' | 'number' | 'string' | 'instant';
|
|
@@ -11,3 +12,4 @@ export type OrderingKind = 'boolean' | 'number' | 'string' | 'instant';
|
|
|
11
12
|
export declare function classifyOrdering(contract: JsonSchema, registry: ValueSchemaRegistry): OrderingKind | undefined;
|
|
12
13
|
/** Recompute the scalar ordering classification from one admitted portable value program. */
|
|
13
14
|
export declare function classifyValueProgramOrdering(program: ValueProgram): OrderingKind | undefined;
|
|
15
|
+
export declare function classifyPreparedOrdering(compiled: CompiledInstanceSchema): OrderingKind | undefined;
|
|
@@ -18,17 +18,13 @@ export function classifyOrdering(contract, registry) {
|
|
|
18
18
|
const prepared = prepareValueSchema(contract, registry, '');
|
|
19
19
|
if (!prepared.compiled)
|
|
20
20
|
return undefined;
|
|
21
|
-
|
|
22
|
-
if (candidates.size !== 1)
|
|
23
|
-
return undefined;
|
|
24
|
-
const [kind] = candidates;
|
|
25
|
-
return kind === 'boolean' || kind === 'number' || kind === 'string' || kind === 'instant'
|
|
26
|
-
? kind
|
|
27
|
-
: undefined;
|
|
21
|
+
return classifyPreparedOrdering(prepared.value);
|
|
28
22
|
}
|
|
29
23
|
/** Recompute the scalar ordering classification from one admitted portable value program. */
|
|
30
24
|
export function classifyValueProgramOrdering(program) {
|
|
31
|
-
|
|
25
|
+
return classifyPreparedOrdering(loadValueProgram(program));
|
|
26
|
+
}
|
|
27
|
+
export function classifyPreparedOrdering(compiled) {
|
|
32
28
|
const candidates = new OrderingClassifier(compiled).classify(compiled.root);
|
|
33
29
|
if (candidates.size !== 1)
|
|
34
30
|
return undefined;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { Pattern } from '../compile/model.js';
|
|
2
|
-
/** Compile
|
|
3
|
-
export declare function compilePattern(source: string): Pattern;
|
|
1
|
+
import type { Pattern, RunPattern } from '../compile/model.js';
|
|
2
|
+
/** Compile scalar sequences as counted runs; preserve the NFA for branching expressions. */
|
|
3
|
+
export declare function compilePattern(source: string): Pattern | RunPattern;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { parseIRegexp } from './ast.js';
|
|
2
|
-
/** Compile
|
|
2
|
+
/** Compile scalar sequences as counted runs; preserve the NFA for branching expressions. */
|
|
3
3
|
export function compilePattern(source) {
|
|
4
4
|
const expression = parseIRegexp(source);
|
|
5
5
|
if (expression === undefined)
|
|
6
6
|
throw new TypeError('Cannot compile an invalid I-Regexp source.');
|
|
7
|
+
const runs = scalarRuns(expression);
|
|
8
|
+
if (runs !== undefined)
|
|
9
|
+
return Object.freeze({ source, runs });
|
|
7
10
|
const builder = new Builder();
|
|
8
11
|
const fragment = builder.expression(expression);
|
|
9
12
|
builder.states[fragment.end].accepting = true;
|
|
@@ -13,9 +16,45 @@ export function compilePattern(source) {
|
|
|
13
16
|
states: Object.freeze(builder.states.map(freezeState)),
|
|
14
17
|
});
|
|
15
18
|
}
|
|
19
|
+
function scalarRuns(expression) {
|
|
20
|
+
if (expression.alternatives.length !== 1)
|
|
21
|
+
return undefined;
|
|
22
|
+
const pieces = expression.alternatives[0].pieces;
|
|
23
|
+
let counted = false;
|
|
24
|
+
for (let index = 0; index < pieces.length; index++) {
|
|
25
|
+
const piece = pieces[index];
|
|
26
|
+
if (piece.atom.kind !== 'ranges' ||
|
|
27
|
+
(index !== pieces.length - 1 && piece.minimum !== piece.maximum))
|
|
28
|
+
return undefined;
|
|
29
|
+
if (piece.maximum !== undefined && piece.maximum > 1)
|
|
30
|
+
counted = true;
|
|
31
|
+
}
|
|
32
|
+
if (!counted)
|
|
33
|
+
return undefined;
|
|
34
|
+
const runs = [];
|
|
35
|
+
for (const piece of pieces) {
|
|
36
|
+
if (piece.atom.kind !== 'ranges')
|
|
37
|
+
return undefined;
|
|
38
|
+
runs.push(Object.freeze({
|
|
39
|
+
ranges: freezeRanges(piece.atom.ranges),
|
|
40
|
+
minimum: piece.minimum,
|
|
41
|
+
...(piece.maximum === undefined ? {} : { maximum: piece.maximum }),
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
return Object.freeze(runs);
|
|
45
|
+
}
|
|
46
|
+
function freezeRanges(ranges) {
|
|
47
|
+
if (Object.isFrozen(ranges))
|
|
48
|
+
return ranges;
|
|
49
|
+
for (const range of ranges)
|
|
50
|
+
Object.freeze(range);
|
|
51
|
+
return Object.freeze(ranges);
|
|
52
|
+
}
|
|
16
53
|
class Builder {
|
|
17
54
|
states = [];
|
|
18
55
|
expression(expression) {
|
|
56
|
+
if (expression.alternatives.length === 1)
|
|
57
|
+
return this.sequence(expression.alternatives[0]);
|
|
19
58
|
const start = this.state();
|
|
20
59
|
const end = this.state();
|
|
21
60
|
for (const alternative of expression.alternatives) {
|
|
@@ -26,20 +65,26 @@ class Builder {
|
|
|
26
65
|
return { start, end };
|
|
27
66
|
}
|
|
28
67
|
sequence(sequence) {
|
|
29
|
-
let output
|
|
30
|
-
for (const piece of sequence.pieces)
|
|
31
|
-
|
|
32
|
-
|
|
68
|
+
let output;
|
|
69
|
+
for (const piece of sequence.pieces) {
|
|
70
|
+
const fragment = this.piece(piece);
|
|
71
|
+
output = output === undefined ? fragment : this.concat(output, fragment);
|
|
72
|
+
}
|
|
73
|
+
return output ?? this.empty();
|
|
33
74
|
}
|
|
34
75
|
piece(piece) {
|
|
35
|
-
let output
|
|
76
|
+
let output;
|
|
36
77
|
for (let index = 0; index < piece.minimum; index++) {
|
|
37
|
-
|
|
78
|
+
const fragment = this.atom(piece.atom);
|
|
79
|
+
output = output === undefined ? fragment : this.concat(output, fragment);
|
|
80
|
+
}
|
|
81
|
+
if (piece.maximum === undefined) {
|
|
82
|
+
const repeated = this.star(piece.atom);
|
|
83
|
+
return output === undefined ? repeated : this.concat(output, repeated);
|
|
38
84
|
}
|
|
39
|
-
if (piece.maximum === undefined)
|
|
40
|
-
return this.concat(output, this.star(piece.atom));
|
|
41
85
|
if (piece.maximum === piece.minimum)
|
|
42
|
-
return output;
|
|
86
|
+
return output ?? this.empty();
|
|
87
|
+
output ??= this.empty();
|
|
43
88
|
const end = this.state();
|
|
44
89
|
for (let index = piece.minimum; index < piece.maximum; index++) {
|
|
45
90
|
// A shorter repetition exits the whole suffix, rather than skipping arbitrary copies.
|
|
@@ -87,7 +132,7 @@ class Builder {
|
|
|
87
132
|
function freezeState(state) {
|
|
88
133
|
return Object.freeze({
|
|
89
134
|
epsilon: Object.freeze(state.epsilon),
|
|
90
|
-
transitions: Object.freeze(state.transitions.map((transition) => Object.freeze({ to: transition.to, ranges:
|
|
135
|
+
transitions: Object.freeze(state.transitions.map((transition) => Object.freeze({ to: transition.to, ranges: freezeRanges(transition.ranges) }))),
|
|
91
136
|
...(state.accepting === true ? { accepting: true } : {}),
|
|
92
137
|
});
|
|
93
138
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { Pattern } from '../compile/model.js';
|
|
1
|
+
import type { Pattern, RunPattern } from '../compile/model.js';
|
|
2
2
|
export interface IRegexpMatcher {
|
|
3
3
|
test(value: string): boolean;
|
|
4
4
|
matches(value: string): boolean;
|
|
5
5
|
}
|
|
6
6
|
/**
|
|
7
|
-
* Load the portable Thompson NFA executor
|
|
7
|
+
* Load the portable counted-run or Thompson NFA executor for search and whole-string matching.
|
|
8
8
|
* Scratch belongs to one invocation: no match-result cache, shared mutable cursor or DFA expansion.
|
|
9
9
|
*/
|
|
10
|
-
export declare function loadPattern(pattern: Pattern): IRegexpMatcher;
|
|
10
|
+
export declare function loadPattern(pattern: Pattern | RunPattern): IRegexpMatcher;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Load the portable Thompson NFA executor
|
|
2
|
+
* Load the portable counted-run or Thompson NFA executor for search and whole-string matching.
|
|
3
3
|
* Scratch belongs to one invocation: no match-result cache, shared mutable cursor or DFA expansion.
|
|
4
4
|
*/
|
|
5
5
|
export function loadPattern(pattern) {
|
|
6
|
+
if ('runs' in pattern)
|
|
7
|
+
return loadRuns(pattern);
|
|
6
8
|
const states = pattern.states;
|
|
7
9
|
const start = initialFrontier(pattern);
|
|
8
10
|
const run = (input, search) => {
|
|
@@ -52,8 +54,8 @@ export function loadPattern(pattern) {
|
|
|
52
54
|
const scalar = character.codePointAt(0);
|
|
53
55
|
for (let index = 0; index < activeCount; index++) {
|
|
54
56
|
for (const transition of active[index].state.transitions) {
|
|
55
|
-
for (const
|
|
56
|
-
if (scalar <
|
|
57
|
+
for (const range of transition.ranges) {
|
|
58
|
+
if (scalar < range[0] || scalar > range[1])
|
|
57
59
|
continue;
|
|
58
60
|
add(transition.to);
|
|
59
61
|
break;
|
|
@@ -83,6 +85,76 @@ export function loadPattern(pattern) {
|
|
|
83
85
|
matches: (input) => run(input, false),
|
|
84
86
|
});
|
|
85
87
|
}
|
|
88
|
+
function loadRuns(pattern) {
|
|
89
|
+
const runs = pattern.runs;
|
|
90
|
+
const run = (input, search) => {
|
|
91
|
+
if (search && runs.length === 1) {
|
|
92
|
+
const scalarRun = runs[0];
|
|
93
|
+
if (scalarRun.minimum === 0)
|
|
94
|
+
return true;
|
|
95
|
+
let count = 0;
|
|
96
|
+
for (const character of input) {
|
|
97
|
+
count = includesScalar(scalarRun.ranges, character.codePointAt(0)) ? count + 1 : 0;
|
|
98
|
+
if (count >= scalarRun.minimum)
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
let start = 0;
|
|
104
|
+
do {
|
|
105
|
+
let offset = start;
|
|
106
|
+
let accepted = true;
|
|
107
|
+
for (let index = 0; index < runs.length; index++) {
|
|
108
|
+
const scalarRun = runs[index];
|
|
109
|
+
const maximum = search && index === runs.length - 1 ? scalarRun.minimum : (scalarRun.maximum ?? Infinity);
|
|
110
|
+
let count = 0;
|
|
111
|
+
while (count < maximum && offset < input.length) {
|
|
112
|
+
const scalar = input.codePointAt(offset);
|
|
113
|
+
if (!includesScalar(scalarRun.ranges, scalar))
|
|
114
|
+
break;
|
|
115
|
+
offset += scalar > 0xffff ? 2 : 1;
|
|
116
|
+
count++;
|
|
117
|
+
}
|
|
118
|
+
if (count < scalarRun.minimum) {
|
|
119
|
+
accepted = false;
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (accepted && (search || offset === input.length))
|
|
124
|
+
return true;
|
|
125
|
+
if (!search || start === input.length)
|
|
126
|
+
return false;
|
|
127
|
+
start += input.codePointAt(start) > 0xffff ? 2 : 1;
|
|
128
|
+
} while (start <= input.length);
|
|
129
|
+
return false;
|
|
130
|
+
};
|
|
131
|
+
return Object.freeze({
|
|
132
|
+
test: (input) => run(input, true),
|
|
133
|
+
matches: (input) => run(input, false),
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function includesScalar(ranges, scalar) {
|
|
137
|
+
if (ranges.length < 8) {
|
|
138
|
+
for (const range of ranges) {
|
|
139
|
+
if (scalar >= range[0] && scalar <= range[1])
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
let low = 0;
|
|
145
|
+
let high = ranges.length - 1;
|
|
146
|
+
while (low <= high) {
|
|
147
|
+
const middle = (low + high) >>> 1;
|
|
148
|
+
const range = ranges[middle];
|
|
149
|
+
if (scalar < range[0])
|
|
150
|
+
high = middle - 1;
|
|
151
|
+
else if (scalar > range[1])
|
|
152
|
+
low = middle + 1;
|
|
153
|
+
else
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
86
158
|
function initialFrontier(pattern) {
|
|
87
159
|
const start = pattern.start;
|
|
88
160
|
const seen = new Set([start]);
|
|
@@ -40,3 +40,24 @@ export interface DefinitionAnalysisResult {
|
|
|
40
40
|
readonly diagnostics: readonly SchemaDiagnostic[];
|
|
41
41
|
}
|
|
42
42
|
export declare function analyzeDefinitions(environment: DomainResolutionContext): DefinitionAnalysisResult;
|
|
43
|
+
/** The exact signature required by abstract Method composition. */
|
|
44
|
+
export declare function sameMethodSignature(left: MethodDefinition, right: MethodDefinition): boolean;
|
|
45
|
+
/** Shared canonical signature used by admission and compatibility explanations. */
|
|
46
|
+
export declare function methodSignature(value: MethodDefinition): {
|
|
47
|
+
input: JsonSchema;
|
|
48
|
+
output: {
|
|
49
|
+
mode: string;
|
|
50
|
+
schema: JsonSchema;
|
|
51
|
+
item?: undefined;
|
|
52
|
+
} | {
|
|
53
|
+
schema?: undefined;
|
|
54
|
+
mode: string;
|
|
55
|
+
item: JsonSchema;
|
|
56
|
+
} | {
|
|
57
|
+
schema?: undefined;
|
|
58
|
+
item?: undefined;
|
|
59
|
+
mode: string;
|
|
60
|
+
};
|
|
61
|
+
auth: "anonymous" | "authenticated" | "authorized";
|
|
62
|
+
static: boolean;
|
|
63
|
+
};
|
|
@@ -113,20 +113,29 @@ function mergeMethods(owner, declaration, parents, diagnostics) {
|
|
|
113
113
|
]));
|
|
114
114
|
return composeMethods(parents, own, declaration.abstract, (left, right) => sameMethodSignature(left.declaration, right.declaration), (message) => diagnostics.push(diagnostic('DV_METHOD_COMPOSITION', '', message, Key.of(owner))));
|
|
115
115
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
/** The exact signature required by abstract Method composition. */
|
|
117
|
+
export function sameMethodSignature(left, right) {
|
|
118
|
+
return json.serialize(methodSignature(left)) === json.serialize(methodSignature(right));
|
|
119
|
+
}
|
|
120
|
+
/** Shared canonical signature used by admission and compatibility explanations. */
|
|
121
|
+
export function methodSignature(value) {
|
|
122
|
+
return {
|
|
123
|
+
input: canonicalizeValueSchema(value.input, 'callable-input'),
|
|
124
|
+
output: canonicalMethodOutput(value),
|
|
125
|
+
auth: value.auth,
|
|
126
|
+
static: value.static,
|
|
127
|
+
};
|
|
122
128
|
}
|
|
123
129
|
function canonicalMethodOutput(value) {
|
|
124
130
|
switch (value.output.mode) {
|
|
125
131
|
case 'value':
|
|
126
|
-
return
|
|
132
|
+
return {
|
|
133
|
+
mode: 'value',
|
|
134
|
+
schema: canonicalizeValueSchema(value.output.schema, 'callable-output'),
|
|
135
|
+
};
|
|
127
136
|
case 'stream':
|
|
128
|
-
return
|
|
137
|
+
return { mode: 'stream', item: canonicalizeValueSchema(value.output.item, 'callable-output') };
|
|
129
138
|
case 'binary':
|
|
130
|
-
return 'binary';
|
|
139
|
+
return { mode: 'binary' };
|
|
131
140
|
}
|
|
132
141
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { compare, compareMeaning, compareStructure } from './compare.js';
|
|
2
|
+
export type { Analysis, Assessment, Comparison, ComparisonEntry, ComparisonEvidence, ComparisonRequest, ComparisonScope, Facet, Fingerprint, MeaningComparison, Observation, SchemaIdentity, StructureComparison, Subject, Usage, } from './model.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { compare, compareMeaning, compareStructure } from './compare.js';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Value } from '@astrale-os/kernel-dsl/value';
|
|
2
|
+
import type { CallableOutput, FunctionDefinition, MethodDefinition } from '../../../language/callable/model/callable.js';
|
|
3
|
+
import type { ResolvedFunction, ResolvedMethod } from '../../../language/callable/resolved.js';
|
|
4
|
+
import type { JsonSchema } from '../../../language/value/model/schema.js';
|
|
5
|
+
import type { EffectiveMethod } from '../../analysis/class.js';
|
|
6
|
+
import type { Fingerprint } from '../model.js';
|
|
7
|
+
export declare function methodExistence(method: EffectiveMethod): Value;
|
|
8
|
+
export declare function methodSignature(method: EffectiveMethod): Value;
|
|
9
|
+
export declare function methodInheritance(method: EffectiveMethod): Value;
|
|
10
|
+
export declare function functionSignature(declaration: FunctionDefinition): Value;
|
|
11
|
+
export declare function functionExistence(): Value;
|
|
12
|
+
/**
|
|
13
|
+
* Fingerprint the contract a caller compiles against, independently of its Domain revision: the
|
|
14
|
+
* exact key, the owner, dispatch and abstractness of a Method, and the semantic input and output
|
|
15
|
+
* contracts; authorization is excluded (DM-CALLABLE-FINGERPRINT). A static consumer pins this
|
|
16
|
+
* instead of a Domain revision, so a dependency release that leaves the callable contract untouched
|
|
17
|
+
* keeps every bound invocation valid.
|
|
18
|
+
*/
|
|
19
|
+
export declare function callableFingerprint(callable: ResolvedFunction | ResolvedMethod): Fingerprint;
|
|
20
|
+
type CallableSignature = {
|
|
21
|
+
readonly static?: boolean;
|
|
22
|
+
readonly abstract?: boolean;
|
|
23
|
+
readonly input: JsonSchema;
|
|
24
|
+
readonly output: CallableOutput;
|
|
25
|
+
};
|
|
26
|
+
/** The `always` fields of CALLABLE_FIELDS: dispatch shape and the semantic input and output. */
|
|
27
|
+
export declare function callableSignature(declaration: Pick<FunctionDefinition, 'input' | 'output'> | Pick<MethodDefinition, 'static' | 'abstract' | 'input' | 'output'>): CallableSignature;
|
|
28
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { methodSignature as inheritedSignature } from '../../analysis/class.js';
|
|
2
|
+
import { deepFreeze } from '../../canonical/freeze.js';
|
|
3
|
+
import { keyForDefinitionRef } from '../coordinate.js';
|
|
4
|
+
import { CALLABLE_FIELDS, fieldsOf } from '../fields.js';
|
|
5
|
+
import { fingerprintMeaning, semanticValueSchema } from './values.js';
|
|
6
|
+
export function methodExistence(method) {
|
|
7
|
+
return deepFreeze({
|
|
8
|
+
coordinate: 'method',
|
|
9
|
+
owner: keyForDefinitionRef(method.owner),
|
|
10
|
+
name: method.name,
|
|
11
|
+
static: method.declaration.static,
|
|
12
|
+
abstract: method.declaration.abstract,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
export function methodSignature(method) {
|
|
16
|
+
return deepFreeze({
|
|
17
|
+
coordinate: 'method',
|
|
18
|
+
owner: keyForDefinitionRef(method.owner),
|
|
19
|
+
name: method.name,
|
|
20
|
+
...callableSignature(method.declaration),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function methodInheritance(method) {
|
|
24
|
+
return deepFreeze({
|
|
25
|
+
coordinate: 'method',
|
|
26
|
+
owner: keyForDefinitionRef(method.owner),
|
|
27
|
+
name: method.name,
|
|
28
|
+
abstract: method.declaration.abstract,
|
|
29
|
+
...inheritedSignature(method.declaration),
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
export function functionSignature(declaration) {
|
|
33
|
+
return deepFreeze({
|
|
34
|
+
coordinate: 'function',
|
|
35
|
+
...callableSignature(declaration),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
export function functionExistence() {
|
|
39
|
+
return Object.freeze({ coordinate: 'function' });
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Fingerprint the contract a caller compiles against, independently of its Domain revision: the
|
|
43
|
+
* exact key, the owner, dispatch and abstractness of a Method, and the semantic input and output
|
|
44
|
+
* contracts; authorization is excluded (DM-CALLABLE-FINGERPRINT). A static consumer pins this
|
|
45
|
+
* instead of a Domain revision, so a dependency release that leaves the callable contract untouched
|
|
46
|
+
* keeps every bound invocation valid.
|
|
47
|
+
*/
|
|
48
|
+
export function callableFingerprint(callable) {
|
|
49
|
+
// This digest input travels in every bound invocation and is recomputed by the Kernel; its
|
|
50
|
+
// shape is frozen, `kind` included, so a Kernel upgrade never invalidates an unchanged pin.
|
|
51
|
+
return fingerprintMeaning('owner' in callable
|
|
52
|
+
? {
|
|
53
|
+
kind: 'method',
|
|
54
|
+
key: callable.key,
|
|
55
|
+
owner: callable.owner.key,
|
|
56
|
+
...callableSignature(callable),
|
|
57
|
+
}
|
|
58
|
+
: { kind: 'function', key: callable.key, ...callableSignature(callable) });
|
|
59
|
+
}
|
|
60
|
+
/** The `always` fields of CALLABLE_FIELDS: dispatch shape and the semantic input and output. */
|
|
61
|
+
export function callableSignature(declaration) {
|
|
62
|
+
return {
|
|
63
|
+
...fieldsOf({ declaration, roles: CALLABLE_FIELDS }),
|
|
64
|
+
input: semanticValueSchema(declaration.input),
|
|
65
|
+
output: semanticOutput(declaration.output),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function semanticOutput(output) {
|
|
69
|
+
switch (output.mode) {
|
|
70
|
+
case 'value':
|
|
71
|
+
return { mode: 'value', schema: semanticValueSchema(output.schema) };
|
|
72
|
+
case 'stream':
|
|
73
|
+
return { mode: 'stream', item: semanticValueSchema(output.item) };
|
|
74
|
+
case 'binary':
|
|
75
|
+
return { mode: 'binary' };
|
|
76
|
+
}
|
|
77
|
+
}
|