@astrale-os/kernel-dsl 0.2.0-beta.44 → 0.2.0-beta.46

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.
Files changed (53) hide show
  1. package/dist/v1/addressing/class-key.js +5 -4
  2. package/dist/v1/addressing/method-key.js +3 -2
  3. package/dist/v1/builder/lowering/value/zod.js +11 -2
  4. package/dist/v1/builder/value/full-match.js +6 -3
  5. package/dist/v1/compiled/admission/accept.js +27 -6
  6. package/dist/v1/compiled/admission/semantics.js +41 -16
  7. package/dist/v1/compiled/loader/load.js +1 -0
  8. package/dist/v1/domain/model/input.d.ts +3 -0
  9. package/dist/v1/domain/projection/classes.js +1 -0
  10. package/dist/v1/domain/validation/create.d.ts +2 -0
  11. package/dist/v1/domain/validation/create.js +12 -0
  12. package/dist/v1/language/class/resolved.d.ts +3 -0
  13. package/dist/v1/language/index.d.ts +2 -0
  14. package/dist/v1/language/index.js +1 -0
  15. package/dist/v1/language/policy/normalization/normalize.d.ts +4 -0
  16. package/dist/v1/language/policy/normalization/normalize.js +51 -13
  17. package/dist/v1/language/value/inclusion/includes.d.ts +9 -0
  18. package/dist/v1/language/value/inclusion/includes.js +777 -0
  19. package/dist/v1/language/value/profile/rules.js +11 -32
  20. package/dist/v1/language/value/regexp/ast.js +3 -64
  21. package/dist/v1/language/value/regexp/compile.d.ts +25 -2
  22. package/dist/v1/language/value/regexp/compile.js +48 -12
  23. package/dist/v1/language/value/regexp/syntax.d.ts +11 -1
  24. package/dist/v1/language/value/regexp/syntax.js +14 -2
  25. package/dist/v1/language/value/regexp/unicode.d.ts +32 -0
  26. package/dist/v1/language/value/regexp/unicode.js +139 -0
  27. package/dist/v1/language/value/syntax/full-match.js +6 -3
  28. package/dist/v1/schema/compatibility/comparators/callables.d.ts +7 -0
  29. package/dist/v1/schema/compatibility/comparators/callables.js +13 -0
  30. package/dist/v1/schema/compatibility/comparators/classes.d.ts +18 -0
  31. package/dist/v1/schema/compatibility/comparators/classes.js +54 -0
  32. package/dist/v1/schema/compatibility/comparators/index.d.ts +1 -1
  33. package/dist/v1/schema/compatibility/comparators/index.js +14 -2
  34. package/dist/v1/schema/compatibility/evidence.d.ts +8 -1
  35. package/dist/v1/schema/compatibility/evidence.js +10 -1
  36. package/dist/v1/schema/compatibility/meaning/footprint.js +3 -1
  37. package/dist/v1/schema/compatibility/meaning/index.js +4 -2
  38. package/dist/v1/schema/compatibility/model.d.ts +11 -6
  39. package/dist/v1/schema/compatibility/references.d.ts +8 -1
  40. package/dist/v1/schema/compatibility/references.js +39 -6
  41. package/dist/v1/schema/compatibility/scope.d.ts +5 -0
  42. package/dist/v1/schema/compatibility/scope.js +5 -2
  43. package/dist/v1/schema/compatibility/structure/index.js +21 -2
  44. package/dist/v1/schema/compatibility/structure/requirements.js +26 -1
  45. package/dist/v1/schema/compatibility/subjects.js +20 -2
  46. package/dist/v1/schema/resolution/domain.js +1 -0
  47. package/dist/v1/schema/resolution/origins.d.ts +11 -4
  48. package/dist/v1/schema/resolution/origins.js +7 -8
  49. package/dist/v1/schema/validation/class.d.ts +10 -0
  50. package/dist/v1/schema/validation/class.js +4 -2
  51. package/dist/v1/schema/validation/policy/budgets.js +16 -3
  52. package/dist/v1/schema/validation/policy/typing.js +15 -0
  53. 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 { compilePattern, PatternComplexityError } from '../regexp/compile.js';
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' && !isIRegexp(schema.fullMatch)) {
100
- diagnostics.push(diagnostic('VS_REGEX_PORTABLE', `${pointer}/fullMatch`, 'fullMatch must satisfy the complete RFC 9485 I-Regexp syntax.'));
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
- else if (typeof schema.fullMatch === 'string') {
103
- checkPatternComplexity(schema.fullMatch, `${pointer}/fullMatch`, diagnostics);
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
- if (!isIRegexp(pattern)) {
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
- * Reject a syntactically valid I-Regexp that compiles to an unbounded NFA (nested counted
124
- * repetitions, P1-I-01) at admission, rather than letting it unroll to ~120k states and cost
125
- * seconds per validated value. Other compile failures are already reported by the syntax checks.
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
- const CATEGORY = /^(?:L[lmotu]?|M[cen]?|N[dlo]?|P[cdfios]?|Z[lps]?|S[ckmo]?|C[cfno]?)$/u;
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
- export declare const MAX_PATTERN_STATES = 10000;
3
- /** Thrown when an I-Regexp source compiles past `MAX_PATTERN_STATES` NFA states. */
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
- // A branching I-Regexp compiles to an NFA whose size is the product of its nested counted
3
- // repetitions, so a short source such as `(a{0,1000}){0,60}b` unrolls to ~120k states and costs
4
- // seconds per validated value (P1-I-01). Bound the NFA so a pathological source is rejected at
5
- // schema admission instead. The limit is well above realistic patterns: the RFC 9485 domain-name
6
- // regex `([a-z0-9-]{1,63}\.){1,32}[a-z]{2,63}` compiles to ~4.3k states. Pure scalar runs
7
- // (`a{0,1000}`) never reach this path: they compile to counted runs, not NFA states.
8
- export const MAX_PATTERN_STATES = 10_000;
9
- /** Thrown when an I-Regexp source compiles past `MAX_PATTERN_STATES` NFA states. */
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 = MAX_PATTERN_STATES) {
13
- super(`I-Regexp compiles to more than ${limit} states.`);
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
- /** Complete RFC 9485 syntax admission through the same AST consumed by compilation. */
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
- /** Complete RFC 9485 syntax admission through the same AST consumed by compilation. */
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 parseIRegexp(source) !== undefined;
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 { isIRegexp } from '../regexp/syntax.js';
4
+ import { refuseIRegexp, stateBudgetMessage } from '../regexp/syntax.js';
5
5
  const sources = new WeakMap();
6
6
  export function fullMatch(source) {
7
- if (!isIRegexp(source)) {
8
- throw new TypeError('fullMatch(...) requires a complete RFC 9485 I-Regexp source.');
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), {
@@ -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;