@astrale-os/kernel-dsl 0.2.0-beta.33 → 0.2.0-beta.34

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.
@@ -3,5 +3,8 @@ export interface IRegexpMatcher {
3
3
  test(value: string): boolean;
4
4
  matches(value: string): boolean;
5
5
  }
6
- /** Load portable NFA data without a regex engine or dynamic compilation. */
6
+ /**
7
+ * Load the portable Thompson NFA executor shared by search and whole-string matching.
8
+ * Scratch belongs to one invocation: no match-result cache, shared mutable cursor or DFA expansion.
9
+ */
7
10
  export declare function loadPattern(pattern: Pattern): IRegexpMatcher;
@@ -1,50 +1,135 @@
1
- /** Load portable NFA data without a regex engine or dynamic compilation. */
1
+ /**
2
+ * Load the portable Thompson NFA executor shared by search and whole-string matching.
3
+ * Scratch belongs to one invocation: no match-result cache, shared mutable cursor or DFA expansion.
4
+ */
2
5
  export function loadPattern(pattern) {
3
- const start = closure(pattern.states, new Set([pattern.start]));
6
+ const states = pattern.states;
7
+ const start = initialFrontier(pattern);
4
8
  const run = (input, search) => {
5
- let active = new Set(start);
6
- if ((search || input.length === 0) && accepts(pattern.states, active))
9
+ if (input.length === 0)
10
+ return start.accepting;
11
+ if (search && start.accepting)
7
12
  return true;
13
+ const resolve = executionStates(states);
14
+ // Initial frontier entries consume characters, so none can be a dead conduit cycle.
15
+ const initial = start.frontier.map((index) => resolve(index));
16
+ let active = [...initial];
17
+ let next = [];
18
+ const pending = [];
19
+ let activeCount = start.frontier.length;
20
+ let nextCount = 0;
21
+ let epoch = 0;
22
+ let accepting = false;
23
+ function add(destination) {
24
+ const seed = resolve(destination);
25
+ if (seed === null || seed.epoch === epoch)
26
+ return;
27
+ let count = 1;
28
+ pending[0] = seed;
29
+ seed.epoch = epoch;
30
+ while (count > 0) {
31
+ const entry = pending[--count];
32
+ const state = entry.state;
33
+ if (state.accepting === true)
34
+ accepting = true;
35
+ if (state.transitions.length > 0)
36
+ next[nextCount++] = entry;
37
+ for (const destination of state.epsilon) {
38
+ const target = resolve(destination);
39
+ if (target === null || target.epoch === epoch)
40
+ continue;
41
+ target.epoch = epoch;
42
+ pending[count++] = target;
43
+ }
44
+ }
45
+ }
8
46
  for (const character of input) {
47
+ if (!search && activeCount === 0)
48
+ return false;
49
+ epoch++;
50
+ nextCount = 0;
51
+ accepting = false;
9
52
  const scalar = character.codePointAt(0);
10
- const next = new Set();
11
- for (const index of active) {
12
- for (const transition of pattern.states[index].transitions) {
13
- if (transition.ranges.some(([minimum, maximum]) => scalar >= minimum && scalar <= maximum)) {
14
- next.add(transition.to);
53
+ for (let index = 0; index < activeCount; index++) {
54
+ for (const transition of active[index].state.transitions) {
55
+ for (const [minimum, maximum] of transition.ranges) {
56
+ if (scalar < minimum || scalar > maximum)
57
+ continue;
58
+ add(transition.to);
59
+ break;
15
60
  }
16
61
  }
17
62
  }
18
- active = closure(pattern.states, next);
19
- if (search)
20
- for (const index of start)
21
- active.add(index);
22
- if (search && accepts(pattern.states, active))
23
- return true;
63
+ if (search) {
64
+ if (accepting)
65
+ return true;
66
+ // The complete initial epsilon closure is already known; do not traverse it again.
67
+ for (const entry of initial) {
68
+ if (entry.epoch === epoch)
69
+ continue;
70
+ entry.epoch = epoch;
71
+ next[nextCount++] = entry;
72
+ }
73
+ }
74
+ const previous = active;
75
+ active = next;
76
+ next = previous;
77
+ activeCount = nextCount;
24
78
  }
25
- return accepts(pattern.states, active);
79
+ return accepting;
26
80
  };
27
81
  return Object.freeze({
28
82
  test: (input) => run(input, true),
29
83
  matches: (input) => run(input, false),
30
84
  });
31
85
  }
32
- function closure(states, seed) {
33
- const output = new Set(seed);
34
- const pending = [...seed];
86
+ function initialFrontier(pattern) {
87
+ const start = pattern.start;
88
+ const seen = new Set([start]);
89
+ const pending = [start];
90
+ const frontier = [];
91
+ let accepting = false;
35
92
  while (pending.length > 0) {
36
- for (const next of states[pending.pop()].epsilon) {
37
- if (output.has(next))
93
+ const index = pending.pop();
94
+ const state = pattern.states[index];
95
+ if (state.accepting === true)
96
+ accepting = true;
97
+ if (state.transitions.length > 0)
98
+ frontier.push(index);
99
+ for (const target of state.epsilon) {
100
+ if (seen.has(target))
38
101
  continue;
39
- output.add(next);
40
- pending.push(next);
102
+ seen.add(target);
103
+ pending.push(target);
41
104
  }
42
105
  }
43
- return output;
106
+ return { frontier: Object.freeze(frontier), accepting };
44
107
  }
45
- function accepts(states, active) {
46
- for (const index of active)
47
- if (states[index]?.accepting === true)
48
- return true;
49
- return false;
108
+ /** Resolve only visited conduits, once within this invocation; never retain cross-call state. */
109
+ function executionStates(states) {
110
+ const destinations = new Map();
111
+ return (index) => {
112
+ const known = destinations.get(index);
113
+ if (known !== undefined)
114
+ return known;
115
+ const path = [];
116
+ let current = index;
117
+ let destination = destinations.get(current);
118
+ while (destination === undefined) {
119
+ const state = states[current];
120
+ if (state.accepting === true || state.transitions.length > 0 || state.epsilon.length !== 1) {
121
+ destination = { state, epoch: 0 };
122
+ destinations.set(current, destination);
123
+ break;
124
+ }
125
+ // A revisit before resolving the path is a dead, epsilon-only cycle.
126
+ destinations.set(current, null);
127
+ path.push(current);
128
+ current = state.epsilon[0];
129
+ destination = destinations.get(current);
130
+ }
131
+ for (const member of path)
132
+ destinations.set(member, destination);
133
+ return destination;
134
+ };
50
135
  }
@@ -2,16 +2,31 @@ import type { Key as SemanticKey } from '../../addressing/index.js';
2
2
  import type { Revision } from '../../addressing/index.js';
3
3
  import type { DomainSchema } from '../model/schema.js';
4
4
  import { Key } from '../../addressing/index.js';
5
- export type DependencyMeaningChangeKind = 'missing' | 'changed';
5
+ /**
6
+ * How strongly a dependent relies on a dependency Class. `inherit` covers `extends` and Core
7
+ * instantiation, which take on the Class creation contract; `reference` covers every other site,
8
+ * which only names the Class and reads its surface.
9
+ */
10
+ export type DependencyMeaningLevel = 'reference' | 'inherit';
11
+ export type DependencyMeaningChangeKind = 'missing' | 'changed' | 'added';
6
12
  export type MeaningFingerprint = `sha256:${string}`;
7
- export interface DependencyMeaningChange {
13
+ export type DependencyMeaningChange = {
8
14
  readonly key: SemanticKey;
9
- readonly kind: DependencyMeaningChangeKind;
15
+ readonly kind: 'missing';
10
16
  readonly sourceFingerprint: MeaningFingerprint;
11
- readonly targetFingerprint?: MeaningFingerprint;
12
- }
17
+ } | {
18
+ readonly key: SemanticKey;
19
+ readonly kind: 'changed';
20
+ readonly sourceFingerprint: MeaningFingerprint;
21
+ readonly targetFingerprint: MeaningFingerprint;
22
+ } | {
23
+ /** A required, non-automatic Property the target adds to an inherited Class. */
24
+ readonly key: SemanticKey;
25
+ readonly kind: 'added';
26
+ readonly targetFingerprint: MeaningFingerprint;
27
+ };
13
28
  export interface DependencyMeaningComparison {
14
- readonly version: 1;
29
+ readonly version: 2;
15
30
  readonly dependency: Key.Origin;
16
31
  readonly source: Revision;
17
32
  readonly target: Revision;
@@ -1,6 +1,6 @@
1
1
  import { json } from '@astrale-os/kernel-dsl/value';
2
2
  import { Key, MethodKey, PropertyKey } from '../../addressing/index.js';
3
- import { compareDefinitionRefs, compareUnicode, definitionRefIndex, } from '../../addressing/ordering.js';
3
+ import { compareUnicode, definitionRefIndex } from '../../addressing/ordering.js';
4
4
  import { isValueObject as isJsonObject } from '../../language/value/model/object.js';
5
5
  import { SCHEMA_ARRAY_KEYWORDS, SCHEMA_MAP_KEYWORDS, SCHEMA_SINGLE_KEYWORDS, } from '../../language/value/syntax/keywords.js';
6
6
  import { digestDomainSchemaBytes } from '../canonical/domain.digest.js';
@@ -8,7 +8,7 @@ import { revisionOfDomainSchema } from '../canonical/revision.js';
8
8
  import { SchemaError, diagnostic } from '../diagnostic.js';
9
9
  import { knownDomainClosure, lookupForDomainClosure } from '../resolution/closure.js';
10
10
  import { resolve } from '../resolution/domain.js';
11
- import { collectCallableRefs, collectClassRefs, collectPolicyDefinitionRefs, collectValueDefinitionRefs, } from '../resolution/origins.js';
11
+ import { collectCallableRefs, collectClassPolicyRefs, collectClassRefs, collectCoreRefs, collectEndpointRefs, collectPolicyDefinitionRefs, collectValueDefinitionRefs, } from '../resolution/origins.js';
12
12
  import { validateDomainSchemaSemantics } from '../validation/domain.js';
13
13
  const VALUE_SCHEMA_ANNOTATIONS = new Set([
14
14
  '$comment',
@@ -31,19 +31,15 @@ const VALUE_SCHEMA_ANNOTATIONS = new Set([
31
31
  */
32
32
  export function compareDependencyMeaning(dependent, target) {
33
33
  resolve(dependent);
34
- const closure = knownDomainClosure(dependent) ?? [];
35
- const source = closure.find((candidate) => candidate.schema.origin === target.origin)?.schema;
34
+ const context = contextOf(dependent);
35
+ const source = (knownDomainClosure(dependent) ?? []).find((candidate) => candidate.schema.origin === target.origin)?.schema;
36
36
  if (source === undefined) {
37
37
  throw new SchemaError([
38
38
  diagnostic('DM_DEPENDENCY_NOT_RETAINED', '/target/origin', `Domain ${dependent.origin} was not admitted with a dependency at ${target.origin}.`),
39
39
  ]);
40
40
  }
41
- const schemas = new Map([
42
- [dependent.origin, dependent],
43
- ...closure.map(({ schema: candidate }) => [candidate.origin, candidate]),
44
- ]);
45
- const sourceMeanings = collectDependencyMeanings(dependent, source.origin, schemas);
46
- return compareSourceMeanings(source, target, sourceMeanings);
41
+ const meanings = collectReachableMeanings(collectSchemaRoots(dependent), source.origin, context);
42
+ return compareSourceMeanings(source, target, meanings);
47
43
  }
48
44
  /** Compare one explicit non-empty source member surface and its reachable V1 meaning. */
49
45
  export function compareMemberMeaning(source, target, roots) {
@@ -58,24 +54,38 @@ export function compareMemberMeaning(source, target, roots) {
58
54
  ]);
59
55
  }
60
56
  resolve(source);
61
- const closure = knownDomainClosure(source) ?? [];
57
+ const context = contextOf(source);
58
+ const coordinates = roots.map((key, index) => coordinateForKey(context, source, key, index));
59
+ const meanings = collectReachableMeanings(coordinates, source.origin, context);
60
+ return compareSourceMeanings(source, target, meanings);
61
+ }
62
+ function contextOf(root) {
62
63
  const schemas = new Map([
63
- [source.origin, source],
64
- ...closure.map(({ schema: candidate }) => [candidate.origin, candidate]),
64
+ [root.origin, root],
65
+ ...(knownDomainClosure(root) ?? []).map(({ schema }) => [schema.origin, schema]),
65
66
  ]);
66
- const coordinates = roots.map((key, index) => coordinateForKey(source, key, index));
67
- const sourceMeanings = collectReachableMeanings(coordinates, source.origin, schemas);
68
- return compareSourceMeanings(source, target, sourceMeanings);
67
+ const analyses = new Map();
68
+ return {
69
+ schemas,
70
+ analysis(owner) {
71
+ if (!analyses.has(owner)) {
72
+ const validation = validateDomainSchemaSemantics(owner, lookupForDomainClosure(owner));
73
+ analyses.set(owner, validation.diagnostics.length > 0 ? undefined : validation.analysis.definitions);
74
+ }
75
+ return analyses.get(owner);
76
+ },
77
+ };
69
78
  }
70
79
  function compareSourceMeanings(source, target, sourceMeanings) {
71
80
  // Prove the candidate still has an exact accepted semantic context before comparing it.
72
81
  resolve(target);
82
+ const context = contextOf(target);
73
83
  const footprint = Object.freeze([...sourceMeanings.keys()].sort(compareUnicode));
74
84
  const changes = [];
75
85
  const comparisonCoordinates = [];
76
86
  for (const key of footprint) {
77
87
  const sourceMeaning = sourceMeanings.get(key);
78
- const targetMeaning = meaningForCoordinate(target, sourceMeaning.coordinate);
88
+ const targetMeaning = meaningForCoordinate(context, sourceMeaning.coordinate, sourceMeaning.requirement);
79
89
  const targetFingerprint = targetMeaning === undefined ? undefined : fingerprintMeaning(targetMeaning);
80
90
  comparisonCoordinates.push([
81
91
  key,
@@ -83,11 +93,7 @@ function compareSourceMeanings(source, target, sourceMeanings) {
83
93
  targetFingerprint ?? null,
84
94
  ]);
85
95
  if (targetFingerprint === undefined) {
86
- changes.push(Object.freeze({
87
- key,
88
- kind: 'missing',
89
- sourceFingerprint: sourceMeaning.fingerprint,
90
- }));
96
+ changes.push(Object.freeze({ key, kind: 'missing', sourceFingerprint: sourceMeaning.fingerprint }));
91
97
  }
92
98
  else if (targetFingerprint !== sourceMeaning.fingerprint) {
93
99
  changes.push(Object.freeze({
@@ -98,177 +104,214 @@ function compareSourceMeanings(source, target, sourceMeanings) {
98
104
  }));
99
105
  }
100
106
  }
107
+ const additions = addedObligations(context, sourceMeanings);
101
108
  const sourceRevision = revisionOfDomainSchema(source);
102
109
  const targetRevision = revisionOfDomainSchema(target);
103
- const immutableChanges = Object.freeze(changes);
104
110
  const fingerprint = fingerprintMeaning({
105
- version: 1,
111
+ version: 2,
106
112
  dependency: source.origin,
107
113
  source: sourceRevision,
108
114
  target: targetRevision,
109
115
  coordinates: comparisonCoordinates,
116
+ additions: additions.map(({ key, targetFingerprint }) => [key, targetFingerprint]),
110
117
  });
111
118
  return Object.freeze({
112
- version: 1,
119
+ version: 2,
113
120
  dependency: source.origin,
114
121
  source: sourceRevision,
115
122
  target: targetRevision,
116
123
  footprint,
117
- changes: immutableChanges,
124
+ changes: Object.freeze([...changes, ...additions]),
118
125
  fingerprint,
119
126
  });
120
127
  }
121
- function collectDependencyMeanings(dependent, dependencyOrigin, schemas) {
122
- return collectReachableMeanings(collectSchemaSemanticRefs(dependent).map(memberCoordinate), dependencyOrigin, schemas);
128
+ /** A target may extend an inherited Class only with optional or automatic Properties. */
129
+ function addedObligations(context, sourceMeanings) {
130
+ const additions = new Map();
131
+ for (const { coordinate, requirement } of sourceMeanings.values()) {
132
+ if (coordinate.kind !== 'class' || coordinate.level !== 'inherit' || requirement === undefined)
133
+ continue;
134
+ const owner = context.schemas.get(coordinate.ref.origin);
135
+ const analysis = owner === undefined ? undefined : effectiveDefinition(context, owner, coordinate.ref);
136
+ if (analysis === undefined)
137
+ continue;
138
+ for (const field of analysis.properties.values()) {
139
+ if (requirement.properties.has(field.identity) || !field.required || field.auto !== undefined)
140
+ continue;
141
+ additions.set(field.identity, Object.freeze({
142
+ key: field.identity,
143
+ kind: 'added',
144
+ targetFingerprint: fingerprintMeaning(propertyMeaning(field)),
145
+ }));
146
+ }
147
+ }
148
+ return [...additions.values()].sort((left, right) => compareUnicode(left.key, right.key));
149
+ }
150
+ function collectSchemaRoots(owner) {
151
+ const classes = new Map();
152
+ const members = new Map();
153
+ const add = (ref, site) => {
154
+ if (ref.kind === 'class') {
155
+ const index = definitionRefIndex(ref);
156
+ const level = levelOf(site);
157
+ const previous = classes.get(index);
158
+ if (previous === undefined || (previous.level === 'reference' && level === 'inherit')) {
159
+ classes.set(index, classCoordinate(ref, level));
160
+ }
161
+ return;
162
+ }
163
+ members.set(definitionRefIndex(ref), memberCoordinate(ref));
164
+ };
165
+ for (const declaration of Object.values(owner.classes))
166
+ collectClassRefs(declaration, add);
167
+ for (const declaration of Object.values(owner.functions))
168
+ collectCallableRefs(declaration, add);
169
+ for (const declaration of Object.values(owner.policies)) {
170
+ collectPolicyDefinitionRefs(declaration, add);
171
+ }
172
+ for (const declaration of Object.values(owner.views)) {
173
+ if (declaration.target.kind === 'definition') {
174
+ declaration.target.definitions.forEach((ref) => add(ref, 'view'));
175
+ }
176
+ }
177
+ collectCoreRefs(owner, add);
178
+ return [...classes.values(), ...members.values()].sort((left, right) => compareUnicode(left.key, right.key));
123
179
  }
124
- function collectReachableMeanings(roots, selectedOrigin, schemas) {
180
+ function collectReachableMeanings(roots, selectedOrigin, context) {
125
181
  const meanings = new Map();
126
- const pending = [];
127
- const visited = new Set();
182
+ const classLevels = new Map();
183
+ const visitedMembers = new Set();
184
+ const pendingClasses = [];
185
+ const pendingMembers = [];
186
+ const push = (ref, site) => {
187
+ if (ref.kind === 'class')
188
+ pendingClasses.push(classCoordinate(ref, levelOf(site)));
189
+ else
190
+ pendingMembers.push(memberCoordinate(ref));
191
+ };
128
192
  for (const coordinate of roots) {
193
+ if (coordinate.kind === 'class') {
194
+ pendingClasses.push(coordinate);
195
+ continue;
196
+ }
129
197
  if (coordinate.kind === 'member') {
130
- pending.push(coordinate.ref);
198
+ pendingMembers.push(coordinate);
131
199
  continue;
132
200
  }
133
- const owner = schemas.get(Key.origin(coordinate.key));
134
- const meaning = owner === undefined ? undefined : meaningForCoordinate(owner, coordinate);
135
- if (owner === undefined || meaning === undefined) {
201
+ const meaning = meaningForCoordinate(context, coordinate);
202
+ if (meaning === undefined) {
136
203
  throw new SchemaError([
137
204
  diagnostic('DM_MEMBER_ROOT_INVALID', '/roots', `Member comparison root ${coordinate.key} does not exist in its source context.`, coordinate.key),
138
205
  ]);
139
206
  }
140
207
  retainMeaning(meanings, selectedOrigin, coordinate, meaning);
141
- collectCoordinateSemanticRefs(owner, coordinate, (candidate) => pending.push(requireMemberRef(candidate)));
208
+ collectCoordinateSemanticRefs(context, coordinate, push);
142
209
  }
143
- for (let index = 0; index < pending.length; index += 1) {
144
- const ref = pending[index];
145
- const refIndex = definitionRefIndex(ref);
146
- if (visited.has(refIndex))
147
- continue;
148
- visited.add(refIndex);
149
- const owner = schemas.get(ref.origin);
150
- if (owner === undefined) {
151
- throw new SchemaError([
152
- diagnostic('DM_DEPENDENCY_CONTEXT_INVALID', '', `Retained acceptance context cannot resolve ${keyForDefinitionRef(ref)}.`, keyForDefinitionRef(ref)),
153
- ]);
210
+ const visitClass = (coordinate) => {
211
+ const index = definitionRefIndex(coordinate.ref);
212
+ const previous = classLevels.get(index);
213
+ if (previous === 'inherit' || previous === coordinate.level)
214
+ return;
215
+ classLevels.set(index, coordinate.level);
216
+ const owner = requireOwner(context, coordinate.ref, coordinate.key);
217
+ const declaration = owner.classes[coordinate.ref.name];
218
+ const analysis = effectiveDefinition(context, owner, coordinate.ref);
219
+ const resolved = classMeaning(context, coordinate);
220
+ if (declaration === undefined || analysis === undefined || resolved === undefined) {
221
+ throw contextInvalid(`Retained acceptance context has no meaning for ${coordinate.key}.`, coordinate.key);
154
222
  }
155
- const primary = memberCoordinate(ref);
156
- const primaryMeaning = meaningForCoordinate(owner, primary);
157
- if (primaryMeaning === undefined) {
158
- throw new SchemaError([
159
- diagnostic('DM_DEPENDENCY_CONTEXT_INVALID', '', `Retained acceptance context has no meaning for ${primary.key}.`, primary.key),
160
- ]);
223
+ retainMeaning(meanings, selectedOrigin, coordinate, resolved.meaning, resolved.requirement);
224
+ if (previous === undefined) {
225
+ // The surface is level-independent: every effective Property and Method the dependent may
226
+ // read or call, with the Definitions their contracts and policies name.
227
+ for (const field of analysis.properties.values()) {
228
+ retainMeaning(meanings, selectedOrigin, propertyCoordinate(field.owner, field.name), propertyMeaning(field));
229
+ collectValueDefinitionRefs(field.schema, push);
230
+ }
231
+ for (const method of analysis.methods.values()) {
232
+ retainMeaning(meanings, selectedOrigin, methodCoordinate(method.owner, method.name), methodMeaning(method));
233
+ collectCallableRefs(method.declaration, push);
234
+ }
235
+ collectClassPolicyRefs(declaration, push);
161
236
  }
162
- retainMeaning(meanings, selectedOrigin, primary, primaryMeaning);
163
- collectMemberSemanticRefs(owner, ref, (candidate) => pending.push(requireMemberRef(candidate)));
164
- if (!isDefinitionRef(ref))
165
- continue;
166
- const analysis = effectiveDefinition(owner, ref);
167
- if (analysis === undefined)
168
- continue;
169
- collectEffectiveMeanings(analysis, selectedOrigin, meanings, (candidate) => pending.push(requireMemberRef(candidate)));
237
+ if (coordinate.level === 'inherit') {
238
+ // Only inheritance takes on the ancestry and, for an Edge, the endpoint contract.
239
+ declaration.extends.forEach((parent) => push(parent, 'extends'));
240
+ if (declaration.kind === 'edge')
241
+ collectEndpointRefs(declaration, push);
242
+ }
243
+ };
244
+ const visitMember = (coordinate) => {
245
+ const index = definitionRefIndex(coordinate.ref);
246
+ if (visitedMembers.has(index))
247
+ return;
248
+ visitedMembers.add(index);
249
+ const owner = requireOwner(context, coordinate.ref, coordinate.key);
250
+ const meaning = memberMeaning(owner, coordinate.ref);
251
+ if (meaning === undefined) {
252
+ throw contextInvalid(`Retained acceptance context has no meaning for ${coordinate.key}.`, coordinate.key);
253
+ }
254
+ retainMeaning(meanings, selectedOrigin, coordinate, meaning);
255
+ collectMemberSemanticRefs(owner, coordinate.ref, push);
256
+ };
257
+ let classIndex = 0;
258
+ let memberIndex = 0;
259
+ while (classIndex < pendingClasses.length || memberIndex < pendingMembers.length) {
260
+ if (classIndex < pendingClasses.length)
261
+ visitClass(pendingClasses[classIndex++]);
262
+ else
263
+ visitMember(pendingMembers[memberIndex++]);
170
264
  }
171
265
  return meanings;
172
266
  }
173
- function collectCoordinateSemanticRefs(owner, coordinate, add) {
174
- const analysis = effectiveDefinition(owner, coordinate.owner);
267
+ function requireOwner(context, ref, key) {
268
+ const owner = context.schemas.get(ref.origin);
269
+ if (owner === undefined) {
270
+ throw contextInvalid(`Retained acceptance context cannot resolve ${key}.`, key);
271
+ }
272
+ return owner;
273
+ }
274
+ function contextInvalid(message, key) {
275
+ return new SchemaError([diagnostic('DM_DEPENDENCY_CONTEXT_INVALID', '', message, key)]);
276
+ }
277
+ function collectCoordinateSemanticRefs(context, coordinate, add) {
278
+ const owner = context.schemas.get(coordinate.owner.origin);
279
+ const analysis = owner === undefined ? undefined : effectiveDefinition(context, owner, coordinate.owner);
175
280
  if (analysis === undefined)
176
281
  return;
177
282
  if (coordinate.kind === 'property') {
178
283
  const field = analysis.properties.get(coordinate.key);
179
284
  if (field !== undefined)
180
- collectValueDefinitionRefs(semanticValueSchema(field.schema), add);
285
+ collectValueDefinitionRefs(field.schema, add);
181
286
  return;
182
287
  }
183
288
  const method = [...analysis.methods.values()].find((candidate) => MethodKey.of(candidate.owner, candidate.name) === coordinate.key);
184
289
  if (method !== undefined)
185
- collectCallableRefs(semanticMethod(method.declaration), add);
186
- }
187
- function collectEffectiveMeanings(analysis, dependencyOrigin, meanings, add) {
188
- for (const field of analysis.properties.values()) {
189
- const coordinate = propertyCoordinate(field.owner, field.name);
190
- retainMeaning(meanings, dependencyOrigin, coordinate, propertyMeaning(field));
191
- add(field.owner);
192
- collectValueDefinitionRefs(semanticValueSchema(field.schema), add);
193
- }
194
- for (const method of analysis.methods.values()) {
195
- const coordinate = methodCoordinate(method.owner, method.name);
196
- retainMeaning(meanings, dependencyOrigin, coordinate, methodMeaning(method));
197
- add(method.owner);
198
- collectCallableRefs(semanticMethod(method.declaration), add);
199
- }
200
- }
201
- function retainMeaning(meanings, dependencyOrigin, coordinate, meaning) {
202
- if (Key.origin(coordinate.key) !== dependencyOrigin)
203
- return;
204
- const fingerprint = fingerprintMeaning(meaning);
205
- const previous = meanings.get(coordinate.key);
206
- if (previous !== undefined && previous.fingerprint !== fingerprint) {
207
- throw new SchemaError([
208
- diagnostic('DM_MEANING_INCONSISTENT', '', `Semantic coordinate ${coordinate.key} produced inconsistent effective meaning.`, coordinate.key),
209
- ]);
210
- }
211
- meanings.set(coordinate.key, Object.freeze({ coordinate, fingerprint }));
212
- }
213
- function collectSchemaSemanticRefs(owner) {
214
- const refs = new Map();
215
- const add = (ref) => {
216
- const member = requireMemberRef(ref);
217
- refs.set(definitionRefIndex(member), member);
218
- };
219
- for (const declaration of Object.values(owner.classes)) {
220
- collectClassRefs(semanticDefinition(declaration), add);
221
- }
222
- for (const declaration of Object.values(owner.functions)) {
223
- collectCallableRefs(semanticFunction(declaration), add);
224
- }
225
- for (const declaration of Object.values(owner.policies)) {
226
- collectPolicyDefinitionRefs(semanticPolicy(declaration), add);
227
- }
228
- for (const declaration of Object.values(owner.views)) {
229
- if (declaration.target.kind === 'definition')
230
- declaration.target.definitions.forEach(add);
231
- }
232
- for (const node of Object.values(owner.core.nodes))
233
- add(node.class);
234
- for (const edge of owner.core.edges) {
235
- add(edge.class);
236
- if (edge.source.kind !== 'domain')
237
- add(edge.source);
238
- if (edge.target.kind !== 'domain')
239
- add(edge.target);
240
- }
241
- return [...refs.values()].sort(compareDefinitionRefs);
290
+ collectCallableRefs(method.declaration, add);
242
291
  }
243
292
  function collectMemberSemanticRefs(owner, ref, add) {
244
293
  switch (ref.kind) {
245
- case 'class': {
246
- const declaration = owner.classes[ref.name];
247
- if (declaration !== undefined) {
248
- collectClassRefs(semanticDefinition(declaration), add);
249
- }
250
- return;
251
- }
252
294
  case 'function': {
253
295
  const declaration = owner.functions[ref.name];
254
296
  if (declaration !== undefined)
255
- collectCallableRefs(semanticFunction(declaration), add);
297
+ collectCallableRefs(declaration, add);
256
298
  return;
257
299
  }
258
300
  case 'policy': {
259
301
  const declaration = owner.policies[ref.name];
260
- if (declaration !== undefined) {
261
- collectPolicyDefinitionRefs(semanticPolicy(declaration), add);
262
- }
302
+ if (declaration !== undefined)
303
+ collectPolicyDefinitionRefs(declaration, add);
263
304
  return;
264
305
  }
265
306
  case 'view': {
266
307
  const declaration = owner.views[ref.name];
267
- if (declaration?.target.kind === 'definition')
268
- declaration.target.definitions.forEach(add);
308
+ if (declaration?.target.kind === 'definition') {
309
+ declaration.target.definitions.forEach((definition) => add(definition, 'view'));
310
+ }
269
311
  return;
270
312
  }
271
313
  case 'core': {
314
+ // The dependency instantiates its own Core; the dependent only refers to it.
272
315
  const node = owner.core.nodes[ref.name];
273
316
  if (node === undefined)
274
317
  return;
@@ -283,12 +326,41 @@ function collectMemberSemanticRefs(owner, ref, add) {
283
326
  }
284
327
  }
285
328
  }
286
- function meaningForCoordinate(owner, coordinate) {
287
- if (Key.origin(coordinate.key) !== owner.origin)
329
+ function retainMeaning(meanings, selectedOrigin, coordinate, meaning, requirement) {
330
+ if (Key.origin(coordinate.key) !== selectedOrigin)
331
+ return;
332
+ const fingerprint = fingerprintMeaning(meaning);
333
+ const previous = meanings.get(coordinate.key);
334
+ if (previous !== undefined) {
335
+ const upgraded = previous.coordinate.kind === 'class' &&
336
+ coordinate.kind === 'class' &&
337
+ previous.coordinate.level !== coordinate.level;
338
+ if (upgraded) {
339
+ // A stronger requirement replaces the weaker one for the same Class.
340
+ if (coordinate.level === 'reference')
341
+ return;
342
+ }
343
+ else if (previous.fingerprint !== fingerprint) {
344
+ throw new SchemaError([
345
+ diagnostic('DM_MEANING_INCONSISTENT', '', `Semantic coordinate ${coordinate.key} produced inconsistent effective meaning.`, coordinate.key),
346
+ ]);
347
+ }
348
+ }
349
+ meanings.set(coordinate.key, Object.freeze({
350
+ coordinate,
351
+ fingerprint,
352
+ ...(requirement === undefined ? {} : { requirement }),
353
+ }));
354
+ }
355
+ function meaningForCoordinate(context, coordinate, requirement) {
356
+ const owner = context.schemas.get(Key.origin(coordinate.key));
357
+ if (owner === undefined)
288
358
  return undefined;
359
+ if (coordinate.kind === 'class')
360
+ return classMeaning(context, coordinate, requirement)?.meaning;
289
361
  if (coordinate.kind === 'member')
290
362
  return memberMeaning(owner, coordinate.ref);
291
- const analysis = effectiveDefinition(owner, coordinate.owner);
363
+ const analysis = effectiveDefinition(context, owner, coordinate.owner);
292
364
  if (analysis === undefined)
293
365
  return undefined;
294
366
  if (coordinate.kind === 'property') {
@@ -298,12 +370,69 @@ function meaningForCoordinate(owner, coordinate) {
298
370
  const method = [...analysis.methods.values()].find((candidate) => MethodKey.of(candidate.owner, candidate.name) === coordinate.key);
299
371
  return method === undefined ? undefined : methodMeaning(method);
300
372
  }
373
+ /**
374
+ * The requirement one usage level places on a Class. A reference needs the Class to exist with
375
+ * its kind; inheritance additionally needs the source ancestors, class policies, data contract
376
+ * and, for an Edge, the endpoint contract. A target projected onto a source requirement keeps only
377
+ * the ancestors the source named, so a superset satisfies it while a removal changes it.
378
+ */
379
+ function classMeaning(context, coordinate, onto) {
380
+ const owner = context.schemas.get(coordinate.ref.origin);
381
+ const declaration = owner?.classes[coordinate.ref.name];
382
+ const analysis = owner === undefined ? undefined : effectiveDefinition(context, owner, coordinate.ref);
383
+ if (owner === undefined || declaration === undefined || analysis === undefined)
384
+ return undefined;
385
+ const ancestors = ancestorKeys(context, coordinate.ref);
386
+ const requirement = Object.freeze({
387
+ ancestors,
388
+ properties: new Set(analysis.properties.keys()),
389
+ });
390
+ const meaning = {
391
+ kind: 'class',
392
+ level: coordinate.level,
393
+ classKind: declaration.kind,
394
+ ...(declaration.kind === 'edge' ? { orientation: declaration.orientation } : {}),
395
+ ...(coordinate.level === 'inherit'
396
+ ? {
397
+ ancestors: onto === undefined
398
+ ? ancestors
399
+ : onto.ancestors.filter((ancestor) => ancestors.includes(ancestor)),
400
+ policies: Object.fromEntries(Object.entries(declaration.policies)
401
+ .filter(([, ref]) => ref !== undefined)
402
+ .map(([role, ref]) => [role, keyForDefinitionRef(ref)])),
403
+ data: analysis.data === undefined
404
+ ? null
405
+ : {
406
+ owner: keyForDefinitionRef(analysis.data.owner),
407
+ declaration: analysis.data.declaration,
408
+ },
409
+ ...(declaration.kind === 'edge' ? { endpoints: declaration.endpoints } : {}),
410
+ }
411
+ : {}),
412
+ };
413
+ return { meaning, requirement };
414
+ }
415
+ function ancestorKeys(context, ref) {
416
+ const seen = new Set();
417
+ const visit = (current) => {
418
+ const declaration = context.schemas.get(current.origin)?.classes[current.name];
419
+ if (declaration === undefined)
420
+ return;
421
+ for (const parent of declaration.extends) {
422
+ const key = keyForDefinitionRef(parent);
423
+ if (seen.has(key))
424
+ continue;
425
+ seen.add(key);
426
+ visit(parent);
427
+ }
428
+ };
429
+ visit(ref);
430
+ return [...seen].sort(compareUnicode);
431
+ }
301
432
  function memberMeaning(owner, ref) {
302
433
  if (ref.origin !== owner.origin)
303
434
  return undefined;
304
435
  switch (ref.kind) {
305
- case 'class':
306
- return definitionMeaning(owner, ref);
307
436
  case 'function': {
308
437
  const declaration = owner.functions[ref.name];
309
438
  return declaration === undefined
@@ -334,35 +463,8 @@ function memberMeaning(owner, ref) {
334
463
  }
335
464
  }
336
465
  }
337
- function definitionMeaning(owner, ref) {
338
- const declaration = owner.classes[ref.name];
339
- const effective = effectiveDefinition(owner, ref);
340
- if (declaration === undefined || effective === undefined || ref.origin !== owner.origin)
341
- return undefined;
342
- return {
343
- kind: ref.kind,
344
- declaration: semanticDefinition(declaration),
345
- effective: {
346
- properties: [...effective.properties.keys()].sort(compareUnicode),
347
- methods: [...effective.methods.values()]
348
- .map((method) => MethodKey.of(method.owner, method.name))
349
- .sort(compareUnicode),
350
- ...(effective.data === undefined
351
- ? {}
352
- : {
353
- data: {
354
- owner: keyForDefinitionRef(effective.data.owner),
355
- declaration: effective.data.declaration,
356
- },
357
- }),
358
- },
359
- };
360
- }
361
- function effectiveDefinition(owner, ref) {
362
- const validation = validateDomainSchemaSemantics(owner, lookupForDomainClosure(owner));
363
- if (validation.diagnostics.length > 0)
364
- return undefined;
365
- return validation.analysis.definitions.get(definitionRefIndex(ref));
466
+ function effectiveDefinition(context, owner, ref) {
467
+ return context.analysis(owner)?.get(definitionRefIndex(ref));
366
468
  }
367
469
  function propertyMeaning(field) {
368
470
  return {
@@ -382,23 +484,6 @@ function methodMeaning(method) {
382
484
  declaration: semanticMethod(method.declaration),
383
485
  };
384
486
  }
385
- function semanticDefinition(declaration) {
386
- const { description: _description, ...rest } = declaration;
387
- const semantic = {
388
- ...rest,
389
- properties: Object.fromEntries(Object.entries(declaration.properties).map(([name, property]) => [
390
- name,
391
- {
392
- ...property,
393
- schema: semanticValueSchema(property.schema),
394
- },
395
- ])),
396
- };
397
- if ('methods' in declaration) {
398
- semantic.methods = Object.fromEntries(Object.entries(declaration.methods).map(([name, method]) => [name, semanticMethod(method)]));
399
- }
400
- return semantic;
401
- }
402
487
  function semanticFunction(declaration) {
403
488
  const { description: _description, ...rest } = declaration;
404
489
  return {
@@ -458,7 +543,7 @@ function semanticValueSchema(value) {
458
543
  }
459
544
  return semantic;
460
545
  }
461
- function coordinateForKey(source, input, index) {
546
+ function coordinateForKey(context, source, input, index) {
462
547
  let key;
463
548
  try {
464
549
  key = Key(input);
@@ -474,72 +559,45 @@ function coordinateForKey(source, input, index) {
474
559
  if ((reference.member === 'property' || reference.member === 'method') &&
475
560
  reference.ownerKind === 'class' &&
476
561
  reference.ownerName !== undefined) {
477
- const owner = definitionRefFor(source, reference.ownerKind, reference.ownerName);
478
- if (owner === undefined) {
562
+ if (source.classes[reference.ownerName] === undefined) {
479
563
  return invalidMemberRoot(key, index, 'has no exact owning Definition');
480
564
  }
565
+ const owner = { origin: source.origin, kind: 'class', name: reference.ownerName };
481
566
  coordinate =
482
567
  reference.member === 'property'
483
568
  ? propertyCoordinate(owner, reference.name)
484
569
  : methodCoordinate(owner, reference.name);
485
570
  }
486
- else if (reference.kind !== 'core') {
487
- const member = memberRefFor(source, {
488
- origin: source.origin,
489
- kind: reference.kind,
490
- name: reference.name,
491
- });
492
- if (member === undefined)
571
+ else if (reference.kind === 'class') {
572
+ if (source.classes[reference.name] === undefined) {
493
573
  return invalidMemberRoot(key, index, 'does not exist in the source');
494
- coordinate = memberCoordinate(member);
574
+ }
575
+ // An explicit Class root compares its surface; it never infers an inheritance contract.
576
+ coordinate = classCoordinate({ origin: source.origin, kind: 'class', name: reference.name }, 'reference');
495
577
  }
496
578
  else {
497
579
  coordinate = memberCoordinate({
498
580
  origin: source.origin,
499
- kind: 'core',
581
+ kind: reference.kind,
500
582
  name: reference.name,
501
583
  });
502
584
  }
503
- if (meaningForCoordinate(source, coordinate) === undefined) {
585
+ if (meaningForCoordinate(context, coordinate) === undefined) {
504
586
  return invalidMemberRoot(key, index, 'does not exist in the source');
505
587
  }
506
588
  return coordinate;
507
589
  }
508
- function memberRefFor(source, ref) {
509
- const name = ref.name;
510
- const owner = definitionRefFor(source, ref.kind, name);
511
- if (owner !== undefined)
512
- return owner;
513
- switch (ref.kind) {
514
- case 'function':
515
- return source.functions[name] === undefined
516
- ? undefined
517
- : { origin: source.origin, kind: 'function', name };
518
- case 'policy':
519
- return source.policies[name] === undefined
520
- ? undefined
521
- : { origin: source.origin, kind: 'policy', name };
522
- case 'view':
523
- return source.views[name] === undefined
524
- ? undefined
525
- : { origin: source.origin, kind: 'view', name };
526
- default:
527
- return undefined;
528
- }
529
- }
530
- function definitionRefFor(source, namespace, name) {
531
- if (namespace === 'class') {
532
- return source.classes[name] === undefined
533
- ? undefined
534
- : { origin: source.origin, kind: 'class', name };
535
- }
536
- return undefined;
537
- }
538
590
  function invalidMemberRoot(key, index, reason) {
539
591
  throw new SchemaError([
540
592
  diagnostic('DM_MEMBER_ROOT_INVALID', `/roots/${index}`, `Member comparison root ${JSON.stringify(key)} ${reason}.`, typeof key === 'string' ? key : undefined),
541
593
  ]);
542
594
  }
595
+ function levelOf(site) {
596
+ return site === 'extends' || site === 'core' ? 'inherit' : 'reference';
597
+ }
598
+ function classCoordinate(ref, level) {
599
+ return Object.freeze({ kind: 'class', key: keyForDefinitionRef(ref), ref, level });
600
+ }
543
601
  function memberCoordinate(ref) {
544
602
  return Object.freeze({ kind: 'member', key: keyForDefinitionRef(ref), ref });
545
603
  }
@@ -585,21 +643,6 @@ function incidentCoreEdges(owner, name) {
585
643
  function fingerprintMeaning(value) {
586
644
  return digestDomainSchemaBytes(json.encode(value));
587
645
  }
588
- function isDefinitionRef(ref) {
589
- return ref.kind === 'class';
590
- }
591
- function requireMemberRef(ref) {
592
- switch (ref.kind) {
593
- case 'class':
594
- case 'function':
595
- case 'policy':
596
- case 'view':
597
- return ref;
598
- case 'core':
599
- return ref;
600
- }
601
- return ref;
602
- }
603
646
  function isJsonSchema(value) {
604
647
  return typeof value === 'boolean' || isJsonObject(value);
605
648
  }
@@ -12,7 +12,7 @@ export { acceptDomainSchema as accept } from './admission/accept.js';
12
12
  export { decodeDomainSchema as decode, encodeDomainSchema as encode, serializeDomainSchema as serialize, } from './codec/schema.js';
13
13
  export { revisionOfDomainSchema as revision } from './canonical/revision.js';
14
14
  export { compareDependencyMeaning, compareMemberMeaning } from './compatibility/meaning.js';
15
- export type { DependencyMeaningChange, DependencyMeaningChangeKind, DependencyMeaningComparison, MeaningFingerprint, } from './compatibility/meaning.js';
15
+ export type { DependencyMeaningChange, DependencyMeaningChangeKind, DependencyMeaningComparison, DependencyMeaningLevel, MeaningFingerprint, } from './compatibility/meaning.js';
16
16
  export { header } from './codec/header.js';
17
17
  export { DOMAIN_FORMAT as FORMAT, DOMAIN_VERSION as VERSION } from './model/format.js';
18
18
  export { compile, load } from '../compiled/index.js';
@@ -4,11 +4,22 @@ import type { ClassDefinition } from '../../language/class/model/class.js';
4
4
  import type { PolicyDefinition } from '../../language/policy/model/policy.js';
5
5
  import type { JsonSchema } from '../../language/value/model/schema.js';
6
6
  import type { DomainSchemaIR } from '../model/schema.js';
7
+ /**
8
+ * Where one schema references a Definition. `extends` and `core` mean the schema inherits from or
9
+ * instantiates the Definition; every other site only refers to it.
10
+ */
11
+ export type ReferenceSite = 'extends' | 'endpoint' | 'value' | 'policy' | 'view' | 'core';
12
+ export type ReferenceCollector = (ref: DefinitionRef, site: ReferenceSite) => void;
7
13
  /** Collect only semantic refs, never strings or objects embedded as JSON data. */
8
14
  export declare function collectUsedForeignOrigins(schema: DomainSchemaIR): ReadonlySet<string>;
9
15
  /** Complete structured references in one schema, in canonical order. */
10
16
  export declare function collectDomainDefinitionRefs(schema: DomainSchemaIR): readonly DefinitionRef[];
11
- export declare function collectClassRefs(declaration: ClassDefinition, add: (ref: DefinitionRef) => void): void;
12
- export declare function collectCallableRefs(declaration: FunctionDefinition | MethodDefinition, add: (ref: DefinitionRef) => void): void;
13
- export declare function collectPolicyDefinitionRefs(declaration: PolicyDefinition, add: (ref: DefinitionRef) => void): void;
14
- export declare function collectValueDefinitionRefs(schema: JsonSchema, add: (ref: DefinitionRef) => void): void;
17
+ export declare function collectCoreRefs(schema: DomainSchemaIR, add: ReferenceCollector): void;
18
+ export declare function collectClassRefs(declaration: ClassDefinition, add: ReferenceCollector): void;
19
+ export declare function collectClassPolicyRefs(declaration: ClassDefinition, add: ReferenceCollector): void;
20
+ export declare function collectEndpointRefs(declaration: Extract<ClassDefinition, {
21
+ readonly kind: 'edge';
22
+ }>, add: ReferenceCollector): void;
23
+ export declare function collectCallableRefs(declaration: FunctionDefinition | MethodDefinition, add: ReferenceCollector): void;
24
+ export declare function collectPolicyDefinitionRefs(declaration: PolicyDefinition, add: ReferenceCollector): void;
25
+ export declare function collectValueDefinitionRefs(schema: JsonSchema, add: ReferenceCollector): void;
@@ -24,39 +24,46 @@ export function collectDomainDefinitionRefs(schema) {
24
24
  collectPolicyDefinitionRefs(declaration, add);
25
25
  }
26
26
  for (const declaration of Object.values(schema.views)) {
27
- if (declaration.target.kind === 'definition')
28
- declaration.target.definitions.forEach(add);
27
+ if (declaration.target.kind === 'definition') {
28
+ declaration.target.definitions.forEach((ref) => add(ref, 'view'));
29
+ }
29
30
  }
31
+ collectCoreRefs(schema, add);
32
+ return Object.freeze([...result.values()].sort(compareDefinitionRefs));
33
+ }
34
+ export function collectCoreRefs(schema, add) {
30
35
  for (const node of Object.values(schema.core.nodes))
31
- add(node.class);
36
+ add(node.class, 'core');
32
37
  for (const edge of schema.core.edges) {
33
- add(edge.class);
38
+ add(edge.class, 'core');
34
39
  if (edge.source.kind !== 'domain')
35
- add(edge.source);
40
+ add(edge.source, 'core');
36
41
  if (edge.target.kind !== 'domain')
37
- add(edge.target);
42
+ add(edge.target, 'core');
38
43
  }
39
- return Object.freeze([...result.values()].sort(compareDefinitionRefs));
40
44
  }
41
45
  export function collectClassRefs(declaration, add) {
42
- declaration.extends.forEach(add);
46
+ declaration.extends.forEach((ref) => add(ref, 'extends'));
43
47
  Object.values(declaration.properties).forEach((property) => collectValueDefinitionRefs(property.schema, add));
44
- if (declaration.policies.read !== undefined)
45
- add(declaration.policies.read);
46
- if (declaration.policies.traverse !== undefined)
47
- add(declaration.policies.traverse);
48
+ collectClassPolicyRefs(declaration, add);
48
49
  if (declaration.kind === 'edge') {
49
50
  collectEndpointRefs(declaration, add);
50
51
  return;
51
52
  }
52
53
  Object.values(declaration.methods).forEach((method) => collectCallableRefs(method, add));
53
54
  }
54
- function collectEndpointRefs(declaration, add) {
55
+ export function collectClassPolicyRefs(declaration, add) {
56
+ if (declaration.policies.read !== undefined)
57
+ add(declaration.policies.read, 'policy');
58
+ if (declaration.policies.traverse !== undefined)
59
+ add(declaration.policies.traverse, 'policy');
60
+ }
61
+ export function collectEndpointRefs(declaration, add) {
55
62
  const endpoints = declaration.orientation === 'directed'
56
63
  ? [declaration.endpoints.source, declaration.endpoints.target]
57
64
  : declaration.endpoints;
58
- endpoints[0].accepts.forEach(add);
59
- endpoints[1].accepts.forEach(add);
65
+ endpoints[0].accepts.forEach((ref) => add(ref, 'endpoint'));
66
+ endpoints[1].accepts.forEach((ref) => add(ref, 'endpoint'));
60
67
  }
61
68
  export function collectCallableRefs(declaration, add) {
62
69
  collectValueDefinitionRefs(declaration.input, add);
@@ -76,27 +83,27 @@ function collectPolicyExpressionRefs(expression, add) {
76
83
  return;
77
84
  }
78
85
  const refs = 'allOf' in expression ? expression.allOf : expression.anyOf;
79
- refs.forEach(add);
86
+ refs.forEach((ref) => add(ref, 'policy'));
80
87
  }
81
88
  function collectPolicyPatternRefs(expression, add) {
82
89
  if ('sameNode' in expression) {
83
90
  for (const term of [expression.sameNode.left, expression.sameNode.right])
84
91
  if (term.kind === 'ref')
85
- add(term.ref.kind === 'method' ? term.ref.owner : term.ref);
92
+ add(term.ref.kind === 'method' ? term.ref.owner : term.ref, 'policy');
86
93
  return;
87
94
  }
88
95
  if ('source' in expression) {
89
96
  for (const term of [expression.source, expression.target])
90
97
  if (term.kind === 'ref')
91
- add(term.ref.kind === 'method' ? term.ref.owner : term.ref);
92
- add(expression.class);
98
+ add(term.ref.kind === 'method' ? term.ref.owner : term.ref, 'policy');
99
+ add(expression.class, 'policy');
93
100
  return;
94
101
  }
95
102
  if ('exists' in expression) {
96
103
  expression.exists.nodes.forEach((node) => {
97
104
  const ref = policyVariableClass(node);
98
105
  if (ref !== undefined)
99
- add(ref);
106
+ add(ref, 'policy');
100
107
  });
101
108
  collectPolicyPatternRefs(expression.exists.where, add);
102
109
  return;
@@ -108,15 +115,15 @@ function collectPolicyCheckRefs(expression, add) {
108
115
  if ('sameNode' in expression) {
109
116
  for (const term of [expression.sameNode.left, expression.sameNode.right])
110
117
  if (term.kind === 'ref')
111
- add(term.ref.kind === 'method' ? term.ref.owner : term.ref);
118
+ add(term.ref.kind === 'method' ? term.ref.owner : term.ref, 'policy');
112
119
  return;
113
120
  }
114
121
  if ('check' in expression) {
115
- add(expression.check);
122
+ add(expression.check, 'policy');
116
123
  if (expression.object.kind === 'ref')
117
124
  add(expression.object.ref.kind === 'method'
118
125
  ? expression.object.ref.owner
119
- : expression.object.ref);
126
+ : expression.object.ref, 'policy');
120
127
  return;
121
128
  }
122
129
  const children = 'allOf' in expression ? expression.allOf : expression.anyOf;
@@ -134,7 +141,7 @@ export function collectValueDefinitionRefs(schema, add) {
134
141
  typeof candidate.origin === 'string' &&
135
142
  typeof candidate.kind === 'string' &&
136
143
  typeof candidate.name === 'string') {
137
- add(candidate);
144
+ add(candidate, 'value');
138
145
  }
139
146
  }
140
147
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-dsl",
3
- "version": "0.2.0-beta.33",
3
+ "version": "0.2.0-beta.34",
4
4
  "description": "Astrale Kernel DSL - Schema authoring, inference, and IR serialization",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -105,7 +105,7 @@
105
105
  "generate:value-schema-validator": "node scripts/generate-value-schema-meta-validator.mjs",
106
106
  "layout:check": "node scripts/check-layout.mjs",
107
107
  "spec:typecheck": "tsgo --noEmit -p tsconfig.spec.json",
108
- "typecheck": "tsgo --noEmit && tsgo --noEmit -p tsconfig.types.json",
108
+ "typecheck": "tsgo --noEmit && tsgo --noEmit -p tsconfig.types.json && tsgo -p tsconfig.workers.json",
109
109
  "test": "pnpm run conformance && pnpm run layout:check && vitest run",
110
110
  "test:file": "vitest run",
111
111
  "test:semantic-lock": "pnpm run conformance && pnpm run layout:check && vitest run",