@astrale-os/kernel-dsl 1.0.0-beta.16 → 1.0.0-beta.18

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 (75) hide show
  1. package/dist/v1/builder/class/authoring.d.ts +40 -4
  2. package/dist/v1/builder/class/authoring.js +3 -2
  3. package/dist/v1/builder/class/input.d.ts +45 -14
  4. package/dist/v1/builder/index.d.ts +2 -1
  5. package/dist/v1/builder/lowering/class.d.ts +8 -0
  6. package/dist/v1/builder/lowering/class.js +40 -16
  7. package/dist/v1/builder/lowering/schema/compile.js +3 -3
  8. package/dist/v1/builder/policy/authoring.js +97 -88
  9. package/dist/v1/bundle/model.js +1 -1
  10. package/dist/v1/compiled/admission/accept.js +1 -7
  11. package/dist/v1/compiled/admission/semantics/class.js +2 -3
  12. package/dist/v1/compiled/admission/semantics/policy/parity.js +25 -13
  13. package/dist/v1/compiled/admission/wire/class.js +23 -18
  14. package/dist/v1/compiled/admission/wire/policy.d.ts +4 -3
  15. package/dist/v1/compiled/admission/wire/policy.js +8 -7
  16. package/dist/v1/compiled/loader/load.js +1 -1
  17. package/dist/v1/domain/create.js +1 -1
  18. package/dist/v1/domain/model/input.d.ts +2 -1
  19. package/dist/v1/domain/projection/classes.js +1 -1
  20. package/dist/v1/domain/validation/create.d.ts +2 -1
  21. package/dist/v1/domain/validation/create.js +1 -1
  22. package/dist/v1/index.d.ts +1 -1
  23. package/dist/v1/language/callable/resolved.d.ts +11 -2
  24. package/dist/v1/language/class/model/class.d.ts +17 -5
  25. package/dist/v1/language/freeze.d.ts +5 -0
  26. package/dist/v1/{schema/canonical → language}/freeze.js +4 -0
  27. package/dist/v1/language/policy/analysis/typing.d.ts +1 -0
  28. package/dist/v1/language/policy/analysis/typing.js +74 -79
  29. package/dist/v1/language/policy/model/policy.d.ts +44 -4
  30. package/dist/v1/language/policy/model/policy.js +14 -0
  31. package/dist/v1/language/policy/normalization/normalize.d.ts +6 -6
  32. package/dist/v1/language/policy/normalization/normalize.js +5 -5
  33. package/dist/v1/language/validation/rule.d.ts +18 -8
  34. package/dist/v1/language/validation/rule.js +2 -11
  35. package/dist/v1/language/value/evaluate/program.d.ts +2 -1
  36. package/dist/v1/language/value/evaluate/program.js +2 -2
  37. package/dist/v1/language/value/evaluate/validate.d.ts +2 -2
  38. package/dist/v1/language/value/evaluate/validate.js +2 -2
  39. package/dist/v1/language/value/profile/json.validate.d.ts +3 -1
  40. package/dist/v1/language/value/profile/json.validate.js +3 -2
  41. package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +180 -25
  42. package/dist/v1/schema/admission/callable.js +1 -1
  43. package/dist/v1/schema/canonical/revision.js +4 -4
  44. package/dist/v1/schema/compatibility/comparators/callables.js +1 -1
  45. package/dist/v1/schema/compatibility/comparators/classes.js +4 -4
  46. package/dist/v1/schema/compatibility/comparators/members.js +1 -1
  47. package/dist/v1/schema/compatibility/comparators/values.js +2 -2
  48. package/dist/v1/schema/compatibility/report.js +1 -1
  49. package/dist/v1/schema/compatibility/versioning/index.js +1 -1
  50. package/dist/v1/schema/model/schema.js +1 -1
  51. package/dist/v1/schema/normalization/domain.js +9 -8
  52. package/dist/v1/schema/resolution/domain.js +1 -1
  53. package/dist/v1/schema/resolution/origins.js +3 -2
  54. package/dist/v1/schema/rule.d.ts +8 -20
  55. package/dist/v1/schema/rule.js +1 -1
  56. package/dist/v1/schema/validation/class.js +2 -3
  57. package/dist/v1/schema/validation/domain.json-schema.d.ts +2 -2
  58. package/dist/v1/schema/validation/domain.json-schema.js +20 -6
  59. package/dist/v1/schema/validation/domain.structure.d.ts +4765 -1005
  60. package/dist/v1/schema/validation/domain.structure.js +21 -9
  61. package/dist/v1/schema/validation/json.json-schema.d.ts +18 -2
  62. package/dist/v1/schema/validation/json.json-schema.js +14 -4
  63. package/dist/v1/schema/validation/policy/budgets.d.ts +1 -1
  64. package/dist/v1/schema/validation/policy/budgets.js +4 -4
  65. package/dist/v1/schema/validation/policy/uses.d.ts +10 -2
  66. package/dist/v1/schema/validation/policy/uses.js +26 -13
  67. package/dist/v1/schema/validation/policy/validate.js +2 -3
  68. package/dist/v1/schema/validation/value.json-schema.d.ts +4 -6
  69. package/dist/v1/schema/validation/value.json-schema.js +18 -25
  70. package/package.json +1 -1
  71. package/dist/v1/schema/canonical/domain.d.ts +0 -3
  72. package/dist/v1/schema/canonical/domain.digest.d.ts +0 -3
  73. package/dist/v1/schema/canonical/domain.digest.js +0 -5
  74. package/dist/v1/schema/canonical/domain.js +0 -5
  75. package/dist/v1/schema/canonical/freeze.d.ts +0 -1
@@ -16,9 +16,7 @@ export class PolicyTyping {
16
16
  // paths; for that to merge distinct variables, the Policy expands deeper than
17
17
  // `limits.policy.patternDepth`.
18
18
  #expansions = new Map();
19
- #nodeClasses;
20
- #concreteClasses;
21
- #satisfaction;
19
+ #index;
22
20
  constructor(scope) {
23
21
  this.scope = scope;
24
22
  }
@@ -31,9 +29,7 @@ export class PolicyTyping {
31
29
  }
32
30
  /** Whether the Policy accepts every concrete source and target pair the Edge Class accepts. */
33
31
  policyAcceptsEdgeClass(name, declaration) {
34
- const endpoints = declaration.orientation === 'directed'
35
- ? [declaration.endpoints.source, declaration.endpoints.target]
36
- : declaration.endpoints;
32
+ const endpoints = endpointPair(declaration);
37
33
  const sources = this.#concreteEndpointClasses(endpoints[0].accepts);
38
34
  const targets = this.#concreteEndpointClasses(endpoints[1].accepts);
39
35
  return sources.every((source) => targets.every((target) => this.#policyAcceptsEdge(name, { kind: 'exact', ref: source }, { kind: 'exact', ref: target })));
@@ -73,10 +69,13 @@ export class PolicyTyping {
73
69
  return branches !== undefined && branches.every((branch) => this.branchIsTyped(branch, use));
74
70
  }
75
71
  #concreteEndpointClasses(accepts) {
76
- this.#concreteClasses ??= new Set(this.scope.concreteNodeClasses.map(definitionRefIndex));
77
- const concrete = this.#concreteClasses;
72
+ const { concrete } = this.#nodeClassIndex();
78
73
  return this.#satisfyingOneOf(accepts).filter((ref) => concrete.has(definitionRefIndex(ref)));
79
74
  }
75
+ #nodeClassIndex() {
76
+ this.#index ??= nodeClassIndex(this.scope);
77
+ return this.#index;
78
+ }
80
79
  #expandedBranches(name, visiting, namespace) {
81
80
  if (visiting.has(name))
82
81
  return undefined;
@@ -164,11 +163,7 @@ export class PolicyTyping {
164
163
  if (contract.kind === 'exact')
165
164
  return this.#applyExact(candidates, contract.ref);
166
165
  if (contract.kind === 'default') {
167
- return uniqueCandidates(candidates.flatMap((candidate) => candidate.kind === 'exact'
168
- ? this.#isNodeClass(candidate.ref)
169
- ? [candidate]
170
- : []
171
- : this.#nodeCandidates(candidate)));
166
+ return this.#applyAnyNodeClass(candidates).filter((candidate) => candidate.kind !== 'exact' || this.#isNodeClass(candidate.ref));
172
167
  }
173
168
  return candidates;
174
169
  }
@@ -202,10 +197,10 @@ export class PolicyTyping {
202
197
  result.push({ kind: 'exact', ref: exact });
203
198
  }
204
199
  }
205
- return uniqueCandidates(result);
200
+ return uniqueBy(result, candidateKey);
206
201
  }
207
202
  #applyAnyNodeClass(candidates) {
208
- return uniqueCandidates(candidates.flatMap((candidate) => candidate.kind === 'exact' ? [candidate] : this.#nodeCandidates(candidate)));
203
+ return uniqueBy(candidates.flatMap((candidate) => candidate.kind === 'exact' ? [candidate] : this.#nodeCandidates(candidate)), candidateKey);
209
204
  }
210
205
  #applyRequirement(candidates, required) {
211
206
  const result = [];
@@ -220,11 +215,11 @@ export class PolicyTyping {
220
215
  else {
221
216
  result.push({
222
217
  kind: 'open',
223
- requirements: uniqueClasses([...candidate.requirements, required]),
218
+ requirements: uniqueBy([...candidate.requirements, required], definitionRefIndex),
224
219
  });
225
220
  }
226
221
  }
227
- return uniqueCandidates(result);
222
+ return uniqueBy(result, candidateKey);
228
223
  }
229
224
  #applyEndpoint(candidates, accepts) {
230
225
  const result = [];
@@ -242,11 +237,11 @@ export class PolicyTyping {
242
237
  for (const accept of accepts) {
243
238
  result.push({
244
239
  kind: 'open',
245
- requirements: uniqueClasses([...candidate.requirements, accept]),
240
+ requirements: uniqueBy([...candidate.requirements, accept], definitionRefIndex),
246
241
  });
247
242
  }
248
243
  }
249
- return uniqueCandidates(result);
244
+ return uniqueBy(result, candidateKey);
250
245
  }
251
246
  #classSatisfiesEndpoint(candidate, accepts) {
252
247
  return accepts.some((accept) => this.#classExtends(candidate, accept));
@@ -263,7 +258,7 @@ export class PolicyTyping {
263
258
  * `#classSatisfiesEndpoint` accepts, read from the index rather than tested one by one.
264
259
  */
265
260
  #satisfyingOneOf(accepts) {
266
- const index = (this.#satisfaction ??= satisfactionIndex(this.scope));
261
+ const index = this.#nodeClassIndex();
267
262
  const positions = new Set();
268
263
  for (const accept of accepts) {
269
264
  const required = this.scope.resolveClass(accept);
@@ -271,7 +266,7 @@ export class PolicyTyping {
271
266
  continue;
272
267
  if (isGraphRoot(required))
273
268
  return index.nodeClasses;
274
- for (const position of index.satisfying.get(ancestryKey(required.ref)) ?? []) {
269
+ for (const position of index.satisfying.get(definitionRefIndex(required.ref)) ?? []) {
275
270
  positions.add(position);
276
271
  }
277
272
  }
@@ -281,8 +276,7 @@ export class PolicyTyping {
281
276
  return lists.every((accepts) => this.#classSatisfiesEndpoint(ref, accepts));
282
277
  }
283
278
  #isNodeClass(ref) {
284
- this.#nodeClasses ??= new Set(this.scope.allNodeClasses.map(definitionRefIndex));
285
- return this.#nodeClasses.has(definitionRefIndex(ref));
279
+ return this.#nodeClassIndex().nodes.has(definitionRefIndex(ref));
286
280
  }
287
281
  /** The Node Classes an open or `node` candidate can be. */
288
282
  #nodeCandidates(candidate) {
@@ -307,16 +301,14 @@ export class PolicyTyping {
307
301
  return inhabited ? [{ kind: 'node', lists }] : [];
308
302
  }
309
303
  }
310
- /** Classes are satisfied by origin and name, as `classSatisfies` compares them. */
311
- function ancestryKey(ref) {
312
- return `${ref.origin}\u0000${ref.name}`;
313
- }
314
304
  /**
315
305
  * Index each Node Class of the closure under every Class its ancestry reaches, walked as
316
306
  * `classSatisfies` walks it: the Class itself, then each declared parent that resolves to a Node
317
307
  * Class. A Class then satisfies a non-root Node Class exactly when it is indexed under it.
308
+ * `classSatisfies` compares origin and name; every ClassRef has kind `class`, so the definition
309
+ * index of a ref keys it as that comparison does.
318
310
  */
319
- function satisfactionIndex(scope) {
311
+ function nodeClassIndex(scope) {
320
312
  const nodeClasses = [];
321
313
  const satisfying = new Map();
322
314
  for (const ref of scope.allNodeClasses) {
@@ -324,30 +316,38 @@ function satisfactionIndex(scope) {
324
316
  if (actual?.kind !== 'node')
325
317
  continue;
326
318
  const position = nodeClasses.push(ref) - 1;
327
- const seen = new Set();
328
- const keys = new Set();
329
- const pending = [actual];
330
- for (let value = pending.pop(); value !== undefined; value = pending.pop()) {
331
- if (seen.has(value))
332
- continue;
333
- seen.add(value);
334
- const key = ancestryKey(value.ref);
335
- if (!keys.has(key)) {
336
- keys.add(key);
337
- const positions = satisfying.get(key);
338
- if (positions === undefined)
339
- satisfying.set(key, [position]);
340
- else
341
- positions.push(position);
342
- }
343
- for (const parent of value.extends) {
344
- const resolved = scope.resolveClass(parent);
345
- if (resolved?.kind === 'node')
346
- pending.push(resolved);
347
- }
319
+ for (const key of ancestryKeys(actual, scope)) {
320
+ const positions = satisfying.get(key);
321
+ if (positions === undefined)
322
+ satisfying.set(key, [position]);
323
+ else
324
+ positions.push(position);
325
+ }
326
+ }
327
+ return {
328
+ nodeClasses,
329
+ nodes: new Set(nodeClasses.map(definitionRefIndex)),
330
+ concrete: new Set(scope.concreteNodeClasses.map(definitionRefIndex)),
331
+ satisfying,
332
+ };
333
+ }
334
+ /** The keys of the Node Classes a Node Class's ancestry reaches, itself included. */
335
+ function ancestryKeys(actual, scope) {
336
+ const seen = new Set();
337
+ const keys = new Set();
338
+ const pending = [actual];
339
+ for (let value = pending.pop(); value !== undefined; value = pending.pop()) {
340
+ if (seen.has(value))
341
+ continue;
342
+ seen.add(value);
343
+ keys.add(definitionRefIndex(value.ref));
344
+ for (const parent of value.extends) {
345
+ const resolved = scope.resolveClass(parent);
346
+ if (resolved?.kind === 'node')
347
+ pending.push(resolved);
348
348
  }
349
349
  }
350
- return { nodeClasses, satisfying };
350
+ return keys;
351
351
  }
352
352
  function targetContract(term, use) {
353
353
  if (use.kind === 'object')
@@ -378,9 +378,7 @@ function variantsForEdge(use, typing) {
378
378
  const declaration = typing.scope.edgeDeclaration(edge.class);
379
379
  if (declaration === undefined)
380
380
  return [];
381
- const endpoints = declaration.orientation === 'directed'
382
- ? [declaration.endpoints.source, declaration.endpoints.target]
383
- : declaration.endpoints;
381
+ const endpoints = endpointPair(declaration);
384
382
  const bounds = edge.repeat ?? { min: 1, max: 1 };
385
383
  if (!Number.isInteger(bounds.min) ||
386
384
  !Number.isInteger(bounds.max) ||
@@ -422,7 +420,13 @@ function variantsForEdge(use, typing) {
422
420
  });
423
421
  }
424
422
  }
425
- return uniqueEdgeVariants(variants);
423
+ return uniqueBy(variants, edgeVariantKey);
424
+ }
425
+ /** The two endpoints of an Edge Class, a directed one's as source then target. */
426
+ function endpointPair(declaration) {
427
+ return declaration.orientation === 'directed'
428
+ ? [declaration.endpoints.source, declaration.endpoints.target]
429
+ : declaration.endpoints;
426
430
  }
427
431
  function startEndpoint(endpoints, reversed) {
428
432
  return reversed ? endpoints[1] : endpoints[0];
@@ -430,31 +434,22 @@ function startEndpoint(endpoints, reversed) {
430
434
  function endEndpoint(endpoints, reversed) {
431
435
  return reversed ? endpoints[0] : endpoints[1];
432
436
  }
433
- function uniqueEdgeVariants(values) {
434
- const result = new Map();
435
- for (const value of values) {
436
- const key = `${value.constraints
437
- .map(([term, accepts]) => `${term}:${accepts.map(definitionRefIndex).sort().join(',')}`)
438
- .join('|')}=${value.equalities.map(([left, right]) => `${left}:${right}`).join('|')}`;
439
- result.set(key, value);
440
- }
441
- return [...result.values()];
437
+ /** The values with distinct keys, each at its key's first position and holding its last value. */
438
+ function uniqueBy(values, key) {
439
+ return [...new Map(values.map((value) => [key(value), value])).values()];
442
440
  }
443
- function uniqueCandidates(values) {
444
- const result = new Map();
445
- for (const value of values) {
446
- const key = value.kind === 'exact'
447
- ? `exact:${definitionRefIndex(value.ref)}`
448
- : value.kind === 'open'
449
- ? `open:${value.requirements.map(definitionRefIndex).sort().join('|')}`
450
- : `node:${value.lists
451
- .map((accepts) => accepts.map(definitionRefIndex).sort().join(','))
452
- .sort()
453
- .join('|')}`;
454
- result.set(key, value);
455
- }
456
- return [...result.values()];
441
+ function edgeVariantKey(variant) {
442
+ const constraints = variant.constraints.map(([term, accepts]) => `${term}:${classSetKey(accepts)}`);
443
+ const equalities = variant.equalities.map(([left, right]) => `${left}:${right}`);
444
+ return `${constraints.join('|')}=${equalities.join('|')}`;
445
+ }
446
+ function candidateKey(candidate) {
447
+ if (candidate.kind === 'exact')
448
+ return `exact:${definitionRefIndex(candidate.ref)}`;
449
+ if (candidate.kind === 'open')
450
+ return `open:${classSetKey(candidate.requirements)}`;
451
+ return `node:${candidate.lists.map(classSetKey).sort().join('|')}`;
457
452
  }
458
- function uniqueClasses(values) {
459
- return [...new Map(values.map((value) => [definitionRefIndex(value), value])).values()];
453
+ function classSetKey(refs) {
454
+ return refs.map(definitionRefIndex).sort().join(',');
460
455
  }
@@ -90,23 +90,63 @@ export type PolicyObjectRef = ClassRef | FunctionRef | PolicyRef | ViewRef | Cor
90
90
  export declare function policyObjectKey(ref: PolicyObjectRef): string;
91
91
  export declare function policyObjectOrigin(ref: PolicyObjectRef): string;
92
92
  export declare function projectedMemberClass(ref: Exclude<PolicyObjectRef, CoreRef>): ClassRef;
93
+ export type ClassObservation = 'read' | 'traverse';
94
+ export type ClassWrite = 'create' | 'update' | 'delete';
95
+ export type ClassOperation = ClassObservation | ClassWrite;
96
+ /**
97
+ * Every Class slot: the observations, which admit every caller when absent, then the writes, which
98
+ * admit only the Domain that declares the Class when absent.
99
+ */
100
+ export declare const CLASS_OPERATIONS: readonly ClassOperation[];
101
+ export declare function isClassObservation(operation: ClassOperation): operation is ClassObservation;
93
102
  /**
94
103
  * One Class observation slot: a named Policy, or `false`, the rule no caller satisfies. Its other
95
104
  * value has no spelling there: an absent observation Policy already means `true`.
96
105
  */
97
106
  export type ClassPolicy = PolicyRef | false;
98
- /** What a Node Class rule may check: the observed Node, or one exact projected member. */
107
+ /**
108
+ * One Class write slot: a named Policy, or `true`, the rule every Identity satisfies. Its other
109
+ * value has no spelling there: an absent write Policy already means the owning Domain only.
110
+ */
111
+ export type ClassWritePolicy = PolicyRef | true;
112
+ /** What a Class rule may check: the observed or written instance, or one exact projected member. */
99
113
  export type ClassRuleObject = Extract<PolicyCheckObject, {
100
114
  readonly kind: 'self' | 'ref';
101
115
  }>;
102
116
  /**
103
- * Policy checks deciding one Node Class observation. `self` is the observed Node; a `ref` object
104
- * is the same Node for every candidate. A lone check of `self` is spelled as its Policy reference.
117
+ * Policy checks deciding one Node Class slot. `self` is the observed or written Node; a `ref`
118
+ * object is the same Node for every candidate. A lone check of `self` is spelled as its Policy
119
+ * reference.
105
120
  */
106
121
  export type ClassRuleChecks = PolicyCheckExpression<PolicyRef, ClassRuleObject>;
122
+ /**
123
+ * Policy checks deciding one Edge Class slot. `self` is the observed or written Edge, checked by a
124
+ * Policy whose source and target are its endpoints; a `ref` object is one exact member, the same
125
+ * Node for every candidate. A lone check of `self` is spelled as its Policy reference.
126
+ */
127
+ export type EdgeRuleChecks = ClassRuleChecks;
128
+ /** What a Node create rule may check: one exact projected member, since the Node is not yet. */
129
+ export type CreateRuleObject = Extract<PolicyCheckObject, {
130
+ readonly kind: 'ref';
131
+ }>;
132
+ /** Policy checks deciding the creation of a Node: every object is a `ref` member. */
133
+ export type CreateRuleChecks = PolicyCheckExpression<PolicyRef, CreateRuleObject>;
107
134
  /** One Node Class observation slot: a Class Policy, or Policy checks over the observed Node. */
108
135
  export type NodeClassPolicy = ClassPolicy | ClassRuleChecks;
109
- export declare function isClassRuleChecks(value: ClassPolicy | ClassRuleChecks | undefined): value is ClassRuleChecks;
136
+ /** One Node Class update or delete slot: a write Policy, or Policy checks over the written Node. */
137
+ export type NodeClassWritePolicy = ClassWritePolicy | ClassRuleChecks;
138
+ /**
139
+ * The Node Class create slot: `true`, or Policy checks over members. A named Policy alone has no
140
+ * object there, so it has no spelling.
141
+ */
142
+ export type NodeClassCreatePolicy = true | CreateRuleChecks;
143
+ /** One Edge Class observation slot: a Class Policy, or Policy checks over the observed Edge. */
144
+ export type EdgeClassPolicy = ClassPolicy | EdgeRuleChecks;
145
+ /** One Edge Class write slot: a write Policy, or Policy checks over the written Edge. */
146
+ export type EdgeClassWritePolicy = ClassWritePolicy | EdgeRuleChecks;
147
+ /** The value of any Class slot. */
148
+ export type ClassSlotPolicy = PolicyRef | boolean | ClassRuleChecks;
149
+ export declare function isClassRuleChecks(value: ClassSlotPolicy | undefined): value is ClassRuleChecks;
110
150
  /**
111
151
  * Whether every way Policy checks can hold passes through a check, so that the caller decides each
112
152
  * admission. `sameNode` compares two objects and names no caller.
@@ -33,6 +33,20 @@ export function projectedMemberClass(ref) {
33
33
  function kernelClassRef(name) {
34
34
  return Object.freeze({ origin: 'kernel.astrale.ai', kind: 'class', name });
35
35
  }
36
+ /**
37
+ * Every Class slot: the observations, which admit every caller when absent, then the writes, which
38
+ * admit only the Domain that declares the Class when absent.
39
+ */
40
+ export const CLASS_OPERATIONS = Object.freeze([
41
+ 'read',
42
+ 'traverse',
43
+ 'create',
44
+ 'update',
45
+ 'delete',
46
+ ]);
47
+ export function isClassObservation(operation) {
48
+ return operation === 'read' || operation === 'traverse';
49
+ }
36
50
  export function isClassRuleChecks(value) {
37
51
  return (typeof value === 'object' &&
38
52
  ('check' in value || 'sameNode' in value || 'allOf' in value || 'anyOf' in value));
@@ -1,4 +1,4 @@
1
- import type { NodeClassPolicy, PolicyCheckExpression, PolicyExpression, PolicyPatternExpression } from '../model/policy.js';
1
+ import type { ClassSlotPolicy, PolicyCheckExpression, PolicyExpression, PolicyPatternExpression } from '../model/policy.js';
2
2
  export declare function normalizePolicyExpression(value: PolicyExpression): PolicyExpression;
3
3
  /**
4
4
  * The fixed point of the single pass: a Pattern the single pass reproduces is returned unchanged,
@@ -6,10 +6,10 @@ export declare function normalizePolicyExpression(value: PolicyExpression): Poli
6
6
  */
7
7
  export declare function normalizePolicyPattern(value: PolicyPatternExpression): PolicyPatternExpression;
8
8
  /**
9
- * The one spelling of a Node Class observation rule: normalized Policy checks without a composition
10
- * of one operand, or the Policy that a lone check of `self` names, which is how a Class has always
11
- * declared one. Callable checks keep their compositions of one operand: their revisions predate
12
- * this rule.
9
+ * The one spelling of a Class rule: normalized Policy checks without a composition of one operand,
10
+ * or the Policy that a lone check of `self` names, which is how a Class has always declared one.
11
+ * Callable checks keep their compositions of one operand: their revisions predate this rule. A
12
+ * create rule checks no `self`, so it keeps its checks.
13
13
  */
14
- export declare function normalizeClassRule(value: NodeClassPolicy): NodeClassPolicy;
14
+ export declare function normalizeClassRule<Slot extends ClassSlotPolicy>(value: Slot): Slot;
15
15
  export declare function normalizePolicyChecks(value: PolicyCheckExpression): PolicyCheckExpression;
@@ -40,16 +40,16 @@ function normalizePatternOnce(value) {
40
40
  return normalizePatternNode(rewritten);
41
41
  }
42
42
  /**
43
- * The one spelling of a Node Class observation rule: normalized Policy checks without a composition
44
- * of one operand, or the Policy that a lone check of `self` names, which is how a Class has always
45
- * declared one. Callable checks keep their compositions of one operand: their revisions predate
46
- * this rule.
43
+ * The one spelling of a Class rule: normalized Policy checks without a composition of one operand,
44
+ * or the Policy that a lone check of `self` names, which is how a Class has always declared one.
45
+ * Callable checks keep their compositions of one operand: their revisions predate this rule. A
46
+ * create rule checks no `self`, so it keeps its checks.
47
47
  */
48
48
  export function normalizeClassRule(value) {
49
49
  if (!isClassRuleChecks(value))
50
50
  return value;
51
51
  const checks = unwrapSingleOperands(normalizePolicyChecks(value));
52
- return 'check' in checks && checks.object.kind === 'self' ? checks.check : checks;
52
+ return ('check' in checks && checks.object.kind === 'self' ? checks.check : checks);
53
53
  }
54
54
  /** Replace each composition of one operand by that operand, merging what it then nests. */
55
55
  function unwrapSingleOperands(value) {
@@ -8,13 +8,15 @@ import type { DiagnosticCode } from './diagnostic.js';
8
8
  * `warning`: a value schema, which the rule reports without refusing it.
9
9
  */
10
10
  export type ValueRuleStage = 'schema' | 'context' | 'value' | 'warning';
11
- /** The rule one value diagnostic code reports. */
12
- export interface ValueRule {
13
- readonly stage: ValueRuleStage;
11
+ /**
12
+ * The rule one diagnostic code reports, whatever it checks: what `schema.RULES` and `VALUE_RULES`
13
+ * say of every code, each with the stages of its own checks.
14
+ */
15
+ export interface DiagnosticRule {
14
16
  /**
15
- * The positions the rule constrains, which its diagnostics point at, relative to the value
16
- * schema, the value or the Domain input it checks: JSON Pointer templates in which a `{name}`
17
- * token stands for one token of the kind it names and `{/path*}` for any number of tokens.
17
+ * The positions the rule constrains, which its diagnostics point at: JSON Pointer templates in
18
+ * which a `{name}` token stands for one token of the kind it names and `{/path*}` for any number
19
+ * of tokens.
18
20
  */
19
21
  readonly positions: readonly [string, ...string[]];
20
22
  /** The rule, in one plain sentence. */
@@ -23,11 +25,19 @@ export interface ValueRule {
23
25
  readonly limits?: readonly [LimitKey, ...LimitKey[]];
24
26
  /**
25
27
  * The conditions of the published JSON Schema of the document that state the rule, where some
26
- * do: the URI fragment of each `if` or `not` subschema whose failure refuses a value schema of
27
- * the document with `DV_VALUE_SCHEMA` and this rule's code as its `valueCode`.
28
+ * do: the URI fragment of each `if` or `not` subschema whose failure refuses the document with
29
+ * this rule's code.
28
30
  */
29
31
  readonly conditions?: readonly [string, ...string[]];
30
32
  }
33
+ /**
34
+ * The rule one value diagnostic code reports. Its positions are relative to the value schema, the
35
+ * value or the Domain input it checks. A condition refuses a value schema of the document with
36
+ * `DV_VALUE_SCHEMA` and this rule's code as its `valueCode`.
37
+ */
38
+ export interface ValueRule extends DiagnosticRule {
39
+ readonly stage: ValueRuleStage;
40
+ }
31
41
  /**
32
42
  * Every value diagnostic code with the rule it reports. A code added to the language does not
33
43
  * compile until it is described here.
@@ -1,8 +1,9 @@
1
+ import { deepFreeze } from '../freeze.js';
1
2
  /**
2
3
  * Every value diagnostic code with the rule it reports. A code added to the language does not
3
4
  * compile until it is described here.
4
5
  */
5
- export const VALUE_RULES = frozen({
6
+ export const VALUE_RULES = deepFreeze({
6
7
  CALLABLE_INPUT_FIELD_UNKNOWN: {
7
8
  stage: 'value',
8
9
  positions: [''],
@@ -106,13 +107,3 @@ export const VALUE_RULES = frozen({
106
107
  statement: 'A Property of value kind json is filtered by equality only, so its author is warned.',
107
108
  },
108
109
  });
109
- function frozen(rules) {
110
- for (const rule of Object.values(rules)) {
111
- Object.freeze(Object.freeze(rule).positions);
112
- if (rule.limits !== undefined)
113
- Object.freeze(rule.limits);
114
- if (rule.conditions !== undefined)
115
- Object.freeze(rule.conditions);
116
- }
117
- return Object.freeze(rules);
118
- }
@@ -1,3 +1,4 @@
1
+ import type { Options } from '@astrale-os/kernel-dsl/value';
1
2
  import type { Diagnostic } from '../../validation/diagnostic.js';
2
3
  import type { ValueProgram } from '../compile/model.js';
3
- export declare function validateValueProgram(program: ValueProgram, input: unknown, roots?: readonly number[]): readonly Diagnostic[];
4
+ export declare function validateValueProgram(program: ValueProgram, input: unknown, roots?: readonly number[], options?: Options): readonly Diagnostic[];
@@ -1,8 +1,8 @@
1
1
  import { loadValueProgram } from '../compile/load.js';
2
2
  import { validateCanonicalJsonDomain } from '../profile/json.validate.js';
3
3
  import { evaluateInstance } from './evaluator.js';
4
- export function validateValueProgram(program, input, roots = [program.root]) {
5
- const canonical = validateCanonicalJsonDomain(input, '');
4
+ export function validateValueProgram(program, input, roots = [program.root], options) {
5
+ const canonical = validateCanonicalJsonDomain(input, '', options);
6
6
  if (canonical.length > 0)
7
7
  return canonical;
8
8
  return roots.flatMap((root) => {
@@ -1,4 +1,4 @@
1
- import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
1
+ import type { Value as JsonValue, Options } 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';
@@ -13,4 +13,4 @@ import { type PatternPool } from './preparation.js';
13
13
  */
14
14
  export declare function validateJsonInstance(schema: JsonSchema, value: JsonValue, registry: ValueSchemaRegistry, pointer?: string, patterns?: PatternPool): Diagnostic[];
15
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[];
16
+ export declare function validatePreparedInstance(compiled: CompiledInstanceSchema, value: unknown, options?: Options): Diagnostic[];
@@ -24,8 +24,8 @@ export function validateJsonInstance(schema, value, registry, pointer = '', patt
24
24
  return instanceDiagnostics(prepared.value, value, pointer);
25
25
  }
26
26
  /** Validate with the exact preparation already retained by a source-resolved Property or Callable. */
27
- export function validatePreparedInstance(compiled, value) {
28
- const diagnostics = [...validateCanonicalJsonDomain(value, '')];
27
+ export function validatePreparedInstance(compiled, value, options) {
28
+ const diagnostics = [...validateCanonicalJsonDomain(value, '', options)];
29
29
  return diagnostics.length > 0
30
30
  ? diagnostics
31
31
  : instanceDiagnostics(compiled, value, '');
@@ -1,2 +1,4 @@
1
+ import { type Options } from '@astrale-os/kernel-dsl/value';
1
2
  import type { Diagnostic } from '../../validation/diagnostic.js';
2
- export declare function validateCanonicalJsonDomain(value: unknown, pointer: string): readonly Diagnostic[];
3
+ /** Whether `value` is a portable JSON Value, within the portable limits `options` overrides. */
4
+ export declare function validateCanonicalJsonDomain(value: unknown, pointer: string, options?: Options): readonly Diagnostic[];
@@ -1,8 +1,9 @@
1
1
  import { acceptForRead as acceptJsonValue, BoundaryError as JsonBoundaryError, } from '@astrale-os/kernel-dsl/value';
2
2
  import { diagnostic } from '../../validation/diagnostic.js';
3
- export function validateCanonicalJsonDomain(value, pointer) {
3
+ /** Whether `value` is a portable JSON Value, within the portable limits `options` overrides. */
4
+ export function validateCanonicalJsonDomain(value, pointer, options) {
4
5
  try {
5
- acceptJsonValue(value);
6
+ acceptJsonValue(value, options);
6
7
  return [];
7
8
  }
8
9
  catch (error) {