@telorun/analyzer 0.12.1 → 1.0.0

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 (51) hide show
  1. package/dist/analysis-registry.d.ts +13 -0
  2. package/dist/analysis-registry.d.ts.map +1 -1
  3. package/dist/analysis-registry.js +15 -0
  4. package/dist/analyzer.d.ts.map +1 -1
  5. package/dist/analyzer.js +154 -83
  6. package/dist/builtins.d.ts.map +1 -1
  7. package/dist/builtins.js +85 -0
  8. package/dist/cel-environment.d.ts +1 -1
  9. package/dist/cel-environment.d.ts.map +1 -1
  10. package/dist/cel-environment.js +40 -2
  11. package/dist/dependency-graph.d.ts.map +1 -1
  12. package/dist/dependency-graph.js +41 -62
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1 -0
  16. package/dist/kernel-globals.d.ts +1 -1
  17. package/dist/kernel-globals.d.ts.map +1 -1
  18. package/dist/kernel-globals.js +19 -1
  19. package/dist/manifest-visitor.d.ts +124 -0
  20. package/dist/manifest-visitor.d.ts.map +1 -0
  21. package/dist/manifest-visitor.js +181 -0
  22. package/dist/reference-field-map.js +16 -0
  23. package/dist/resolve-throws-union.d.ts +10 -0
  24. package/dist/resolve-throws-union.d.ts.map +1 -1
  25. package/dist/resolve-throws-union.js +35 -7
  26. package/dist/schema-compat.d.ts +10 -0
  27. package/dist/schema-compat.d.ts.map +1 -1
  28. package/dist/schema-compat.js +32 -0
  29. package/dist/validate-cel-context.d.ts +14 -0
  30. package/dist/validate-cel-context.d.ts.map +1 -1
  31. package/dist/validate-cel-context.js +38 -0
  32. package/dist/validate-references.d.ts.map +1 -1
  33. package/dist/validate-references.js +124 -160
  34. package/dist/validate-unused-declarations.d.ts +25 -0
  35. package/dist/validate-unused-declarations.d.ts.map +1 -0
  36. package/dist/validate-unused-declarations.js +91 -0
  37. package/package.json +3 -3
  38. package/src/analysis-registry.ts +20 -0
  39. package/src/analyzer.ts +256 -168
  40. package/src/builtins.ts +85 -0
  41. package/src/cel-environment.ts +42 -1
  42. package/src/dependency-graph.ts +37 -52
  43. package/src/index.ts +11 -0
  44. package/src/kernel-globals.ts +22 -1
  45. package/src/manifest-visitor.ts +340 -0
  46. package/src/reference-field-map.ts +14 -0
  47. package/src/resolve-throws-union.ts +36 -8
  48. package/src/schema-compat.ts +32 -0
  49. package/src/validate-cel-context.ts +50 -0
  50. package/src/validate-references.ts +175 -211
  51. package/src/validate-unused-declarations.ts +95 -0
@@ -1,4 +1,5 @@
1
1
  import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
2
+ import { type ManifestVisitor } from "./manifest-visitor.js";
2
3
  import type { AnalysisContext } from "./types.js";
3
4
  /**
4
5
  * Accumulates type and alias knowledge for a running kernel or analysis session.
@@ -22,6 +23,18 @@ export declare class AnalysisRegistry {
22
23
  * sub-schema (e.g. Server.notFoundHandler.returns[].content[mime].encoder).
23
24
  */
24
25
  iterateFieldEntries(resource: ResourceManifest, onRef: (fieldPath: string) => void, onScope: (fieldPath: string) => void): void;
26
+ /**
27
+ * Walks a manifest's annotation sites (refs, scopes, schema-from, CEL) via
28
+ * the shared manifest visitor, bound to this registry's definitions and
29
+ * aliases. The public seam for hosts (editor overview graph, tooling) that
30
+ * need the same site discovery the analyzer's own passes use, without
31
+ * reaching into the internal DefinitionRegistry.
32
+ */
33
+ visitManifest(resources: ResourceManifest[], visitor: ManifestVisitor, opts?: {
34
+ skipKinds?: ReadonlySet<string>;
35
+ expand?: boolean;
36
+ discoverNestedRefs?: boolean;
37
+ }): void;
25
38
  /**
26
39
  * Returns the built-in kernel definitions. The underlying DefinitionRegistry already
27
40
  * seeds these on construction; this method exposes them so callers (e.g. the kernel's
@@ -1 +1 @@
1
- {"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAMzE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IAEpE,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI;IAIjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIpE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIpE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;;;;;;OAOG;IACH,mBAAmB,CACjB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GACnC,IAAI;IAkBP;;;;OAIG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAI1C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAM/D,QAAQ,IAAI,MAAM,EAAE;IAIpB;mEAC+D;IAC/D,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC;;;gCAG4B;IAC5B,oBAAoB,IAAI,MAAM,EAAE;IAIhC;wEACoE;IACpE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIhD;;;;;gEAK4D;IAC5D,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IA+B7D,qFAAqF;IACrF,QAAQ,IAAI,eAAe;CAG5B"}
1
+ {"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKzE,OAAO,EAAqC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IAEpE,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI;IAIjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIpE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIpE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;;;;;;OAOG;IACH,mBAAmB,CACjB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GACnC,IAAI;IAkBP;;;;;;OAMG;IACH,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAAE,GACzF,IAAI;IAQP;;;;OAIG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAI1C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAM/D,QAAQ,IAAI,MAAM,EAAE;IAIpB;mEAC+D;IAC/D,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC;;;gCAG4B;IAC5B,oBAAoB,IAAI,MAAM,EAAE;IAIhC;wEACoE;IACpE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIhD;;;;;gEAK4D;IAC5D,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IA+B7D,qFAAqF;IACrF,QAAQ,IAAI,eAAe;CAG5B"}
@@ -2,6 +2,7 @@ import { AliasResolver } from "./alias-resolver.js";
2
2
  import { KERNEL_BUILTINS } from "./builtins.js";
3
3
  import { DefinitionRegistry } from "./definition-registry.js";
4
4
  import { computeSuggestKind, computeValidUserFacingKinds } from "./kind-suggest.js";
5
+ import { visitManifest as runVisitManifest } from "./manifest-visitor.js";
5
6
  import { isRefEntry, isScopeEntry } from "./reference-field-map.js";
6
7
  /**
7
8
  * Accumulates type and alias knowledge for a running kernel or analysis session.
@@ -46,6 +47,20 @@ export class AnalysisRegistry {
46
47
  }
47
48
  }
48
49
  }
50
+ /**
51
+ * Walks a manifest's annotation sites (refs, scopes, schema-from, CEL) via
52
+ * the shared manifest visitor, bound to this registry's definitions and
53
+ * aliases. The public seam for hosts (editor overview graph, tooling) that
54
+ * need the same site discovery the analyzer's own passes use, without
55
+ * reaching into the internal DefinitionRegistry.
56
+ */
57
+ visitManifest(resources, visitor, opts) {
58
+ runVisitManifest(resources, this.defs, visitor, {
59
+ aliases: this.aliases,
60
+ aliasesByModule: this.aliasesByModule,
61
+ ...opts,
62
+ });
63
+ }
49
64
  /**
50
65
  * Returns the built-in kernel definitions. The underlying DefinitionRegistry already
51
66
  * seeds these on construction; this method exposes them so callers (e.g. the kernel's
@@ -1 +1 @@
1
- {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAgB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAif/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAoiBvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE;IAexF,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAiB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAuhB/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAqlBvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE;IAexF,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
package/dist/analyzer.js CHANGED
@@ -1,10 +1,11 @@
1
- import { defaultRegistry, walkCelExpressions } from "@telorun/templating";
1
+ import { defaultRegistry, isTaggedSentinel } from "@telorun/templating";
2
2
  import { AliasResolver } from "./alias-resolver.js";
3
3
  import { buildCelEnvironment, buildTypedCelEnvironment, } from "./cel-environment.js";
4
4
  import { DefinitionRegistry } from "./definition-registry.js";
5
5
  import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
6
6
  import { buildKernelGlobalsSchema, mergeKernelGlobalsIntoContext } from "./kernel-globals.js";
7
7
  import { computeSuggestKind } from "./kind-suggest.js";
8
+ import { visitManifest } from "./manifest-visitor.js";
8
9
  import { isModuleKind } from "./module-kinds.js";
9
10
  import { normalizeInlineResources } from "./normalize-inline-resources.js";
10
11
  import { REF_VALIDATION_SKIP_KINDS } from "./system-kinds.js";
@@ -12,11 +13,12 @@ import { resolveRefSentinels } from "./resolve-ref-sentinels.js";
12
13
  import { rewriteSyntheticOrigins } from "./rewrite-synthetic-origins.js";
13
14
  import { celTypeSatisfiesJsonSchema, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
14
15
  import { DiagnosticSeverity } from "./types.js";
15
- import { getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
16
+ import { extractContextsFromSchema, getManifestItem, pathMatchesScope, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
16
17
  import { validateExtends } from "./validate-extends.js";
17
18
  import { validateNestedInlineResources } from "./validate-nested-inline.js";
18
19
  import { validateProviderCoherence } from "./validate-provider-coherence.js";
19
20
  import { validateReferences } from "./validate-references.js";
21
+ import { validateUnusedDeclarations } from "./validate-unused-declarations.js";
20
22
  import { validateThrowsCoverage } from "./validate-throws-coverage.js";
21
23
  const SELF_PREFIX = "Self.";
22
24
  /**
@@ -142,44 +144,6 @@ function manifestRootForResolver(m, defs, aliases, allManifests) {
142
144
  ...(inputs ? { inputType: inputs } : {}),
143
145
  };
144
146
  }
145
- /**
146
- * Walk a JSON Schema tree and collect all `x-telo-context` annotations,
147
- * returning them as `{ scope, schema }` pairs using JSONPath-style scopes —
148
- * the same format the analyzer uses for CEL context validation.
149
- *
150
- * Result is sorted by scope specificity (longer scope first) so that the
151
- * per-expression resolver's first-match-wins logic picks the most-specific
152
- * context. Without this, a broader ancestor scope (e.g. `$.resources[*]`)
153
- * could shadow a narrower descendant scope whose activation differs.
154
- */
155
- function extractContextsFromSchema(schema, path = "$") {
156
- const all = collectContexts(schema, path);
157
- return all.sort((a, b) => b.scope.length - a.scope.length);
158
- }
159
- function collectContexts(schema, path) {
160
- if (!schema || typeof schema !== "object")
161
- return [];
162
- const results = [];
163
- if (schema["x-telo-context"]) {
164
- results.push({ scope: path, schema: schema["x-telo-context"] });
165
- }
166
- if (schema.properties) {
167
- for (const [key, value] of Object.entries(schema.properties)) {
168
- results.push(...collectContexts(value, `${path}.${key}`));
169
- }
170
- }
171
- if (schema.items && typeof schema.items === "object") {
172
- results.push(...collectContexts(schema.items, `${path}[*]`));
173
- }
174
- for (const key of ["oneOf", "anyOf", "allOf"]) {
175
- if (Array.isArray(schema[key])) {
176
- for (const subschema of schema[key]) {
177
- results.push(...collectContexts(subschema, path));
178
- }
179
- }
180
- }
181
- return results;
182
- }
183
147
  /** Resolve a local `$ref` (only `#/$defs/<name>` form) against the root schema.
184
148
  * Non-refs and unresolved refs pass through unchanged. */
185
149
  function resolveLocalRef(schema, root) {
@@ -343,23 +307,101 @@ function buildStepContextSchema(manifest, defSchema, allManifests, defs, aliases
343
307
  }
344
308
  return undefined;
345
309
  }
310
+ /**
311
+ * Collect every field annotated with `x-telo-error-context` anywhere in a
312
+ * definition schema (resolving local `$ref`s into `$defs`, cycle-safe), mapping
313
+ * the annotated field name to its declared error-shape schema. The field name
314
+ * is matched against CEL paths so the context applies at any nesting depth under
315
+ * that field — e.g. `error` inside a `catch:` nested inside another `try:`. No
316
+ * specific field name (or `Run.Sequence`) is hardcoded; any composer that tags
317
+ * its error-bearing branch fields opts in the same way.
318
+ */
319
+ function collectErrorContextScopes(defSchema) {
320
+ const out = new Map();
321
+ if (!defSchema || typeof defSchema !== "object")
322
+ return out;
323
+ const seen = new Set();
324
+ const walk = (schema) => {
325
+ if (!schema || typeof schema !== "object" || seen.has(schema))
326
+ return;
327
+ seen.add(schema);
328
+ const props = schema.properties;
329
+ if (props) {
330
+ for (const [fieldName, fieldSchema] of Object.entries(props)) {
331
+ if (fieldSchema && typeof fieldSchema === "object") {
332
+ const errCtx = fieldSchema["x-telo-error-context"];
333
+ if (errCtx && typeof errCtx === "object" && !out.has(fieldName)) {
334
+ out.set(fieldName, errCtx);
335
+ }
336
+ }
337
+ walk(resolveLocalRef(fieldSchema, defSchema));
338
+ }
339
+ }
340
+ if (schema.items)
341
+ walk(resolveLocalRef(schema.items, defSchema));
342
+ for (const key of ["oneOf", "anyOf", "allOf"]) {
343
+ const arr = schema[key];
344
+ if (Array.isArray(arr))
345
+ for (const sub of arr)
346
+ walk(resolveLocalRef(sub, defSchema));
347
+ }
348
+ if (schema.$defs && typeof schema.$defs === "object") {
349
+ for (const sub of Object.values(schema.$defs)) {
350
+ walk(sub);
351
+ }
352
+ }
353
+ };
354
+ walk(defSchema);
355
+ return out;
356
+ }
357
+ /**
358
+ * Return the error-context schema for a CEL `path` when the path lies within
359
+ * (any depth under) one of the error-bearing fields, else undefined. A path is
360
+ * "within" field `f` when it contains a segment `f[<index>]`. When multiple
361
+ * error-bearing fields match (e.g. a `finally` nested inside a `catch`), the
362
+ * deepest — the one whose segment appears latest in the path — wins, so the
363
+ * innermost branch's schema governs.
364
+ */
365
+ function errorContextForPath(path, scopes) {
366
+ let best;
367
+ for (const [fieldName, schema] of scopes) {
368
+ const escaped = fieldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
369
+ for (const match of path.matchAll(new RegExp(`(^|\\.)${escaped}\\[\\d+\\]`, "g"))) {
370
+ if (best === undefined || match.index > best.index) {
371
+ best = { index: match.index, schema };
372
+ }
373
+ }
374
+ }
375
+ return best?.schema;
376
+ }
346
377
  const CEL_PURE_RE = /^\s*\$\{\{[^}]*\}\}\s*$/;
347
378
  const CEL_EXPR_RE = /\$\{\{\s*([^}]+?)\s*\}\}/;
348
379
  /** Recursively walk `data`+`schema` together, type-checking every pure CEL template
349
380
  * string via `env.check()`. Returns `SchemaIssue[]` for any type mismatches found. */
350
- function collectCelTypeIssues(data, schema, path, definition, manifest, baseTypedEnv, rootEnv) {
381
+ function collectCelTypeIssues(data, schema, path, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest) {
351
382
  const issues = [];
352
- if (typeof data === "string" && CEL_PURE_RE.test(data)) {
353
- const exprMatch = data.match(CEL_EXPR_RE);
354
- if (exprMatch) {
355
- const expr = exprMatch[1].trim();
383
+ // A pure CEL value type-checks the same regardless of surface form: a
384
+ // `${{ }}` string and a `!cel`-tagged sentinel must behave identically.
385
+ let celExpr;
386
+ if (isTaggedSentinel(data)) {
387
+ // Non-CEL engines (e.g. `!literal`) are analyzed by their own engine pass.
388
+ if (data.engine !== "cel")
389
+ return issues;
390
+ celExpr = data.source;
391
+ }
392
+ else if (typeof data === "string" && CEL_PURE_RE.test(data)) {
393
+ celExpr = data.match(CEL_EXPR_RE)?.[1]?.trim();
394
+ }
395
+ if (celExpr !== undefined) {
396
+ {
397
+ const expr = celExpr;
356
398
  // Merge x-telo-context variables for this path if applicable
357
399
  let typedEnv = baseTypedEnv;
358
400
  if (definition.schema) {
359
401
  for (const ctx of extractContextsFromSchema(definition.schema)) {
360
402
  if (!pathMatchesScope(path, ctx.scope))
361
403
  continue;
362
- typedEnv = buildTypedCelEnvironment(rootEnv, manifest, ctx.schema);
404
+ typedEnv = buildTypedCelEnvironment(rootEnv, manifest, ctx.schema, rootModuleManifest);
363
405
  break;
364
406
  }
365
407
  }
@@ -382,8 +424,9 @@ function collectCelTypeIssues(data, schema, path, definition, manifest, baseType
382
424
  else if (checkResult?.valid && checkResult.type && schema) {
383
425
  const celType = checkResult.type.split("<")[0];
384
426
  if (!celTypeSatisfiesJsonSchema(celType, schema)) {
427
+ const expected = schema["x-telo-type"] ?? schema.type ?? "unknown";
385
428
  issues.push({
386
- message: `CEL returns '${checkResult.type}' but field expects '${schema.type ?? "unknown"}'`,
429
+ message: `CEL returns '${checkResult.type}' but field expects '${expected}'`,
387
430
  path,
388
431
  });
389
432
  }
@@ -394,13 +437,13 @@ function collectCelTypeIssues(data, schema, path, definition, manifest, baseType
394
437
  if (Array.isArray(data)) {
395
438
  const itemSchema = (schema.items ?? {});
396
439
  for (let i = 0; i < data.length; i++) {
397
- issues.push(...collectCelTypeIssues(data[i], itemSchema, `${path}[${i}]`, definition, manifest, baseTypedEnv, rootEnv));
440
+ issues.push(...collectCelTypeIssues(data[i], itemSchema, `${path}[${i}]`, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest));
398
441
  }
399
442
  }
400
443
  else if (data !== null && typeof data === "object") {
401
444
  const props = (schema.properties ?? {});
402
445
  for (const [k, v] of Object.entries(data)) {
403
- issues.push(...collectCelTypeIssues(v, (props[k] ?? {}), path ? `${path}.${k}` : k, definition, manifest, baseTypedEnv, rootEnv));
446
+ issues.push(...collectCelTypeIssues(v, (props[k] ?? {}), path ? `${path}.${k}` : k, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest));
404
447
  }
405
448
  }
406
449
  return issues;
@@ -625,6 +668,11 @@ export class StaticAnalyzer {
625
668
  // Build typed kernel globals schema so x-telo-context chain validation
626
669
  // recognises variables, secrets, resources, env automatically
627
670
  const kernelGlobals = buildKernelGlobalsSchema(allManifests);
671
+ // The module doc (Application/Library) carries the Application-only `ports`
672
+ // namespace; threaded into per-resource CEL typing so `${{ ports.X }}`
673
+ // resolves its nominal brand cross-doc.
674
+ const moduleManifest = allManifests.find((mm) => mm.kind === "Telo.Application") ??
675
+ allManifests.find((mm) => mm.kind === "Telo.Library");
628
676
  // Validate each non-definition, non-system resource
629
677
  for (const m of allManifests) {
630
678
  const filePath = m.metadata?.source;
@@ -678,8 +726,8 @@ export class StaticAnalyzer {
678
726
  }
679
727
  : definition.schema;
680
728
  // Phase 1: CEL type checking — walk data+schema together, check env.check() return types
681
- const baseTypedEnv = buildTypedCelEnvironment(this.celEnv, m);
682
- const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseTypedEnv, this.celEnv);
729
+ const baseTypedEnv = buildTypedCelEnvironment(this.celEnv, m, undefined, moduleManifest);
730
+ const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseTypedEnv, this.celEnv, moduleManifest);
683
731
  // Phase 2+3: AJV on substituted data — CEL fields replaced with typed placeholders
684
732
  const ajvIssues = validateAgainstSchema(substituteCelFields(m, schema), schema);
685
733
  const issues = [...celIssues, ...ajvIssues];
@@ -791,42 +839,54 @@ export class StaticAnalyzer {
791
839
  }
792
840
  }
793
841
  }
794
- // Validate CEL syntax and context variable access in all manifests
795
- for (const m of allManifests) {
796
- const resource = { kind: m.kind, name: m.metadata?.name };
797
- const filePath = m.metadata?.source;
798
- const resolvedKind = aliases.resolveKind(m.kind);
799
- const mDefinition = defs.resolve(m.kind) ?? (resolvedKind ? defs.resolve(resolvedKind) : undefined);
800
- // Pre-compute step context for manifests with x-telo-step-context
801
- const stepContextSchema = mDefinition?.schema
802
- ? buildStepContextSchema(m, mDefinition.schema, allManifests, defs, aliases)
803
- : undefined;
804
- walkCelExpressions(m, "", (expr, path, engineName) => {
805
- // Resolve the effective context for this expression's path. The
806
- // engine receives a single closed schema and validates member-access
807
- // chains against it; per-path resolution (step context, x-telo-context,
808
- // kernel-globals merge) stays on the analyzer side because it depends
809
- // on analyzer-internal state (definitions, aliases).
810
- const contexts = mDefinition?.schema ? extractContextsFromSchema(mDefinition.schema) : [];
811
- const invocationContext = m.metadata?.xTeloInvocationContext;
812
- let matchedContext;
813
- let matchedScope;
814
- for (const ctx of contexts) {
815
- if (pathMatchesScope(path, ctx.scope)) {
816
- matchedContext = ctx.schema;
817
- matchedScope = ctx.scope;
818
- break;
819
- }
842
+ // Validate CEL syntax and context variable access in all manifests. The
843
+ // walker discovers every compiled CEL node by scanning the value tree and
844
+ // hands back the `x-telo-context` schema matched at the enclosing path; the
845
+ // per-path resolution (step context, kernel-globals merge, x-telo-context-*
846
+ // annotation resolution) stays here because it depends on analyzer-internal
847
+ // state (definitions, aliases, the typed CEL env).
848
+ // Per-resource state computed at enter and read by that resource's CEL
849
+ // sites. The manifest / resource / filePath come straight off each CelSite's
850
+ // `source` (no need to capture them); only the derived step / invocation
851
+ // context — which require analyzer state to build — are stashed here.
852
+ let celStepContextSchema;
853
+ let celInvocationContext;
854
+ let celErrorScopes = new Map();
855
+ visitManifest(allManifests, defs, {
856
+ onResourceEnter: (e) => {
857
+ const m = e.source;
858
+ celInvocationContext = m.metadata?.xTeloInvocationContext;
859
+ celStepContextSchema = e.definition?.schema
860
+ ? buildStepContextSchema(m, e.definition.schema, allManifests, defs, aliases)
861
+ : undefined;
862
+ celErrorScopes = collectErrorContextScopes(e.definition?.schema);
863
+ },
864
+ onCel: (e) => {
865
+ const m = e.source;
866
+ const resource = { kind: m.kind, name: m.metadata?.name };
867
+ const filePath = m.metadata?.source;
868
+ const { expr, path, engineName, matchedScope } = e;
869
+ let matchedContext = e.contextSchema ?? celInvocationContext;
870
+ if (celStepContextSchema) {
871
+ const base = matchedContext ?? { type: "object", properties: {}, additionalProperties: true };
872
+ matchedContext = {
873
+ ...base,
874
+ properties: {
875
+ ...(base.properties ?? {}),
876
+ steps: celStepContextSchema,
877
+ },
878
+ };
820
879
  }
821
- if (!matchedContext)
822
- matchedContext = invocationContext;
823
- if (stepContextSchema) {
880
+ // `error` is only in scope inside an error-bearing branch (e.g. a
881
+ // `catch:` / `finally:`), so it's merged per-path, not resource-wide.
882
+ const errorSchema = celErrorScopes.size > 0 ? errorContextForPath(path, celErrorScopes) : undefined;
883
+ if (errorSchema) {
824
884
  const base = matchedContext ?? { type: "object", properties: {}, additionalProperties: true };
825
885
  matchedContext = {
826
886
  ...base,
827
887
  properties: {
828
888
  ...(base.properties ?? {}),
829
- steps: stepContextSchema,
889
+ error: errorSchema,
830
890
  },
831
891
  };
832
892
  }
@@ -877,6 +937,15 @@ export class StaticAnalyzer {
877
937
  data: { resource, filePath, path },
878
938
  });
879
939
  }
940
+ else if (f.code === "CEL_NULLABLE_ACCESS") {
941
+ diagnostics.push({
942
+ severity: DiagnosticSeverity.Error,
943
+ code: "CEL_NULLABLE_ACCESS",
944
+ source: SOURCE,
945
+ message: `${m.kind}/${resource.name}: CEL at '${path}': ${f.message}`,
946
+ data: { resource, filePath, path },
947
+ });
948
+ }
880
949
  else {
881
950
  // Unknown code from a future engine — pass the message through,
882
951
  // tagged with a generic ENGINE_DIAGNOSTIC code so downstream
@@ -890,8 +959,8 @@ export class StaticAnalyzer {
890
959
  });
891
960
  }
892
961
  }
893
- });
894
- }
962
+ },
963
+ }, { aliases });
895
964
  // Validate resource references (Phase 3)
896
965
  diagnostics.push(...validateReferences(allManifests, { aliases, definitions: defs, aliasesByModule }));
897
966
  // Validate `extends` fields and flag legacy `capability: <UserAbstract>` overload.
@@ -900,6 +969,8 @@ export class StaticAnalyzer {
900
969
  diagnostics.push(...validateProviderCoherence(allManifests, defs, aliases));
901
970
  // Validate throws: declarations and catches: coverage (rules 1, 2, 4, 7)
902
971
  diagnostics.push(...validateThrowsCoverage(allManifests, defs, aliases, this.celEnv));
972
+ // Warn about declared variables / secrets / ports that no CEL references.
973
+ diagnostics.push(...validateUnusedDeclarations(allManifests, this.celEnv));
903
974
  // Reroute diagnostics on synthetic (inline-extracted) resources back to
904
975
  // the chain root so position-index lookups land on the parent doc.
905
976
  return rewriteSyntheticOrigins(diagnostics, allManifests);
@@ -1 +1 @@
1
- {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EAiU/C,CAAC"}
1
+ {"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EAsZ/C,CAAC"}
package/dist/builtins.js CHANGED
@@ -232,6 +232,66 @@ export const KERNEL_BUILTINS = [
232
232
  },
233
233
  additionalProperties: true,
234
234
  },
235
+ // Gated reference: run() a Runnable/Service only when the
236
+ // `when` CEL guard holds. Discriminated by the `ref` key. `ref`
237
+ // is a bare name or a resolved `!ref` (`{ kind, name }`).
238
+ {
239
+ type: "object",
240
+ required: ["ref"],
241
+ properties: {
242
+ ref: {
243
+ anyOf: [
244
+ { type: "string", "x-telo-ref": "telo#Runnable" },
245
+ { type: "string", "x-telo-ref": "telo#Service" },
246
+ {
247
+ type: "object",
248
+ required: ["kind", "name"],
249
+ properties: {
250
+ kind: { type: "string" },
251
+ name: { type: "string" },
252
+ },
253
+ additionalProperties: true,
254
+ },
255
+ ],
256
+ },
257
+ when: { type: "string" },
258
+ },
259
+ additionalProperties: false,
260
+ },
261
+ // Inline flat invoke step: invoke an Invocable / Runnable on boot
262
+ // with an optional `name` (for steps.<name>.result plumbing),
263
+ // `when` guard, and `inputs`. Discriminated by the `invoke` key.
264
+ // Control flow (if/while/switch/try) is not available here —
265
+ // reach for Run.Sequence. `invoke` is ref-only and must resolve
266
+ // to a `{ kind, name }` reference (a `!ref` / `{kind,name}`):
267
+ // requiring `name` rejects an inline `{ kind }` definition (no
268
+ // name) at analysis instead of failing at boot with an undefined
269
+ // resource name. The Invocable/Runnable kind set mirrors
270
+ // Run.Sequence invoke steps.
271
+ {
272
+ type: "object",
273
+ required: ["invoke"],
274
+ properties: {
275
+ name: { type: "string" },
276
+ invoke: {
277
+ "x-telo-topology-role": "invoke",
278
+ type: "object",
279
+ required: ["kind", "name"],
280
+ properties: {
281
+ kind: { type: "string" },
282
+ name: { type: "string" },
283
+ },
284
+ additionalProperties: true,
285
+ anyOf: [
286
+ { "x-telo-ref": "telo#Invocable" },
287
+ { "x-telo-ref": "telo#Runnable" },
288
+ ],
289
+ },
290
+ inputs: { type: "object", additionalProperties: true },
291
+ when: { type: "string" },
292
+ },
293
+ additionalProperties: false,
294
+ },
235
295
  ],
236
296
  },
237
297
  },
@@ -277,6 +337,31 @@ export const KERNEL_BUILTINS = [
277
337
  },
278
338
  },
279
339
  },
340
+ // Inbound ports the Application listens on. A name-keyed map mirroring
341
+ // `variables`: each entry binds a host env var (`env:`) that supplies a
342
+ // port integer (implicitly typed `integer`, 1–65535), with an optional
343
+ // `default:` used when the env var is unset. `protocol:` (default `tcp`)
344
+ // selects the transport — the runner reads this list to know the
345
+ // exposed ports before launch, and the analyzer brands the resolved
346
+ // `ports.<name>` value (tcp → TcpPort, udp → UdpPort) for static wiring
347
+ // checks. Application-only. See kernel/nodejs/src/application-env.ts.
348
+ ports: {
349
+ type: "object",
350
+ additionalProperties: {
351
+ type: "object",
352
+ required: ["env"],
353
+ properties: {
354
+ env: { type: "string" },
355
+ protocol: {
356
+ type: "string",
357
+ enum: ["tcp", "udp"],
358
+ default: "tcp",
359
+ },
360
+ default: { type: "integer", minimum: 1, maximum: 65535 },
361
+ },
362
+ additionalProperties: false,
363
+ },
364
+ },
280
365
  },
281
366
  required: ["metadata"],
282
367
  additionalProperties: false,
@@ -12,5 +12,5 @@ export type { CelHandlers } from "@telorun/templating";
12
12
  *
13
13
  * NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
14
14
  * in kernel-globals.ts, which is used for chain-access validation. */
15
- export declare function buildTypedCelEnvironment(baseEnv: Environment, manifest: ResourceManifest, extraContextSchema?: Record<string, any> | null): Environment;
15
+ export declare function buildTypedCelEnvironment(baseEnv: Environment, manifest: ResourceManifest, extraContextSchema?: Record<string, any> | null, rootModuleManifest?: ResourceManifest): Environment;
16
16
  //# sourceMappingURL=cel-environment.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAC9C,WAAW,CAyCb"}
1
+ {"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAI/C,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,WAAW,CAuEb"}
@@ -1,4 +1,10 @@
1
- import { jsonSchemaToCelType } from "./schema-compat.js";
1
+ import { jsonSchemaToCelType, VALUE_BRAND_BASE } from "./schema-compat.js";
2
+ /** Transport protocol on a `ports` entry → the nominal CEL brand its resolved
3
+ * value carries. Mirrors the `protocol` enum in the Application schema. */
4
+ const PORT_PROTOCOL_BRAND = {
5
+ tcp: "TcpPort",
6
+ udp: "UdpPort",
7
+ };
2
8
  export { buildCelEnvironment } from "@telorun/templating";
3
9
  /** Clone `baseEnv` and register typed variable declarations so that
4
10
  * `env.check(expr)` can infer return types for expressions referencing known variables.
@@ -10,9 +16,21 @@ export { buildCelEnvironment } from "@telorun/templating";
10
16
  *
11
17
  * NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
12
18
  * in kernel-globals.ts, which is used for chain-access validation. */
13
- export function buildTypedCelEnvironment(baseEnv, manifest, extraContextSchema) {
19
+ export function buildTypedCelEnvironment(baseEnv, manifest, extraContextSchema,
20
+ // The `ports` namespace is Application-only and lives on the module doc, not
21
+ // on the resource being analyzed. When validating a resource, the caller
22
+ // passes the module manifest here so `${{ ports.X }}` types cross-doc.
23
+ rootModuleManifest) {
14
24
  try {
15
25
  const env = baseEnv.clone();
26
+ // Register nominal value brands (TcpPort/UdpPort/…) on the *clone* so the
27
+ // type-checker can distinguish structurally-identical values. The base env
28
+ // (shared with the kernel runtime) is untouched — a branded value flows as
29
+ // a plain integer at runtime, so only static checking needs these. cel-js
30
+ // auto-generates a field-less wrapper class; no runtime constructor needed.
31
+ for (const brand of Object.keys(VALUE_BRAND_BASE)) {
32
+ env.registerType(brand, { fields: {} });
33
+ }
16
34
  // Build typed ObjectSchema from manifest.variables if it looks like a schema map
17
35
  const vars = manifest.variables;
18
36
  if (vars !== null && typeof vars === "object" && !Array.isArray(vars)) {
@@ -31,6 +49,26 @@ export function buildTypedCelEnvironment(baseEnv, manifest, extraContextSchema)
31
49
  else {
32
50
  env.registerVariable("variables", "map");
33
51
  }
52
+ // `ports` namespace: each entry types as the brand its `protocol` selects
53
+ // (tcp → TcpPort, udp → UdpPort), so `${{ ports.http }}` carries a nominal
54
+ // type that consuming fields can check against.
55
+ const portsManifest = (rootModuleManifest ?? manifest).ports;
56
+ if (portsManifest !== null && typeof portsManifest === "object" && !Array.isArray(portsManifest)) {
57
+ const portEntries = Object.entries(portsManifest).filter(([, v]) => v !== null && typeof v === "object" && !Array.isArray(v));
58
+ if (portEntries.length > 0) {
59
+ const schema = {};
60
+ for (const [k, v] of portEntries) {
61
+ schema[k] = PORT_PROTOCOL_BRAND[v.protocol ?? "tcp"] ?? "int";
62
+ }
63
+ env.registerVariable({ name: "ports", schema });
64
+ }
65
+ else {
66
+ env.registerVariable("ports", "map");
67
+ }
68
+ }
69
+ else {
70
+ env.registerVariable("ports", "map");
71
+ }
34
72
  env.registerVariable("secrets", "map");
35
73
  env.registerVariable("resources", "map");
36
74
  env.registerVariable("env", "map");
@@ -1 +1 @@
1
- {"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CAkHjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}
1
+ {"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAErD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAInE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CAmGjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}