@astrale-os/kernel-dsl 1.0.0-beta.15 → 1.0.0-beta.17

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 (87) hide show
  1. package/dist/v1/builder/class/authoring.js +3 -2
  2. package/dist/v1/builder/class/input.d.ts +45 -14
  3. package/dist/v1/builder/index.d.ts +2 -1
  4. package/dist/v1/builder/lowering/class.d.ts +8 -0
  5. package/dist/v1/builder/lowering/class.js +40 -16
  6. package/dist/v1/builder/lowering/schema/compile.js +3 -3
  7. package/dist/v1/builder/policy/authoring.js +97 -88
  8. package/dist/v1/bundle/model.js +1 -1
  9. package/dist/v1/compiled/admission/accept.js +1 -7
  10. package/dist/v1/compiled/admission/semantics/class.js +2 -3
  11. package/dist/v1/compiled/admission/semantics/policy/parity.js +25 -13
  12. package/dist/v1/compiled/admission/semantics/policy/typing.js +2 -2
  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/.spec/schemas/draft-2020-12.schema.json +462 -0
  24. package/dist/v1/language/callable/resolved.d.ts +11 -2
  25. package/dist/v1/language/class/model/class.d.ts +17 -5
  26. package/dist/v1/language/freeze.d.ts +5 -0
  27. package/dist/v1/{schema/canonical → language}/freeze.js +4 -0
  28. package/dist/v1/language/policy/analysis/typing.d.ts +5 -2
  29. package/dist/v1/language/policy/analysis/typing.js +158 -42
  30. package/dist/v1/language/policy/model/policy.d.ts +44 -4
  31. package/dist/v1/language/policy/model/policy.js +14 -0
  32. package/dist/v1/language/policy/normalization/normalize.d.ts +6 -6
  33. package/dist/v1/language/policy/normalization/normalize.js +5 -5
  34. package/dist/v1/language/validation/rule.d.ts +22 -6
  35. package/dist/v1/language/validation/rule.js +4 -15
  36. package/dist/v1/language/value/evaluate/program.d.ts +2 -1
  37. package/dist/v1/language/value/evaluate/program.js +2 -2
  38. package/dist/v1/language/value/evaluate/validate.d.ts +2 -2
  39. package/dist/v1/language/value/evaluate/validate.js +2 -2
  40. package/dist/v1/language/value/ordering.d.ts +5 -0
  41. package/dist/v1/language/value/ordering.js +14 -0
  42. package/dist/v1/language/value/profile/json.validate.d.ts +3 -1
  43. package/dist/v1/language/value/profile/json.validate.js +3 -2
  44. package/dist/v1/language/value/reference/closure.js +3 -4
  45. package/dist/v1/language/value/reference/registry.index.d.ts +2 -0
  46. package/dist/v1/language/value/reference/registry.index.js +31 -15
  47. package/dist/v1/schema/.spec/schemas/domain-schema.schema.json +210 -27
  48. package/dist/v1/schema/admission/callable.js +1 -1
  49. package/dist/v1/schema/canonical/revision.js +4 -4
  50. package/dist/v1/schema/compatibility/comparators/callables.js +1 -1
  51. package/dist/v1/schema/compatibility/comparators/classes.js +4 -4
  52. package/dist/v1/schema/compatibility/comparators/members.js +1 -1
  53. package/dist/v1/schema/compatibility/comparators/values.js +2 -2
  54. package/dist/v1/schema/compatibility/report.js +1 -1
  55. package/dist/v1/schema/compatibility/versioning/index.js +1 -1
  56. package/dist/v1/schema/model/schema.js +1 -1
  57. package/dist/v1/schema/normalization/domain.js +9 -8
  58. package/dist/v1/schema/resolution/domain.js +1 -1
  59. package/dist/v1/schema/resolution/origins.js +3 -2
  60. package/dist/v1/schema/rule.d.ts +8 -14
  61. package/dist/v1/schema/rule.js +14 -2
  62. package/dist/v1/schema/validation/class.d.ts +6 -2
  63. package/dist/v1/schema/validation/class.js +20 -25
  64. package/dist/v1/schema/validation/domain.js +1 -1
  65. package/dist/v1/schema/validation/domain.json-schema.d.ts +2 -2
  66. package/dist/v1/schema/validation/domain.json-schema.js +20 -6
  67. package/dist/v1/schema/validation/domain.structure.d.ts +4765 -1005
  68. package/dist/v1/schema/validation/domain.structure.js +21 -9
  69. package/dist/v1/schema/validation/json.json-schema.d.ts +18 -2
  70. package/dist/v1/schema/validation/json.json-schema.js +14 -4
  71. package/dist/v1/schema/validation/policy/budgets.d.ts +1 -1
  72. package/dist/v1/schema/validation/policy/budgets.js +4 -4
  73. package/dist/v1/schema/validation/policy/context.d.ts +1 -2
  74. package/dist/v1/schema/validation/policy/context.js +2 -7
  75. package/dist/v1/schema/validation/policy/uses.d.ts +10 -2
  76. package/dist/v1/schema/validation/policy/uses.js +26 -13
  77. package/dist/v1/schema/validation/policy/validate.js +2 -3
  78. package/dist/v1/schema/validation/value-api.d.ts +2 -0
  79. package/dist/v1/schema/validation/value-api.js +2 -0
  80. package/dist/v1/schema/validation/value.json-schema.d.ts +7 -9
  81. package/dist/v1/schema/validation/value.json-schema.js +38 -27
  82. package/package.json +2 -1
  83. package/dist/v1/schema/canonical/domain.d.ts +0 -3
  84. package/dist/v1/schema/canonical/domain.digest.d.ts +0 -3
  85. package/dist/v1/schema/canonical/domain.digest.js +0 -5
  86. package/dist/v1/schema/canonical/domain.js +0 -5
  87. package/dist/v1/schema/canonical/freeze.d.ts +0 -1
@@ -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, '');
@@ -14,6 +14,11 @@ export declare function classifyOrdering(contract: JsonSchema, registry: ValueSc
14
14
  /** Recompute the scalar ordering classification from one admitted portable value program. */
15
15
  export declare function classifyValueProgramOrdering(program: ValueProgram): OrderingKind | undefined;
16
16
  export declare function classifyPreparedOrdering(compiled: CompiledInstanceSchema): OrderingKind | undefined;
17
+ /**
18
+ * Every value kind `classifyValueKind` returns, frozen, in the order `ValueKind` names them. A kind
19
+ * added to `ValueKind` does not compile until it is listed here.
20
+ */
21
+ export declare const VALUE_KINDS: readonly ValueKind[];
17
22
  /**
18
23
  * The value kind of a property schema (page "Fondations Query v7", § Atoms et value kinds, Y4):
19
24
  * the one kind its non-null values admit, `null` being a point of every kind. An annotated
@@ -34,6 +34,20 @@ export function classifyPreparedOrdering(compiled) {
34
34
  ? kind
35
35
  : undefined;
36
36
  }
37
+ const valueKinds = {
38
+ text: true,
39
+ number: true,
40
+ instant: true,
41
+ enum: true,
42
+ boolean: true,
43
+ set: true,
44
+ json: true,
45
+ };
46
+ /**
47
+ * Every value kind `classifyValueKind` returns, frozen, in the order `ValueKind` names them. A kind
48
+ * added to `ValueKind` does not compile until it is listed here.
49
+ */
50
+ export const VALUE_KINDS = Object.freeze(Object.keys(valueKinds));
37
51
  /**
38
52
  * The value kind of a property schema (page "Fondations Query v7", § Atoms et value kinds, Y4):
39
53
  * the one kind its non-null values admit, `null` being a point of every kind. An annotated
@@ -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) {
@@ -36,10 +36,9 @@ export function collectResourceClosure(schema, context, index, pointer) {
36
36
  const target = result.resolved.resource;
37
37
  if (resources.has(target.key))
38
38
  return;
39
- const registered = {
40
- ...target,
41
- diagnosticPointer: `${pointer}/registry/graphNodePath`,
42
- };
39
+ // The graph NodePath resource is no part of the schema: what it holds is reported at the
40
+ // $ref that names it.
41
+ const registered = { ...target, diagnosticPointer: refPointer };
43
42
  resources.set(target.key, registered);
44
43
  queue.push(registered);
45
44
  });
@@ -5,6 +5,8 @@ export interface RegistryIndex {
5
5
  readonly registry: ValueSchemaRegistry;
6
6
  readonly domains: ReadonlyMap<string, ValueSchemaDomainRegistry>;
7
7
  readonly dependenciesByOwner: ReadonlyMap<string, ReadonlyMap<string, string>>;
8
+ /** The `definitionRefIndex` of each Node Definition its own Domain lists. */
9
+ readonly nodeDefinitions: ReadonlySet<string>;
8
10
  }
9
11
  export declare function indexRegistry(registry: ValueSchemaRegistry, pointer: string): {
10
12
  readonly index: RegistryIndex;
@@ -4,11 +4,21 @@ import { diagnostic } from '../../validation/diagnostic.js';
4
4
  import { isValueObject as isJsonObject } from '../model/object.js';
5
5
  import { JSON_SCHEMA_DIALECT } from '../model/schema.js';
6
6
  import { GRAPH_NODE_PATH_SCHEMA_ID } from '../syntax/node-path/schema.js';
7
+ // A registry is an immutable value, and every value schema of a Domain is validated against the
8
+ // same one: an index that admitted it without a diagnostic is reused, so the closure is indexed
9
+ // once per registry rather than once per value schema.
10
+ const admittedIndexes = new WeakMap();
7
11
  export function indexRegistry(registry, pointer) {
12
+ const admitted = admittedIndexes.get(registry);
13
+ if (admitted !== undefined)
14
+ return { index: admitted, diagnostics: [] };
8
15
  const diagnostics = [];
9
16
  const domains = new Map();
10
17
  domains.set(registry.current.origin, registry.current);
11
- validateDomainRegistry(registry.current, `${pointer}/registry/current`, false, diagnostics);
18
+ // The registry is the closure the Kernel builds for the schema at `pointer`, not a document: a
19
+ // fault of one of its entries is a fault of the closure that schema is checked with, reported
20
+ // there, the entry named in the message.
21
+ validateDomainRegistry(registry.current, 'current', false, pointer, diagnostics);
12
22
  const dependencies = [
13
23
  ...registry.directDependencies.map((domain) => ({ domain, lane: 'directDependencies' })),
14
24
  ...registry.transitiveDependencies.map((domain) => ({
@@ -17,10 +27,9 @@ export function indexRegistry(registry, pointer) {
17
27
  })),
18
28
  ];
19
29
  dependencies.forEach(({ domain, lane }, index) => {
20
- const domainPointer = `${pointer}/registry/${lane}/${index}`;
21
- validateDomainRegistry(domain, domainPointer, true, diagnostics);
30
+ validateDomainRegistry(domain, `${lane}/${index}`, true, pointer, diagnostics);
22
31
  if (domain.origin === registry.current.origin || domains.has(domain.origin)) {
23
- diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', domainPointer, `Registry origin ${domain.origin} is duplicated in the exact closure.`));
32
+ diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', pointer, `Registry origin ${domain.origin} is duplicated in the exact closure, at ${lane}/${index}.`));
24
33
  return;
25
34
  }
26
35
  domains.set(domain.origin, domain);
@@ -37,31 +46,38 @@ export function indexRegistry(registry, pointer) {
37
46
  }
38
47
  }
39
48
  }
40
- validateGraphNodePathResource(registry.graphNodePath, `${pointer}/registry/graphNodePath`, diagnostics);
41
- return { index: { registry, domains, dependenciesByOwner }, diagnostics };
49
+ const nodeDefinitions = new Set();
50
+ for (const domain of domains.values()) {
51
+ for (const ref of domain.nodeDefinitions) {
52
+ if (ref.origin === domain.origin)
53
+ nodeDefinitions.add(definitionRefIndex(ref));
54
+ }
55
+ }
56
+ validateGraphNodePathResource(registry.graphNodePath, pointer, diagnostics);
57
+ const index = { registry, domains, dependenciesByOwner, nodeDefinitions };
58
+ if (diagnostics.length === 0)
59
+ admittedIndexes.set(registry, index);
60
+ return { index, diagnostics };
42
61
  }
43
62
  export function isOriginVisibleFrom(index, ownerOrigin, targetOrigin) {
44
63
  return (ownerOrigin === targetOrigin ||
45
64
  index.dependenciesByOwner.get(ownerOrigin)?.has(targetOrigin) === true);
46
65
  }
47
66
  export function hasNodeDefinition(index, ref, ownerOrigin) {
48
- if (!isOriginVisibleFrom(index, ownerOrigin, ref.origin))
49
- return false;
50
- return (index.domains
51
- .get(ref.origin)
52
- ?.nodeDefinitions.some((candidate) => definitionRefIndex(candidate) === definitionRefIndex(ref)) === true);
67
+ return (isOriginVisibleFrom(index, ownerOrigin, ref.origin) &&
68
+ index.nodeDefinitions.has(definitionRefIndex(ref)));
53
69
  }
54
- function validateDomainRegistry(domain, pointer, revisionRequired, diagnostics) {
70
+ function validateDomainRegistry(domain, entry, revisionRequired, pointer, diagnostics) {
55
71
  if (!DOMAIN_ORIGIN_PATTERN.test(domain.origin)) {
56
- diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', `${pointer}/origin`, 'Registry origin is invalid.'));
72
+ diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', pointer, `Registry origin is invalid, at ${entry}.`));
57
73
  }
58
74
  if ((revisionRequired && domain.revision === undefined) ||
59
75
  (domain.revision !== undefined && !DOMAIN_REVISION_PATTERN.test(domain.revision))) {
60
- diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', `${pointer}/revision`, 'A dependency-closure registry entry requires its exact sha256 revision.'));
76
+ diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', pointer, `A dependency-closure registry entry requires its exact sha256 revision, at ${entry}.`));
61
77
  }
62
78
  for (const ref of domain.nodeDefinitions) {
63
79
  if (ref.origin !== domain.origin) {
64
- diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', `${pointer}/nodeDefinitions`, 'A registry domain can list only its own Node Definitions.'));
80
+ diagnostics.push(diagnostic('VS_REFERENCE_RESOLUTION', pointer, `A registry domain can list only its own Node Definitions, at ${entry}.`));
65
81
  }
66
82
  }
67
83
  }
@@ -46,7 +46,7 @@
46
46
  "code": "DV_VALUE_SCHEMA",
47
47
  "valueCode": "VS_PATH_EXTENSION",
48
48
  "stage": "rule",
49
- "statement": "x-astrale-path annotates only a direct Property of a callable input object, beside exactly the $ref of the graph NodePath schema, and reads target node, cardinality one and a non-empty set of Node Class refs it accepts.",
49
+ "statement": "x-astrale-path annotates only a direct Property of a callable input object, beside exactly the $ref of the graph NodePath schema.",
50
50
  "positions": [
51
51
  "#/$defs/value-location/anyOf/1/properties/x-astrale-path"
52
52
  ]
@@ -75,6 +75,7 @@
75
75
  "stage": "context",
76
76
  "statement": "A $ref resolves to a location of its own value schema, and x-astrale-path accepts Node Classes of the Domain or of a direct dependency.",
77
77
  "positions": [
78
+ "#/$defs/path-annotation/properties/accepts/items",
78
79
  "#/$defs/value-location/anyOf/1/properties/$ref"
79
80
  ]
80
81
  },
@@ -322,17 +323,6 @@
322
323
  }
323
324
  ]
324
325
  },
325
- "class-policy": {
326
- "anyOf": [
327
- {
328
- "type": "boolean",
329
- "const": false
330
- },
331
- {
332
- "$ref": "#/$defs/policy-ref"
333
- }
334
- ]
335
- },
336
326
  "class-ref": {
337
327
  "type": "object",
338
328
  "properties": {
@@ -586,6 +576,136 @@
586
576
  "maxLength": 100,
587
577
  "pattern": "^(?=.{1,100}$)[a-z][a-z0-9-]*(?:\\.[a-z][a-z0-9-]*)*$"
588
578
  },
579
+ "create-policy": {
580
+ "anyOf": [
581
+ {
582
+ "type": "boolean",
583
+ "const": true
584
+ },
585
+ {
586
+ "$ref": "#/$defs/create-rule-check-expr"
587
+ }
588
+ ]
589
+ },
590
+ "create-rule-check-expr": {
591
+ "anyOf": [
592
+ {
593
+ "type": "object",
594
+ "properties": {
595
+ "sameNode": {
596
+ "type": "object",
597
+ "properties": {
598
+ "left": {
599
+ "type": "object",
600
+ "properties": {
601
+ "kind": {
602
+ "type": "string",
603
+ "const": "ref"
604
+ },
605
+ "ref": {
606
+ "$ref": "#/$defs/projected-member-ref"
607
+ }
608
+ },
609
+ "required": [
610
+ "kind",
611
+ "ref"
612
+ ],
613
+ "additionalProperties": false
614
+ },
615
+ "right": {
616
+ "type": "object",
617
+ "properties": {
618
+ "kind": {
619
+ "type": "string",
620
+ "const": "ref"
621
+ },
622
+ "ref": {
623
+ "$ref": "#/$defs/projected-member-ref"
624
+ }
625
+ },
626
+ "required": [
627
+ "kind",
628
+ "ref"
629
+ ],
630
+ "additionalProperties": false
631
+ }
632
+ },
633
+ "required": [
634
+ "left",
635
+ "right"
636
+ ],
637
+ "additionalProperties": false
638
+ }
639
+ },
640
+ "required": [
641
+ "sameNode"
642
+ ],
643
+ "additionalProperties": false
644
+ },
645
+ {
646
+ "type": "object",
647
+ "properties": {
648
+ "check": {
649
+ "$ref": "#/$defs/policy-ref"
650
+ },
651
+ "object": {
652
+ "type": "object",
653
+ "properties": {
654
+ "kind": {
655
+ "type": "string",
656
+ "const": "ref"
657
+ },
658
+ "ref": {
659
+ "$ref": "#/$defs/projected-member-ref"
660
+ }
661
+ },
662
+ "required": [
663
+ "kind",
664
+ "ref"
665
+ ],
666
+ "additionalProperties": false
667
+ }
668
+ },
669
+ "required": [
670
+ "check",
671
+ "object"
672
+ ],
673
+ "additionalProperties": false
674
+ },
675
+ {
676
+ "type": "object",
677
+ "properties": {
678
+ "allOf": {
679
+ "minItems": 1,
680
+ "type": "array",
681
+ "items": {
682
+ "$ref": "#/$defs/create-rule-check-expr"
683
+ }
684
+ }
685
+ },
686
+ "required": [
687
+ "allOf"
688
+ ],
689
+ "additionalProperties": false
690
+ },
691
+ {
692
+ "type": "object",
693
+ "properties": {
694
+ "anyOf": {
695
+ "minItems": 1,
696
+ "type": "array",
697
+ "items": {
698
+ "$ref": "#/$defs/create-rule-check-expr"
699
+ }
700
+ }
701
+ },
702
+ "required": [
703
+ "anyOf"
704
+ ],
705
+ "additionalProperties": false
706
+ }
707
+ ]
708
+ },
589
709
  "dependency": {
590
710
  "type": "object",
591
711
  "properties": {
@@ -779,10 +899,19 @@
779
899
  "type": "object",
780
900
  "properties": {
781
901
  "read": {
782
- "$ref": "#/$defs/class-policy"
902
+ "$ref": "#/$defs/observation-policy"
783
903
  },
784
904
  "traverse": {
785
- "$ref": "#/$defs/class-policy"
905
+ "$ref": "#/$defs/observation-policy"
906
+ },
907
+ "create": {
908
+ "$ref": "#/$defs/write-policy"
909
+ },
910
+ "update": {
911
+ "$ref": "#/$defs/write-policy"
912
+ },
913
+ "delete": {
914
+ "$ref": "#/$defs/write-policy"
786
915
  }
787
916
  },
788
917
  "additionalProperties": false
@@ -1259,10 +1388,19 @@
1259
1388
  "type": "object",
1260
1389
  "properties": {
1261
1390
  "read": {
1262
- "$ref": "#/$defs/node-class-policy"
1391
+ "$ref": "#/$defs/observation-policy"
1263
1392
  },
1264
1393
  "traverse": {
1265
- "$ref": "#/$defs/node-class-policy"
1394
+ "$ref": "#/$defs/observation-policy"
1395
+ },
1396
+ "create": {
1397
+ "$ref": "#/$defs/create-policy"
1398
+ },
1399
+ "update": {
1400
+ "$ref": "#/$defs/write-policy"
1401
+ },
1402
+ "delete": {
1403
+ "$ref": "#/$defs/write-policy"
1266
1404
  }
1267
1405
  },
1268
1406
  "additionalProperties": false
@@ -1292,16 +1430,6 @@
1292
1430
  ],
1293
1431
  "additionalProperties": false
1294
1432
  },
1295
- "node-class-policy": {
1296
- "anyOf": [
1297
- {
1298
- "$ref": "#/$defs/class-policy"
1299
- },
1300
- {
1301
- "$ref": "#/$defs/class-rule-check-expr"
1302
- }
1303
- ]
1304
- },
1305
1433
  "node-property-definition": {
1306
1434
  "type": "object",
1307
1435
  "properties": {
@@ -1378,12 +1506,53 @@
1378
1506
  "$ref": "#/$defs/node-property-definition"
1379
1507
  }
1380
1508
  },
1509
+ "observation-policy": {
1510
+ "anyOf": [
1511
+ {
1512
+ "type": "boolean",
1513
+ "const": false
1514
+ },
1515
+ {
1516
+ "$ref": "#/$defs/policy-ref"
1517
+ },
1518
+ {
1519
+ "$ref": "#/$defs/class-rule-check-expr"
1520
+ }
1521
+ ]
1522
+ },
1381
1523
  "origin": {
1382
1524
  "type": "string",
1383
1525
  "minLength": 3,
1384
1526
  "maxLength": 253,
1385
1527
  "pattern": "^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$"
1386
1528
  },
1529
+ "path-annotation": {
1530
+ "type": "object",
1531
+ "properties": {
1532
+ "target": {
1533
+ "type": "string",
1534
+ "const": "node"
1535
+ },
1536
+ "cardinality": {
1537
+ "type": "string",
1538
+ "const": "one"
1539
+ },
1540
+ "accepts": {
1541
+ "type": "array",
1542
+ "minItems": 1,
1543
+ "uniqueItems": true,
1544
+ "items": {
1545
+ "$ref": "#/$defs/class-ref"
1546
+ }
1547
+ }
1548
+ },
1549
+ "required": [
1550
+ "target",
1551
+ "cardinality",
1552
+ "accepts"
1553
+ ],
1554
+ "additionalProperties": false
1555
+ },
1387
1556
  "policy-check-expr": {
1388
1557
  "anyOf": [
1389
1558
  {
@@ -2100,7 +2269,7 @@
2100
2269
  "type": "string"
2101
2270
  },
2102
2271
  "x-astrale-path": {
2103
- "type": "object"
2272
+ "$ref": "#/$defs/path-annotation"
2104
2273
  },
2105
2274
  "additionalProperties": {
2106
2275
  "$ref": "#/$defs/value-subschema"
@@ -2331,6 +2500,20 @@
2331
2500
  ],
2332
2501
  "additionalProperties": false,
2333
2502
  "description": "Every View targets its owning Domain."
2503
+ },
2504
+ "write-policy": {
2505
+ "anyOf": [
2506
+ {
2507
+ "type": "boolean",
2508
+ "const": true
2509
+ },
2510
+ {
2511
+ "$ref": "#/$defs/policy-ref"
2512
+ },
2513
+ {
2514
+ "$ref": "#/$defs/class-rule-check-expr"
2515
+ }
2516
+ ]
2334
2517
  }
2335
2518
  }
2336
2519
  }
@@ -1,12 +1,12 @@
1
1
  import { accept as acceptJsonValue, BoundaryError as JsonBoundaryError, } from '@astrale-os/kernel-dsl/value';
2
2
  import { definitionRefIndex } from '../../addressing/ordering.js';
3
3
  import { isBinaryInput } from '../../language/callable/model/callable.js';
4
+ import { deepFreeze } from '../../language/freeze.js';
4
5
  import { measureChecks } from '../../language/policy/analysis/budgets.js';
5
6
  import { PolicyTyping } from '../../language/policy/analysis/typing.js';
6
7
  import { normalizePolicyChecks } from '../../language/policy/normalization/normalize.js';
7
8
  import {} from '../../language/value/evaluate/preparation.js';
8
9
  import { analyzeDefinitions } from '../analysis/class.js';
9
- import { deepFreeze } from '../canonical/freeze.js';
10
10
  import { SchemaError, diagnostic } from '../diagnostic.js';
11
11
  import { canonicalizeCallable } from '../normalization/domain.js';
12
12
  import { lookupForDomainClosure } from '../resolution/closure.js';
@@ -1,7 +1,7 @@
1
1
  import { json } from '@astrale-os/kernel-dsl/value';
2
+ import { digestRevisionBytes } from '../../addressing/revision.js';
2
3
  import { asDomainSchema } from '../model/schema.js';
3
- import { digestDomainSchemaBytes } from './domain.digest.js';
4
- import { canonicalizeDomainSchema } from './domain.js';
4
+ import { normalizeDomainSchema } from '../normalization/domain.js';
5
5
  // Only canonical schemas created here own a retained revision, never caller-owned input.
6
6
  const revisions = new WeakMap();
7
7
  export function canonicalDomainSchema(value) {
@@ -9,9 +9,9 @@ export function canonicalDomainSchema(value) {
9
9
  }
10
10
  /** Internal acceptance seam. The caller must establish whole-Domain validity first. */
11
11
  export function canonicalDomainSchemaAfterValidation(value) {
12
- const schema = asDomainSchema(canonicalizeDomainSchema(value));
12
+ const schema = asDomainSchema(normalizeDomainSchema(value));
13
13
  const bytes = json.encode(schema);
14
- const revision = digestDomainSchemaBytes(bytes);
14
+ const revision = digestRevisionBytes(bytes);
15
15
  revisions.set(schema, revision);
16
16
  return Object.freeze({ schema, bytes, revision });
17
17
  }
@@ -1,7 +1,7 @@
1
1
  import { isMethodExecutable } from '../../../language/callable/executable.js';
2
2
  import { isBinaryInput } from '../../../language/callable/model/callable.js';
3
+ import { deepFreeze } from '../../../language/freeze.js';
3
4
  import { methodSignature as inheritedSignature } from '../../analysis/class.js';
4
- import { deepFreeze } from '../../canonical/freeze.js';
5
5
  import { keyForDefinitionRef } from '../coordinate.js';
6
6
  import { CALLABLE_FIELDS, fieldsOf } from '../fields.js';
7
7
  import { fingerprintMeaning, semanticValueSchema } from './values.js';
@@ -1,7 +1,7 @@
1
1
  import { compareUnicode } from '../../../addressing/ordering.js';
2
2
  import { compareEdgeUniqueTuples, coveringEdgeUniqueTuples, } from '../../../language/class/unique.js';
3
+ import { deepFreeze } from '../../../language/freeze.js';
3
4
  import { isClassRuleChecks } from '../../../language/policy/model/policy.js';
4
- import { deepFreeze } from '../../canonical/freeze.js';
5
5
  import { effectiveDefinition } from '../context.js';
6
6
  import { propertyCoordinate } from '../coordinate.js';
7
7
  import { keyForDefinitionRef } from '../coordinate.js';
@@ -56,12 +56,12 @@ export function classMeaning(context, coordinate, level, onto) {
56
56
  return { meaning: deepFreeze(meaning), requirement };
57
57
  }
58
58
  /**
59
- * A named Policy compares by its key; `false` or Policy checks by their value, as a callable's
59
+ * A named Policy compares by its key; a constant or Policy checks by their value, as a callable's
60
60
  * Policy does.
61
61
  */
62
62
  function classPolicyMeaning(policy) {
63
- if (policy === false)
64
- return false;
63
+ if (typeof policy === 'boolean')
64
+ return policy;
65
65
  return isClassRuleChecks(policy) ? policy : keyForDefinitionRef(policy);
66
66
  }
67
67
  /**
@@ -1,4 +1,4 @@
1
- import { deepFreeze } from '../../canonical/freeze.js';
1
+ import { deepFreeze } from '../../../language/freeze.js';
2
2
  import { CORE_FIELDS, POLICY_FIELDS, VIEW_FIELDS, fieldsOf } from '../fields.js';
3
3
  import { functionSignature } from './callables.js';
4
4
  export function memberMeaning(owner, ref) {
@@ -1,7 +1,7 @@
1
1
  import { json } from '@astrale-os/kernel-dsl/value';
2
+ import { digestRevisionBytes } from '../../../addressing/revision.js';
2
3
  import { isValueObject as isJsonObject } from '../../../language/value/model/object.js';
3
4
  import { SCHEMA_ARRAY_KEYWORDS, SCHEMA_MAP_KEYWORDS, SCHEMA_SINGLE_KEYWORDS, } from '../../../language/value/syntax/keywords.js';
4
- import { digestDomainSchemaBytes } from '../../canonical/domain.digest.js';
5
5
  import { ANNOTATION_KEYWORDS } from '../fields.js';
6
6
  /** Drop annotation keywords wherever a schema keyword nests a schema; never inside data. */
7
7
  export function semanticValueSchema(value) {
@@ -30,7 +30,7 @@ export function semanticValueSchema(value) {
30
30
  return semantic;
31
31
  }
32
32
  export function fingerprintMeaning(value) {
33
- return digestDomainSchemaBytes(json.encode(value));
33
+ return digestRevisionBytes(json.encode(value));
34
34
  }
35
35
  function isJsonSchema(value) {
36
36
  return typeof value === 'boolean' || isJsonObject(value);
@@ -1,6 +1,6 @@
1
1
  import { json } from '@astrale-os/kernel-dsl/value';
2
2
  import { compareUnicode } from '../../addressing/ordering.js';
3
- import { deepFreeze } from '../canonical/freeze.js';
3
+ import { deepFreeze } from '../../language/freeze.js';
4
4
  import { fingerprintMeaning } from './comparators/values.js';
5
5
  import { differences } from './diff.js';
6
6
  import { schemaIdentity } from './references.js';
@@ -1,4 +1,4 @@
1
- import { deepFreeze } from '../../canonical/freeze.js';
1
+ import { deepFreeze } from '../../../language/freeze.js';
2
2
  import { SchemaError, diagnostic } from '../../diagnostic.js';
3
3
  import { compareFootprint } from '../meaning/footprint.js';
4
4
  import { schemaIdentity } from '../references.js';
@@ -1,4 +1,4 @@
1
- import { deepFreeze } from '../canonical/freeze.js';
1
+ import { deepFreeze } from '../../language/freeze.js';
2
2
  export function asDomainSchema(value) {
3
3
  return deepFreeze(value);
4
4
  }