@astrale-os/kernel-dsl 0.2.0-beta.27 → 0.2.0-beta.29

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.
@@ -91,7 +91,7 @@ export type ProjectionOfCallable<Value extends Function | Method> = Value extend
91
91
  /** Internal lowering access; Callable authoring handles expose no public configuration object. */
92
92
  export declare function callableConfiguration(value: Function | Method): CallableInput;
93
93
  export type PolicyObjectAuthoring = Class | Function | Policy | View | CoreNode;
94
- export type PolicyObjectInput = Lazy<PolicyObjectAuthoring>;
94
+ export type PolicyObjectInput = Lazy<PolicyObjectAuthoring | PolicyObjectRef>;
95
95
  export declare function func<const Config extends FunctionInput, Input = ValueInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth']>(config: Config): Function<CallableProjection<Input, Output, Auth, false, 'default'>>;
96
96
  export declare function method<const Config extends MethodInput, Input = ValueInputOf<Config['input']>, Output = CallableOutputOf<Config['output']>, Auth extends FunctionAuth = Config['auth'], Static extends boolean = Config extends {
97
97
  readonly static: true;
@@ -43,6 +43,7 @@ export interface NodeClassInput {
43
43
  readonly data?: MediaType;
44
44
  readonly methods?: Readonly<Record<string, Method>>;
45
45
  readonly icon?: string;
46
+ /** Local observation Policies; inherited by no descendant and forbidden on abstract Classes. */
46
47
  readonly policies?: {
47
48
  readonly read?: Lazy<Policy>;
48
49
  readonly traverse?: Lazy<Policy>;
@@ -53,6 +54,7 @@ export interface EdgeClassOptions {
53
54
  readonly description?: string;
54
55
  readonly extends?: readonly EdgeClassReferenceInput[];
55
56
  readonly properties?: PropertyRecordInput;
57
+ /** Local observation Policies; inherited by no descendant and forbidden on abstract Classes. */
56
58
  readonly policies?: {
57
59
  readonly read?: Lazy<Policy>;
58
60
  readonly traverse?: Lazy<Policy>;
@@ -50,22 +50,22 @@ export class BuilderContext {
50
50
  this.capturePolicyObject(value, ref);
51
51
  }
52
52
  capturePolicy(value, ref, pointer) {
53
- this.ownership.capture(value, `${ref.origin}:policy.${ref.name}`, pointer);
53
+ this.ownership.captureDefinition(value, ref, pointer);
54
54
  this.policies.set(value, ref);
55
55
  this.capturePolicyObject(value, ref);
56
56
  }
57
57
  captureCoreNode(value, slug, pointer) {
58
- this.ownership.capture(value, `${this.origin}:core.${slug}`, pointer);
59
58
  const ref = { origin: this.origin, kind: 'core', name: slug };
59
+ this.ownership.captureDefinition(value, ref, pointer);
60
60
  this.coreNodes.set(value, ref);
61
61
  this.capturePolicyObject(value, ref);
62
62
  }
63
63
  captureFunction(value, ref, pointer) {
64
- this.ownership.capture(value, `${ref.origin}:function.${ref.name}`, pointer);
64
+ this.ownership.captureDefinition(value, ref, pointer);
65
65
  this.capturePolicyObject(value, ref);
66
66
  }
67
67
  captureView(value, ref, pointer) {
68
- this.ownership.capture(value, `${ref.origin}:view.${ref.name}`, pointer);
68
+ this.ownership.captureDefinition(value, ref, pointer);
69
69
  this.capturePolicyObject(value, ref);
70
70
  }
71
71
  resolveClass(input, kind, pointer) {
@@ -112,13 +112,18 @@ export class BuilderContext {
112
112
  const ref = isPolicyObjectRef(value)
113
113
  ? value
114
114
  : typeof value === 'object' && value !== null
115
- ? this.policyObjects.get(value)
115
+ ? (this.policyObjects.get(value) ?? this.ownership.definition(value))
116
116
  : undefined;
117
- if (ref === undefined ||
118
- ref.origin !== this.origin ||
119
- !this.policyObjectRefs.has(definitionRefIndex(ref))) {
120
- fail('BUILDER_POLICY_REF', pointer, 'Policy ref(...) must name one registered local Definition, Function, Policy, View, or Core Node.');
117
+ const dependency = ref === undefined ? undefined : this.#dependencies.get(ref.origin)?.schema;
118
+ const admitted = ref !== undefined &&
119
+ (ref.origin === this.origin
120
+ ? this.policyObjectRefs.has(definitionRefIndex(ref))
121
+ : dependency !== undefined && hasPolicyObject(dependency, ref));
122
+ if (!admitted || ref === undefined) {
123
+ fail('BUILDER_POLICY_REF', pointer, 'Policy ref(...) must name one registered local or direct-dependency Definition, Function, Policy, View, or Core Node.');
121
124
  }
125
+ if (ref.origin !== this.origin)
126
+ this.usedForeignOrigins.add(ref.origin);
122
127
  return { ...ref };
123
128
  }
124
129
  resolveCoreEndpoint(value, pointer) {
@@ -245,3 +250,17 @@ function definitionRefsOf(schema) {
245
250
  }
246
251
  return refs.sort(compareDefinitionRefs);
247
252
  }
253
+ function hasPolicyObject(schema, ref) {
254
+ switch (ref.kind) {
255
+ case 'class':
256
+ return Object.hasOwn(schema.classes, ref.name);
257
+ case 'function':
258
+ return Object.hasOwn(schema.functions, ref.name);
259
+ case 'policy':
260
+ return Object.hasOwn(schema.policies, ref.name);
261
+ case 'view':
262
+ return Object.hasOwn(schema.views, ref.name);
263
+ case 'core':
264
+ return Object.hasOwn(schema.core.nodes, ref.name);
265
+ }
266
+ }
@@ -1,8 +1,8 @@
1
- import type { DefinitionRef } from '../../../addressing/index.js';
1
+ import type { PolicyObjectRef } from '../../../language/policy/model/policy.js';
2
2
  export declare class OwnershipCapture {
3
3
  #private;
4
- captureDefinition(value: object, ref: DefinitionRef, pointer: string): void;
4
+ captureDefinition(value: object, ref: PolicyObjectRef, pointer: string): void;
5
5
  capture(value: object, owner: string, pointer: string): void;
6
- definition(value: object): DefinitionRef | undefined;
6
+ definition(value: object): PolicyObjectRef | undefined;
7
7
  commit(): void;
8
8
  }
@@ -178,6 +178,10 @@ function policyKey(value) {
178
178
  }
179
179
  function validateClassFacets(context) {
180
180
  for (const located of context.classes.values()) {
181
+ if (located.value.abstract &&
182
+ (located.value.policies.read !== undefined || located.value.policies.traverse !== undefined)) {
183
+ fail(`${located.pointer}/policies`, 'Abstract Classes cannot declare observation Policies.');
184
+ }
181
185
  for (const [index, property] of located.value.properties.entries()) {
182
186
  const path = `${located.pointer}/properties/${index}`;
183
187
  if (property.auto !== undefined) {
@@ -252,26 +256,7 @@ function edgeRefines(context, child, parent) {
252
256
  const direct = refine(0, 0) && refine(1, 1);
253
257
  if (direct)
254
258
  return true;
255
- const reverse = refine(0, 1) && refine(1, 0);
256
- return reverse && !hasEffectiveEdgePolicy(context, parent, new Set());
257
- }
258
- function hasEffectiveEdgePolicy(context, declaration, visited) {
259
- if (declaration.policies.read !== undefined || declaration.policies.traverse !== undefined) {
260
- return true;
261
- }
262
- for (const parentRef of declaration.extends) {
263
- const key = Key.of(parentRef);
264
- if (visited.has(key))
265
- continue;
266
- visited.add(key);
267
- const parent = context.classes.get(key)?.value;
268
- if (parent !== undefined &&
269
- isCompiledEdge(parent) &&
270
- hasEffectiveEdgePolicy(context, parent, visited)) {
271
- return true;
272
- }
273
- }
274
- return false;
259
+ return refine(0, 1) && refine(1, 0);
275
260
  }
276
261
  function endpointRefines(context, child, parent) {
277
262
  return child.accepts.every((accepted) => classSatisfiesAny(context, accepted, parent.accepts));
@@ -438,11 +423,8 @@ function validateCallablePolicy(context, entry, expression, inputIndex, fields,
438
423
  !isSingleNodePathField(context, entry, inputIndex, object.field))
439
424
  fail(`${path}/object`, 'Callable Policy input target must be a direct required single-Node Path field.');
440
425
  }
441
- if (object.kind === 'ref') {
442
- if (object.ref.origin !== entry.origin ||
443
- context.definitions.get(Key.of(object.ref)) === undefined)
444
- fail(`${path}/object/ref`, 'Callable Policy object refs must name an exact local Definition.');
445
- }
426
+ // validateClosure already establishes that object refs name existing local
427
+ // or exact direct-dependency Definitions. Named Policy execution stays local.
446
428
  return;
447
429
  }
448
430
  const children = 'allOf' in expression ? expression.allOf : expression.anyOf;
@@ -1,4 +1,4 @@
1
- import { compareUnicode, Key, MethodKey as MethodKeyValue, PropertyKey as PropertyKeyValue, } from '../../addressing/index.js';
1
+ import { Key, MethodKey as MethodKeyValue, PropertyKey as PropertyKeyValue, } from '../../addressing/index.js';
2
2
  import { acceptPropertyValues } from '../../language/class/property-values.js';
3
3
  import { appendPointer } from '../../language/value/syntax/pointer.js';
4
4
  import { createMethodCollection, createPropertyCollection } from '../namespace/member-collection.js';
@@ -24,16 +24,6 @@ export function createClasses(definitions, validation) {
24
24
  throw new TypeError(`Class ${key} extends a Class of another graph kind.`);
25
25
  }
26
26
  const shell = {};
27
- const policies = Object.freeze({
28
- read: Object.freeze(uniquePolicyRefs([
29
- ...parents.flatMap((parent) => [...parent.policies.read]),
30
- ...(definition.policies.read === undefined ? [] : [definition.policies.read]),
31
- ])),
32
- traverse: Object.freeze(uniquePolicyRefs([
33
- ...parents.flatMap((parent) => [...parent.policies.traverse]),
34
- ...(definition.policies.traverse === undefined ? [] : [definition.policies.traverse]),
35
- ])),
36
- });
37
27
  const effectiveProperties = new Map();
38
28
  const effectiveMethods = new Map();
39
29
  let inheritedData;
@@ -102,7 +92,7 @@ export function createClasses(definitions, validation) {
102
92
  abstract: definition.abstract,
103
93
  definition,
104
94
  extends: Object.freeze(parents),
105
- policies,
95
+ policies: definition.policies,
106
96
  data: definition.kind === 'node' ? (ownData ?? inheritedData) : undefined,
107
97
  properties: createPropertyCollection(effectiveProperties.values()),
108
98
  methods: createMethodCollection(instanceMethods),
@@ -110,7 +100,6 @@ export function createClasses(definitions, validation) {
110
100
  self: Object.freeze({
111
101
  data: definition.kind === 'node' ? ownData : undefined,
112
102
  properties: createPropertyCollection(ownProperties),
113
- policies: definition.policies,
114
103
  methods: createMethodCollection(ownInstanceMethods),
115
104
  static: Object.freeze({ methods: createMethodCollection(ownStaticMethods) }),
116
105
  }),
@@ -138,12 +127,6 @@ export function createClasses(definitions, validation) {
138
127
  resolve(definition.ref);
139
128
  return resolved;
140
129
  }
141
- function uniquePolicyRefs(values) {
142
- const result = new Map();
143
- for (const value of values)
144
- result.set(Key.of(value), value);
145
- return [...result.values()].sort((left, right) => compareUnicode(Key.of(left), Key.of(right)));
146
- }
147
130
  function validateProperties(abstract, properties, input) {
148
131
  if (abstract) {
149
132
  return [
@@ -1,5 +1,5 @@
1
1
  import type { Value as PortableValue } from '@astrale-os/kernel-dsl/value';
2
- import type { ClassRef, DefinitionKey, Key, PolicyRef, PropertyKey } from '../../addressing/index.js';
2
+ import type { ClassRef, DefinitionKey, Key, PropertyKey } from '../../addressing/index.js';
3
3
  import type { ResolvedMethodCollection } from '../callable/resolved.js';
4
4
  import type { StateTopology } from '../state/index.js';
5
5
  import type { Diagnostic } from '../validation/diagnostic.js';
@@ -25,11 +25,6 @@ export interface ResolvedData {
25
25
  readonly owner: ResolvedClass<'node'>;
26
26
  readonly definition: DataDefinition;
27
27
  }
28
- export interface ResolvedClassPolicies {
29
- /** Effective read Policies for this Node or Edge Class. */
30
- readonly read: readonly PolicyRef[];
31
- readonly traverse: readonly PolicyRef[];
32
- }
33
28
  export type ResolvedPropertyCollection<ByKey extends Readonly<Record<PropertyKey, ResolvedProperty<unknown>>> = Readonly<Record<PropertyKey, ResolvedProperty<unknown>>>, ByName extends Readonly<Record<string, ResolvedProperty<unknown>>> = Readonly<Record<never, never>>> = Readonly<ByName> & Iterable<ByKey[keyof ByKey]> & {
34
29
  readonly byKey: ByKey;
35
30
  get<Key extends keyof ByKey>(key: Key): ByKey[Key];
@@ -47,8 +42,8 @@ export interface ResolvedClass<Kind extends ClassKind = ClassKind, Properties ex
47
42
  readonly abstract: boolean;
48
43
  readonly definition: Definition;
49
44
  readonly extends: Parents & readonly ResolvedClass<Kind>[];
50
- /** Effective inherited Policy conjunction, deduplicated by exact Policy ref. */
51
- readonly policies: ResolvedClassPolicies;
45
+ /** Local observation Policies; Class inheritance never contributes an attachment. */
46
+ readonly policies: Definition['policies'];
52
47
  readonly data: Kind extends 'node' ? ResolvedData | undefined : undefined;
53
48
  readonly properties: Properties;
54
49
  readonly methods: Kind extends 'node' ? InstanceMethods : ResolvedMethodCollection<Record<never, never>>;
@@ -58,8 +53,6 @@ export interface ResolvedClass<Kind extends ClassKind = ClassKind, Properties ex
58
53
  readonly self: {
59
54
  readonly data: Kind extends 'node' ? ResolvedData | undefined : undefined;
60
55
  readonly properties: SelfProperties;
61
- /** Policies declared by this exact Class only. */
62
- readonly policies: Definition['policies'];
63
56
  readonly methods: Kind extends 'node' ? SelfInstanceMethods : ResolvedMethodCollection<Record<never, never>>;
64
57
  readonly static: {
65
58
  readonly methods: Kind extends 'node' ? SelfStaticMethods : ResolvedMethodCollection<Record<never, never>>;
@@ -15,6 +15,10 @@ export function validateDefinitions(environment, analysis) {
15
15
  if (!sameRef(declaration.ref, ref)) {
16
16
  diagnostics.push(diagnostic('DV_REF_RESOLVES', `${pointer}/ref`, `Class ref must be exactly ${formatDefinitionRef(ref)}.`, formatDefinitionRef(declaration.ref)));
17
17
  }
18
+ if (declaration.abstract &&
19
+ (declaration.policies.read !== undefined || declaration.policies.traverse !== undefined)) {
20
+ diagnostics.push(diagnostic('DV_CLASS_ABSTRACT', `${pointer}/policies`, 'Abstract Classes cannot declare observation Policies.'));
21
+ }
18
22
  validateFacet(environment, declaration, pointer, diagnostics);
19
23
  validateParents(environment, ref, declaration.kind, declaration.extends, diagnostics);
20
24
  if (declaration.kind === 'edge') {
@@ -159,27 +163,7 @@ function edgeContractRefines(environment, child, parent) {
159
163
  const direct = refines(0, 0) && refines(1, 1);
160
164
  if (direct)
161
165
  return true;
162
- const reverse = refines(0, 1) && refines(1, 0);
163
- return reverse && !hasEffectiveEdgePolicy(environment, parent, new Set());
164
- }
165
- function hasEffectiveEdgePolicy(environment, declaration, visited) {
166
- if (declaration.policies.read !== undefined || declaration.policies.traverse !== undefined) {
167
- return true;
168
- }
169
- for (const parentRef of declaration.extends) {
170
- const key = definitionRefIndex(parentRef);
171
- if (visited.has(key))
172
- continue;
173
- visited.add(key);
174
- const parent = environment.resolveAny(parentRef);
175
- if (parent !== undefined &&
176
- parent.ref.kind === 'class' &&
177
- parent.declaration.kind === 'edge' &&
178
- hasEffectiveEdgePolicy(environment, parent.declaration, visited)) {
179
- return true;
180
- }
181
- }
182
- return false;
166
+ return refines(0, 1) && refines(1, 0);
183
167
  }
184
168
  function endpointRefines(environment, child, parent) {
185
169
  return child.accepts.every((accepted) => classSatisfies(environment, accepted, parent.accepts));
@@ -122,7 +122,14 @@ function visitChecks(validation, typing, expression, context, pointer) {
122
122
  }
123
123
  function referenceContracts(validation, ref, pointer) {
124
124
  const candidate = validation.environment.candidate;
125
- if (ref.origin !== candidate.origin)
125
+ const local = ref.origin === candidate.origin;
126
+ const direct = Object.values(candidate.dependencies).some((entry) => entry.origin === ref.origin);
127
+ const owner = local
128
+ ? candidate
129
+ : direct
130
+ ? validation.environment.dependencies.get(ref.origin)
131
+ : undefined;
132
+ if (owner === undefined)
126
133
  return invalidPolicyObjectRef(validation, ref, pointer);
127
134
  switch (ref.kind) {
128
135
  case 'class': {
@@ -132,26 +139,26 @@ function referenceContracts(validation, ref, pointer) {
132
139
  : invalidPolicyObjectRef(validation, ref, pointer);
133
140
  }
134
141
  case 'function':
135
- return candidate.functions[ref.name] === undefined
142
+ return owner.functions[ref.name] === undefined
136
143
  ? invalidPolicyObjectRef(validation, ref, pointer)
137
144
  : [{ kind: 'exact', ref: PROJECTION_FUNCTION }];
138
145
  case 'policy':
139
- return candidate.policies[ref.name] === undefined
146
+ return owner.policies[ref.name] === undefined
140
147
  ? invalidPolicyObjectRef(validation, ref, pointer)
141
148
  : [{ kind: 'exact', ref: PROJECTION_POLICY }];
142
149
  case 'view':
143
- return candidate.views[ref.name] === undefined
150
+ return owner.views[ref.name] === undefined
144
151
  ? invalidPolicyObjectRef(validation, ref, pointer)
145
152
  : [{ kind: 'exact', ref: PROJECTION_VIEW }];
146
153
  case 'core': {
147
- const node = candidate.core.nodes[ref.name];
154
+ const node = owner.core.nodes[ref.name];
148
155
  if (node === undefined)
149
156
  return invalidPolicyObjectRef(validation, ref, pointer);
150
157
  const resolved = validation.environment.resolve(node.class);
151
158
  const analyzed = validation.analysis.definitions.get(definitionRefIndex(node.class));
152
159
  // Core validation owns invalid Core Class diagnostics. Avoid adding Policy noise for
153
160
  // a Node that cannot materialize in the first place.
154
- if (node.class.origin !== candidate.origin ||
161
+ if (node.class.origin !== owner.origin ||
155
162
  resolved?.ref.kind !== 'class' ||
156
163
  resolved.kind !== 'node' ||
157
164
  analyzed === undefined) {
@@ -163,7 +170,7 @@ function referenceContracts(validation, ref, pointer) {
163
170
  return invalidPolicyObjectRef(validation, ref, pointer);
164
171
  }
165
172
  function invalidPolicyObjectRef(validation, ref, pointer) {
166
- validation.add('PL_REF', `${pointer}/ref`, `${formatDefinitionRef(ref)} is not an exact local projected Policy object.`, formatDefinitionRef(ref));
173
+ validation.add('PL_REF', `${pointer}/ref`, `${formatDefinitionRef(ref)} is not an exact local or direct-dependency projected Policy object.`, formatDefinitionRef(ref));
167
174
  return undefined;
168
175
  }
169
176
  function selfContracts(validation, context, pointer) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-dsl",
3
- "version": "0.2.0-beta.27",
3
+ "version": "0.2.0-beta.29",
4
4
  "description": "Astrale Kernel DSL - Schema authoring, inference, and IR serialization",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {