@astrale-os/kernel-dsl 0.2.0-beta.45 → 0.2.0-beta.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/v1/addressing/class-key.js +5 -4
- package/dist/v1/addressing/method-key.js +3 -2
- package/dist/v1/builder/class/property.d.ts +3 -1
- package/dist/v1/builder/class/property.js +9 -1
- package/dist/v1/builder/lowering/schema/compile.js +1 -1
- package/dist/v1/builder/lowering/value/compile.js +3 -0
- package/dist/v1/builder/lowering/value/zod.js +11 -2
- package/dist/v1/builder/lowering/view.d.ts +2 -2
- package/dist/v1/builder/lowering/view.js +2 -14
- package/dist/v1/builder/schema/diagnostic.d.ts +1 -1
- package/dist/v1/builder/value/full-match.js +6 -3
- package/dist/v1/builder/view/authoring.d.ts +3 -7
- package/dist/v1/builder/view/authoring.js +5 -2
- package/dist/v1/compiled/admission/accept.js +31 -6
- package/dist/v1/compiled/admission/semantics.js +49 -16
- package/dist/v1/compiled/compiler/compile.js +1 -0
- package/dist/v1/compiled/loader/load.js +1 -0
- package/dist/v1/domain/create.js +0 -19
- package/dist/v1/domain/model/domain.d.ts +0 -1
- package/dist/v1/domain/model/input.d.ts +3 -0
- package/dist/v1/domain/projection/classes.js +1 -0
- package/dist/v1/domain/validation/create.d.ts +2 -0
- package/dist/v1/domain/validation/create.js +12 -0
- package/dist/v1/language/class/model/property.d.ts +3 -0
- package/dist/v1/language/class/resolved.d.ts +3 -0
- package/dist/v1/language/index.d.ts +2 -0
- package/dist/v1/language/index.js +1 -0
- package/dist/v1/language/policy/normalization/normalize.d.ts +4 -0
- package/dist/v1/language/policy/normalization/normalize.js +51 -13
- package/dist/v1/language/value/inclusion/includes.d.ts +9 -0
- package/dist/v1/language/value/inclusion/includes.js +777 -0
- package/dist/v1/language/value/profile/rules.js +11 -32
- package/dist/v1/language/value/regexp/ast.js +3 -64
- package/dist/v1/language/value/regexp/compile.d.ts +25 -2
- package/dist/v1/language/value/regexp/compile.js +48 -12
- package/dist/v1/language/value/regexp/syntax.d.ts +11 -1
- package/dist/v1/language/value/regexp/syntax.js +14 -2
- package/dist/v1/language/value/regexp/unicode.d.ts +32 -0
- package/dist/v1/language/value/regexp/unicode.js +139 -0
- package/dist/v1/language/value/syntax/full-match.js +6 -3
- package/dist/v1/language/view/model/view.d.ts +6 -0
- package/dist/v1/language/view/resolved.d.ts +2 -2
- package/dist/v1/schema/analysis/class.d.ts +2 -1
- package/dist/v1/schema/analysis/class.js +1 -0
- package/dist/v1/schema/compatibility/comparators/callables.d.ts +7 -0
- package/dist/v1/schema/compatibility/comparators/callables.js +13 -0
- package/dist/v1/schema/compatibility/comparators/classes.d.ts +18 -0
- package/dist/v1/schema/compatibility/comparators/classes.js +54 -0
- package/dist/v1/schema/compatibility/comparators/index.d.ts +1 -1
- package/dist/v1/schema/compatibility/comparators/index.js +14 -2
- package/dist/v1/schema/compatibility/context.d.ts +6 -2
- package/dist/v1/schema/compatibility/context.js +12 -6
- package/dist/v1/schema/compatibility/evidence.d.ts +8 -1
- package/dist/v1/schema/compatibility/evidence.js +10 -1
- package/dist/v1/schema/compatibility/fields.d.ts +2 -0
- package/dist/v1/schema/compatibility/fields.js +1 -0
- package/dist/v1/schema/compatibility/meaning/footprint.js +3 -1
- package/dist/v1/schema/compatibility/meaning/index.js +4 -2
- package/dist/v1/schema/compatibility/model.d.ts +11 -6
- package/dist/v1/schema/compatibility/references.d.ts +8 -1
- package/dist/v1/schema/compatibility/references.js +39 -6
- package/dist/v1/schema/compatibility/scope.d.ts +5 -0
- package/dist/v1/schema/compatibility/scope.js +5 -2
- package/dist/v1/schema/compatibility/structure/index.js +21 -2
- package/dist/v1/schema/compatibility/structure/requirements.js +26 -1
- package/dist/v1/schema/compatibility/subjects.js +20 -2
- package/dist/v1/schema/diagnostic.d.ts +1 -1
- package/dist/v1/schema/normalization/domain.js +1 -0
- package/dist/v1/schema/resolution/domain.d.ts +4 -1
- package/dist/v1/schema/resolution/domain.js +7 -7
- package/dist/v1/schema/resolution/origins.d.ts +11 -4
- package/dist/v1/schema/resolution/origins.js +7 -8
- package/dist/v1/schema/validation/class.d.ts +10 -0
- package/dist/v1/schema/validation/class.js +13 -2
- package/dist/v1/schema/validation/domain.structure.js +1 -0
- package/package.json +1 -1
|
@@ -3,8 +3,7 @@ import { diagnostic } from '../../validation/diagnostic.js';
|
|
|
3
3
|
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
|
-
import {
|
|
7
|
-
import { isIRegexp } from '../regexp/syntax.js';
|
|
6
|
+
import { refuseIRegexp, stateBudgetMessage } from '../regexp/syntax.js';
|
|
8
7
|
import { walkSchema } from '../syntax/walk.js';
|
|
9
8
|
import { FORBIDDEN_DYNAMIC_KEYWORDS, STANDARD_SCHEMA_KEYWORDS } from './vocabulary.js';
|
|
10
9
|
export function validateSchemaProfile(schema, context, pointer) {
|
|
@@ -96,43 +95,23 @@ function validateRegexes(schema, pointer, diagnostics) {
|
|
|
96
95
|
if (Object.hasOwn(schema, 'fullMatch') && typeof schema.fullMatch !== 'string') {
|
|
97
96
|
diagnostics.push(diagnostic('VS_META_SCHEMA', `${pointer}/fullMatch`, 'fullMatch must be an I-Regexp source string.'));
|
|
98
97
|
}
|
|
99
|
-
if (typeof schema.fullMatch === 'string'
|
|
100
|
-
|
|
98
|
+
if (typeof schema.fullMatch === 'string') {
|
|
99
|
+
portableRegex(schema.fullMatch, `${pointer}/fullMatch`, 'fullMatch', 'fullMatch must satisfy the complete RFC 9485 I-Regexp syntax.', diagnostics);
|
|
101
100
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
if (typeof schema.pattern === 'string' && !isIRegexp(schema.pattern)) {
|
|
106
|
-
diagnostics.push(diagnostic('VS_REGEX_PORTABLE', `${pointer}/pattern`, 'pattern must satisfy the complete RFC 9485 I-Regexp syntax.'));
|
|
107
|
-
}
|
|
108
|
-
else if (typeof schema.pattern === 'string') {
|
|
109
|
-
checkPatternComplexity(schema.pattern, `${pointer}/pattern`, diagnostics);
|
|
101
|
+
if (typeof schema.pattern === 'string') {
|
|
102
|
+
portableRegex(schema.pattern, `${pointer}/pattern`, 'pattern', 'pattern must satisfy the complete RFC 9485 I-Regexp syntax.', diagnostics);
|
|
110
103
|
}
|
|
111
104
|
if (isJsonObject(schema.patternProperties)) {
|
|
112
105
|
for (const pattern of Object.keys(schema.patternProperties)) {
|
|
113
|
-
|
|
114
|
-
diagnostics.push(diagnostic('VS_REGEX_PORTABLE', `${pointer}/patternProperties/${escapeToken(pattern)}`, 'patternProperties keys must satisfy RFC 9485 I-Regexp syntax.'));
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
checkPatternComplexity(pattern, `${pointer}/patternProperties/${escapeToken(pattern)}`, diagnostics);
|
|
118
|
-
}
|
|
106
|
+
portableRegex(pattern, `${pointer}/patternProperties/${escapeToken(pattern)}`, 'A patternProperties key', 'patternProperties keys must satisfy RFC 9485 I-Regexp syntax.', diagnostics);
|
|
119
107
|
}
|
|
120
108
|
}
|
|
121
109
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
function checkPatternComplexity(source, pointer, diagnostics) {
|
|
128
|
-
try {
|
|
129
|
-
compilePattern(source);
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
if (error instanceof PatternComplexityError) {
|
|
133
|
-
diagnostics.push(diagnostic('VS_REGEX_PORTABLE', pointer, `${error.message} Simplify nested counted repetitions to stay within the I-Regexp complexity bound.`));
|
|
134
|
-
}
|
|
135
|
-
}
|
|
110
|
+
function portableRegex(source, pointer, subject, syntax, diagnostics) {
|
|
111
|
+
const refusal = refuseIRegexp(source);
|
|
112
|
+
if (refusal === undefined)
|
|
113
|
+
return;
|
|
114
|
+
diagnostics.push(diagnostic('VS_REGEX_PORTABLE', pointer, refusal === 'syntax' ? syntax : stateBudgetMessage(subject)));
|
|
136
115
|
}
|
|
137
116
|
function rejectPrimitiveDuplicates(values, pointer, name, diagnostics) {
|
|
138
117
|
const seen = new Set();
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
import { categoryRanges, complement, mergeRanges } from './unicode.js';
|
|
2
|
+
const CATEGORY = /^(?:L[lmotu]?|M[cen]?|N[dlo]?|P[cdefios]?|Z[lps]?|S[ckmo]?|C[cfno]?)$/u;
|
|
2
3
|
const MAX_CODE_POINTS = 4096;
|
|
3
4
|
const MAX_NESTING = 32;
|
|
4
5
|
const MAX_REPEAT = 1000;
|
|
5
|
-
const SCALAR_RANGES = [
|
|
6
|
-
[0, 0xd7ff],
|
|
7
|
-
[0xe000, 0x10ffff],
|
|
8
|
-
];
|
|
9
6
|
const DOT_RANGES = [
|
|
10
7
|
[0, 9],
|
|
11
8
|
[11, 12],
|
|
@@ -200,7 +197,7 @@ class Parser {
|
|
|
200
197
|
if (this.peek() !== '\\')
|
|
201
198
|
return undefined;
|
|
202
199
|
const escaped = this.peek(1);
|
|
203
|
-
if (escaped === undefined || !'()*+-.?[\\]^nrt{}'.includes(escaped))
|
|
200
|
+
if (escaped === undefined || !'()*+-.?[\\]^nrt{|}'.includes(escaped))
|
|
204
201
|
return undefined;
|
|
205
202
|
this.index += 2;
|
|
206
203
|
const value = escaped === 'n' ? '\n' : escaped === 'r' ? '\r' : escaped === 't' ? '\t' : escaped;
|
|
@@ -237,68 +234,10 @@ class Parser {
|
|
|
237
234
|
return this.characters[this.index + offset];
|
|
238
235
|
}
|
|
239
236
|
}
|
|
240
|
-
const categories = new Map();
|
|
241
|
-
function categoryRanges(category) {
|
|
242
|
-
const retained = categories.get(category);
|
|
243
|
-
if (retained !== undefined)
|
|
244
|
-
return retained;
|
|
245
|
-
const expression = new RegExp(`^\\p{${category}}$`, 'u');
|
|
246
|
-
const output = [];
|
|
247
|
-
let start;
|
|
248
|
-
let previous = -2;
|
|
249
|
-
for (const [minimum, maximum] of SCALAR_RANGES) {
|
|
250
|
-
for (let scalar = minimum; scalar <= maximum; scalar++) {
|
|
251
|
-
if (!expression.test(String.fromCodePoint(scalar)))
|
|
252
|
-
continue;
|
|
253
|
-
if (start === undefined)
|
|
254
|
-
start = scalar;
|
|
255
|
-
else if (scalar !== previous + 1) {
|
|
256
|
-
output.push([start, previous]);
|
|
257
|
-
start = scalar;
|
|
258
|
-
}
|
|
259
|
-
previous = scalar;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (start !== undefined)
|
|
263
|
-
output.push([start, previous]);
|
|
264
|
-
const frozen = Object.freeze(output.map((range) => Object.freeze(range)));
|
|
265
|
-
categories.set(category, frozen);
|
|
266
|
-
return frozen;
|
|
267
|
-
}
|
|
268
237
|
function singleton(value) {
|
|
269
238
|
const scalar = value.codePointAt(0);
|
|
270
239
|
return scalar === undefined ? [] : [[scalar, scalar]];
|
|
271
240
|
}
|
|
272
|
-
function mergeRanges(input) {
|
|
273
|
-
const sorted = [...input].sort((left, right) => left[0] - right[0] || left[1] - right[1]);
|
|
274
|
-
const output = [];
|
|
275
|
-
for (const range of sorted) {
|
|
276
|
-
const previous = output.at(-1);
|
|
277
|
-
if (previous === undefined || range[0] > previous[1] + 1)
|
|
278
|
-
output.push([range[0], range[1]]);
|
|
279
|
-
else
|
|
280
|
-
output[output.length - 1] = [previous[0], Math.max(previous[1], range[1])];
|
|
281
|
-
}
|
|
282
|
-
return output;
|
|
283
|
-
}
|
|
284
|
-
function complement(input) {
|
|
285
|
-
const output = [];
|
|
286
|
-
for (const [minimum, maximum] of SCALAR_RANGES) {
|
|
287
|
-
let cursor = minimum;
|
|
288
|
-
for (const [start, end] of input) {
|
|
289
|
-
if (end < minimum || start > maximum)
|
|
290
|
-
continue;
|
|
291
|
-
if (start > cursor)
|
|
292
|
-
output.push([cursor, Math.min(start - 1, maximum)]);
|
|
293
|
-
cursor = Math.max(cursor, end + 1);
|
|
294
|
-
if (cursor > maximum)
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
if (cursor <= maximum)
|
|
298
|
-
output.push([cursor, maximum]);
|
|
299
|
-
}
|
|
300
|
-
return output;
|
|
301
|
-
}
|
|
302
241
|
function isNormalCharacter(value) {
|
|
303
242
|
const code = value.codePointAt(0);
|
|
304
243
|
return (code !== undefined &&
|
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
import type { Pattern, RunPattern } from '../compile/model.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
2
|
+
import type { Expression } from './ast.js';
|
|
3
|
+
/**
|
|
4
|
+
* The most states the machine of one I-Regexp may have (`limits.regexp.maxStates`), an
|
|
5
|
+
* implementation limit as RFC 9485 §8 allows. Counted repetitions are expanded copy by copy, so the
|
|
6
|
+
* parser's bounds on length, nesting and counts alone admit machines of billions of states. The
|
|
7
|
+
* counted-run form never expands, so it is not subject to this budget.
|
|
8
|
+
*/
|
|
9
|
+
export declare const MAX_STATES = 10000;
|
|
10
|
+
/**
|
|
11
|
+
* Thrown when an I-Regexp source compiles to a machine of more than `MAX_STATES` states. A short
|
|
12
|
+
* branching source such as `(a{0,1000}){0,60}b` would unroll to ~120k states and cost seconds per
|
|
13
|
+
* validated value (P1-I-01); it is refused before any state is built.
|
|
14
|
+
*/
|
|
4
15
|
export declare class PatternComplexityError extends RangeError {
|
|
5
16
|
readonly limit: number;
|
|
6
17
|
constructor(limit?: number);
|
|
7
18
|
}
|
|
8
19
|
/** Compile scalar sequences as counted runs; preserve the NFA for branching expressions. */
|
|
9
20
|
export declare function compilePattern(source: string): Pattern | RunPattern;
|
|
21
|
+
/**
|
|
22
|
+
* Whether a parsed I-Regexp compiles within the state budget: as counted runs, which never expand,
|
|
23
|
+
* or as a machine of at most `MAX_STATES` states, counted from the AST before anything is built.
|
|
24
|
+
*/
|
|
25
|
+
export declare function withinStateBudget(expression: Expression): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The number of states `Builder` builds for an expression, computed from the AST alone: `states`
|
|
28
|
+
* in `proofs/Regexp.lean`, where `compile_size` proves it exact. Nesting and counts are bounded by
|
|
29
|
+
* the parser, so the count stays finite; it is exact up to `Number.MAX_SAFE_INTEGER`, and above it
|
|
30
|
+
* every rounding keeps it above the budget.
|
|
31
|
+
*/
|
|
32
|
+
export declare function states(expression: Expression): number;
|
|
@@ -1,16 +1,20 @@
|
|
|
1
1
|
import { parseIRegexp } from './ast.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export const
|
|
9
|
-
/**
|
|
2
|
+
/**
|
|
3
|
+
* The most states the machine of one I-Regexp may have (`limits.regexp.maxStates`), an
|
|
4
|
+
* implementation limit as RFC 9485 §8 allows. Counted repetitions are expanded copy by copy, so the
|
|
5
|
+
* parser's bounds on length, nesting and counts alone admit machines of billions of states. The
|
|
6
|
+
* counted-run form never expands, so it is not subject to this budget.
|
|
7
|
+
*/
|
|
8
|
+
export const MAX_STATES = 10_000;
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when an I-Regexp source compiles to a machine of more than `MAX_STATES` states. A short
|
|
11
|
+
* branching source such as `(a{0,1000}){0,60}b` would unroll to ~120k states and cost seconds per
|
|
12
|
+
* validated value (P1-I-01); it is refused before any state is built.
|
|
13
|
+
*/
|
|
10
14
|
export class PatternComplexityError extends RangeError {
|
|
11
15
|
limit;
|
|
12
|
-
constructor(limit =
|
|
13
|
-
super(`I-Regexp
|
|
16
|
+
constructor(limit = MAX_STATES) {
|
|
17
|
+
super(`Cannot compile an I-Regexp machine of more than ${limit} states.`);
|
|
14
18
|
this.limit = limit;
|
|
15
19
|
this.name = 'PatternComplexityError';
|
|
16
20
|
}
|
|
@@ -23,6 +27,8 @@ export function compilePattern(source) {
|
|
|
23
27
|
const runs = scalarRuns(expression);
|
|
24
28
|
if (runs !== undefined)
|
|
25
29
|
return Object.freeze({ source, runs });
|
|
30
|
+
if (states(expression) > MAX_STATES)
|
|
31
|
+
throw new PatternComplexityError();
|
|
26
32
|
const builder = new Builder();
|
|
27
33
|
const fragment = builder.expression(expression);
|
|
28
34
|
builder.states[fragment.end].accepting = true;
|
|
@@ -32,6 +38,38 @@ export function compilePattern(source) {
|
|
|
32
38
|
states: Object.freeze(builder.states.map(freezeState)),
|
|
33
39
|
});
|
|
34
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Whether a parsed I-Regexp compiles within the state budget: as counted runs, which never expand,
|
|
43
|
+
* or as a machine of at most `MAX_STATES` states, counted from the AST before anything is built.
|
|
44
|
+
*/
|
|
45
|
+
export function withinStateBudget(expression) {
|
|
46
|
+
return scalarRuns(expression) !== undefined || states(expression) <= MAX_STATES;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The number of states `Builder` builds for an expression, computed from the AST alone: `states`
|
|
50
|
+
* in `proofs/Regexp.lean`, where `compile_size` proves it exact. Nesting and counts are bounded by
|
|
51
|
+
* the parser, so the count stays finite; it is exact up to `Number.MAX_SAFE_INTEGER`, and above it
|
|
52
|
+
* every rounding keeps it above the budget.
|
|
53
|
+
*/
|
|
54
|
+
export function states(expression) {
|
|
55
|
+
const [only, ...others] = expression.alternatives;
|
|
56
|
+
if (only !== undefined && others.length === 0)
|
|
57
|
+
return sequenceStates(only);
|
|
58
|
+
return expression.alternatives.reduce((total, sequence) => total + sequenceStates(sequence), 2);
|
|
59
|
+
}
|
|
60
|
+
function sequenceStates({ pieces }) {
|
|
61
|
+
if (pieces.length === 0)
|
|
62
|
+
return 2;
|
|
63
|
+
return pieces.reduce((total, piece) => total + pieceStates(piece), 0);
|
|
64
|
+
}
|
|
65
|
+
/** `pieceStates`: `minimum` copies, then a star, or a shared end and the optional copies. */
|
|
66
|
+
function pieceStates({ atom, minimum, maximum }) {
|
|
67
|
+
const copy = atom.kind === 'ranges' ? 2 : states(atom.expression);
|
|
68
|
+
if (maximum === undefined)
|
|
69
|
+
return minimum * copy + (copy + 2);
|
|
70
|
+
return ((minimum === 0 ? 2 : minimum * copy) +
|
|
71
|
+
(maximum === minimum ? 0 : 1 + (maximum - minimum) * copy));
|
|
72
|
+
}
|
|
35
73
|
function scalarRuns(expression) {
|
|
36
74
|
if (expression.alternatives.length !== 1)
|
|
37
75
|
return undefined;
|
|
@@ -139,8 +177,6 @@ class Builder {
|
|
|
139
177
|
return { start: left.start, end: right.end };
|
|
140
178
|
}
|
|
141
179
|
state() {
|
|
142
|
-
if (this.states.length >= MAX_PATTERN_STATES)
|
|
143
|
-
throw new PatternComplexityError();
|
|
144
180
|
return this.states.push({ epsilon: [], transitions: [] }) - 1;
|
|
145
181
|
}
|
|
146
182
|
epsilon(from, to) {
|
|
@@ -1,2 +1,12 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Why a source is not an admitted I-Regexp: `syntax` when it is outside the RFC 9485 grammar or the
|
|
3
|
+
* parser's bounds on length, nesting and counts; `states` when its machine would exceed the state
|
|
4
|
+
* budget.
|
|
5
|
+
*/
|
|
6
|
+
export type IRegexpRefusal = 'syntax' | 'states';
|
|
7
|
+
/** The refusal of one source, through the same AST and state count as compilation. */
|
|
8
|
+
export declare function refuseIRegexp(source: string): IRegexpRefusal | undefined;
|
|
9
|
+
/** Complete RFC 9485 syntax admission, within the implementation limits RFC 9485 §8 allows. */
|
|
2
10
|
export declare function isIRegexp(source: string): boolean;
|
|
11
|
+
/** The authoring explanation of a `states` refusal of `subject` (`pattern`, `fullMatch`, ...). */
|
|
12
|
+
export declare function stateBudgetMessage(subject: string): string;
|
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
import { parseIRegexp } from './ast.js';
|
|
2
|
-
|
|
2
|
+
import { MAX_STATES, withinStateBudget } from './compile.js';
|
|
3
|
+
/** The refusal of one source, through the same AST and state count as compilation. */
|
|
4
|
+
export function refuseIRegexp(source) {
|
|
5
|
+
const expression = parseIRegexp(source);
|
|
6
|
+
if (expression === undefined)
|
|
7
|
+
return 'syntax';
|
|
8
|
+
return withinStateBudget(expression) ? undefined : 'states';
|
|
9
|
+
}
|
|
10
|
+
/** Complete RFC 9485 syntax admission, within the implementation limits RFC 9485 §8 allows. */
|
|
3
11
|
export function isIRegexp(source) {
|
|
4
|
-
return
|
|
12
|
+
return refuseIRegexp(source) === undefined;
|
|
13
|
+
}
|
|
14
|
+
/** The authoring explanation of a `states` refusal of `subject` (`pattern`, `fullMatch`, ...). */
|
|
15
|
+
export function stateBudgetMessage(subject) {
|
|
16
|
+
return `${subject} expands its counted repetitions into more than ${MAX_STATES} states, the I-Regexp implementation limit (RFC 9485 §8); nest fewer or smaller counts.`;
|
|
5
17
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unicode scalar sets for the I-Regexp compiler: the scalar space, unions and complements of ordered
|
|
3
|
+
* ranges, and the General_Category table that `\p{..}` and `\P{..}` denote.
|
|
4
|
+
*
|
|
5
|
+
* The table is pinned to Unicode 17.0, the version of the Node runtime the Kernel pins (26.7.0 in
|
|
6
|
+
* `.nvmrc`, ICU 78.3), so a category denotes the same scalars on every host whatever Unicode data
|
|
7
|
+
* its own `RegExp` engine carries (Node, Bun/JavaScriptCore, workerd). It was generated under that
|
|
8
|
+
* Node by testing every scalar against `new RegExp('^\\p{<name>}$', 'u')` and keeping the maximal
|
|
9
|
+
* runs. `__tests__/categories.test.ts` holds that generation: on a host whose
|
|
10
|
+
* `process.versions.unicode` is {@link UNICODE_VERSION} it requires this table to equal the encoding
|
|
11
|
+
* of the host's runs, and a deliberate Unicode upgrade replaces the table with the value that test
|
|
12
|
+
* then expects.
|
|
13
|
+
*
|
|
14
|
+
* Only the 28 assigned two-letter categories that RFC 9485 `IsCategory` can name are stored. A major
|
|
15
|
+
* category is the union of its subcategories, `Cn` holds every scalar that no stored category holds,
|
|
16
|
+
* and `Cs` (the surrogates) holds no scalar. An entry lists its runs in scalar order, each as two
|
|
17
|
+
* base-32 numbers: the gap from the end of the previous run (from U+0000 for the first), then the run
|
|
18
|
+
* length minus one. A number is written most significant digit first, and every digit but its last
|
|
19
|
+
* is taken from the upper half of `DIGITS`. A category is decoded on its first use only.
|
|
20
|
+
*/
|
|
21
|
+
import type { ScalarRange } from './ast.js';
|
|
22
|
+
/** The Unicode version whose General_Category values the table below records. */
|
|
23
|
+
export declare const UNICODE_VERSION = "17.0";
|
|
24
|
+
export declare const SCALAR_RANGES: readonly ScalarRange[];
|
|
25
|
+
/** The encoded runs of each stored General_Category, generated under Unicode 17.0. */
|
|
26
|
+
export declare const GENERAL_CATEGORIES: Readonly<Record<string, string>>;
|
|
27
|
+
/** The maximal runs of the scalars in one General_Category that the I-Regexp grammar admits. */
|
|
28
|
+
export declare function categoryRanges(category: string): readonly ScalarRange[];
|
|
29
|
+
/** Sorted, disjoint and non-adjacent runs covering exactly the scalars of `input`. */
|
|
30
|
+
export declare function mergeRanges(input: readonly ScalarRange[]): readonly ScalarRange[];
|
|
31
|
+
/** The runs of the scalars, surrogates excluded, outside the sorted, disjoint runs of `input`. */
|
|
32
|
+
export declare function complement(input: readonly ScalarRange[]): readonly ScalarRange[];
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unicode scalar sets for the I-Regexp compiler: the scalar space, unions and complements of ordered
|
|
3
|
+
* ranges, and the General_Category table that `\p{..}` and `\P{..}` denote.
|
|
4
|
+
*
|
|
5
|
+
* The table is pinned to Unicode 17.0, the version of the Node runtime the Kernel pins (26.7.0 in
|
|
6
|
+
* `.nvmrc`, ICU 78.3), so a category denotes the same scalars on every host whatever Unicode data
|
|
7
|
+
* its own `RegExp` engine carries (Node, Bun/JavaScriptCore, workerd). It was generated under that
|
|
8
|
+
* Node by testing every scalar against `new RegExp('^\\p{<name>}$', 'u')` and keeping the maximal
|
|
9
|
+
* runs. `__tests__/categories.test.ts` holds that generation: on a host whose
|
|
10
|
+
* `process.versions.unicode` is {@link UNICODE_VERSION} it requires this table to equal the encoding
|
|
11
|
+
* of the host's runs, and a deliberate Unicode upgrade replaces the table with the value that test
|
|
12
|
+
* then expects.
|
|
13
|
+
*
|
|
14
|
+
* Only the 28 assigned two-letter categories that RFC 9485 `IsCategory` can name are stored. A major
|
|
15
|
+
* category is the union of its subcategories, `Cn` holds every scalar that no stored category holds,
|
|
16
|
+
* and `Cs` (the surrogates) holds no scalar. An entry lists its runs in scalar order, each as two
|
|
17
|
+
* base-32 numbers: the gap from the end of the previous run (from U+0000 for the first), then the run
|
|
18
|
+
* length minus one. A number is written most significant digit first, and every digit but its last
|
|
19
|
+
* is taken from the upper half of `DIGITS`. A category is decoded on its first use only.
|
|
20
|
+
*/
|
|
21
|
+
/** The Unicode version whose General_Category values the table below records. */
|
|
22
|
+
export const UNICODE_VERSION = '17.0';
|
|
23
|
+
export const SCALAR_RANGES = [
|
|
24
|
+
[0, 0xd7ff],
|
|
25
|
+
[0xe000, 0x10ffff],
|
|
26
|
+
];
|
|
27
|
+
const DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
28
|
+
/** The encoded runs of each stored General_Category, generated under Unicode 17.0. */
|
|
29
|
+
export const GENERAL_CATEGORIES = {
|
|
30
|
+
Cc: 'AfifhA',
|
|
31
|
+
Cf: 'lNAhqSFWAmAAhRAsABiQAj5LAh/cEaEhREBJh30PAnZCkmBAPAo7CPhijADlmPH4r0GAeif',
|
|
32
|
+
Co: 'h4gAmnf8h4Ah//dCh//d',
|
|
33
|
+
Ll: 'jBZhaAhJXBHBABABABABABABABABABABABABABABABABABABABABABABABABABABABBBABABABABABABABBBABABABABABABABABABABABABABABABABABABABABABABACABABCCABACADBEACADCCACABABACABBBACADABACBCCGACACABABABABABABABABBBABABABABABABABABBCABADABABABABABABABABABABABABABABABABABABABABABABABABABABABABABGCACBBAEABABABABiECZmBABADADCSAbhCBBDCBABABABABABABABABABABABEBACACBhThPBABABABABABABABABABABABABABABABABAJABABABABABABABABABABABABABABABABABABABABABABABABABABACABABABABABABBBABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAhQhIi6HhKCC3YFikCIBAjVhLhfMBhBjGABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABIBABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABIIFKHIHIFKHIHINCHIHIHIEBBGADCBBIDCBIHKCBBoSADBDAbAEAEACBIDEAhVAi1LhPBADBBABABAEABBBFFABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABBHABAEAMhFBAFA+oTABABABABABABABABABABABABABABABABABABABABABABATABABABABABABABABABABABABABAkHABABABABABABCBABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABHBABACABABABABAEABACABCBABABABABABABABABABAFAFABABABABABABABAEABACABABABABABABABAaADA5VhKFIHiPz6AGMEhhJZhmNhHkIhDkbKBOBGBBh4DhSjdVi5af1sAfhbY5qGZaGBRaZaDBABGBKaZaZaZaZaZaZaZaZabcYBFaYBFaYBFaYBFaYBFBAh5UJBTGFivXhB',
|
|
34
|
+
Lm: '1QRELOEHABAkFAFAueAnGAlEBoNBEAfAJADAlAAlHAhmUAjfAxVAh2aAjLAzDAuQFlOheNAhChE1RANAQMi+fBnRAlfAuVAhLEFAjBBidC74WAhnCFoOAjSAcBjZIiQAXAjIDDBuVAWAkJAjMAVBjHDJA1gGAhNBh/AFBhJBIhsTAhAAqVAj4TAzrGDvcChIBxGMiABBAOBv/cDBGBBshRhdmJG9NAwTAyLA',
|
|
35
|
+
Lo: 'lKAPAoAAEDmQB5aaEDhNfBJhDBBjCBAYBKCCAQABddiYLAYhAVVhKYHKFXBGQhIhbhVDASAHJQOEHCBCVBGBADDDAQANBBCOBKAIFEBCVBGBBBBBBfDBATCQIBCBVBGBBBEDASAPBXALHCBCVBGBBBEDAeBBCPARABFDCBDDBBABBDBDCDLWAhUHBCBWBPDAaCBBCBeAEHBCBWBJBEDAeCBBPBRIBCBhICAQAFCICYFFRDXBIBACGhahPBBMFhbBBABEBXBABJBBJACEXDhAAhfHBhDbEjThKUAQFEDDADBHCEMMAjRqIBDCGBABDChIBDChABDCGBABDCOBhYBDCiChFPjRzLCQBZFiKGHHRNSOROMBCPhThIAiDhCBhUHEChBBAFiFKehRdCELhLEZhWWJhUlQhORHhWdNBKhLahDhJCKdjRDBFBBDAhhaDi/XhXYWJGBGBGBGBGBGBGBGxHAhVAEiVIABiZEAFhKBidRfhQPwAmtfiA0wUBhjWiDhHIoLEPKBiCAhRiFlJAjHADGBCBDBWdhTOhRheFDABBLbKWZcHhOhNECIKEBhIXCBHUPBFDADhRBADBCECABAYBDKHAOFCFCFJGBGkRhCdq9DMWEhQooErNCjJiDABJBMBEBABBBBBjLhBrKShfChVhILjUEBkGjJJBhMCeDFCFCFCChDLBZBSBBBOCNhCjasFcDhQhPfNTBHGhFKdChDEHkAiNjChHIhTichTMpWJVKHkYFCABhLBBDACWKWJeiBSBBKVKZGZhGhXGBiAAPDBCBchKcDchDHBbbhVKVKSNRjOiIlXhDhGDBApQhJGBQCBBhYcKAIVhKRhOUbWMhUhZBCANhMhAYahDdACAIhCDAMhPODVABAhDRBYTBhfGBABDBOBJHhOhGHCBCVBGBBBEDASAMEeJBACABhFBAZABAhMhUSDUCehPUBBAlYhOhJDhEhPUAhbhKNAiHahFGlZhLmTHCACHBBBXPABAieHChGQABAcAKhHHAVALhNTASiImHhAfIBhERAhRdjQGBBBhFVAZFBBBfOAXhIBBoESPABMBhBjcAiP8ZnGmDiyMjAPhhPRFZj8aFyGm1Zdh3CxYHeRiORdShPhTUFStThHsViKFAlPnmVhJfjBjSowNpCPAdCCAODIsLioEjKFMDIHJozQAvVhMhBAqBdShLvEanFdCAmPeBCBBBGCEJAnBGBDBBBOBmEhpbDBaBBBACABJBDBABAGAEABABABCBBBACABABABABABBBACDBGBDBDBABJBQFCBEBQkqEhp2fhAkodCl0NCnpQPzNitCwdhvCk6KFopJ',
|
|
36
|
+
Lt: 'uFACACAhGAnsVHIHIHMAPAhPA',
|
|
37
|
+
Lu: 'iBZjFWBGhBABABABABABABABABABABABABABABABABABABABABABABABABABABABACABABABABABABABACABABABABABABABABABABABABABABABABABABABABABABABBBABADBBABBBCCDBBBCDBBBBABABBBACABBBCBABBDAHACACACABABABABABABABACABABABABABABABABACACABCBABABABABABABABABABABABABABABABABABABABABABABABABABABABABAHBBBCABDBABABABApBABADAIAGABCBABBBQBIhDACCDABABABABABABABABABABABAFACABBChShQABABABABABABABABABABABABABABABABAJABABABABABABABABABABABABABABABABABABABABABABABABABABABBBABABABABABACABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAChFi6JhFBAFA2SiVikTAGhKCCqAABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAJABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAJHIFKHIHIFLABABABAIHiIDMDMDMELDoGAEADCCCCADEGABABABDCDKBFAhdAizchPhQABCCABABABDBACAICBABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAIABAEA+qNABABABABABABABABABABABABABABABABABABABABABABATABABABABABABABABABABABABABAkHABABABABABABADABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABAKABABBBABABABAEABACABADABABABABABABABABABABEBEBABABABABABABABDBABBBABABABABABABABAYA15LZhmFhHkIhDkcKBOBGBBh3KhSkdVi5af1sAfiAY5qHZaZaZaABBCACBCDBHaZaBBDCHBGbBBDBEBADGbZaZaZaZaZaZeYhBYhBYhBYhBYhBAkpVhB',
|
|
38
|
+
Mc: 'ioDAhXACCIDBBhSBhaCGBCBKAhLAhaCiCAhaCIABBhVBhaABAGBCBKAjGBBBDCBCKAhJChdDhdBhaABECBBBJBcAOBhaCFCBCKAhKBiLCGHSBqKBhfAlLBEAGACBZBKCCGVBCFCAKChzYAeAkBAHHBBqaDCCEBBFnABhaABAJABBIFkRAhQAFABEBBhdAeAEBCAhcACCBADBhQHIBlLAVAk5WB9/TBCAiYBhSPkOBhPAhQBEBCCjOBCBYAhNABAjNACBFAnNBBBBBBA5gTABAjfAhNCEBjTAYBhbAhQCJBNAidCDBBAlKCfBhaBBDCBCCJAKBiUCHACABDBBBAjFCIBDAjKCGABDCAnNCGDCAjRCIBBAjNABBGAjHABBEAoFCJAnXFBBEACABAkOCIDEAiUAdBheAmJADABAmHAOAjKAHACAmVEEBBAqeBMAhQBIBBAwvICjxEhWjIB4rTBGF',
|
|
39
|
+
Me: 'hkIBlxUAhweDBChhsLC',
|
|
40
|
+
Mn: '4AjPoTEoJhMBABBBBBAiIKhQUQAjFGCFCBBDhDAeaibKhaIJAYDBIBCBEhLChbIhKXBfhXABAEHEADGKBdAhaAEDIAUBaACBhZAEBEBCCDAeBDALBhZAEEBBEAUBWFBAhaACABDIAHBLBeAhdAMAhSADAhXABCFCBDHBLBdAhaACAGAFBUBcBhZBEDIAUBdAiIAHCBAiaACGMHjCACILGiJBbABABAhXNBEBBFKBhDJAjGDBFBBCBZBECQDNACBGAPA1fC9SCdBeBeBiABBGIACKJAhNCBAjVBhCAjWCEBJAGCmbBCAhaABGBABACHGJCAhQNBeCLUDhQABEBAFAhIIMBhADCBBChYABBDABChaHCBkYCBMBGEAGADBmGhf2QMEADLi/eCkNAjAfxKDjLB9uUAEJhABiQBoQADAEAZBFAkXBaRNAhGHZKhOChQACDCBhHAiDFCBCBMAIAhPAhTABCCBFBBAhKBIAnOACAEAz5QA3BPQP+NAnCAkVEh0GCBBFDhICEAlFBxdDiBEpdBiNFiGKhRDjbAhWOhJACBKChRDCBHAhdChEEBHheAMBhUIKDCAifCCABBGACAkdADHVBhZBDAhFGDEiGFNABABAOBiVHCCBAXAiUFBAEBBBnODGBBBbBiVHCABBjKABACFBAjFABACDBEoDIBBoABBAEAkQDCBEAhAJhIFCDIAJFCChOMBBmGABCBAmJGBFBAiSVCGBBBBjaFDABBBGBAiIBDABAqbBLBhUEFABAXAlnFAGOrmILDCiuAEhbGhgYAhfDiRAzlYBkzBhNCWxACRHCGeDkUCh9bhWEhRIAOAWEBOhqQGBQCGBBBEjEAlAGrXAhdDvcDneBnTACAHBFAuaGjNG4l9VnP',
|
|
41
|
+
Nd: 'hQJhxGJkGJmGJscJjWJjWJjWJjWJjWJjWJjWJjWJjWJjAJjWJiGJoWJiGJh6GJhGJpMJkAJlGJGJlWJiWJkGJGJhiuGJ1GJhGJmGJWJiWJsWJ04WJhsGJikGJGJ4cJkAJhcJkQJoWJqWJjWJrWJjGJGTiMJtGJjGJ0WJiWJnWJiGJhWJrGJwuWJipGJiWJkGJwWJ37WJi2UhRiqAJtGJvWJnXJ6VJk0WJ',
|
|
42
|
+
Nl: 'l3OCizPhCCDjzeAZIOC91LJ2yQhUuMAIAkGEohKjOy8FC',
|
|
43
|
+
No: 'lSBFACCipVFrYFjYCkFGmZGRItRJhhVThjTJvAAh0VADFGJmGPhJA2WhbiOVzWdhrJAhkUDkKJeHBOhAJhHO9rQF2mRhMiBDRBqVahEDhpUHZGhHIiLEWFlABCPChNiAIhUBeCiLEjIHYHhJGqKFrAekeJhKDjQGkGTrbThqFBtOI7HS6TUy8GG4eW5hJTMTjMYlqOI9BhaBCBDiMhMBO+CM',
|
|
44
|
+
Pc: 'ifAn+fBTAh3ueBYCnPA',
|
|
45
|
+
Pd: 'hNAhqcAhTAjyBAhgFAigJFjwBACAfBEAcAteATAjPAhzsQBhFAKAlJAjzAApeA',
|
|
46
|
+
Pe: 'hJAhTAfAj1dABAh6eAitJAhXAPAzaABAeAhheABABABABABABAiQAhAABABABABAsUABABABABABABABABABABAiAABAhBAhhFABABABAhMABABABAtMABABABABADABABABACBhzoeAmZAdABABABABABABABADARABABAlKAhTAfACACA',
|
|
47
|
+
Pf: 'lbAn6dADAcAjuIABAEACAPADA',
|
|
48
|
+
Pi: 'lLAn7MACBCAZAjuIABAEACAPADA',
|
|
49
|
+
Po: 'hBCBCCABABBKBDBbAiEAFAOBHA1eAIAuSFhJAhWACACAhMBUBBBNABCiKDjGAhLNnJChWOfAoFBKAkMAjYAjZAsGAMArPAiaAKBlIOBAjQAiKEEBjPFlLAzEI4FAjcCiHBkdCBChFFBDpZBmYBkAGBFlABKGcCjcDhbEheBiAHLA6CBIHIICDCCDKBABJjkaDBBjQAkPBECCACIBBBACBKEBJCDBABMCCtMChZAldA9gCBoNCjDAKAjTFrcDiWBhICBAhRBhPAjBMQBjcDjeBQBnZA0xEGCAWAUBCDDCBDHCGABBkVCBCCABABBKBDBbAhEACBsaC0cAhQAseA3HAmHAfAoQIhGAjQGiCGiZD5TAkEEhMDldGjNBBDjeDhQBiPDEANABCiYFjLApKBBBjSEKBBAjIAnaWjJCcMiMAkCCncAoICkbAicHiTCBEidJmXAifEhKB0FBiKMlPAhjQEi7cBuzbBkFAiBEIAxICpHDqHAzlcAnvHEi7TA6eB',
|
|
50
|
+
Ps: 'hIAhSAfAj1eABAh6eAireADAhGAhXAPAzaABAeAhheABABABABABABAiQAhAABABABABAsUABABABABABABABABABABAiAABAhBAhhFABABABAZASABABABAtMABABABABADABABABACAhzpBAmXAdABABABABABABABADARABABAlKAhSAfADACA',
|
|
51
|
+
Sc: 'hEAjdDhnJAjbAvSBvSBHAnVAoHAyFAisbAimEhBhh7WA1uDAjMAkaAmbBDBn/WDhw4eAitQA',
|
|
52
|
+
Sk: 'ieABAiHAGAEADAwJDMNFGBABQjVAOBhoCAl5UABCLCNCNCNBkkcB9zDWJBjHB+QAOB0iGQ7bABAlCAh9gXE',
|
|
53
|
+
Sm: 'hLAQChdABAhNAEAhFAfA3eAwPCmxbANAhHCNCkLAhHEGAiEEFBEACACAHAfBCABAfoLhABiaAeYhIFuVAJAhWHjPAqQECeKPoAkCWheEfCoBhQUCFhz+cA5YABClEAQChdABAkDAGDjtBBhwrAAh+QAZAfAZAfAZAfAZAfAZAl5MBiueI',
|
|
54
|
+
So: 'lGACAEABA+RAoKBjfBmOAKATBnXAwDArVAkCFBAkEAmPAhJAsHCPABCCFUABABAkFHBFBBFDmFB3QJ2TA2SAkdhBrBJJIhsDBBDBBKABBGFBABABAEALBOABBBAhaBJECDBBBBBGBeCBBABeoMHETCGCiQBdZhHGiHWKiRiNWlWBIBhVIjOBnXhMhLiAnfwAhPVBGhGCkJnFFrFBhOZBiYMmVaPEANBMAVBGBqQBEJhAhFJAQeLdIAPfKhGPpfmuAhf10QhW7BDKBBAxdC0qJPrNPiABhWHhNCvEADAEBNBpZIhZQCCBMDAhPhMhzaByPAhgIHijGAikVHEQy6KDFA0qWAj7DnPKCDtTGWPPjAjThcnVKhGChbFCWBHdEhcViBDAlaiWhlJvfhXDhSHBNBBh2IAi6cAkBA2RhLEjDMOCOBOBhEXlAhYcNhLEIHBOFkanaF2YDQDMDmZGLEAPLEhXIJGhHIdCLEBheqXINCMDKDhYBAEPCLEJHkSBibKA',
|
|
55
|
+
Zl: 'ohIA',
|
|
56
|
+
Zp: 'ohJA',
|
|
57
|
+
Zs: 'hAAjfAlufAirfKhEAhPAj9AA',
|
|
58
|
+
};
|
|
59
|
+
const MAJOR_CATEGORIES = {
|
|
60
|
+
C: ['Cc', 'Cf', 'Cn', 'Co'],
|
|
61
|
+
L: ['Ll', 'Lm', 'Lo', 'Lt', 'Lu'],
|
|
62
|
+
M: ['Mc', 'Me', 'Mn'],
|
|
63
|
+
N: ['Nd', 'Nl', 'No'],
|
|
64
|
+
P: ['Pc', 'Pd', 'Pe', 'Pf', 'Pi', 'Po', 'Ps'],
|
|
65
|
+
S: ['Sc', 'Sk', 'Sm', 'So'],
|
|
66
|
+
Z: ['Zl', 'Zp', 'Zs'],
|
|
67
|
+
};
|
|
68
|
+
const categories = new Map();
|
|
69
|
+
/** The maximal runs of the scalars in one General_Category that the I-Regexp grammar admits. */
|
|
70
|
+
export function categoryRanges(category) {
|
|
71
|
+
const known = categories.get(category);
|
|
72
|
+
if (known !== undefined)
|
|
73
|
+
return known;
|
|
74
|
+
let ranges;
|
|
75
|
+
if (Object.hasOwn(GENERAL_CATEGORIES, category))
|
|
76
|
+
ranges = decode(GENERAL_CATEGORIES[category]);
|
|
77
|
+
else if (Object.hasOwn(MAJOR_CATEGORIES, category))
|
|
78
|
+
ranges = mergeRanges(MAJOR_CATEGORIES[category].flatMap(categoryRanges));
|
|
79
|
+
else if (category === 'Cn')
|
|
80
|
+
ranges = complement(mergeRanges(Object.keys(GENERAL_CATEGORIES).flatMap(categoryRanges)));
|
|
81
|
+
else
|
|
82
|
+
throw new TypeError(`The I-Regexp grammar admits no General_Category ${category}.`);
|
|
83
|
+
const frozen = Object.freeze(ranges.map((range) => Object.freeze(range)));
|
|
84
|
+
categories.set(category, frozen);
|
|
85
|
+
return frozen;
|
|
86
|
+
}
|
|
87
|
+
function decode(encoded) {
|
|
88
|
+
const output = [];
|
|
89
|
+
let next = 0;
|
|
90
|
+
let start;
|
|
91
|
+
let value = 0;
|
|
92
|
+
for (const character of encoded) {
|
|
93
|
+
const digit = DIGITS.indexOf(character);
|
|
94
|
+
value = value * 32 + (digit % 32);
|
|
95
|
+
if (digit >= 32)
|
|
96
|
+
continue;
|
|
97
|
+
if (start === undefined)
|
|
98
|
+
start = next + value;
|
|
99
|
+
else {
|
|
100
|
+
output.push([start, start + value]);
|
|
101
|
+
next = start + value + 1;
|
|
102
|
+
start = undefined;
|
|
103
|
+
}
|
|
104
|
+
value = 0;
|
|
105
|
+
}
|
|
106
|
+
return output;
|
|
107
|
+
}
|
|
108
|
+
/** Sorted, disjoint and non-adjacent runs covering exactly the scalars of `input`. */
|
|
109
|
+
export function mergeRanges(input) {
|
|
110
|
+
const sorted = [...input].sort((left, right) => left[0] - right[0] || left[1] - right[1]);
|
|
111
|
+
const output = [];
|
|
112
|
+
for (const range of sorted) {
|
|
113
|
+
const previous = output.at(-1);
|
|
114
|
+
if (previous === undefined || range[0] > previous[1] + 1)
|
|
115
|
+
output.push([range[0], range[1]]);
|
|
116
|
+
else
|
|
117
|
+
output[output.length - 1] = [previous[0], Math.max(previous[1], range[1])];
|
|
118
|
+
}
|
|
119
|
+
return output;
|
|
120
|
+
}
|
|
121
|
+
/** The runs of the scalars, surrogates excluded, outside the sorted, disjoint runs of `input`. */
|
|
122
|
+
export function complement(input) {
|
|
123
|
+
const output = [];
|
|
124
|
+
for (const [minimum, maximum] of SCALAR_RANGES) {
|
|
125
|
+
let cursor = minimum;
|
|
126
|
+
for (const [start, end] of input) {
|
|
127
|
+
if (end < minimum || start > maximum)
|
|
128
|
+
continue;
|
|
129
|
+
if (start > cursor)
|
|
130
|
+
output.push([cursor, Math.min(start - 1, maximum)]);
|
|
131
|
+
cursor = Math.max(cursor, end + 1);
|
|
132
|
+
if (cursor > maximum)
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
if (cursor <= maximum)
|
|
136
|
+
output.push([cursor, maximum]);
|
|
137
|
+
}
|
|
138
|
+
return output;
|
|
139
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { refine } from 'zod';
|
|
2
2
|
import { compilePattern } from '../regexp/compile.js';
|
|
3
3
|
import { loadPattern } from '../regexp/matcher.js';
|
|
4
|
-
import {
|
|
4
|
+
import { refuseIRegexp, stateBudgetMessage } from '../regexp/syntax.js';
|
|
5
5
|
const sources = new WeakMap();
|
|
6
6
|
export function fullMatch(source) {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
const refusal = refuseIRegexp(source);
|
|
8
|
+
if (refusal !== undefined) {
|
|
9
|
+
throw new TypeError(refusal === 'syntax'
|
|
10
|
+
? 'fullMatch(...) requires a complete RFC 9485 I-Regexp source.'
|
|
11
|
+
: stateBudgetMessage('fullMatch(...)'));
|
|
9
12
|
}
|
|
10
13
|
const matcher = loadPattern(compilePattern(source));
|
|
11
14
|
const check = refine((value) => matcher.matches(value), {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { ClassRef, DefinitionBase, ViewRef } from '../../../addressing/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Document target of one View. Every View targets its owning Domain: authoring and compilation
|
|
4
|
+
* only produce `{ kind: 'domain' }`. The `definition` member is the legacy Class target of earlier
|
|
5
|
+
* V1 documents; admission still accepts it so already-persisted documents keep their exact revision
|
|
6
|
+
* and recover, but it carries no meaning and Runtime installation refuses it for a new revision.
|
|
7
|
+
*/
|
|
2
8
|
export type ViewTarget = {
|
|
3
9
|
readonly kind: 'domain';
|
|
4
10
|
} | {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { DefinitionKey, ViewRef } from '../../addressing/index.js';
|
|
2
|
-
import type { ViewDefinition
|
|
2
|
+
import type { ViewDefinition } from './model/view.js';
|
|
3
|
+
/** One resolved View of its owning Domain. A View has no Class or Node target. */
|
|
3
4
|
export interface ResolvedView {
|
|
4
5
|
readonly ref: ViewRef;
|
|
5
6
|
readonly key: DefinitionKey<string, 'view'>;
|
|
6
|
-
readonly target: ViewTarget;
|
|
7
7
|
readonly description?: ViewDefinition['description'];
|
|
8
8
|
readonly definition: ViewDefinition;
|
|
9
9
|
}
|
|
@@ -2,7 +2,7 @@ import type { ClassRef } from '../../addressing/index.js';
|
|
|
2
2
|
import type { MethodDefinition } from '../../language/callable/model/callable.js';
|
|
3
3
|
import type { ClassDefinition } from '../../language/class/model/class.js';
|
|
4
4
|
import type { DataDefinition } from '../../language/class/model/data.js';
|
|
5
|
-
import type { PropertyAuto } from '../../language/class/model/property.js';
|
|
5
|
+
import type { PropertyAuto, PropertyImmutability } from '../../language/class/model/property.js';
|
|
6
6
|
import type { StateTopology } from '../../language/state/index.js';
|
|
7
7
|
import type { JsonSchema } from '../../language/value/model/schema.js';
|
|
8
8
|
import type { SchemaDiagnostic } from '../diagnostic.js';
|
|
@@ -16,6 +16,7 @@ export interface EffectiveField {
|
|
|
16
16
|
readonly schema: JsonSchema;
|
|
17
17
|
readonly required: boolean;
|
|
18
18
|
readonly auto?: PropertyAuto;
|
|
19
|
+
readonly immutable?: PropertyImmutability;
|
|
19
20
|
readonly state?: StateTopology;
|
|
20
21
|
}
|
|
21
22
|
export interface EffectiveMethod {
|
|
@@ -63,6 +63,7 @@ function ownFields(owner, declaration) {
|
|
|
63
63
|
schema: property.schema,
|
|
64
64
|
required: property.required,
|
|
65
65
|
...(property.auto === undefined ? {} : { auto: property.auto }),
|
|
66
|
+
...(property.immutable === undefined ? {} : { immutable: property.immutable }),
|
|
66
67
|
...(property.state === undefined ? {} : { state: property.state }),
|
|
67
68
|
});
|
|
68
69
|
}
|
|
@@ -4,6 +4,13 @@ import type { ResolvedFunction, ResolvedMethod } from '../../../language/callabl
|
|
|
4
4
|
import type { EffectiveMethod } from '../../analysis/class.js';
|
|
5
5
|
import type { Fingerprint } from '../model.js';
|
|
6
6
|
export declare function methodExistence(method: EffectiveMethod): Value;
|
|
7
|
+
/**
|
|
8
|
+
* A Method as a Policy object resolves it: its owner's own declaration and whether that Class can
|
|
9
|
+
* execute it. Dispatch and the signature are the caller's, not the Policy's.
|
|
10
|
+
*/
|
|
11
|
+
export declare function methodObject(method: EffectiveMethod, owner: {
|
|
12
|
+
readonly abstract: boolean;
|
|
13
|
+
}): Value;
|
|
7
14
|
export declare function methodSignature(method: EffectiveMethod): Value;
|
|
8
15
|
export declare function methodInheritance(method: EffectiveMethod): Value;
|
|
9
16
|
export declare function functionSignature(declaration: FunctionDefinition): Value;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isMethodExecutable } from '../../../language/callable/executable.js';
|
|
1
2
|
import { isBinaryInput } from '../../../language/callable/model/callable.js';
|
|
2
3
|
import { methodSignature as inheritedSignature } from '../../analysis/class.js';
|
|
3
4
|
import { deepFreeze } from '../../canonical/freeze.js';
|
|
@@ -13,6 +14,18 @@ export function methodExistence(method) {
|
|
|
13
14
|
abstract: method.declaration.abstract,
|
|
14
15
|
});
|
|
15
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* A Method as a Policy object resolves it: its owner's own declaration and whether that Class can
|
|
19
|
+
* execute it. Dispatch and the signature are the caller's, not the Policy's.
|
|
20
|
+
*/
|
|
21
|
+
export function methodObject(method, owner) {
|
|
22
|
+
return deepFreeze({
|
|
23
|
+
coordinate: 'method',
|
|
24
|
+
owner: keyForDefinitionRef(method.owner),
|
|
25
|
+
name: method.name,
|
|
26
|
+
executable: isMethodExecutable(owner, method.declaration),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
16
29
|
export function methodSignature(method) {
|
|
17
30
|
return deepFreeze({
|
|
18
31
|
coordinate: 'method',
|
|
@@ -19,7 +19,25 @@ export declare function classMeaning(context: Context, coordinate: ClassCoordina
|
|
|
19
19
|
readonly meaning: JsonValue;
|
|
20
20
|
readonly requirement: ClassRequirement;
|
|
21
21
|
} | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* What a Policy pattern reads of an Edge it matches: its orientation and the Classes each endpoint
|
|
24
|
+
* accepts, without cardinalities or roles. Undirected endpoints form an unordered pair.
|
|
25
|
+
*/
|
|
26
|
+
export declare function edgeEndpoints(context: Context, coordinate: ClassCoordinate): JsonValue | undefined;
|
|
22
27
|
export declare function propertyMeaning(field: EffectiveField): JsonValue;
|
|
28
|
+
/**
|
|
29
|
+
* A Class a dependent references or inherits keeps every effective Property it had, whichever
|
|
30
|
+
* ancestor declares it. Each Property is compared on its owner, which still declares it when the
|
|
31
|
+
* Class stops extending that owner, and is not compared at all when another origin owns it: only the
|
|
32
|
+
* Class's own identity set shows those losses. Every loss is recorded, a Property its owner removed
|
|
33
|
+
* included, and only a loss is: a comparison that keeps every identity keeps its facts and evidence
|
|
34
|
+
* bytes, and an added ancestor or Property obliges nothing. The target is projected onto the source
|
|
35
|
+
* identities, as inherited ancestry is, so the differences name the lost Properties.
|
|
36
|
+
*/
|
|
37
|
+
export declare function compareLostProperties({ pair, report, }: {
|
|
38
|
+
readonly pair: SubjectPair;
|
|
39
|
+
readonly report: Report;
|
|
40
|
+
}): void;
|
|
23
41
|
/** Newly required data is an obligation even though it was absent from the source footprint. */
|
|
24
42
|
export declare function compareAddedProperties({ context, pair, report, }: {
|
|
25
43
|
readonly context: ComparisonContext;
|