@astrale-os/kernel-dsl 0.2.0-beta.44 → 0.2.0-beta.45

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,8 +3,14 @@ import { BRANCH_OVERFLOW, MAX_BRANCHES, MAX_CHECK_DEPTH, MAX_CHECK_LEAVES, MAX_D
3
3
  import { escapePointer } from './context.js';
4
4
  export { BRANCH_OVERFLOW, MAX_BRANCHES, MAX_CHECK_DEPTH, MAX_CHECK_LEAVES, MAX_DOMAINS, MAX_EDGES, MAX_PATTERN_DEPTH, MAX_REPEAT, MAX_VARIABLES, measureChecks, };
5
5
  export function validatePolicyBudgets(context) {
6
+ // Measure every distinct composition node once. A node's expanded measure is independent of the
7
+ // path that reaches it (the only path-sensitive outcome, the back-edge `undefined`, already holds
8
+ // for every node on or reaching a composition cycle, regardless of entry), so caching the
9
+ // completed result by node identity turns path enumeration over the composition DAG into work
10
+ // linear in the number of nodes while preserving the exact measure and PL_BUDGET decision.
11
+ const measures = new Map();
6
12
  for (const name of Object.keys(context.environment.candidate.policies)) {
7
- const measure = measurePolicy(context, name, new Set());
13
+ const measure = measurePolicy(context, name, new Set(), measures);
8
14
  if (measure === undefined)
9
15
  continue;
10
16
  const pointer = `/policies/${escapePointer(name)}`;
@@ -23,14 +29,21 @@ export function validatePolicyBudgets(context) {
23
29
  }
24
30
  }
25
31
  }
26
- function measurePolicy(context, name, visiting) {
32
+ function measurePolicy(context, name, visiting, measures) {
27
33
  if (visiting.has(name))
28
34
  return undefined;
35
+ if (measures.has(name))
36
+ return measures.get(name);
29
37
  const declaration = context.normalized.get(name);
30
38
  if (declaration === undefined)
31
39
  return undefined;
32
40
  const nested = new Set(visiting);
33
41
  nested.add(name);
42
+ const measure = measureDeclaration(context, declaration, nested, measures);
43
+ measures.set(name, measure);
44
+ return measure;
45
+ }
46
+ function measureDeclaration(context, declaration, nested, measures) {
34
47
  if ('match' in declaration) {
35
48
  return measurePattern(declaration.match, context.environment.candidate.origin);
36
49
  }
@@ -38,7 +51,7 @@ function measurePolicy(context, name, visiting) {
38
51
  const children = refs.flatMap((ref) => {
39
52
  if (!context.isLocalPolicy(ref))
40
53
  return [];
41
- const value = measurePolicy(context, ref.name, nested);
54
+ const value = measurePolicy(context, ref.name, nested, measures);
42
55
  return value === undefined ? [] : [value];
43
56
  });
44
57
  if (children.length !== refs.length || children.length === 0)
@@ -5,6 +5,13 @@ import { escapePointer } from './context.js';
5
5
  import { referenceContracts } from './objects.js';
6
6
  export class PolicyTyping {
7
7
  context;
8
+ // Expanded branches of each named Policy, cached by node identity. A Policy's expansion is reused
9
+ // across every composition path that reaches it (and across every use site and object contract,
10
+ // none of which the expansion depends on), so a shared-child composition DAG is expanded in work
11
+ // linear in the number of Policies instead of enumerating every path. The expansion result is
12
+ // independent of the reaching path up to the opaque term-namespace labels, which only ever
13
+ // disambiguate variables within one branch and leave targetMode and branch typing unchanged.
14
+ #expansions = new Map();
8
15
  constructor(context) {
9
16
  this.context = context;
10
17
  }
@@ -59,6 +66,14 @@ export class PolicyTyping {
59
66
  #expandedBranches(name, visiting, namespace) {
60
67
  if (visiting.has(name))
61
68
  return undefined;
69
+ const cached = this.#expansions.get(name);
70
+ if (cached !== undefined || this.#expansions.has(name))
71
+ return cached;
72
+ const expanded = this.#expandDeclaration(name, visiting, namespace);
73
+ this.#expansions.set(name, expanded);
74
+ return expanded;
75
+ }
76
+ #expandDeclaration(name, visiting, namespace) {
62
77
  const declaration = this.context.normalized.get(name);
63
78
  if (declaration === undefined)
64
79
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrale-os/kernel-dsl",
3
- "version": "0.2.0-beta.44",
3
+ "version": "0.2.0-beta.45",
4
4
  "description": "Astrale Kernel DSL - Schema authoring, inference, and IR serialization",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {