@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
@@ -277,7 +277,7 @@ const policyCheckObjectSchema = discriminatedUnion('kind', [
277
277
  }),
278
278
  refObjectSchema,
279
279
  ]).register(documentJsonSchema, { id: 'policy-check-object' });
280
- /** A Node Class rule checks the observed Node or one exact member: it has no input. */
280
+ /** A Class rule checks the observed or written instance or one exact member: it has no input. */
281
281
  const classRuleObjectSchema = discriminatedUnion('kind', [
282
282
  selfObjectSchema,
283
283
  refObjectSchema,
@@ -294,6 +294,8 @@ function policyChecksSchema(objectSchema, id) {
294
294
  }
295
295
  const policyCheckExpressionSchema = policyChecksSchema(policyCheckObjectSchema, 'policy-check-expr');
296
296
  const classRuleChecksSchema = policyChecksSchema(classRuleObjectSchema, 'class-rule-check-expr');
297
+ /** A Node create rule has no Node to check yet: its objects are exact members only. */
298
+ const createRuleChecksSchema = policyChecksSchema(refObjectSchema, 'create-rule-check-expr');
297
299
  const callableOutputSchema = discriminatedUnion('mode', [
298
300
  strictObject({
299
301
  mode: literal('value'),
@@ -353,17 +355,27 @@ const methodDeclarationSchema = strictObject({
353
355
  const methodRecordSchema = record(memberNameSchema, methodDeclarationSchema).register(documentJsonSchema, {
354
356
  id: 'method-record',
355
357
  });
356
- const classPolicySchema = union([literal(false), policyRefSchema]).register(documentJsonSchema, {
357
- id: 'class-policy',
358
- });
359
- const nodeClassPolicySchema = union([classPolicySchema, classRuleChecksSchema]).register(documentJsonSchema, { id: 'node-class-policy' });
358
+ // Each slot spells one constant: the other one is what its absence already means.
359
+ const observationPolicySchema = union([
360
+ literal(false),
361
+ policyRefSchema,
362
+ classRuleChecksSchema,
363
+ ]).register(documentJsonSchema, { id: 'observation-policy' });
364
+ const writePolicySchema = union([literal(true), policyRefSchema, classRuleChecksSchema]).register(documentJsonSchema, { id: 'write-policy' });
365
+ const createPolicySchema = union([literal(true), createRuleChecksSchema]).register(documentJsonSchema, { id: 'create-policy' });
360
366
  const nodeClassPoliciesSchema = strictObject({
361
- read: nodeClassPolicySchema.optional(),
362
- traverse: nodeClassPolicySchema.optional(),
367
+ read: observationPolicySchema.optional(),
368
+ traverse: observationPolicySchema.optional(),
369
+ create: createPolicySchema.optional(),
370
+ update: writePolicySchema.optional(),
371
+ delete: writePolicySchema.optional(),
363
372
  });
364
373
  const edgeClassPoliciesSchema = strictObject({
365
- read: classPolicySchema.optional(),
366
- traverse: classPolicySchema.optional(),
374
+ read: observationPolicySchema.optional(),
375
+ traverse: observationPolicySchema.optional(),
376
+ create: writePolicySchema.optional(),
377
+ update: writePolicySchema.optional(),
378
+ delete: writePolicySchema.optional(),
367
379
  });
368
380
  // The default `'internal'` is the absence of the field: one spelling per meaning, one revision.
369
381
  const inheritanceSchema = enumeration(['open', 'final']);
@@ -1,5 +1,22 @@
1
1
  import type { core as zodCore } from 'zod';
2
- type JsonSchema = zodCore.JSONSchema.JSONSchema;
2
+ import type { DiagnosticCode as ValueDiagnosticCode } from '../../language/validation/diagnostic.js';
3
+ import type { SchemaDiagnosticCode } from '../diagnostic.js';
4
+ /** A JSON Schema of the published document, or a part of one. */
5
+ export type JsonSchema = zodCore.JSONSchema.JSONSchema;
6
+ /**
7
+ * A check JSON Schema cannot state, which the published document names: the code the Kernel
8
+ * reports, with its value code for a check on value schemas, and the rule in one plain sentence.
9
+ */
10
+ export interface NamedRuleEntry {
11
+ readonly code: SchemaDiagnosticCode;
12
+ readonly valueCode?: ValueDiagnosticCode;
13
+ readonly statement: string;
14
+ }
15
+ /**
16
+ * `schema` with the `x-astrale-rule` marker naming the checks of `Rule` it leaves to the Kernel.
17
+ * `Rule` is given at each call, so that a name outside it does not compile.
18
+ */
19
+ export declare function named<Rule extends string = never>(schema: JsonSchema, ...rules: [NoInfer<Rule>, ...NoInfer<Rule>[]]): JsonSchema;
3
20
  /**
4
21
  * The check that a document is portable JSON, which `schema.decode` and `schema.accept` make first.
5
22
  * JSON Schema reads a parsed value and compares numbers by their value: it states that no string or
@@ -21,4 +38,3 @@ export declare function jsonValueJsonSchema(): {
21
38
  readonly root: JsonSchema;
22
39
  readonly definitions: Readonly<Record<string, JsonSchema>>;
23
40
  };
24
- export {};
@@ -1,4 +1,11 @@
1
1
  import { RULES } from '../rule.js';
2
+ /**
3
+ * `schema` with the `x-astrale-rule` marker naming the checks of `Rule` it leaves to the Kernel.
4
+ * `Rule` is given at each call, so that a name outside it does not compile.
5
+ */
6
+ export function named(schema, ...rules) {
7
+ return { ...schema, 'x-astrale-rule': rules };
8
+ }
2
9
  /**
3
10
  * The check that a document is portable JSON, which `schema.decode` and `schema.accept` make first.
4
11
  * JSON Schema reads a parsed value and compares numbers by their value: it states that no string or
@@ -18,17 +25,20 @@ export function jsonValueJsonSchema() {
18
25
  return {
19
26
  root: { $ref: VALUE },
20
27
  definitions: {
21
- 'portable-json': {
28
+ 'portable-json': named({
22
29
  anyOf: [
23
30
  { type: 'null' },
24
31
  { type: 'boolean' },
25
32
  { type: 'number' },
26
33
  { $ref: TEXT },
27
34
  { type: 'array', items: { $ref: VALUE } },
28
- { type: 'object', propertyNames: { $ref: TEXT }, additionalProperties: { $ref: VALUE } },
35
+ {
36
+ type: 'object',
37
+ propertyNames: { $ref: TEXT },
38
+ additionalProperties: { $ref: VALUE },
39
+ },
29
40
  ],
30
- 'x-astrale-rule': 'portable-json',
31
- },
41
+ }, 'portable-json'),
32
42
  // The pattern is the character itself, which every regular-expression dialect reads.
33
43
  'portable-string': { type: 'string', not: { pattern: '\u0000' } },
34
44
  },
@@ -13,7 +13,7 @@ export interface PolicyMeasures {
13
13
  readonly unmeasured: readonly string[];
14
14
  /**
15
15
  * Each check tree by the JSON Pointer of its position: the Policy of a Function or a Method, or
16
- * the read or traverse rule of a Node Class.
16
+ * the rule of a Class slot.
17
17
  */
18
18
  readonly checks: Readonly<Record<string, CheckMeasure>>;
19
19
  }
@@ -1,9 +1,9 @@
1
+ import { deepFreeze } from '../../../language/freeze.js';
1
2
  import { limits } from '../../../language/limits.js';
2
3
  import { exceededLimits, measureChecks, policyMeasurer, } from '../../../language/policy/analysis/budgets.js';
3
- import { isClassRuleChecks } from '../../../language/policy/model/policy.js';
4
+ import { CLASS_OPERATIONS, isClassRuleChecks } from '../../../language/policy/model/policy.js';
4
5
  import { normalizePolicyChecks, normalizePolicyExpression, } from '../../../language/policy/normalization/normalize.js';
5
6
  import { escapePointerToken } from '../../../language/value/syntax/pointer.js';
6
- import { deepFreeze } from '../../canonical/freeze.js';
7
7
  import { structureDomainSchema } from '../domain.route.js';
8
8
  import { SITE } from './uses.js';
9
9
  /**
@@ -55,7 +55,7 @@ export function validateCheckBudget(context, pointer, measure) {
55
55
  if (exceededLimits(measure).length === 0)
56
56
  return;
57
57
  const { checkDepth, checkBranches, checkLeaves } = limits.policy;
58
- // A callable holds its checks at `policy`; a Node Class rule at `policies/{operation}`.
58
+ // A callable holds its checks at `policy`; a Class rule at `policies/{operation}`.
59
59
  const site = SITE[pointer.endsWith('/policy') ? 'callable' : 'class'];
60
60
  context.add('PL_BUDGET', pointer, `${site.checks} require depth <= ${checkDepth}, branches <= ${checkBranches}, and leaves <= ${checkLeaves}; got ${measure.checkDepth}, ${measure.checkBranches}, ${measure.checkLeaves}.`);
61
61
  }
@@ -76,7 +76,7 @@ function normalizedPolicies(schema) {
76
76
  function* checkTrees(schema) {
77
77
  for (const [name, declaration] of Object.entries(schema.classes)) {
78
78
  const owner = `/classes/${escapePointerToken(name)}`;
79
- for (const operation of ['read', 'traverse']) {
79
+ for (const operation of CLASS_OPERATIONS) {
80
80
  const rule = declaration.policies[operation];
81
81
  if (typeof rule === 'object' && isClassRuleChecks(rule)) {
82
82
  yield [`${owner}/policies/${operation}`, rule];
@@ -22,8 +22,7 @@ export declare class PolicyValidationContext implements PolicyTypingScope {
22
22
  edgeDeclaration(ref: ClassRef): Extract<ClassDefinition, {
23
23
  readonly kind: 'edge';
24
24
  }> | undefined;
25
- /** Return true when candidate is the required Class or a same-kind descendant. */
26
- classExtends(candidate: ClassRef, required: ClassRef): boolean;
25
+ resolveClass(ref: ClassRef): ClassDefinition | undefined;
27
26
  /** The contract a Policy pattern reference types, reporting an invalid one under `/policies`. */
28
27
  referenceContract(ref: PolicyObjectRef): ObjectContract | undefined;
29
28
  add(code: SchemaDiagnosticCode, pointer: string, message: string, ref?: string): void;
@@ -1,6 +1,5 @@
1
1
  import { Key } from '../../../addressing/index.js';
2
2
  import { definitionRefIndex } from '../../../addressing/ordering.js';
3
- import { classSatisfies } from '../../../language/class/inheritance.js';
4
3
  import { diagnostic } from '../../diagnostic.js';
5
4
  import { referenceContracts } from './objects.js';
6
5
  const formatDefinitionRef = Key.of;
@@ -63,12 +62,8 @@ export class PolicyValidationContext {
63
62
  }
64
63
  return resolved.declaration;
65
64
  }
66
- /** Return true when candidate is the required Class or a same-kind descendant. */
67
- classExtends(candidate, required) {
68
- const resolve = (ref) => this.environment.resolveAny(ref)?.declaration;
69
- const actual = resolve(candidate);
70
- const target = resolve(required);
71
- return actual !== undefined && target !== undefined && classSatisfies(actual, target, resolve);
65
+ resolveClass(ref) {
66
+ return this.environment.resolveAny(ref)?.declaration;
72
67
  }
73
68
  /** The contract a Policy pattern reference types, reporting an invalid one under `/policies`. */
74
69
  referenceContract(ref) {
@@ -1,4 +1,5 @@
1
1
  import type { CallableDefinition } from '../../../language/callable/model/callable.js';
2
+ import type { EdgeContract } from '../../../language/class/model/edge.js';
2
3
  import type { PolicyTyping } from '../../../language/policy/analysis/typing.js';
3
4
  import type { PolicyCheckExpression } from '../../../language/policy/model/policy.js';
4
5
  import type { JsonSchema } from '../../../language/value/model/schema.js';
@@ -10,6 +11,13 @@ interface CheckContext {
10
11
  readonly input: JsonSchema;
11
12
  /** The Node Class that receives the call, which the checks name `self`; none, and none is named. */
12
13
  readonly receiver?: ClassRef;
14
+ /** In an Edge Class rule, the Edge `self` names, in place of a receiver. */
15
+ readonly edge?: EdgeUse;
16
+ }
17
+ /** One Edge Class, whose declaration fixes the concrete Node Classes of its endpoints. */
18
+ interface EdgeUse {
19
+ readonly ref: ClassRef;
20
+ readonly declaration: EdgeContract;
13
21
  }
14
22
  /** How a refusal names the checks of each site and the contract of their objects. */
15
23
  export declare const SITE: {
@@ -22,8 +30,8 @@ export declare const SITE: {
22
30
  readonly objects: 'the Class rule object contract';
23
31
  };
24
32
  };
25
- export declare function validateReadUses(context: PolicyValidationContext, typing: PolicyTyping): void;
26
- export declare function validateTraverseUses(context: PolicyValidationContext, typing: PolicyTyping): void;
33
+ /** Type every Class slot: its Policy against the instance it decides, or its Policy checks. */
34
+ export declare function validateClassUses(context: PolicyValidationContext, typing: PolicyTyping): void;
27
35
  export declare function validateCallableUses(context: PolicyValidationContext, typing: PolicyTyping): void;
28
36
  /**
29
37
  * Validate the Policy of one callable contract written at `pointer`, wherever it is declared:
@@ -2,7 +2,7 @@ import { Key } from '../../../addressing/index.js';
2
2
  import { definitionRefIndex } from '../../../addressing/ordering.js';
3
3
  import { isBinaryInput } from '../../../language/callable/model/callable.js';
4
4
  import { neverAdmittedClasses, } from '../../../language/policy/analysis/checks.js';
5
- import { checksNameTheCaller, isClassRuleChecks } from '../../../language/policy/model/policy.js';
5
+ import { CLASS_OPERATIONS, checksNameTheCaller, isClassRuleChecks, } from '../../../language/policy/model/policy.js';
6
6
  import { normalizePolicyChecks } from '../../../language/policy/normalization/normalize.js';
7
7
  import { GRAPH_NODE_PATH_SCHEMA_ID } from '../../../language/value/syntax/node-path/schema.js';
8
8
  import { escapePointerToken } from '../../../language/value/syntax/pointer.js';
@@ -15,11 +15,10 @@ export const SITE = {
15
15
  callable: { checks: 'Callable checks', objects: 'the callable object contract' },
16
16
  class: { checks: 'Class rule checks', objects: 'the Class rule object contract' },
17
17
  };
18
- export function validateReadUses(context, typing) {
19
- validateClassPolicyUses(context, typing, 'read');
20
- }
21
- export function validateTraverseUses(context, typing) {
22
- validateClassPolicyUses(context, typing, 'traverse');
18
+ /** Type every Class slot: its Policy against the instance it decides, or its Policy checks. */
19
+ export function validateClassUses(context, typing) {
20
+ for (const operation of CLASS_OPERATIONS)
21
+ validateClassPolicyUses(context, typing, operation);
23
22
  }
24
23
  function validateClassPolicyUses(context, typing, operation) {
25
24
  for (const [name, declaration] of Object.entries(context.environment.candidate.classes)) {
@@ -27,23 +26,23 @@ function validateClassPolicyUses(context, typing, operation) {
27
26
  if (typeof ref !== 'object')
28
27
  continue;
29
28
  const pointer = `/classes/${escapePointerToken(name)}/policies/${operation}`;
29
+ const self = { origin: context.environment.candidate.origin, kind: 'class', name };
30
30
  if (isClassRuleChecks(ref)) {
31
- // Structure admits Policy checks only on a Node Class: `self` is the observed exact Class.
31
+ // Structure admits `self` everywhere but in a Node create rule: there is no Node yet.
32
32
  validateChecks(context, typing, ref, {
33
33
  site: 'class',
34
34
  pointer,
35
35
  input: {},
36
- receiver: { origin: context.environment.candidate.origin, kind: 'class', name },
36
+ ...(declaration.kind === 'node'
37
+ ? { receiver: self }
38
+ : { edge: { ref: self, declaration } }),
37
39
  });
38
40
  continue;
39
41
  }
40
42
  if (!context.resolvePolicy(ref, pointer))
41
43
  continue;
42
44
  if (declaration.kind === 'node') {
43
- const object = {
44
- kind: 'exact',
45
- ref: { origin: context.environment.candidate.origin, kind: 'class', name },
46
- };
45
+ const object = { kind: 'exact', ref: self };
47
46
  if (typing.policyAccepts(ref.name, object))
48
47
  continue;
49
48
  context.add('PL_USE', pointer, `${formatDefinitionRef(ref)} is not valid for every normalized branch of ${operation} Node Class ${name}.`, formatDefinitionRef(ref));
@@ -98,7 +97,11 @@ function visitChecks(validation, typing, expression, context, pointer, leaves) {
98
97
  if ('sameNode' in expression) {
99
98
  for (const [side, object] of Object.entries(expression.sameNode)) {
100
99
  const objectPointer = `${pointer}/sameNode/${side}`;
101
- if (object.kind === 'self')
100
+ if (object.kind === 'self' && context.edge !== undefined) {
101
+ // `self` is an Edge there, and sameNode compares Nodes.
102
+ validation.add('PL_USE', objectPointer, 'sameNode compares Nodes: self is an Edge here.');
103
+ }
104
+ else if (object.kind === 'self')
102
105
  selfContracts(validation, context, objectPointer);
103
106
  else if (object.kind === 'input')
104
107
  inputContracts(validation, context, object.field, objectPointer);
@@ -110,6 +113,16 @@ function visitChecks(validation, typing, expression, context, pointer, leaves) {
110
113
  if ('check' in expression) {
111
114
  if (!validation.resolvePolicy(expression.check, `${pointer}/check`))
112
115
  return;
116
+ if (expression.object.kind === 'self' && context.edge !== undefined) {
117
+ // An Edge is checked as an Edge Class Policy is: over every concrete endpoint pair.
118
+ if (!typing.policyAcceptsEdgeClass(expression.check.name, context.edge.declaration)) {
119
+ validation.add('PL_USE', pointer, `${formatDefinitionRef(expression.check)} is not valid for every concrete source/target pair of Edge Class ${context.edge.ref.name}.`, formatDefinitionRef(expression.check));
120
+ return;
121
+ }
122
+ const edge = formatDefinitionRef(context.edge.ref);
123
+ leaves.set(expression, { accepted: [edge], admitted: new Set([edge]) });
124
+ return;
125
+ }
113
126
  const objectPointer = `${pointer}/object`;
114
127
  const contracts = expression.object.kind === 'self'
115
128
  ? selfContracts(validation, context, objectPointer)
@@ -3,7 +3,7 @@ import { validatePolicyBudgets } from './budgets.js';
3
3
  import { PolicyValidationContext } from './context.js';
4
4
  import { validatePolicyReferences } from './references.js';
5
5
  import { validatePolicyTyping } from './typing.js';
6
- import { validateCallableUses, validateReadUses, validateTraverseUses } from './uses.js';
6
+ import { validateCallableUses, validateClassUses } from './uses.js';
7
7
  /**
8
8
  * Validates the closed V1 Policy language after structural parsing.
9
9
  *
@@ -17,8 +17,7 @@ export function validatePolicies(environment, analysis) {
17
17
  validatePolicyBudgets(context);
18
18
  const typing = new PolicyTyping(context);
19
19
  validatePolicyTyping(context, typing);
20
- validateReadUses(context, typing);
21
- validateTraverseUses(context, typing);
20
+ validateClassUses(context, typing);
22
21
  validateCallableUses(context, typing);
23
22
  return Object.freeze(context.diagnostics);
24
23
  }
@@ -10,6 +10,8 @@ export type { BlobOptions, BlobReceipt, BlobRef, BlobSchema, } from '../../langu
10
10
  export type { JsonSchema as Schema, JsonSchemaObject as SchemaObject, PathAnnotation, PropertyObjectSchema as PropertySchema, ValueSchemaContext as Context, } from '../../language/value/model/schema.js';
11
11
  export { instant } from '../../language/value/model/instant.js';
12
12
  export type { OrderingKind } from '../../language/value/ordering.js';
13
+ export { VALUE_KINDS } from '../../language/value/ordering.js';
14
+ export { GRAPH_NODE_PATH_SCHEMA_ID as NODE_PATH } from '../../language/value/syntax/node-path/schema.js';
13
15
  export { DECLARED_VALUE_KINDS as DECLARED_KINDS, FORBIDDEN_KEYWORDS, SUBSCHEMA_KEYWORDS, VALUE_KEYWORDS as KEYWORDS, } from '../../language/value/profile/vocabulary.js';
14
16
  export type { DeclaredValueKind as DeclaredKind, ValueKeyword as Keyword, } from '../../language/value/profile/vocabulary.js';
15
17
  export { VALUE_RULES as RULES } from '../../language/validation/rule.js';
@@ -8,6 +8,8 @@ const registries = new WeakMap();
8
8
  export { VALUE_SCHEMA_DIALECT as DIALECT, VALUE_SCHEMA_VOCABULARY as VOCABULARY, } from '../../language/value/model/schema.js';
9
9
  export { blob, isBlobValueSchema as isBlob } from '../../language/value/model/blob.js';
10
10
  export { instant } from '../../language/value/model/instant.js';
11
+ export { VALUE_KINDS } from '../../language/value/ordering.js';
12
+ export { GRAPH_NODE_PATH_SCHEMA_ID as NODE_PATH } from '../../language/value/syntax/node-path/schema.js';
11
13
  export { DECLARED_VALUE_KINDS as DECLARED_KINDS, FORBIDDEN_KEYWORDS, SUBSCHEMA_KEYWORDS, VALUE_KEYWORDS as KEYWORDS, } from '../../language/value/profile/vocabulary.js';
12
14
  export { VALUE_RULES as RULES } from '../../language/validation/rule.js';
13
15
  /** Validate one complete JSON value under the exact accepted schema that owns the contract. */
@@ -1,5 +1,4 @@
1
- import type { core as zodCore } from 'zod';
2
- type JsonSchema = zodCore.JSONSchema.JSONSchema;
1
+ import { type JsonSchema } from './json.json-schema.js';
3
2
  /**
4
3
  * The checks on value schemas JSON Schema cannot state, which the published document names with
5
4
  * the positions it constrains. The Kernel reports each as `DV_VALUE_SCHEMA`, with its value code.
@@ -13,17 +12,17 @@ export declare const valueSchemaRules: {
13
12
  readonly 'value-path': {
14
13
  readonly code: 'DV_VALUE_SCHEMA';
15
14
  readonly valueCode: 'VS_PATH_EXTENSION';
16
- readonly 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.';
15
+ readonly statement: 'x-astrale-path annotates only a direct Property of a callable input object, beside exactly the $ref of the graph NodePath schema.';
17
16
  };
18
17
  readonly 'value-recursion': {
19
18
  readonly code: 'DV_VALUE_SCHEMA';
20
19
  readonly valueCode: 'VS_RECURSION_PRODUCTIVE';
21
- readonly statement: 'Every reference cycle of a value schema crosses a keyword that steps into the value: to a member, a member name or an item.';
20
+ readonly statement: string;
22
21
  };
23
22
  readonly 'value-reference-form': {
24
23
  readonly code: 'DV_VALUE_SCHEMA';
25
24
  readonly valueCode: 'VS_REFERENCE_FORM';
26
- readonly statement: 'A $ref is a JSON Pointer fragment into its own value schema, or the id of the graph NodePath schema beside x-astrale-path.';
25
+ readonly statement: string;
27
26
  };
28
27
  readonly 'value-reference-resolution': {
29
28
  readonly code: 'DV_VALUE_SCHEMA';
@@ -33,19 +32,18 @@ export declare const valueSchemaRules: {
33
32
  readonly 'value-regexp': {
34
33
  readonly code: 'DV_VALUE_SCHEMA';
35
34
  readonly valueCode: 'VS_REGEX_PORTABLE';
36
- readonly statement: 'Every pattern, fullMatch and patternProperties name is an RFC 9485 I-Regexp within the regular-expression limits.';
35
+ readonly statement: string;
37
36
  };
38
37
  };
39
38
  export type ValueSchemaRule = keyof typeof valueSchemaRules;
40
39
  /**
41
40
  * The value language in JSON Schema, for the document that embeds value schemas: `root` constrains
42
41
  * the `value-schema` definition with the published dialect and what the profile adds to it,
43
- * `definitions` holds the locations it reaches, and `nodeProperty` constrains a Node Property
44
- * definition. A named check leaves `x-astrale-rule` at the positions it constrains.
42
+ * `definitions` holds the locations it reaches and the members of the Path annotation, and
43
+ * `nodeProperty` constrains a Node Property definition. A named check leaves `x-astrale-rule` at the positions it constrains.
45
44
  */
46
45
  export declare function valueSchemaJsonSchema(): {
47
46
  readonly root: readonly JsonSchema[];
48
47
  readonly definitions: Readonly<Record<string, JsonSchema>>;
49
48
  readonly nodeProperty: JsonSchema;
50
49
  };
51
- export {};
@@ -1,5 +1,7 @@
1
+ import { VALUE_RULES } from '../../language/validation/rule.js';
1
2
  import { VALUE_SCHEMA_DIALECT } from '../../language/value/model/schema.js';
2
3
  import { FORBIDDEN_KEYWORDS, SUBSCHEMA_KEYWORDS, VALUE_KEYWORDS, } from '../../language/value/profile/vocabulary.js';
4
+ import { named } from './json.json-schema.js';
3
5
  /**
4
6
  * The checks on value schemas JSON Schema cannot state, which the published document names with
5
7
  * the positions it constrains. The Kernel reports each as `DV_VALUE_SCHEMA`, with its value code.
@@ -10,20 +12,21 @@ export const valueSchemaRules = {
10
12
  valueCode: 'VS_VALUE_KIND',
11
13
  statement: 'A blob is declared only by the whole schema of a Property, exactly as values.blob writes it.',
12
14
  },
15
+ // Its members are stated (`path-annotation`); where it stands is named.
13
16
  'value-path': {
14
17
  code: 'DV_VALUE_SCHEMA',
15
18
  valueCode: 'VS_PATH_EXTENSION',
16
- 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.',
19
+ statement: 'x-astrale-path annotates only a direct Property of a callable input object, beside exactly the $ref of the graph NodePath schema.',
17
20
  },
18
21
  'value-recursion': {
19
22
  code: 'DV_VALUE_SCHEMA',
20
23
  valueCode: 'VS_RECURSION_PRODUCTIVE',
21
- statement: 'Every reference cycle of a value schema crosses a keyword that steps into the value: to a member, a member name or an item.',
24
+ statement: VALUE_RULES.VS_RECURSION_PRODUCTIVE.statement,
22
25
  },
23
26
  'value-reference-form': {
24
27
  code: 'DV_VALUE_SCHEMA',
25
28
  valueCode: 'VS_REFERENCE_FORM',
26
- statement: 'A $ref is a JSON Pointer fragment into its own value schema, or the id of the graph NodePath schema beside x-astrale-path.',
29
+ statement: VALUE_RULES.VS_REFERENCE_FORM.statement,
27
30
  },
28
31
  'value-reference-resolution': {
29
32
  code: 'DV_VALUE_SCHEMA',
@@ -33,17 +36,18 @@ export const valueSchemaRules = {
33
36
  'value-regexp': {
34
37
  code: 'DV_VALUE_SCHEMA',
35
38
  valueCode: 'VS_REGEX_PORTABLE',
36
- statement: 'Every pattern, fullMatch and patternProperties name is an RFC 9485 I-Regexp within the regular-expression limits.',
39
+ statement: VALUE_RULES.VS_REGEX_PORTABLE.statement,
37
40
  },
38
41
  };
39
42
  const KIND = 'x-astrale-value-kind';
40
43
  const LOCATION = '#/$defs/value-location';
41
44
  const SUBSCHEMA = '#/$defs/value-subschema';
45
+ const PATH = '#/$defs/path-annotation';
42
46
  /**
43
47
  * The value language in JSON Schema, for the document that embeds value schemas: `root` constrains
44
48
  * the `value-schema` definition with the published dialect and what the profile adds to it,
45
- * `definitions` holds the locations it reaches, and `nodeProperty` constrains a Node Property
46
- * definition. A named check leaves `x-astrale-rule` at the positions it constrains.
49
+ * `definitions` holds the locations it reaches and the members of the Path annotation, and
50
+ * `nodeProperty` constrains a Node Property definition. A named check leaves `x-astrale-rule` at the positions it constrains.
47
51
  */
48
52
  export function valueSchemaJsonSchema() {
49
53
  return {
@@ -53,10 +57,7 @@ export function valueSchemaJsonSchema() {
53
57
  objectOrBoolean({
54
58
  properties: {
55
59
  $schema: { const: VALUE_SCHEMA_DIALECT },
56
- [KIND]: {
57
- enum: ['instant', 'blob'],
58
- 'x-astrale-rule': 'value-blob',
59
- },
60
+ [KIND]: named({ enum: ['instant', 'blob'] }, 'value-blob'),
60
61
  },
61
62
  }),
62
63
  ],
@@ -68,37 +69,47 @@ export function valueSchemaJsonSchema() {
68
69
  },
69
70
  properties: {
70
71
  $id: false,
71
- $ref: {
72
- type: 'string',
73
- 'x-astrale-rule': [
74
- 'value-recursion',
75
- 'value-reference-form',
76
- 'value-reference-resolution',
77
- ],
78
- },
72
+ $ref: named({ type: 'string' }, 'value-recursion', 'value-reference-form', 'value-reference-resolution'),
79
73
  enum: { type: 'array', uniqueItems: true },
80
- fullMatch: { type: 'string', 'x-astrale-rule': 'value-regexp' },
81
- pattern: { type: 'string', 'x-astrale-rule': 'value-regexp' },
82
- 'x-astrale-path': { type: 'object', 'x-astrale-rule': 'value-path' },
74
+ fullMatch: named({ type: 'string' }, 'value-regexp'),
75
+ pattern: named({ type: 'string' }, 'value-regexp'),
76
+ 'x-astrale-path': named({ $ref: PATH }, 'value-path'),
83
77
  ...Object.fromEntries(SUBSCHEMA_KEYWORDS.single.map((keyword) => [keyword, { $ref: SUBSCHEMA }])),
84
78
  ...Object.fromEntries(SUBSCHEMA_KEYWORDS.array.map((keyword) => [
85
79
  keyword,
86
80
  { type: 'array', items: { $ref: SUBSCHEMA } },
87
81
  ])),
88
- ...Object.fromEntries(SUBSCHEMA_KEYWORDS.map.map((keyword) => [
89
- keyword,
90
- {
82
+ ...Object.fromEntries(SUBSCHEMA_KEYWORDS.map.map((keyword) => {
83
+ const members = {
91
84
  type: 'object',
92
85
  additionalProperties: { $ref: SUBSCHEMA },
93
- ...(keyword === 'patternProperties' ? { 'x-astrale-rule': 'value-regexp' } : {}),
94
- },
95
- ])),
86
+ };
87
+ if (keyword !== 'patternProperties')
88
+ return [keyword, members];
89
+ return [keyword, named(members, 'value-regexp')];
90
+ })),
96
91
  },
97
92
  // An instant is a string.
98
93
  if: { required: [KIND], properties: { [KIND]: { const: 'instant' } } },
99
94
  // oxlint-disable-next-line unicorn/no-thenable -- the JSON Schema keyword
100
95
  then: { required: ['type'], properties: { type: { const: 'string' } } },
101
96
  }),
97
+ // The Path annotation: the one Node a Path field resolves, of a Node Class it accepts.
98
+ 'path-annotation': {
99
+ type: 'object',
100
+ properties: {
101
+ target: { type: 'string', const: 'node' },
102
+ cardinality: { type: 'string', const: 'one' },
103
+ accepts: {
104
+ type: 'array',
105
+ minItems: 1,
106
+ uniqueItems: true,
107
+ items: named({ $ref: '#/$defs/class-ref' }, 'value-reference-resolution'),
108
+ },
109
+ },
110
+ required: ['target', 'cardinality', 'accepts'],
111
+ additionalProperties: false,
112
+ },
102
113
  // A location below the root: no $schema, and no blob.
103
114
  'value-subschema': {
104
115
  allOf: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-dsl",
3
- "version": "1.0.0-beta.15",
3
+ "version": "1.0.0-beta.17",
4
4
  "description": "Astrale Kernel DSL - Schema authoring, inference, and IR serialization",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -81,6 +81,7 @@
81
81
  },
82
82
  "./v1/schema/domain-schema.schema.json": "./dist/v1/schema/.spec/schemas/domain-schema.schema.json",
83
83
  "./v1/schema/value-schema.schema.json": "./dist/v1/language/.spec/schemas/value-schema.schema.json",
84
+ "./v1/schema/draft-2020-12.schema.json": "./dist/v1/language/.spec/schemas/draft-2020-12.schema.json",
84
85
  "./v1/value/full-match": {
85
86
  "types": "./dist/v1/value/full-match.d.ts",
86
87
  "import": "./dist/v1/value/full-match.js"
@@ -1,3 +0,0 @@
1
- import type { DomainSchemaIR } from '../model/schema.js';
2
- /** Produce the deterministic semantic document consumed by canonical JSON serialization. */
3
- export declare function canonicalizeDomainSchema(schema: DomainSchemaIR): DomainSchemaIR;
@@ -1,3 +0,0 @@
1
- import type { Revision } from '../../addressing/index.js';
2
- /** Content-address canonical DomainSchema bytes with the V1 revision algorithm. */
3
- export declare function digestDomainSchemaBytes(bytes: Uint8Array): Revision;
@@ -1,5 +0,0 @@
1
- import { digestRevisionBytes } from '../../addressing/revision.js';
2
- /** Content-address canonical DomainSchema bytes with the V1 revision algorithm. */
3
- export function digestDomainSchemaBytes(bytes) {
4
- return digestRevisionBytes(bytes);
5
- }
@@ -1,5 +0,0 @@
1
- import { normalizeDomainSchema } from '../normalization/domain.js';
2
- /** Produce the deterministic semantic document consumed by canonical JSON serialization. */
3
- export function canonicalizeDomainSchema(schema) {
4
- return normalizeDomainSchema(schema);
5
- }
@@ -1 +0,0 @@
1
- export declare function deepFreeze<T>(value: T): Readonly<T>;