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

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 (74) hide show
  1. package/dist/v1/addressing/domain-dependency.d.ts +0 -6
  2. package/dist/v1/addressing/domain-dependency.js +1 -6
  3. package/dist/v1/builder/class/authoring.d.ts +43 -0
  4. package/dist/v1/builder/index.d.ts +1 -1
  5. package/dist/v1/builder/lowering/policy.js +1 -0
  6. package/dist/v1/builder/lowering/schema/context.d.ts +5 -0
  7. package/dist/v1/builder/lowering/schema/context.js +22 -0
  8. package/dist/v1/builder/policy/authoring.d.ts +84 -4
  9. package/dist/v1/builder/policy/authoring.js +41 -12
  10. package/dist/v1/builder/schema/projection/class.d.ts +116 -14
  11. package/dist/v1/bundle/codec.js +7 -7
  12. package/dist/v1/compiled/admission/accept.js +3 -3
  13. package/dist/v1/compiled/admission/closure.js +2 -2
  14. package/dist/v1/compiled/admission/semantics/core.js +3 -7
  15. package/dist/v1/compiled/admission/semantics/policy/parity.js +3 -10
  16. package/dist/v1/compiled/admission/semantics/policy/typing.js +2 -2
  17. package/dist/v1/language/.spec/schemas/draft-2020-12.schema.json +462 -0
  18. package/dist/v1/language/core/facts.d.ts +17 -4
  19. package/dist/v1/language/core/facts.js +26 -5
  20. package/dist/v1/language/limits.d.ts +64 -0
  21. package/dist/v1/language/limits.js +60 -0
  22. package/dist/v1/language/policy/analysis/budgets.d.ts +39 -18
  23. package/dist/v1/language/policy/analysis/budgets.js +30 -20
  24. package/dist/v1/language/policy/analysis/checks.js +3 -2
  25. package/dist/v1/language/policy/analysis/typing.d.ts +4 -2
  26. package/dist/v1/language/policy/analysis/typing.js +136 -14
  27. package/dist/v1/language/validation/rule.d.ts +9 -0
  28. package/dist/v1/language/validation/rule.js +9 -7
  29. package/dist/v1/language/value/ordering.d.ts +5 -0
  30. package/dist/v1/language/value/ordering.js +14 -0
  31. package/dist/v1/language/value/reference/closure.js +3 -4
  32. package/dist/v1/language/value/reference/registry.index.d.ts +2 -0
  33. package/dist/v1/language/value/reference/registry.index.js +31 -15
  34. package/dist/v1/language/value/regexp/ast.js +6 -7
  35. package/dist/v1/language/value/regexp/compile.d.ts +5 -11
  36. package/dist/v1/language/value/regexp/compile.js +14 -12
  37. package/dist/v1/language/value/regexp/syntax.js +3 -2
  38. package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +30 -2
  39. package/dist/v1/schema/admission/accept.js +3 -14
  40. package/dist/v1/schema/admission/callable.d.ts +21 -0
  41. package/dist/v1/schema/admission/callable.js +96 -0
  42. package/dist/v1/schema/index.d.ts +7 -0
  43. package/dist/v1/schema/index.js +3 -0
  44. package/dist/v1/schema/normalization/domain.d.ts +2 -0
  45. package/dist/v1/schema/normalization/domain.js +1 -1
  46. package/dist/v1/schema/resolution/context.js +7 -7
  47. package/dist/v1/schema/resolution/domain.d.ts +5 -0
  48. package/dist/v1/schema/resolution/domain.js +1 -1
  49. package/dist/v1/schema/rule.d.ts +9 -0
  50. package/dist/v1/schema/rule.js +29 -4
  51. package/dist/v1/schema/validation/callable.d.ts +33 -3
  52. package/dist/v1/schema/validation/callable.js +49 -15
  53. package/dist/v1/schema/validation/class.d.ts +6 -2
  54. package/dist/v1/schema/validation/class.js +18 -22
  55. package/dist/v1/schema/validation/core.js +16 -10
  56. package/dist/v1/schema/validation/domain.js +3 -3
  57. package/dist/v1/schema/validation/domain.route.d.ts +6 -1
  58. package/dist/v1/schema/validation/domain.route.js +18 -0
  59. package/dist/v1/schema/validation/domain.structure.d.ts +28 -26
  60. package/dist/v1/schema/validation/domain.structure.js +22 -6
  61. package/dist/v1/schema/validation/policy/budgets.d.ts +33 -2
  62. package/dist/v1/schema/validation/policy/budgets.js +89 -16
  63. package/dist/v1/schema/validation/policy/context.d.ts +1 -2
  64. package/dist/v1/schema/validation/policy/context.js +2 -7
  65. package/dist/v1/schema/validation/policy/references.js +2 -1
  66. package/dist/v1/schema/validation/policy/uses.d.ts +29 -0
  67. package/dist/v1/schema/validation/policy/uses.js +28 -48
  68. package/dist/v1/schema/validation/value-api.d.ts +2 -0
  69. package/dist/v1/schema/validation/value-api.js +2 -0
  70. package/dist/v1/schema/validation/value.d.ts +3 -0
  71. package/dist/v1/schema/validation/value.js +3 -9
  72. package/dist/v1/schema/validation/value.json-schema.d.ts +3 -3
  73. package/dist/v1/schema/validation/value.json-schema.js +22 -4
  74. package/package.json +2 -1
@@ -1,4 +1,5 @@
1
1
  import { defaultLimits } from '@astrale-os/kernel-dsl/value';
2
+ import { limits } from '../language/limits.js';
2
3
  import { deepFreeze } from './canonical/freeze.js';
3
4
  const ANY = ['{/path*}'];
4
5
  const CHECKS = [
@@ -8,6 +9,7 @@ const CHECKS = [
8
9
  ];
9
10
  const DEPENDENCIES = ['/dependencies/{alias}', '/dependencies'];
10
11
  const count = (value) => value.toLocaleString('en-US');
12
+ const { dependency, policy } = limits;
11
13
  const at = (field) => [
12
14
  `/functions/{function}${field}`,
13
15
  `/classes/{class}/methods/{method}${field}`,
@@ -36,7 +38,8 @@ export const RULES = deepFreeze({
36
38
  },
37
39
  DV_CALLABLE_IO: {
38
40
  stage: 'rule',
39
- positions: [...at('/input/mode'), ...at('/output/mode')],
41
+ // A Method takes a value input only: its input has no mode.
42
+ positions: ['/functions/{function}/input/mode', ...at('/output/mode')],
40
43
  statement: 'A durable Function or Method takes a value input and returns a value output.',
41
44
  },
42
45
  DV_CLASS_ABSTRACT: {
@@ -68,7 +71,7 @@ export const RULES = deepFreeze({
68
71
  DV_CORE_EDGE_CONSTRAINTS: {
69
72
  stage: 'context',
70
73
  positions: ['/core/edges', '/core/edges/{index}', '/core/nodes/{slug}'],
71
- statement: 'The Core Edges of a Schema hold the noSelf and acyclic constraints of their Classes, and every Core Node, and every Definition a Core Edge names, the Edge counts of the endpoints that accept it, a Definition its maximum only.',
74
+ statement: 'The Core Edges of a Schema hold the noSelf and acyclic constraints of their Classes, and every Core Node, and every Definition a Core Edge names, the Edge counts of the concrete Edge Classes whose endpoints accept it, each over its own Edges, a Definition its maximum only.',
72
75
  },
73
76
  DV_CORE_EDGE_KEY: {
74
77
  stage: 'rule',
@@ -128,7 +131,8 @@ export const RULES = deepFreeze({
128
131
  DV_DEPENDENCY_LIMITS: {
129
132
  stage: 'context',
130
133
  positions: ['/dependencies', '/dependencies/{alias}'],
131
- statement: 'A Domain has at most 32 direct dependencies, a closure of at most 256 Domains and a dependency depth of at most 32.',
134
+ statement: `A Domain has at most ${dependency.direct} direct dependencies, a closure of at most ${dependency.closure} Domains and a dependency depth of at most ${dependency.depth}.`,
135
+ limits: ['dependency.direct', 'dependency.closure', 'dependency.depth'],
132
136
  },
133
137
  DV_DEPENDENCY_REVISION_COHERENCE: {
134
138
  stage: 'context',
@@ -182,6 +186,7 @@ export const RULES = deepFreeze({
182
186
  stage: 'structure',
183
187
  positions: ANY,
184
188
  statement: `The document is portable JSON: as text, UTF-8 that parses, within ${count(defaultLimits.maxBytes)} bytes, with no byte-order mark and no repeated member name; as a value, null, booleans, finite numbers other than negative zero, strings, arrays and plain objects, with no string or member name that holds U+0000 or a lone surrogate or exceeds ${count(defaultLimits.maxStringBytes)} bytes of UTF-8, at most ${count(defaultLimits.maxDepth)} nested arrays and objects, and at most ${count(defaultLimits.maxMembers)} members and items in all.`,
189
+ conditions: ['#/$defs/portable-string/not'],
185
190
  },
186
191
  DV_METHOD_ABSTRACT: {
187
192
  stage: 'rule',
@@ -244,11 +249,21 @@ export const RULES = deepFreeze({
244
249
  ...at('/output/item{/path*}'),
245
250
  ],
246
251
  statement: 'Every embedded value schema is in the value language: its diagnostic names the refusal in `valueCode`, and the schema of an automatic or State Property is not a blob.',
252
+ conditions: ['#/$defs/node-property-definition/allOf/0/if'],
247
253
  },
248
254
  PL_BUDGET: {
249
255
  stage: 'rule',
250
256
  positions: ['/policies/{policy}', ...CHECKS],
251
- statement: 'A Policy stays within its budgets once expanded: pattern depth 4, 8 normalized branches, 6 Edge predicates a branch and 4 referenced Domains; and checks within depth 4, 8 branches and 8 leaves.',
257
+ statement: `A Policy stays within its budgets once expanded: pattern depth ${policy.patternDepth}, ${policy.normalizedBranches} normalized branches, ${policy.edgePredicates} Edge predicates a branch and ${policy.referencedDomains} referenced Domains; and checks within depth ${policy.checkDepth}, ${policy.checkBranches} branches and ${policy.checkLeaves} leaves.`,
258
+ limits: [
259
+ 'policy.patternDepth',
260
+ 'policy.normalizedBranches',
261
+ 'policy.edgePredicates',
262
+ 'policy.referencedDomains',
263
+ 'policy.checkDepth',
264
+ 'policy.checkBranches',
265
+ 'policy.checkLeaves',
266
+ ],
252
267
  },
253
268
  PL_COMPOSITION: {
254
269
  stage: 'rule',
@@ -295,16 +310,19 @@ export const RULES = deepFreeze({
295
310
  stage: 'structure',
296
311
  positions: ['/functions/{function}/input'],
297
312
  statement: 'A Function input in binary mode is a binary input: it declares its metadata and maximumBytes.',
313
+ conditions: ['#/$defs/function-definition/properties/input/anyOf/1/not'],
298
314
  },
299
315
  STRUCTURE_CALLABLE_POLICY_AUTH: {
300
316
  stage: 'structure',
301
317
  positions: at('/policy'),
302
318
  statement: 'Only an authorized Function or Method declares a Policy.',
319
+ conditions: ['#/$defs/function-definition/if', '#/$defs/method-definition/if'],
303
320
  },
304
321
  STRUCTURE_DEPENDENCY_ALIAS: {
305
322
  stage: 'structure',
306
323
  positions: ['/dependencies/{alias}'],
307
324
  statement: 'A dependency alias is neither prototype nor constructor.',
325
+ conditions: ['#/$defs/dependency-alias/not'],
308
326
  },
309
327
  STRUCTURE_DOMAIN_NAME: {
310
328
  stage: 'structure',
@@ -326,6 +344,7 @@ export const RULES = deepFreeze({
326
344
  stage: 'structure',
327
345
  positions: ['/classes/{class}/properties/{property}/schema/x-astrale-value-kind'],
328
346
  statement: 'The schema of an Edge Property is not a blob: only a Node holds Blob contents.',
347
+ conditions: ['#/$defs/edge-property-definition/properties/schema/not'],
329
348
  },
330
349
  STRUCTURE_EDGE_ORIENTATION: {
331
350
  stage: 'structure',
@@ -336,6 +355,7 @@ export const RULES = deepFreeze({
336
355
  ],
337
356
  statement: 'A directed Edge Class has source and target endpoints and an undirected one two incident endpoints, and only a directed one declares unique tuples or is acyclic.',
338
357
  enforces: 'No Edges of an acyclic Class form a directed cycle.',
358
+ conditions: ['#/$defs/edge-class/if'],
339
359
  },
340
360
  STRUCTURE_FORMAT: {
341
361
  stage: 'structure',
@@ -351,6 +371,7 @@ export const RULES = deepFreeze({
351
371
  stage: 'structure',
352
372
  positions: ANY,
353
373
  statement: 'A string, list or number is no greater than the bound of its position.',
374
+ limits: ['policy.variablesPerExists', 'policy.repetition'],
354
375
  },
355
376
  STRUCTURE_MINIMUM: {
356
377
  stage: 'structure',
@@ -371,6 +392,10 @@ export const RULES = deepFreeze({
371
392
  stage: 'structure',
372
393
  positions: ANY,
373
394
  statement: 'No object of the document has a member named __proto__, except the transitions of a State.',
395
+ conditions: [
396
+ '#/$defs/json-value/anyOf/5/propertyNames/not',
397
+ '#/$defs/value-schema/anyOf/1/propertyNames/not',
398
+ ],
374
399
  },
375
400
  STRUCTURE_REQUIRED: {
376
401
  stage: 'structure',
@@ -1,7 +1,37 @@
1
+ import type { ClassRef } from '../../addressing/index.js';
2
+ import type { CallableDefinition } from '../../language/callable/model/callable.js';
3
+ import type { ValueSchemaRegistry } from '../../language/value/reference/registry.js';
1
4
  import type { SchemaDiagnostic } from '../diagnostic.js';
2
5
  import type { DomainSchemaIR } from '../model/schema.js';
3
6
  /**
4
- * Validate the ref of every local Function, and the execution modifier and admission age of every
5
- * local Function and Node Method.
7
+ * One callable contract a Domain schema declares: where it is written, and the Node Class that
8
+ * receives its call, which its Policy names `self`. A contract is validated by the same rules
9
+ * wherever it is: in a Domain schema, at its declaration, or on the Node of a Function instance,
10
+ * which carries it (`acceptCallable`).
6
11
  */
7
- export declare function validateCallables(schema: DomainSchemaIR): readonly SchemaDiagnostic[];
12
+ export interface DeclaredContract {
13
+ readonly contract: CallableDefinition;
14
+ /** Its declaration's JSON Pointer, which its diagnostics point within. */
15
+ readonly pointer: string;
16
+ /** The owner of an instance Method; a Function and a static Method have none. */
17
+ readonly receiver?: ClassRef;
18
+ /** The Node Class and name of a Method; absent for a Function. */
19
+ readonly method?: {
20
+ readonly owner: ClassRef;
21
+ readonly name: string;
22
+ };
23
+ }
24
+ /** Every callable contract one Domain schema declares: each Function, then each Node Method. */
25
+ export declare function declaredContracts(schema: DomainSchemaIR): readonly DeclaredContract[];
26
+ /**
27
+ * Validate the ref of every Function declaration, then the value schemas, execution modifier and
28
+ * admission age of every declared contract.
29
+ */
30
+ export declare function validateContracts(schema: DomainSchemaIR, registry: ValueSchemaRegistry): readonly SchemaDiagnostic[];
31
+ /**
32
+ * Validate one callable contract written at `pointer`, except its Policy (`validateContractPolicy`):
33
+ * its value schemas against the owner registry, its execution modifier and its admission age.
34
+ */
35
+ export declare function validateContract(contract: CallableDefinition, pointer: string, registry: ValueSchemaRegistry, diagnostics: SchemaDiagnostic[]): void;
36
+ /** Validate the execution modifier and admission age of one callable contract at `pointer`. */
37
+ export declare function validateCallableModifiers(declaration: CallableDefinition, pointer: string, diagnostics: SchemaDiagnostic[]): void;
@@ -4,32 +4,66 @@ import { isAdmissionMaxAge, MAXIMUM_ADMISSION_AGE_SECONDS, } from '../../languag
4
4
  import { isBinaryInput } from '../../language/callable/model/callable.js';
5
5
  import { escapePointerToken } from '../../language/value/syntax/pointer.js';
6
6
  import { diagnostic } from '../diagnostic.js';
7
- /**
8
- * Validate the ref of every local Function, and the execution modifier and admission age of every
9
- * local Function and Node Method.
10
- */
11
- export function validateCallables(schema) {
12
- const diagnostics = [];
7
+ import { validateCallable } from './value.js';
8
+ /** Every callable contract one Domain schema declares: each Function, then each Node Method. */
9
+ export function declaredContracts(schema) {
10
+ const contracts = [];
13
11
  for (const [name, declaration] of Object.entries(schema.functions)) {
14
- const pointer = `/functions/${escapePointerToken(name)}`;
15
- const ref = { origin: schema.origin, kind: 'function', name };
16
- if (!equalDefinitionRefs(declaration.ref, ref)) {
17
- diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `Function ref must be exactly ${Key.of(ref)}.`, Key.of(declaration.ref)));
18
- }
19
- validateExecution(declaration, pointer, diagnostics);
20
- validateAdmission(declaration, pointer, diagnostics);
12
+ contracts.push({ contract: declaration, pointer: `/functions/${escapePointerToken(name)}` });
21
13
  }
22
14
  for (const [name, declaration] of Object.entries(schema.classes)) {
23
15
  if (declaration.kind !== 'node')
24
16
  continue;
17
+ const owner = { origin: schema.origin, kind: 'class', name };
25
18
  for (const [method, value] of Object.entries(declaration.methods)) {
26
19
  const pointer = `/classes/${escapePointerToken(name)}/methods/${escapePointerToken(method)}`;
27
- validateExecution(value, pointer, diagnostics);
28
- validateAdmission(value, pointer, diagnostics);
20
+ contracts.push({
21
+ contract: value,
22
+ pointer,
23
+ ...(value.static ? {} : { receiver: owner }),
24
+ method: { owner, name: method },
25
+ });
29
26
  }
30
27
  }
28
+ return contracts;
29
+ }
30
+ /**
31
+ * Validate the ref of every Function declaration, then the value schemas, execution modifier and
32
+ * admission age of every declared contract.
33
+ */
34
+ export function validateContracts(schema, registry) {
35
+ const diagnostics = [];
36
+ for (const [name, declaration] of Object.entries(schema.functions)) {
37
+ validateFunctionRef(schema.origin, name, declaration.ref, diagnostics);
38
+ }
39
+ for (const { contract, pointer } of declaredContracts(schema)) {
40
+ validateContract(contract, pointer, registry, diagnostics);
41
+ }
31
42
  return Object.freeze(diagnostics);
32
43
  }
44
+ /**
45
+ * A Function declaration names itself by its ref: its own key in its own Domain. The ref belongs to
46
+ * the declaration, not to its contract, which a Node carries without one.
47
+ */
48
+ function validateFunctionRef(origin, name, declared, diagnostics) {
49
+ const ref = { origin, kind: 'function', name };
50
+ if (equalDefinitionRefs(declared, ref))
51
+ return;
52
+ diagnostics.push(diagnostic('DV_REF_RESOLVES', `/functions/${escapePointerToken(name)}/ref`, `Function ref must be exactly ${Key.of(ref)}.`, Key.of(declared)));
53
+ }
54
+ /**
55
+ * Validate one callable contract written at `pointer`, except its Policy (`validateContractPolicy`):
56
+ * its value schemas against the owner registry, its execution modifier and its admission age.
57
+ */
58
+ export function validateContract(contract, pointer, registry, diagnostics) {
59
+ validateCallable(contract, pointer, registry, diagnostics);
60
+ validateCallableModifiers(contract, pointer, diagnostics);
61
+ }
62
+ /** Validate the execution modifier and admission age of one callable contract at `pointer`. */
63
+ export function validateCallableModifiers(declaration, pointer, diagnostics) {
64
+ validateExecution(declaration, pointer, diagnostics);
65
+ validateAdmission(declaration, pointer, diagnostics);
66
+ }
33
67
  /**
34
68
  * An admission age is a whole number of seconds within the language ceiling. It has no zero
35
69
  * spelling: an absent admission age is the one canonical form of a call admitted afresh.
@@ -1,10 +1,14 @@
1
1
  import type { ClassRef } from '../../addressing/index.js';
2
2
  import type { ClassDefinition } from '../../language/class/model/class.js';
3
+ import type { ValueSchemaRegistry } from '../../language/value/reference/registry.js';
3
4
  import type { DefinitionAnalysisResult } from '../analysis/class.js';
4
5
  import type { SchemaDiagnostic } from '../diagnostic.js';
5
6
  import type { DomainResolutionContext } from '../resolution/context.js';
6
- /** Validate the semantic contracts owned by Class declarations. */
7
- export declare function validateDefinitions(environment: DomainResolutionContext, analysis: DefinitionAnalysisResult): readonly SchemaDiagnostic[];
7
+ /**
8
+ * Validate the semantic contracts owned by Class declarations. `registry` is the candidate's value
9
+ * registry; each dependency's is built at most once, so the Classes share them.
10
+ */
11
+ export declare function validateDefinitions(environment: DomainResolutionContext, analysis: DefinitionAnalysisResult, registry: ValueSchemaRegistry): readonly SchemaDiagnostic[];
8
12
  /** Whether `child` safely refines its parent Edge contract `parent`. */
9
13
  export declare function edgeContractRefines(environment: DomainResolutionContext, child: Extract<ClassDefinition, {
10
14
  readonly kind: 'edge';
@@ -10,13 +10,24 @@ import { isBlobValueSchema } from '../../language/value/model/blob.js';
10
10
  import { classifyOrdering } from '../../language/value/ordering.js';
11
11
  import { escapePointerToken } from '../../language/value/syntax/pointer.js';
12
12
  import { diagnostic } from '../diagnostic.js';
13
- import { createDomainValueSchemaRegistry, createValueSchemaRegistry, } from '../resolution/value.registry.js';
13
+ import { createDomainValueSchemaRegistry } from '../resolution/value.registry.js';
14
14
  const formatDefinitionRef = Key.of;
15
- /** Validate the semantic contracts owned by Class declarations. */
16
- export function validateDefinitions(environment, analysis) {
15
+ /**
16
+ * Validate the semantic contracts owned by Class declarations. `registry` is the candidate's value
17
+ * registry; each dependency's is built at most once, so the Classes share them.
18
+ */
19
+ export function validateDefinitions(environment, analysis, registry) {
17
20
  const diagnostics = [...analysis.diagnostics];
18
21
  const schema = environment.candidate;
19
22
  let scope;
23
+ const registries = new Map([[schema.origin, registry]]);
24
+ const registryOf = (origin) => {
25
+ if (!registries.has(origin)) {
26
+ const dependency = environment.dependencies.get(origin);
27
+ registries.set(origin, dependency === undefined ? undefined : createDomainValueSchemaRegistry(dependency));
28
+ }
29
+ return registries.get(origin);
30
+ };
20
31
  for (const [name, declaration] of Object.entries(schema.classes)) {
21
32
  const ref = { origin: schema.origin, kind: 'class', name };
22
33
  const pointer = `/classes/${escapePointerToken(name)}`;
@@ -27,7 +38,7 @@ export function validateDefinitions(environment, analysis) {
27
38
  (declaration.policies.read !== undefined || declaration.policies.traverse !== undefined)) {
28
39
  diagnostics.push(diagnostic('DV_CLASS_ABSTRACT', `${pointer}/policies`, 'Abstract Classes cannot declare observation Policies.'));
29
40
  }
30
- validateFacet(environment, declaration, pointer, diagnostics);
41
+ validateFacet(registry, declaration, pointer, diagnostics);
31
42
  validateParents(environment, ref, declaration.kind, declaration.extends, diagnostics);
32
43
  if (declaration.kind === 'edge') {
33
44
  validateEdgeContract(environment, ref, declaration, pointer, diagnostics);
@@ -35,12 +46,11 @@ export function validateDefinitions(environment, analysis) {
35
46
  validateEdgeLifetimes(scope, ref, declaration, pointer, diagnostics);
36
47
  validateEdgeClassComposition(environment, ref, declaration, pointer, diagnostics);
37
48
  }
38
- validateUniqueProperties(environment, analysis, ref, declaration, pointer, diagnostics);
49
+ validateUniqueProperties(registryOf, analysis, ref, declaration, pointer, diagnostics);
39
50
  }
40
51
  return Object.freeze(diagnostics);
41
52
  }
42
- function validateFacet(environment, declaration, pointer, diagnostics) {
43
- const valueRegistry = createValueSchemaRegistry(environment);
53
+ function validateFacet(valueRegistry, declaration, pointer, diagnostics) {
44
54
  for (const [name, property] of Object.entries(declaration.properties)) {
45
55
  const propertyPointer = `${pointer}/properties/${escapePointerToken(name)}`;
46
56
  // The structure refuses a blob on an Edge Property and a State Property on an Edge Class.
@@ -147,7 +157,7 @@ function validateEdgeContract(environment, owner, declaration, pointer, diagnost
147
157
  * inherit without redefinition, so the key reads one meaning on every descendant it covers. A Node
148
158
  * tuple names Properties only; an Edge tuple names them as `{ property }` beside its other fields.
149
159
  */
150
- function validateUniqueProperties(environment, analysis, owner, declaration, pointer, diagnostics) {
160
+ function validateUniqueProperties(registryOf, analysis, owner, declaration, pointer, diagnostics) {
151
161
  if (declaration.kind === 'edge' && declaration.orientation !== 'directed')
152
162
  return;
153
163
  if (declaration.unique === undefined)
@@ -156,20 +166,6 @@ function validateUniqueProperties(environment, analysis, owner, declaration, poi
156
166
  if (properties === undefined)
157
167
  return;
158
168
  const code = declaration.kind === 'node' ? 'DV_NODE_UNIQUE' : 'DV_EDGE_UNIQUE';
159
- const registries = new Map();
160
- const registryOf = (origin) => {
161
- if (!registries.has(origin)) {
162
- const schema = environment.dependencies.get(origin);
163
- const registry = origin === environment.candidate.origin
164
- ? createValueSchemaRegistry(environment)
165
- : schema === undefined
166
- ? undefined
167
- : createDomainValueSchemaRegistry(schema);
168
- if (registry !== undefined)
169
- registries.set(origin, registry);
170
- }
171
- return registries.get(origin);
172
- };
173
169
  const check = (key, at) => {
174
170
  const property = properties.get(key);
175
171
  if (property === undefined) {
@@ -3,7 +3,7 @@ import { Key } from '../../addressing/key.js';
3
3
  import { compareUnicode, definitionRefIndex } from '../../addressing/ordering.js';
4
4
  import { isMethodExecutable } from '../../language/callable/executable.js';
5
5
  import { coveringEdgeUniqueTuples, coveringNodeUniqueTuples, edgeUniqueKeys, nodeUniqueKeys, } from '../../language/class/unique.js';
6
- import { checkCoreEdgeSet, checkHeldKeys, coreEndpointsSatisfy, } from '../../language/core/facts.js';
6
+ import { checkCoreEdgeSet, checkHeldKeys, coreEndpointsSatisfy, DOMAIN_ENDPOINT_CLASS, } from '../../language/core/facts.js';
7
7
  import { policyObjectKey, policyObjectOrigin, projectedMemberClass, } from '../../language/policy/model/policy.js';
8
8
  import { validateJsonInstance } from '../../language/value/evaluate/validate.js';
9
9
  import { escapePointerToken } from '../../language/value/syntax/pointer.js';
@@ -83,7 +83,9 @@ function resolveCoreEdges(environment, analysis, registry, nodesByRef, diagnosti
83
83
  continue;
84
84
  const endpointsSatisfy = coreEndpointsSatisfy(declaration, source, target, (endpoint, accepts) => endpointSatisfies(environment, endpoint, accepts));
85
85
  if (!endpointsSatisfy) {
86
- diagnostics.push(diagnostic('DV_CORE_EDGE_CLASS', pointer, `Core Edge endpoints do not satisfy ${formatDefinitionRef(edge.class)}.`, formatDefinitionRef(edge.class)));
86
+ diagnostics.push(diagnostic('DV_CORE_EDGE_CLASS', pointer, `Core Edge endpoints do not satisfy ${formatDefinitionRef(edge.class)}.${source.kind === 'domain' || target.kind === 'domain'
87
+ ? ` The owning Domain is a ${formatDefinitionRef(DOMAIN_ENDPOINT_CLASS)} Node: the endpoint that names it must accept that Class or one it extends.`
88
+ : ''}`, formatDefinitionRef(edge.class)));
87
89
  continue;
88
90
  }
89
91
  edges.push({ source, target, declaration, value: edge, pointer });
@@ -92,7 +94,7 @@ function resolveCoreEdges(environment, analysis, registry, nodesByRef, diagnosti
92
94
  }
93
95
  function resolveCoreEndpoint(environment, ref, pointer, nodesByRef, diagnostics) {
94
96
  if (ref.kind === 'domain')
95
- return { kind: 'domain', ref, key: 'domain' };
97
+ return { kind: 'domain', ref, key: 'domain', classRef: DOMAIN_ENDPOINT_CLASS };
96
98
  const undeclared = () => {
97
99
  const key = policyObjectKey(ref);
98
100
  diagnostics.push(diagnostic('DV_CORE_REF', pointer, `Core endpoint ${key} does not name a declared local Definition.`, key));
@@ -122,10 +124,14 @@ function declaresProjectedMember(environment, ref) {
122
124
  return Object.hasOwn(candidate.policies, ref.name);
123
125
  return Object.hasOwn(candidate.views, ref.name);
124
126
  }
127
+ /**
128
+ * A Core Node is admitted by its own authored Class, a projected member by the Kernel declaration
129
+ * Class it projects as, and the owning Domain by the Kernel `Domain` Class of its Node: the write
130
+ * that projects the Edge binds exactly that Node, so an Edge Class that does not accept it can
131
+ * never be installed (DV-CORE-EDGE-CLASS).
132
+ */
125
133
  function endpointSatisfies(environment, endpoint, accepts) {
126
- // The owning Domain is a graph-projection identity, not a Definition declared by this DSL.
127
- // Its concrete Class contract is therefore admitted by the projection compiler.
128
- return endpoint.kind === 'domain' || classSatisfies(environment, endpoint.classRef, accepts);
134
+ return classSatisfies(environment, endpoint.classRef, accepts);
129
135
  }
130
136
  function validateCreationProperties(definition, properties, registry, code, pointer, diagnostics) {
131
137
  const schema = {
@@ -181,11 +187,11 @@ function validateCoreEdgeSet(environment, edges, nodesByRef, diagnostics) {
181
187
  report: (issue) => diagnostics.push(coreEdgeDiagnostic(issue)),
182
188
  });
183
189
  }
184
- /** A projected member's degree is reported at the Edge that names it. */
190
+ /** A projected member's degree, and the owning Domain's, is reported at the Edge that names it. */
185
191
  function factEndpoint(endpoint, pointer) {
186
- return endpoint.kind === 'member'
187
- ? { key: endpoint.key, member: { class: endpoint.classRef, pointer } }
188
- : endpoint;
192
+ return endpoint.kind === 'core'
193
+ ? endpoint
194
+ : { key: endpoint.key, member: { class: endpoint.classRef, pointer } };
189
195
  }
190
196
  function coreEdgeDiagnostic(issue) {
191
197
  switch (issue.kind) {
@@ -2,7 +2,7 @@ import { orderAndDeduplicate } from '../../language/validation/diagnostic.js';
2
2
  import { analyzeDefinitions } from '../analysis/class.js';
3
3
  import { buildDomainResolutionContext } from '../resolution/context.js';
4
4
  import { createValueSchemaRegistry } from '../resolution/value.registry.js';
5
- import { validateCallables } from './callable.js';
5
+ import { validateContracts } from './callable.js';
6
6
  import { validateDefinitions } from './class.js';
7
7
  import { validateCore } from './core.js';
8
8
  import { validatePolicies } from './policy/validate.js';
@@ -15,8 +15,8 @@ export function validateDomainSchemaSemantics(schema, lookup) {
15
15
  const diagnostics = [
16
16
  ...context.diagnostics,
17
17
  ...validateEmbeddedValueSchemas(schema, registry),
18
- ...validateDefinitions(context, analysis),
19
- ...validateCallables(schema),
18
+ ...validateDefinitions(context, analysis, registry),
19
+ ...validateContracts(schema, registry),
20
20
  ...validatePolicies(context, analysis),
21
21
  ...validateViews(context),
22
22
  ...validateCore(context, analysis, registry),
@@ -1,4 +1,9 @@
1
- import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
1
+ import { type Value as JsonValue } from '@astrale-os/kernel-dsl/value';
2
2
  import type { DomainSchemaIR } from '../model/schema.js';
3
+ /**
4
+ * Read an unknown value as a Domain Schema document of its exact language contract: portable JSON
5
+ * (`DV_JSON_VALUE`), then the structure of its format and version. Throws `SchemaError`.
6
+ */
7
+ export declare function structureDomainSchema(input: unknown): DomainSchemaIR;
3
8
  /** Select the exact Domain language contract before entering its structural grammar. */
4
9
  export declare function routeDomainSchema(input: JsonValue): DomainSchemaIR;
@@ -1,9 +1,27 @@
1
+ import { accept as acceptJsonValue, BoundaryError as JsonBoundaryError, } from '@astrale-os/kernel-dsl/value';
1
2
  import { SchemaError, diagnostic } from '../diagnostic.js';
2
3
  import { DOMAIN_FORMAT, DOMAIN_VERSION } from '../model/format.js';
3
4
  import { parseDomainSchemaStructure } from './domain.structure.js';
4
5
  const decoders = new Map([
5
6
  [DOMAIN_FORMAT, new Map([[DOMAIN_VERSION, parseDomainSchemaStructure]])],
6
7
  ]);
8
+ /**
9
+ * Read an unknown value as a Domain Schema document of its exact language contract: portable JSON
10
+ * (`DV_JSON_VALUE`), then the structure of its format and version. Throws `SchemaError`.
11
+ */
12
+ export function structureDomainSchema(input) {
13
+ let admitted;
14
+ try {
15
+ admitted = acceptJsonValue(input);
16
+ }
17
+ catch (error) {
18
+ if (error instanceof JsonBoundaryError) {
19
+ throw new SchemaError([diagnostic('DV_JSON_VALUE', error.pointer, error.message)]);
20
+ }
21
+ throw error;
22
+ }
23
+ return routeDomainSchema(admitted);
24
+ }
7
25
  /** Select the exact Domain language contract before entering its structural grammar. */
8
26
  export function routeDomainSchema(input) {
9
27
  if (!isJsonObject(input))
@@ -1,7 +1,7 @@
1
1
  import type { Value as JsonValue } from '@astrale-os/kernel-dsl/value';
2
2
  import { type ZodArray, type ZodType, type core as zodCore } from 'zod';
3
3
  import type { PolicyRef } from '../../addressing/index.js';
4
- import type { FunctionAuth } from '../../language/callable/model/callable.js';
4
+ import type { CallableDefinition, FunctionAuth } from '../../language/callable/model/callable.js';
5
5
  import type { PolicyCheckExpression, PolicyPatternExpression } from '../../language/policy/model/policy.js';
6
6
  import type { DomainSchemaIR } from '../model/schema.js';
7
7
  /**
@@ -3641,16 +3641,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
3641
3641
  name: string;
3642
3642
  };
3643
3643
  }>, unknown>>]>>;
3644
- ref: import("zod").ZodObject<{
3645
- origin: import("zod").ZodString;
3646
- kind: import("zod").ZodLiteral<"function">;
3647
- name: import("zod").ZodString;
3648
- }, zodCore.$strict>;
3649
3644
  input: import("zod").ZodUnion<readonly [import("zod").ZodObject<{
3650
3645
  mode: import("zod").ZodLiteral<"binary">;
3651
3646
  metadata: import("zod").ZodUnion<readonly [import("zod").ZodBoolean, import("zod").ZodRecord<import("zod").ZodString, ZodType<JsonValue, unknown, zodCore.$ZodTypeInternals<JsonValue, unknown>>>]>;
3652
3647
  maximumBytes: import("zod").ZodNumber;
3653
3648
  }, zodCore.$strict>, import("zod").ZodUnion<readonly [import("zod").ZodBoolean, import("zod").ZodRecord<import("zod").ZodString, ZodType<JsonValue, unknown, zodCore.$ZodTypeInternals<JsonValue, unknown>>>]>]>;
3649
+ ref: import("zod").ZodObject<{
3650
+ origin: import("zod").ZodString;
3651
+ kind: import("zod").ZodLiteral<"function">;
3652
+ name: import("zod").ZodString;
3653
+ }, zodCore.$strict>;
3654
3654
  }, zodCore.$strict> & ZodType<{
3655
3655
  description?: string | undefined;
3656
3656
  output: {
@@ -3706,16 +3706,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
3706
3706
  name: string;
3707
3707
  };
3708
3708
  }> | undefined;
3709
- ref: {
3710
- origin: string;
3711
- kind: "function";
3712
- name: string;
3713
- };
3714
3709
  input: boolean | Record<string, JsonValue> | {
3715
3710
  mode: "binary";
3716
3711
  metadata: boolean | Record<string, JsonValue>;
3717
3712
  maximumBytes: number;
3718
3713
  };
3714
+ ref: {
3715
+ origin: string;
3716
+ kind: "function";
3717
+ name: string;
3718
+ };
3719
3719
  } & AuthorizedPolicy, {
3720
3720
  description?: string | undefined;
3721
3721
  output: {
@@ -3735,16 +3735,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
3735
3735
  } | undefined;
3736
3736
  auth: "anonymous" | "authenticated" | "authorized";
3737
3737
  policy?: unknown;
3738
- ref: {
3739
- origin: string;
3740
- kind: "function";
3741
- name: string;
3742
- };
3743
3738
  input: boolean | Record<string, unknown> | {
3744
3739
  mode: "binary";
3745
3740
  metadata: boolean | Record<string, unknown>;
3746
3741
  maximumBytes: number;
3747
3742
  };
3743
+ ref: {
3744
+ origin: string;
3745
+ kind: "function";
3746
+ name: string;
3747
+ };
3748
3748
  }, zodCore.$ZodTypeInternals<{
3749
3749
  description?: string | undefined;
3750
3750
  output: {
@@ -3800,16 +3800,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
3800
3800
  name: string;
3801
3801
  };
3802
3802
  }> | undefined;
3803
- ref: {
3804
- origin: string;
3805
- kind: "function";
3806
- name: string;
3807
- };
3808
3803
  input: boolean | Record<string, JsonValue> | {
3809
3804
  mode: "binary";
3810
3805
  metadata: boolean | Record<string, JsonValue>;
3811
3806
  maximumBytes: number;
3812
3807
  };
3808
+ ref: {
3809
+ origin: string;
3810
+ kind: "function";
3811
+ name: string;
3812
+ };
3813
3813
  } & AuthorizedPolicy, {
3814
3814
  description?: string | undefined;
3815
3815
  output: {
@@ -3829,16 +3829,16 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
3829
3829
  } | undefined;
3830
3830
  auth: "anonymous" | "authenticated" | "authorized";
3831
3831
  policy?: unknown;
3832
- ref: {
3833
- origin: string;
3834
- kind: "function";
3835
- name: string;
3836
- };
3837
3832
  input: boolean | Record<string, unknown> | {
3838
3833
  mode: "binary";
3839
3834
  metadata: boolean | Record<string, unknown>;
3840
3835
  maximumBytes: number;
3841
3836
  };
3837
+ ref: {
3838
+ origin: string;
3839
+ kind: "function";
3840
+ name: string;
3841
+ };
3842
3842
  }>>>;
3843
3843
  policies: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
3844
3844
  ref: import("zod").ZodObject<{
@@ -4017,4 +4017,6 @@ export declare const domainSchemaStructure: import("zod").ZodObject<{
4017
4017
  }, zodCore.$strict>;
4018
4018
  }, zodCore.$strict>;
4019
4019
  export declare function parseDomainSchemaStructure(input: unknown): DomainSchemaIR;
4020
+ /** Parse the structure of one callable contract: a Function declaration without its ref. */
4021
+ export declare function parseCallableContract(input: unknown): CallableDefinition;
4020
4022
  export {};