@telorun/language-server 0.102.0 → 0.103.1

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 (2) hide show
  1. package/dist/language-server.mjs +1174 -1015
  2. package/package.json +5 -5
@@ -17440,8 +17440,8 @@ function normalizePath(path) {
17440
17440
  function splitSource(source) {
17441
17441
  const match = SCHEME.exec(source);
17442
17442
  if (match) return { prefix: `${match[1]}://${match[2]}`, path: match[3] || "/" };
17443
- const unc = /^(\/\/[^/]+)(.*)$/.exec(source);
17444
- if (unc) return { prefix: unc[1], path: unc[2] || "/" };
17443
+ const root = /^(\/\/[^/]+|\/[A-Za-z]:(?=\/|$))(.*)$/.exec(source);
17444
+ if (root) return { prefix: root[1], path: root[2] || "/" };
17445
17445
  return { prefix: "", path: source };
17446
17446
  }
17447
17447
  function dirnameOf(source) {
@@ -17478,7 +17478,7 @@ function resolveAgainst(base, relative) {
17478
17478
  }
17479
17479
 
17480
17480
  // src/engine-version.ts
17481
- var TELO_ENGINE_VERSION = "0.102.0";
17481
+ var TELO_ENGINE_VERSION = "0.103.1";
17482
17482
 
17483
17483
  // src/engine-port.ts
17484
17484
  var import_browser = __toESM(require_main3(), 1);
@@ -38699,21 +38699,22 @@ function createAjv() {
38699
38699
  }
38700
38700
  var ajv = createAjv();
38701
38701
  var compiledSchemaValidators = /* @__PURE__ */ new WeakMap();
38702
+ var DEFAULT_ROLES = { source: "source", target: "target" };
38702
38703
  function unionBranches(schema4) {
38703
38704
  const branches = schema4.anyOf ?? schema4.oneOf;
38704
38705
  if (!Array.isArray(branches) || branches.length === 0) return void 0;
38705
38706
  return branches.filter((b) => b && typeof b === "object");
38706
38707
  }
38707
- function checkSchemaCompatibility(source, target, resolveRef2) {
38708
+ function checkSchemaCompatibility(source, target, resolveRef2, roles = DEFAULT_ROLES) {
38708
38709
  const issues = [];
38709
- compare(source, target, "", issues, resolveRef2, /* @__PURE__ */ new Set());
38710
+ compare(source, target, "", issues, resolveRef2, /* @__PURE__ */ new Set(), roles);
38710
38711
  return { compatible: issues.length === 0, issues };
38711
38712
  }
38712
38713
  function deref(schema4, resolveRef2) {
38713
38714
  if (!resolveRef2 || typeof schema4.$ref !== "string") return schema4;
38714
38715
  return resolveRef2(schema4.$ref) ?? schema4;
38715
38716
  }
38716
- function compare(rawSource, rawTarget, path, issues, resolveRef2, seen) {
38717
+ function compare(rawSource, rawTarget, path, issues, resolveRef2, seen, roles) {
38717
38718
  if (!rawSource || !rawTarget || typeof rawSource !== "object" || typeof rawTarget !== "object") {
38718
38719
  return;
38719
38720
  }
@@ -38734,7 +38735,7 @@ function compare(rawSource, rawTarget, path, issues, resolveRef2, seen) {
38734
38735
  for (const left of lefts) {
38735
38736
  for (const right of rights) {
38736
38737
  const probe = [];
38737
- compare(left, right, path, probe, resolveRef2, new Set(seen));
38738
+ compare(left, right, path, probe, resolveRef2, new Set(seen), roles);
38738
38739
  if (probe.length === 0) return;
38739
38740
  reasons.push(...probe);
38740
38741
  }
@@ -38749,7 +38750,7 @@ function compare(rawSource, rawTarget, path, issues, resolveRef2, seen) {
38749
38750
  if (sourceType && targetType) {
38750
38751
  if (sourceType.name !== targetType.name) {
38751
38752
  issues.push(
38752
- `${path || "/"}: value type mismatch \u2014 source is '${sourceType.name}', target expects '${targetType.name}'`
38753
+ `${path || "/"}: value type mismatch \u2014 ${roles.source} is '${sourceType.name}', ${roles.target} expects '${targetType.name}'`
38753
38754
  );
38754
38755
  return;
38755
38756
  }
@@ -38762,7 +38763,8 @@ function compare(rawSource, rawTarget, path, issues, resolveRef2, seen) {
38762
38763
  `${path}<${argument}>`,
38763
38764
  issues,
38764
38765
  resolveRef2,
38765
- seen
38766
+ seen,
38767
+ roles
38766
38768
  );
38767
38769
  }
38768
38770
  return;
@@ -38772,7 +38774,7 @@ function compare(rawSource, rawTarget, path, issues, resolveRef2, seen) {
38772
38774
  const other = sourceType ? target : source;
38773
38775
  if (targetType?.entry?.fromHost !== void 0 && (source.type !== void 0 || source.const !== void 0 || source.enum !== void 0)) {
38774
38776
  issues.push(
38775
- `${path || "/"}: value type mismatch \u2014 target expects '${targetType.name}', source is a plain value`
38777
+ `${path || "/"}: value type mismatch \u2014 ${roles.target} expects '${targetType.name}', ${roles.source} is a plain value`
38776
38778
  );
38777
38779
  return;
38778
38780
  }
@@ -38781,7 +38783,7 @@ function compare(rawSource, rawTarget, path, issues, resolveRef2, seen) {
38781
38783
  const asJson = base === void 0 ? void 0 : declared.entry.base;
38782
38784
  if (asJson !== other.type) {
38783
38785
  issues.push(
38784
- `${path || "/"}: value type mismatch \u2014 ${sourceType ? "source is" : "target expects"} '${declared.name}', ${sourceType ? "target expects" : "source is"} '${other.type}'`
38786
+ `${path || "/"}: value type mismatch \u2014 ${sourceType ? `${roles.source} is` : `${roles.target} expects`} '${declared.name}', ${sourceType ? `${roles.target} expects` : `${roles.source} is`} '${other.type}'`
38785
38787
  );
38786
38788
  return;
38787
38789
  }
@@ -38789,7 +38791,7 @@ function compare(rawSource, rawTarget, path, issues, resolveRef2, seen) {
38789
38791
  }
38790
38792
  if (typeof source.type === "string" && typeof target.type === "string" && source.type !== target.type) {
38791
38793
  issues.push(
38792
- `${path || "/"}: type mismatch \u2014 source is '${source.type}', target expects '${target.type}'`
38794
+ `${path || "/"}: type mismatch \u2014 ${roles.source} is '${source.type}', ${roles.target} expects '${target.type}'`
38793
38795
  );
38794
38796
  return;
38795
38797
  }
@@ -38800,7 +38802,8 @@ function compare(rawSource, rawTarget, path, issues, resolveRef2, seen) {
38800
38802
  `${path}[]`,
38801
38803
  issues,
38802
38804
  resolveRef2,
38803
- seen
38805
+ seen,
38806
+ roles
38804
38807
  );
38805
38808
  }
38806
38809
  const targetRequired = Array.isArray(target.required) ? target.required : [];
@@ -38809,13 +38812,13 @@ function compare(rawSource, rawTarget, path, issues, resolveRef2, seen) {
38809
38812
  for (const field of targetRequired) {
38810
38813
  if (!(field in sourceProps)) {
38811
38814
  if (source.properties === void 0) continue;
38812
- issues.push(`${path}/${field}: required by target but missing from source`);
38815
+ issues.push(`${path}/${field}: required by ${roles.target} but missing from ${roles.source}`);
38813
38816
  continue;
38814
38817
  }
38815
38818
  const srcProp = sourceProps[field];
38816
38819
  const tgtProp = targetProps[field];
38817
38820
  if (tgtProp && srcProp) {
38818
- compare(srcProp, tgtProp, `${path}/${field}`, issues, resolveRef2, seen);
38821
+ compare(srcProp, tgtProp, `${path}/${field}`, issues, resolveRef2, seen, roles);
38819
38822
  }
38820
38823
  }
38821
38824
  }
@@ -39914,6 +39917,7 @@ function readRefSlot(node) {
39914
39917
  let useCases;
39915
39918
  let inputs;
39916
39919
  let throwsThrough = false;
39920
+ let outputType;
39917
39921
  for (const carrier of nodes) {
39918
39922
  const annotation = carrier["x-telo-ref"];
39919
39923
  for (const kind of readKinds(annotation)) {
@@ -39925,6 +39929,7 @@ function readRefSlot(node) {
39925
39929
  useCases ??= readUseCases(obj.use);
39926
39930
  if (typeof obj.inputs === "string") inputs ??= obj.inputs;
39927
39931
  if (obj.throwsThrough === true) throwsThrough = true;
39932
+ if (isSchemaObject(obj.outputType)) outputType ??= obj.outputType;
39928
39933
  }
39929
39934
  const slot = {
39930
39935
  kinds,
@@ -39935,8 +39940,12 @@ function readRefSlot(node) {
39935
39940
  if (useCases) slot.useCases = useCases;
39936
39941
  if (inputs !== void 0) slot.inputs = inputs;
39937
39942
  if (throwsThrough) slot.throwsThrough = true;
39943
+ if (outputType) slot.outputType = outputType;
39938
39944
  return slot;
39939
39945
  }
39946
+ function isSchemaObject(value) {
39947
+ return !!value && typeof value === "object" && !Array.isArray(value);
39948
+ }
39940
39949
  function isRefSlot(node) {
39941
39950
  return annotationNodes(node).length > 0;
39942
39951
  }
@@ -39949,6 +39958,7 @@ function refSlotAnnotation(slot) {
39949
39958
  else if (slot.uses.length > 1) annotation.use = slot.uses;
39950
39959
  if (slot.inputs !== void 0) annotation.inputs = slot.inputs;
39951
39960
  if (slot.throwsThrough) annotation.throwsThrough = true;
39961
+ if (slot.outputType) annotation.outputType = slot.outputType;
39952
39962
  return annotation;
39953
39963
  }
39954
39964
  function rewriteRefSlotKinds(annotationHolder, map3) {
@@ -41990,6 +42000,7 @@ function refSlotOfEntry(entry) {
41990
42000
  if (entry.useCases) slot.useCases = entry.useCases;
41991
42001
  if (entry.inputs !== void 0) slot.inputs = entry.inputs;
41992
42002
  if (entry.throwsThrough) slot.throwsThrough = true;
42003
+ if (entry.outputType) slot.outputType = entry.outputType;
41993
42004
  return slot;
41994
42005
  }
41995
42006
  function isRefEntry(entry) {
@@ -42197,6 +42208,7 @@ function traverseNode(node, path, sink, root, onStack = /* @__PURE__ */ new Map(
42197
42208
  if (node["x-telo-context"]) entry.context = node["x-telo-context"];
42198
42209
  if (slot.inline) entry.inline = true;
42199
42210
  if (slot.throwsThrough) entry.throwsThrough = true;
42211
+ if (slot.outputType) entry.outputType = slot.outputType;
42200
42212
  sink.ref(path, entry, node);
42201
42213
  for (const variantKey2 of ["oneOf", "anyOf", "allOf"]) {
42202
42214
  const variants = node[variantKey2];
@@ -46153,690 +46165,195 @@ function dataEdges(node, manifest, fromModule, resolveName, rowIdByPath, rowsByO
46153
46165
  return out;
46154
46166
  }
46155
46167
 
46156
- // ../../analyzer/nodejs/src/manifest-analysis.ts
46157
- var ManifestAnalysis = class {
46158
- constructor(manifests, ctx) {
46159
- this.manifests = manifests;
46160
- this.ctx = ctx;
46161
- const rootModules = /* @__PURE__ */ new Set();
46162
- for (const m of manifests) {
46163
- if (isModuleKind(m.kind) && m.metadata?.name) rootModules.add(m.metadata.name);
46164
- }
46165
- this.scopes = { aliasesByModule: ctx.aliasesByModule, rootModules };
46166
- }
46167
- scopes;
46168
- celScopeQuery;
46169
- callGraphMemo;
46170
- moduleGraphMemo;
46171
- /** What CEL sees, per site. Built on first use — its indices are a function of
46172
- * the whole set, and a host that never opens a CEL body should not pay for
46173
- * them. */
46174
- get celScope() {
46175
- return this.celScopeQuery ??= new CelScopeQuery(this.manifests, this.ctx);
46176
- }
46177
- /** What calls what. Built on first use and kept, so the projection and any
46178
- * other consumer over this set share one. */
46179
- get callGraph() {
46180
- return this.callGraphMemo ??= buildCallGraph(this.manifests, this.ctx.defs, {
46181
- aliases: this.ctx.aliases,
46182
- aliasesByModule: this.ctx.aliasesByModule
46183
- });
46184
- }
46185
- /**
46186
- * The module graph: boxes, ordered rows and classed edges — the projection an
46187
- * editor draws.
46188
- *
46189
- * Here rather than on the registry because it is the registry AND a manifest
46190
- * set, which is the pairing this class exists to name; a fifth
46191
- * `registry.x(manifests, …)` factory is the accretion naming it stopped. It
46192
- * shares this analysis's call graph rather than building one of its own.
46193
- *
46194
- * `deps` is supplied by the registry, which owns capability resolution — this
46195
- * class holds the manifests, not the rules for reading a kind.
46196
- */
46197
- moduleGraph(deps, options = {}) {
46198
- return this.moduleGraphMemo ??= buildModuleGraph(
46199
- this.manifests,
46200
- this.callGraph,
46201
- deps,
46202
- options
46203
- );
46204
- }
46205
- /** Whether a resource's name denotes a type or a value — the rule the naming
46206
- * check applies to it, so a rename proposes only a name `telo check` accepts. */
46207
- nameLevel(manifest) {
46208
- const resolveDef = moduleScopedDefResolver(this.ctx.defs, this.ctx.aliases, this.scopes);
46209
- return levelFor(manifest, resolveDef, manifest.metadata?.module);
46210
- }
46211
- /** The manifest a `(kind, name)` pair addresses. */
46212
- resourceFor(kind, name) {
46213
- return findManifest(this.manifests, kind, name);
46214
- }
46215
- /**
46216
- * The invocation contract of the resource a reference names.
46217
- *
46218
- * The shared resolver, so an editor offering a target's input keys is offering
46219
- * exactly what `telo check` validates that call site against and what the
46220
- * kernel binds at dispatch. Layered instance-first: a resource declaring its
46221
- * own `inputType:` narrows the kind's, which is the common case for a
46222
- * `Run.Sequence` used as a handler.
46223
- */
46224
- contractFor(ref, direction) {
46225
- const target = this.resolveRef(ref);
46226
- const definition = ref.kind ? this.definitionFor(ref.kind) : void 0;
46227
- if (!target && !definition) return void 0;
46228
- return resolveContract(
46229
- direction,
46230
- target,
46231
- definition,
46232
- analyzerContractScope(
46233
- this.ctx.defs,
46234
- this.ctx.aliases,
46235
- this.scopes,
46236
- this.manifests
46237
- )
46238
- )?.schema;
46239
- }
46240
- /**
46241
- * The manifest a reference names.
46242
- *
46243
- * An ALIAS narrows before the name does: a flattened set carries every
46244
- * imported library's exported instances, so two libraries exporting a `store`
46245
- * are two manifests with one name. Matching the alias to its target module
46246
- * picks the right one; where the alias resolves to nothing the name alone is
46247
- * used, which is what a local reference needs anyway.
46248
- */
46249
- resolveRef(ref) {
46250
- if (!ref.name) return void 0;
46251
- const byName = this.manifests.filter(
46252
- (m) => m.metadata?.name === ref.name
46253
- );
46254
- if (byName.length === 0) return void 0;
46255
- if (byName.length === 1) return byName[0];
46256
- const targetModule = ref.alias ? this.ctx.aliases.moduleForAlias?.(ref.alias) : void 0;
46257
- if (targetModule) {
46258
- const scoped = byName.find(
46259
- (m) => m.metadata?.module === targetModule
46260
- );
46261
- if (scoped) return scoped;
46168
+ // ../../analyzer/nodejs/src/plain-literal-decoding.ts
46169
+ function decodePlainLiterals(value, schema4, external, rootSchema = schema4) {
46170
+ return mapTextLeaves(value, schema4, decodePlainText, external, rootSchema);
46171
+ }
46172
+ function mapTextLeaves(value, schema4, leaf, external, rootSchema = schema4, visit4) {
46173
+ const walk4 = (node, raw, base, pointer) => {
46174
+ const entered = resolveRefIn(raw, base, external);
46175
+ const selected = selectUnionBranch(entered.schema, node, entered.root, external);
46176
+ const { schema: here, root } = resolveRefIn(selected, entered.root, external);
46177
+ visit4?.(here, node, pointer);
46178
+ if (typeof node === "string") return leaf(here, node, pointer);
46179
+ if (!node || typeof node !== "object") return node;
46180
+ if (here["x-telo-ref"] !== void 0) return node;
46181
+ if (isCompiledValue(node) || isTaggedSentinel(node)) return node;
46182
+ if (Array.isArray(node)) {
46183
+ const item = resolveRefIn(here.items ?? {}, root, external);
46184
+ for (let i = 0; i < node.length; i++) node[i] = walk4(node[i], item.schema, item.root, `${pointer}/${i}`);
46185
+ return node;
46262
46186
  }
46263
- return ref.kind ? byName.find((m) => m.kind === ref.kind) : void 0;
46264
- }
46265
- definitionFor(kind) {
46266
- const canonical = this.ctx.aliases.resolveKind(kind);
46267
- return this.ctx.defs.resolve(kind) ?? (canonical ? this.ctx.defs.resolve(canonical) : void 0);
46268
- }
46269
- };
46270
-
46271
- // ../../analyzer/nodejs/src/schema-tag-text.ts
46272
- function schemaWithTagsAsText(value) {
46273
- if (isCompiledValue(value) || isTaggedSentinel(value)) {
46274
- return typeof value.source === "string" ? value.source : "";
46275
- }
46276
- if (Array.isArray(value)) return value.map(schemaWithTagsAsText);
46277
- if (value && typeof value === "object") {
46278
- const out = {};
46279
- for (const [k, v] of Object.entries(value)) {
46280
- out[k] = schemaWithTagsAsText(v);
46187
+ const proto = Object.getPrototypeOf(node);
46188
+ if (proto !== Object.prototype && proto !== null) return node;
46189
+ const properties = collectProperties(here);
46190
+ const additional = here.additionalProperties && typeof here.additionalProperties === "object" ? here.additionalProperties : void 0;
46191
+ const record = node;
46192
+ for (const key of Object.keys(record)) {
46193
+ const child = properties[key] ?? additional;
46194
+ if (child) {
46195
+ record[key] = walk4(record[key], child, root, `${pointer}/${key.replace(/~/g, "~0").replace(/\//g, "~1")}`);
46196
+ }
46281
46197
  }
46282
- return out;
46198
+ return node;
46199
+ };
46200
+ return walk4(value, schema4, rootSchema, "");
46201
+ }
46202
+ function substituteDecodedCelFields(data, schema4, rootSchema, options = {}) {
46203
+ const decoded = decodePlainLiterals(
46204
+ copyPlainContainers(data),
46205
+ schema4,
46206
+ options.external,
46207
+ rootSchema ?? schema4
46208
+ );
46209
+ return substituteCelFields(decoded, schema4, rootSchema, options);
46210
+ }
46211
+ function copyPlainContainers(value) {
46212
+ if (Array.isArray(value)) return value.map(copyPlainContainers);
46213
+ if (!value || typeof value !== "object" || isCompiledValue(value) || isTaggedSentinel(value)) {
46214
+ return value;
46283
46215
  }
46284
- return value;
46216
+ const proto = Object.getPrototypeOf(value);
46217
+ if (proto !== Object.prototype && proto !== null) return value;
46218
+ return Object.fromEntries(
46219
+ Object.entries(value).map(([key, item]) => [
46220
+ key,
46221
+ copyPlainContainers(item)
46222
+ ])
46223
+ );
46285
46224
  }
46286
46225
 
46287
- // ../../analyzer/nodejs/src/definition-registry.ts
46288
- var DefinitionRegistry = class {
46289
- constructor() {
46290
- for (const def of KERNEL_BUILTINS) this.add(def, true);
46291
- }
46292
- /** Per-instance AJV for cross-module $ref resolution. Isolated so each registry
46293
- * (and thus each AnalysisContext) has its own schema store — no stale schemas
46294
- * across analyze() calls and no unbounded growth across the process lifetime. */
46295
- ajv = createAjv();
46296
- registeredSchemaIds = /* @__PURE__ */ new Set();
46297
- compiledValidators = /* @__PURE__ */ new WeakMap();
46298
- /** The subset of `registeredSchemaIds` claimed by a kind's schema. Kinds and
46299
- * named `Telo.Type`s share one `telo://<module>/<Name>` id space, so this is
46300
- * what lets a colliding type name be reported instead of silently dropped. */
46301
- definitionSchemaIds = /* @__PURE__ */ new Set();
46302
- defs = /* @__PURE__ */ new Map();
46303
- fieldMaps = /* @__PURE__ */ new Map();
46304
- /** Reverse inheritance index: parent kind → direct child kinds. */
46305
- extendedBy = /* @__PURE__ */ new Map();
46306
- /** Memoized inheritance-resolved schemas. `null` records "resolved to
46307
- * nothing", so a kind with no schema is not re-walked on every resource. */
46308
- effectiveSchemas = /* @__PURE__ */ new Map();
46309
- /** DEPRECATED module identity table: identity string → canonical module name
46310
- * ("std/pipeline" → "pipeline"). Serves only the legacy
46311
- * `<namespace>/<module>#<Kind>` form of `x-telo-ref`, kept resolvable for
46312
- * module versions published before constraints named their target by import
46313
- * alias. Fed by `metadata.namespace`, which nothing else reads. */
46314
- identityMap = /* @__PURE__ */ new Map();
46315
- register(definition) {
46316
- this.add(definition, false);
46317
- }
46318
- /** `trustedSchema` skips meta-validating the schema — only for the built-ins,
46319
- * whose validity a test asserts once. */
46320
- add(definition, trustedSchema) {
46321
- const { name, module: mod } = definition.metadata;
46322
- const key = mod ? `${mod}.${name}` : name;
46323
- this.defs.set(key, definition);
46324
- this.fieldMaps.clear();
46325
- this.effectiveSchemas.clear();
46326
- if (definition.capability) {
46327
- this.addExtendedBy(definition.capability, key);
46226
+ // ../../analyzer/nodejs/src/resolve-schema-type-refs.ts
46227
+ var SCHEMA_FIELDS = ["schema", "inputType", "outputType"];
46228
+ var SIGNATURE_FIELDS = ["params", "returns"];
46229
+ function resolveSchemaTypeRefs(resources, aliases, aliasesByModule) {
46230
+ const walk4 = (value, resolveAuthority, ownModule, isSlotRoot) => {
46231
+ if (value === null || typeof value !== "object") return;
46232
+ if (Array.isArray(value)) {
46233
+ for (const item of value) walk4(item, resolveAuthority, ownModule, false);
46234
+ return;
46328
46235
  }
46329
- if (definition.extends) {
46330
- this.addExtendedBy(definition.extends, key);
46236
+ const obj = value;
46237
+ const parsed = parseTeloTypeRef(obj.$ref);
46238
+ if (parsed) {
46239
+ const module = resolveAuthority(parsed.authority);
46240
+ if (module) obj.$ref = canonicalTypeSchemaId(module, parsed.typeName);
46331
46241
  }
46332
- if (definition.kind === "Telo.Abstract" && mod === "Telo") {
46333
- this.identityMap.set("telo", "Telo");
46242
+ const named = resolvedRefInSchema(obj);
46243
+ if (named) {
46244
+ const module = named.alias === void 0 ? ownModule : resolveAuthority(named.alias);
46245
+ if (module) {
46246
+ const canonical = canonicalTypeSchemaId(module, named.name);
46247
+ if (isSlotRoot) {
46248
+ obj.$ref = canonical;
46249
+ } else {
46250
+ delete obj.kind;
46251
+ delete obj.name;
46252
+ delete obj.alias;
46253
+ obj.$ref = canonical;
46254
+ }
46255
+ }
46334
46256
  }
46335
- if (mod && definition.schema) {
46336
- this.tryRegisterSchema(
46337
- mod,
46338
- name,
46339
- definition.schema,
46340
- trustedSchema
46341
- );
46257
+ for (const key of Object.keys(obj)) walk4(obj[key], resolveAuthority, ownModule, false);
46258
+ };
46259
+ for (const r of resources) {
46260
+ const ownModule = r.metadata?.module;
46261
+ const resolver = moduleAliasScope(r.metadata, aliases, aliasesByModule);
46262
+ const resolveAuthority = (authority) => authority === "Self" ? ownModule : resolver?.moduleForAlias(authority);
46263
+ for (const field of SCHEMA_FIELDS) {
46264
+ walk4(r[field], resolveAuthority, ownModule, true);
46342
46265
  }
46343
- }
46344
- addExtendedBy(parent, child) {
46345
- const children = this.extendedBy.get(parent);
46346
- if (children) {
46347
- if (!children.includes(child)) children.push(child);
46348
- } else {
46349
- this.extendedBy.set(parent, [child]);
46266
+ for (const field of SIGNATURE_FIELDS) {
46267
+ walk4(r[field], resolveAuthority, ownModule, false);
46350
46268
  }
46351
46269
  }
46352
- /** DEPRECATED. Register a module identity so the legacy
46353
- * `<namespace>/<module>#<Kind>` form of `x-telo-ref` still resolves for module
46354
- * versions published before constraints named their target by import alias.
46355
- * New manifests declare no namespace and need no identity — their constraints
46356
- * are canonicalized to `<module>.<Kind>` before registration.
46357
- *
46358
- * The "telo" identity is reserved for the built-in module and is populated
46359
- * automatically when a `Telo.Abstract` registers. A namespace-less module must
46360
- * not claim it: overwriting the entry would repoint every legacy `telo#…`
46361
- * constraint at a module that declares no such kind, and the resulting
46362
- * unresolvable ref reads as partial context rather than an error.
46363
- *
46364
- * @param namespace The module's `metadata.namespace`, or null when it declares none.
46365
- * @param moduleName The module's `metadata.name` (e.g. "pipeline", "http-server"). */
46366
- registerModuleIdentity(namespace, moduleName2) {
46367
- if (!namespace || moduleName2 === "Telo") return;
46368
- this.identityMap.set(`${namespace}/${moduleName2}`, moduleName2);
46369
- }
46370
- /** Registers a named `Telo.Type` resource's schema under its canonical
46371
- * module-scoped URI `$id` (`telo://<module>/<name>`), so a sibling schema's
46372
- * `$ref: "telo://Self/<name>"` (rewritten to the canonical form by
46373
- * `resolveSchemaTypeRefs`) resolves during AJV compilation. Mirrors the
46374
- * kernel type controller's `registerSchema(canonicalTypeSchemaId(...))`.
46375
- *
46376
- * Returns `false` when a kind schema in the same module already owns the id —
46377
- * a name collision between a kind and a named type. Definitions register
46378
- * first, so the type is the one that would be dropped, and every
46379
- * `$ref: "telo://<module>/<Name>"` would then silently validate against the
46380
- * kind's schema instead. The caller reports it; nothing is overwritten. */
46381
- registerNamedTypeSchema(id, schema4) {
46382
- if (this.definitionSchemaIds.has(id)) return false;
46383
- if (this.registeredSchemaIds.has(id) || this.ajv.getSchema(id)) return true;
46384
- if (!this.tryAddSchema(schema4, id)) return true;
46385
- this.registeredSchemaIds.add(id);
46386
- return true;
46387
- }
46388
- /**
46389
- * Register a schema, surviving one AJV refuses.
46390
- *
46391
- * `addSchema` META-VALIDATES and THROWS, and a throw here escapes the whole
46392
- * analyze pass: one author schema with `minimum: "3"` in it aborted the run
46393
- * with AJV's own unanchored text and took every other diagnostic in the file
46394
- * down with it — including the anchored one that says exactly which keyword is
46395
- * wrong. Registration is a lookup table for `$ref` resolution, so failing to
46396
- * fill one entry costs a reference that could not have resolved anyway.
46397
- *
46398
- * Nothing is swallowed: an unregisterable schema is invalid, and the two
46399
- * checks that report it both run afterwards and both anchor on the offending
46400
- * line — `SCHEMA_VIOLATION` from the `KindSchema` / `JsonSchema7` fragment the
46401
- * slot points at, and `SCHEMA_COMPILE_ERROR` from {@link schemaCompileError},
46402
- * which wraps `compile` for this same reason.
46403
- */
46404
- tryAddSchema(schema4, id, trusted = false) {
46405
- try {
46406
- this.ajv.addSchema(schemaWithTagsAsText(schema4), id, void 0, !trusted);
46407
- return true;
46408
- } catch {
46409
- return false;
46410
- }
46270
+ }
46271
+ function withCanonicalRefSentinels(value, ownModule, resolveModule) {
46272
+ const target = refSentinelTarget(value);
46273
+ if (target) {
46274
+ const module = target.alias === void 0 || target.alias === "Self" ? ownModule : resolveModule(target.alias);
46275
+ return module ? { $ref: canonicalTypeSchemaId(module, target.name) } : value;
46411
46276
  }
46412
- /** True when a schema is registered under `id` (a canonical `telo://` type id
46413
- * or a definition `$id`). Used to flag schema `$ref`s that resolve to nothing. */
46414
- hasSchemaId(id) {
46415
- return this.registeredSchemaIds.has(id) || this.ajv.getSchema(id) !== void 0;
46277
+ if (Array.isArray(value)) {
46278
+ return value.map((item) => withCanonicalRefSentinels(item, ownModule, resolveModule));
46416
46279
  }
46417
- /** The schema registered under `id`, for a structural comparison that must see
46418
- * THROUGH a named shape. Declaring a shape once and referencing it is the
46419
- * sanctioned way to reuse one, so a comparator that cannot follow the
46420
- * reference judges two opaque nodes and learns nothing.
46421
- *
46422
- * A registered id is READ, never compiled: this is a structural lookup, and
46423
- * compiling a shape the registry holds but cannot compile throws out of
46424
- * whichever walk asked. That shape's compile failure is reported where the
46425
- * shape is validated, not here. */
46426
- schemaForId(id) {
46427
- let entry = this.ajv.schemas[id] ?? this.ajv.refs[id];
46428
- while (typeof entry === "string") entry = this.ajv.schemas[entry] ?? this.ajv.refs[entry];
46429
- const schema4 = entry ? entry.schema : this.ajv.getSchema(id)?.schema;
46430
- return schema4 && typeof schema4 === "object" ? schema4 : void 0;
46280
+ if (value === null || typeof value !== "object") return value;
46281
+ const prototype = Object.getPrototypeOf(value);
46282
+ if (prototype !== Object.prototype && prototype !== null) return value;
46283
+ const out = {};
46284
+ for (const [key, child] of Object.entries(value)) {
46285
+ out[key] = withCanonicalRefSentinels(child, ownModule, resolveModule);
46431
46286
  }
46432
- /**
46433
- * Validates a resource's configuration against its kind's schema, with the
46434
- * offending field's path — what a `SCHEMA_VIOLATION` diagnostic is built from.
46435
- *
46436
- * On THIS registry's AJV, which is the point: it holds every registered
46437
- * definition schema and every named `Telo.Type`, so a kind whose schema
46438
- * references a shape declared elsewhere is checked rather than skipped. The
46439
- * module-level instance this used to run on had none of them registered, so
46440
- * such a schema failed to compile and the failure was swallowed — a resource
46441
- * could be arbitrarily wrong and `telo check` reported nothing, while the
46442
- * kernel (whose validator does resolve the reference) rejected it at boot.
46443
- * Two AJVs answering one question is what made that possible; there is now
46444
- * one, and it is the same one `schemaCompileError` reports through.
46445
- */
46446
- validateWithRefs(data, schema4) {
46447
- const validate2 = this.compiledFor(schema4);
46448
- if (!validate2 || validate2(data)) return [];
46449
- return reduceSchemaErrors(explainFormatErrors(validate2.errors, data)).map(formatSingleError);
46287
+ return out;
46288
+ }
46289
+ function resolvedRefInSchema(obj) {
46290
+ if (typeof obj.kind !== "string" || typeof obj.name !== "string") return null;
46291
+ for (const key of Object.keys(obj)) {
46292
+ if (key !== "kind" && key !== "name" && key !== "alias" && key !== "$ref") return null;
46450
46293
  }
46451
- /** {@link validateWithRefs}, with the path each issue is anchored at. */
46452
- validateResourceConfig(data, schema4) {
46453
- const validate2 = this.compiledFor(schema4);
46454
- if (!validate2 || validate2(data)) return [];
46455
- return schemaIssues(validate2.errors, data);
46294
+ return typeof obj.alias === "string" ? { name: obj.name, alias: obj.alias } : { name: obj.name };
46295
+ }
46296
+
46297
+ // ../../analyzer/nodejs/src/schema-from-sites.ts
46298
+ function schemaFromSites(resource, fieldPath, schemaFrom, ctx) {
46299
+ const out = [];
46300
+ const label2 = `${resource.kind}/${resource.metadata?.name}`;
46301
+ const isAbsolute = schemaFrom.startsWith("/");
46302
+ const expr = isAbsolute ? schemaFrom.slice(1) : schemaFrom;
46303
+ const slashIdx = expr.indexOf("/");
46304
+ if (slashIdx === -1) {
46305
+ out.push({
46306
+ code: "INVALID_SCHEMA_FROM",
46307
+ message: `${label2}: x-telo-schema-from "${schemaFrom}" must contain at least one "/" to separate anchor from JSON Pointer`,
46308
+ path: fieldPath
46309
+ });
46310
+ return out;
46456
46311
  }
46457
- /** Memoized per schema OBJECT — the analyzer validates every resource of a
46458
- * kind against the same one, and this runs at keystroke time in an editor.
46459
- * A schema AJV refuses compiles to `undefined`; that is reported once,
46460
- * anchored on the owning definition, by `schemaCompileError`. */
46461
- compiledFor(schema4) {
46462
- const cached = this.compiledValidators.get(schema4);
46463
- if (cached) return cached;
46464
- try {
46465
- const validate2 = this.ajv.compile(schemaWithTagsAsText(schema4));
46466
- this.compiledValidators.set(schema4, validate2);
46467
- return validate2;
46468
- } catch {
46469
- return void 0;
46312
+ const anchorName = expr.slice(0, slashIdx);
46313
+ const jsonPointer = "/" + expr.slice(slashIdx + 1);
46314
+ if (!isAbsolute && anchorName.includes(".")) {
46315
+ const resolvedResourceKind = ctx.aliases.resolveKind(resource.kind) ?? resource.kind;
46316
+ const resourceDef = ctx.defs.resolve(resource.kind) ?? ctx.defs.resolve(resolvedResourceKind);
46317
+ const ownerScope = moduleAliasScope(resourceDef?.metadata, ctx.aliases, ctx.aliasesByModule);
46318
+ const targetKind = ownerScope.resolveKind(anchorName);
46319
+ if (!targetKind) {
46320
+ const aliasName = anchorName.slice(0, anchorName.indexOf("."));
46321
+ out.push({
46322
+ code: "SCHEMA_FROM_MISSING_PATH",
46323
+ message: `${label2}: x-telo-schema-from at '${fieldPath}' \u2192 cannot resolve alias '${aliasName}' (in '${anchorName}'). Check the import that declares it.`,
46324
+ path: fieldPath
46325
+ });
46326
+ return out;
46470
46327
  }
46471
- }
46472
- /** Returns the AJV compile error for `schema`, or `undefined` when it compiles.
46473
- * Compiles on this registry's instance, which has every loaded module schema
46474
- * plus the manifest root registered, so local `#/$defs`, `telo://manifest`,
46475
- * and cross-module `$ref`s all resolve. Used to fail loud on a definition
46476
- * schema that AJV cannot compile — otherwise `validateAgainstSchema` /
46477
- * `validateWithRefs` would swallow the failure and silently skip every
46478
- * resource of that kind. */
46479
- schemaCompileError(schema4) {
46480
- try {
46481
- this.ajv.compile(schemaWithTagsAsText(schema4));
46482
- return void 0;
46483
- } catch (err) {
46484
- return err instanceof Error ? err.message : String(err);
46328
+ const targetDef = ctx.defs.resolve(targetKind);
46329
+ if (!targetDef?.schema) {
46330
+ out.push({
46331
+ code: "SCHEMA_FROM_MISSING_PATH",
46332
+ message: `${label2}: x-telo-schema-from at '${fieldPath}' \u2192 kind '${targetKind}' has no schema`,
46333
+ path: fieldPath
46334
+ });
46335
+ return out;
46485
46336
  }
46486
- }
46487
- /** Registers a definition schema under the same module-scoped `telo://` id a
46488
- * named `Telo.Type` uses, so a kind schema and a type schema are addressable
46489
- * the same way and a `$ref` between them resolves at AJV compile time. One id
46490
- * space per module: a kind and a named type may not share a name, which
46491
- * `registerNamedTypeSchema` reports rather than resolving silently. */
46492
- tryRegisterSchema(moduleName2, typeName2, schema4, trusted) {
46493
- const id = canonicalTypeSchemaId(moduleName2, typeName2);
46494
- if (this.registeredSchemaIds.has(id)) {
46495
- this.definitionSchemaIds.add(id);
46496
- return;
46337
+ const subSchema = navigateJsonPointer(targetDef.schema, jsonPointer);
46338
+ if (subSchema === void 0) {
46339
+ out.push({
46340
+ code: "SCHEMA_FROM_MISSING_PATH",
46341
+ message: `${label2}: x-telo-schema-from at '${fieldPath}' \u2192 kind '${targetKind}' has no schema path '${jsonPointer}'`,
46342
+ path: fieldPath
46343
+ });
46344
+ return out;
46497
46345
  }
46498
- if (this.ajv.getSchema(id)) {
46499
- throw new Error(`Duplicate definition schema $id: "${id}" is already registered`);
46346
+ for (const { value, path } of resolveFieldEntries(resource, fieldPath)) {
46347
+ if (value == null) continue;
46348
+ out.push({
46349
+ path,
46350
+ value,
46351
+ schema: subSchema,
46352
+ source: `${anchorName}${jsonPointer}`,
46353
+ form: "anchored"
46354
+ });
46500
46355
  }
46501
- this.tryAddSchema(schema4, id, trusted);
46502
- this.registeredSchemaIds.add(id);
46503
- this.definitionSchemaIds.add(id);
46504
- }
46505
- /** Resolves an `x-telo-ref` constraint to a canonical registry kind key.
46506
- *
46507
- * The constraint is already canonical `<module>.<Kind>`: alias-form values
46508
- * (`KvStore.Store`, `Self.Store`, `Telo.Invocable`) are rewritten in the
46509
- * declaring module's scope by `resolveSchemaRefKinds` before registration, so
46510
- * no module context is needed here.
46511
- *
46512
- * The legacy `<namespace>/<module>#<Kind>` form still resolves through the
46513
- * identity table for module versions published before the alias form existed:
46514
- *
46515
- * "telo#Invocable" → "Telo.Invocable"
46516
- * "std/http-server#Server" → "http-server.Server"
46517
- *
46518
- * Returns undefined when a legacy string is malformed or its identity was
46519
- * never registered. */
46520
- resolveRef(xTeloRef) {
46521
- const hash = xTeloRef.indexOf("#");
46522
- if (hash === -1) return xTeloRef;
46523
- if (hash === xTeloRef.length - 1) return void 0;
46524
- const moduleName2 = this.identityMap.get(xTeloRef.slice(0, hash));
46525
- if (!moduleName2) return void 0;
46526
- return `${moduleName2}.${xTeloRef.slice(hash + 1)}`;
46527
- }
46528
- resolve(kind) {
46529
- return this.defs.get(kind);
46530
- }
46531
- /**
46532
- * The kind's AUTHOR-FACING schema — its own merged with everything it
46533
- * inherits along `extends` — memoized per kind.
46534
- *
46535
- * This is what every consumer reading a kind's schema to interpret an
46536
- * INSTANCE must use, because that is the schema the instance was authored
46537
- * against and the one the kernel stamps at definition registration. Reading
46538
- * `resolve(kind).schema` instead sees only what the leaf declared, so an
46539
- * annotation on a parent — a CEL context region, a step body, an error
46540
- * context — is invisible on every child, silently and per consumer. Sharing
46541
- * one memo is what keeps those consumers from drifting apart.
46542
- *
46543
- * Lazy for the same reason `getFieldMap` is: a child registered before its
46544
- * parent still sees the inherited half once both are present.
46545
- */
46546
- effectiveSchema(kind) {
46547
- const cached = this.effectiveSchemas.get(kind);
46548
- if (cached !== void 0) return cached ?? void 0;
46549
- const def = this.defs.get(kind);
46550
- if (!def) return void 0;
46551
- const schema4 = effectiveAuthorSchema(def, (k) => this.resolve(k));
46552
- this.effectiveSchemas.set(kind, schema4 ?? null);
46553
- return schema4;
46554
- }
46555
- /** The effective schema for a definition, by the kind it is registered under.
46556
- * Falls back to the definition's own schema when it is not in this registry
46557
- * (a synthesized or not-yet-registered kind), which is the pre-inheritance
46558
- * behaviour and the safe direction: it types less, never wrongly. */
46559
- effectiveSchemaOf(definition) {
46560
- if (!definition) return void 0;
46561
- const { name, module: mod } = definition.metadata;
46562
- const resolved = this.effectiveSchema(mod ? `${mod}.${name}` : name);
46563
- return resolved ?? definition.schema;
46564
- }
46565
- /** Returns the reference field map for the given kind, computed lazily from the
46566
- * kind's AUTHOR-FACING (inheritance-resolved) schema and memoized. Lazy so a
46567
- * child registered before its parent still sees the parent's inherited ref
46568
- * slots once both are present. */
46569
- getFieldMap(kind) {
46570
- const cached = this.fieldMaps.get(kind);
46571
- if (cached) return cached;
46572
- const def = this.defs.get(kind);
46573
- if (!def) return void 0;
46574
- const schema4 = effectiveAuthorSchema(def, (k) => this.resolve(k));
46575
- const map3 = buildReferenceFieldMap(schema4 ?? {});
46576
- this.fieldMaps.set(kind, map3);
46577
- return map3;
46578
- }
46579
- /** Returns the field map for `kind`, falling back to the alias-resolved kind when not found. */
46580
- getFieldMapForKind(kind, aliases) {
46581
- const fm = this.getFieldMap(kind);
46582
- if (fm) return fm;
46583
- const resolved = aliases?.resolveKind(kind);
46584
- return resolved ? this.getFieldMap(resolved) : void 0;
46585
- }
46586
- /** Returns the field map for `resource.kind` with x-telo-schema-from entries replaced
46587
- * by their nested ref/scope slots — so Phase 2 inline normalization and Phase 5
46588
- * injection see encoders nested behind a schema-from indirection (e.g.
46589
- * http-server `Server.notFoundHandler.returns[].content[mime].encoder`).
46590
- *
46591
- * Only static absolute schema-from paths with a dotted alias anchor are expanded
46592
- * (e.g. "HttpDispatch.Outcomes/$defs/Returns"). Relative and unqualified absolute
46593
- * anchors depend on a sibling property at runtime and stay unexpanded; the
46594
- * analyzer's reference validation phase already flags the cases that matter. */
46595
- expandedFieldMapForResource(resource, aliases, aliasesByModule) {
46596
- const moduleScope = moduleAliasScope(resource.metadata, aliases, aliasesByModule);
46597
- const baseMap = this.getFieldMapForKind(resource.kind, moduleScope);
46598
- if (!baseMap) return void 0;
46599
- const resolvedKind = moduleScope.resolveKind(resource.kind) ?? resource.kind;
46600
- const def = this.resolve(resource.kind) ?? this.resolve(resolvedKind);
46601
- const ownerScope = moduleAliasScope(def?.metadata, aliases, aliasesByModule);
46602
- const expanded = /* @__PURE__ */ new Map();
46603
- for (const [path, entry] of baseMap) {
46604
- if (!isSchemaFromEntry(entry)) {
46605
- expanded.set(path, entry);
46606
- continue;
46607
- }
46608
- const sub = this.resolveSchemaFromSubMap(entry.schemaFrom, path, ownerScope);
46609
- if (!sub) continue;
46610
- for (const [subPath, subEntry] of sub) expanded.set(subPath, subEntry);
46611
- }
46612
- return expanded;
46613
- }
46614
- /**
46615
- * The schema an `x-telo-schema-from` slot derives its shape from.
46616
- *
46617
- * Only the STATIC form resolves: an anchor that is a dotted alias-qualified
46618
- * kind (`HttpDispatch.Request/$defs/Matcher`). The polymorphic forms — a
46619
- * relative anchor, or a single-segment absolute one — name a sibling property
46620
- * whose value is known per resource, so a definition-level lookup would be
46621
- * guessing at one instance's shape.
46622
- *
46623
- * Its own method because a schema-from slot is otherwise INVISIBLE to anything
46624
- * reading `properties`: the field map needs the nested ref slots, and an IDE
46625
- * needs the very same node to offer a key or describe one. Two resolutions of
46626
- * one annotation would eventually disagree about which anchors are static.
46627
- */
46628
- resolveSchemaFromNode(schemaFrom, ownerScope) {
46629
- const isAbsolute = schemaFrom.startsWith("/");
46630
- const expr = isAbsolute ? schemaFrom.slice(1) : schemaFrom;
46631
- const slashIdx = expr.indexOf("/");
46632
- if (slashIdx === -1) return void 0;
46633
- const anchorName = expr.slice(0, slashIdx);
46634
- const jsonPointer = "/" + expr.slice(slashIdx + 1);
46635
- if (!anchorName.includes(".")) return void 0;
46636
- const targetKind = ownerScope.resolveKind(anchorName);
46637
- if (!targetKind) return void 0;
46638
- const targetDef = this.resolve(targetKind);
46639
- if (!targetDef?.schema) return void 0;
46640
- const subSchema = navigateJsonPointer(
46641
- targetDef.schema,
46642
- jsonPointer
46643
- );
46644
- if (!subSchema || typeof subSchema !== "object") return void 0;
46645
- return subSchema;
46646
- }
46647
- resolveSchemaFromSubMap(schemaFrom, fieldPath, ownerScope) {
46648
- const subSchema = this.resolveSchemaFromNode(schemaFrom, ownerScope);
46649
- if (!subSchema) return null;
46650
- return buildFieldMapAtPath(subSchema, fieldPath);
46651
- }
46652
- /** The kinds a definition descends from DIRECTLY — the same two edges
46653
- * `register` feeds into `extendedBy`, read the other way.
46654
- *
46655
- * Both spellings, because either can carry the edge that satisfies a slot:
46656
- * `capability:` is the legacy implements-this form, `extends:` the canonical
46657
- * one. A consumer walking these by hand would be a second reading of what an
46658
- * inheritance edge IS, and the downward index and the upward walk have to
46659
- * agree about that forever. */
46660
- parentsOf(kind) {
46661
- const def = this.defs.get(kind);
46662
- if (!def) return [];
46663
- return [def.capability, def.extends].filter(
46664
- (parent) => typeof parent === "string" && parent.length > 0
46665
- );
46666
- }
46667
- /** Whether every kind this one descends from is registered.
46668
- *
46669
- * When it is, what the kind implements is FULLY KNOWN: a target it does not
46670
- * reach is one it genuinely does not implement, so a mismatch is a verdict.
46671
- * When a hop is missing — an unimported abstract, an alias the declaring file
46672
- * could not resolve — a mismatch cannot be told from a missing dependency,
46673
- * which is the partial context every reference check stays lenient in. */
46674
- ancestryResolved(kind) {
46675
- const queue = [kind];
46676
- const seen = /* @__PURE__ */ new Set();
46677
- while (queue.length > 0) {
46678
- const current = queue.shift();
46679
- if (seen.has(current)) continue;
46680
- seen.add(current);
46681
- if (!this.defs.has(current)) return false;
46682
- queue.push(...this.parentsOf(current));
46683
- }
46684
- return true;
46685
- }
46686
- /** Returns all definitions that transitively extend the given abstract kind.
46687
- * Follows the capability chain to any depth (equivalent to instanceof in OOP).
46688
- * Definitions are included regardless of registration order. */
46689
- getByExtends(abstractKind) {
46690
- const result = [];
46691
- const queue = [abstractKind];
46692
- while (queue.length > 0) {
46693
- const parent = queue.shift();
46694
- const children = this.extendedBy.get(parent);
46695
- if (!children) continue;
46696
- for (const child of children) {
46697
- const def = this.defs.get(child);
46698
- if (def) result.push(def);
46699
- queue.push(child);
46700
- }
46701
- }
46702
- return result;
46703
- }
46704
- kinds() {
46705
- return Array.from(this.defs.keys());
46706
- }
46707
- };
46708
-
46709
- // ../../analyzer/nodejs/src/resolve-schema-type-refs.ts
46710
- var SCHEMA_FIELDS = ["schema", "inputType", "outputType"];
46711
- var SIGNATURE_FIELDS = ["params", "returns"];
46712
- function resolveSchemaTypeRefs(resources, aliases, aliasesByModule) {
46713
- const walk4 = (value, resolveAuthority, ownModule, isSlotRoot) => {
46714
- if (value === null || typeof value !== "object") return;
46715
- if (Array.isArray(value)) {
46716
- for (const item of value) walk4(item, resolveAuthority, ownModule, false);
46717
- return;
46718
- }
46719
- const obj = value;
46720
- const parsed = parseTeloTypeRef(obj.$ref);
46721
- if (parsed) {
46722
- const module = resolveAuthority(parsed.authority);
46723
- if (module) obj.$ref = canonicalTypeSchemaId(module, parsed.typeName);
46724
- }
46725
- const named = resolvedRefInSchema(obj);
46726
- if (named) {
46727
- const module = named.alias === void 0 ? ownModule : resolveAuthority(named.alias);
46728
- if (module) {
46729
- const canonical = canonicalTypeSchemaId(module, named.name);
46730
- if (isSlotRoot) {
46731
- obj.$ref = canonical;
46732
- } else {
46733
- delete obj.kind;
46734
- delete obj.name;
46735
- delete obj.alias;
46736
- obj.$ref = canonical;
46737
- }
46738
- }
46739
- }
46740
- for (const key of Object.keys(obj)) walk4(obj[key], resolveAuthority, ownModule, false);
46741
- };
46742
- for (const r of resources) {
46743
- const ownModule = r.metadata?.module;
46744
- const resolver = moduleAliasScope(r.metadata, aliases, aliasesByModule);
46745
- const resolveAuthority = (authority) => authority === "Self" ? ownModule : resolver?.moduleForAlias(authority);
46746
- for (const field of SCHEMA_FIELDS) {
46747
- walk4(r[field], resolveAuthority, ownModule, true);
46748
- }
46749
- for (const field of SIGNATURE_FIELDS) {
46750
- walk4(r[field], resolveAuthority, ownModule, false);
46751
- }
46752
- }
46753
- }
46754
- function withCanonicalRefSentinels(value, ownModule, resolveModule) {
46755
- const target = refSentinelTarget(value);
46756
- if (target) {
46757
- const module = target.alias === void 0 || target.alias === "Self" ? ownModule : resolveModule(target.alias);
46758
- return module ? { $ref: canonicalTypeSchemaId(module, target.name) } : value;
46759
- }
46760
- if (Array.isArray(value)) {
46761
- return value.map((item) => withCanonicalRefSentinels(item, ownModule, resolveModule));
46762
- }
46763
- if (value === null || typeof value !== "object") return value;
46764
- const prototype = Object.getPrototypeOf(value);
46765
- if (prototype !== Object.prototype && prototype !== null) return value;
46766
- const out = {};
46767
- for (const [key, child] of Object.entries(value)) {
46768
- out[key] = withCanonicalRefSentinels(child, ownModule, resolveModule);
46769
- }
46770
- return out;
46771
- }
46772
- function resolvedRefInSchema(obj) {
46773
- if (typeof obj.kind !== "string" || typeof obj.name !== "string") return null;
46774
- for (const key of Object.keys(obj)) {
46775
- if (key !== "kind" && key !== "name" && key !== "alias" && key !== "$ref") return null;
46776
- }
46777
- return typeof obj.alias === "string" ? { name: obj.name, alias: obj.alias } : { name: obj.name };
46778
- }
46779
-
46780
- // ../../analyzer/nodejs/src/schema-from-sites.ts
46781
- function schemaFromSites(resource, fieldPath, schemaFrom, ctx) {
46782
- const out = [];
46783
- const label2 = `${resource.kind}/${resource.metadata?.name}`;
46784
- const isAbsolute = schemaFrom.startsWith("/");
46785
- const expr = isAbsolute ? schemaFrom.slice(1) : schemaFrom;
46786
- const slashIdx = expr.indexOf("/");
46787
- if (slashIdx === -1) {
46788
- out.push({
46789
- code: "INVALID_SCHEMA_FROM",
46790
- message: `${label2}: x-telo-schema-from "${schemaFrom}" must contain at least one "/" to separate anchor from JSON Pointer`,
46791
- path: fieldPath
46792
- });
46793
- return out;
46794
- }
46795
- const anchorName = expr.slice(0, slashIdx);
46796
- const jsonPointer = "/" + expr.slice(slashIdx + 1);
46797
- if (!isAbsolute && anchorName.includes(".")) {
46798
- const resolvedResourceKind = ctx.aliases.resolveKind(resource.kind) ?? resource.kind;
46799
- const resourceDef = ctx.defs.resolve(resource.kind) ?? ctx.defs.resolve(resolvedResourceKind);
46800
- const ownerScope = moduleAliasScope(resourceDef?.metadata, ctx.aliases, ctx.aliasesByModule);
46801
- const targetKind = ownerScope.resolveKind(anchorName);
46802
- if (!targetKind) {
46803
- const aliasName = anchorName.slice(0, anchorName.indexOf("."));
46804
- out.push({
46805
- code: "SCHEMA_FROM_MISSING_PATH",
46806
- message: `${label2}: x-telo-schema-from at '${fieldPath}' \u2192 cannot resolve alias '${aliasName}' (in '${anchorName}'). Check the import that declares it.`,
46807
- path: fieldPath
46808
- });
46809
- return out;
46810
- }
46811
- const targetDef = ctx.defs.resolve(targetKind);
46812
- if (!targetDef?.schema) {
46813
- out.push({
46814
- code: "SCHEMA_FROM_MISSING_PATH",
46815
- message: `${label2}: x-telo-schema-from at '${fieldPath}' \u2192 kind '${targetKind}' has no schema`,
46816
- path: fieldPath
46817
- });
46818
- return out;
46819
- }
46820
- const subSchema = navigateJsonPointer(targetDef.schema, jsonPointer);
46821
- if (subSchema === void 0) {
46822
- out.push({
46823
- code: "SCHEMA_FROM_MISSING_PATH",
46824
- message: `${label2}: x-telo-schema-from at '${fieldPath}' \u2192 kind '${targetKind}' has no schema path '${jsonPointer}'`,
46825
- path: fieldPath
46826
- });
46827
- return out;
46828
- }
46829
- for (const { value, path } of resolveFieldEntries(resource, fieldPath)) {
46830
- if (value == null) continue;
46831
- out.push({
46832
- path,
46833
- value,
46834
- schema: subSchema,
46835
- source: `${anchorName}${jsonPointer}`,
46836
- form: "anchored"
46837
- });
46838
- }
46839
- return out;
46356
+ return out;
46840
46357
  }
46841
46358
  let anchorPath;
46842
46359
  if (isAbsolute) {
@@ -46958,132 +46475,913 @@ function stepCallSites(manifest, defSchema, ctx) {
46958
46475
  );
46959
46476
  });
46960
46477
  }
46961
- return out;
46962
- }
46963
- function slotCallSites(manifest, fieldMap, ctx) {
46964
- const out = [];
46965
- if (!fieldMap) return out;
46966
- for (const [fieldPath, entry] of fieldMap) {
46967
- if (!isRefEntry(entry) || !entry.inputs) continue;
46968
- const pointer = pointerSegments(entry.inputs);
46969
- if (!pointer) continue;
46970
- for (const { value: invoke, path: slotPath } of resolveFieldEntries(manifest, fieldPath)) {
46971
- if (!invoke || typeof invoke !== "object" || Array.isArray(invoke)) continue;
46972
- const enclosing = slotPath.slice(0, Math.max(0, slotPath.lastIndexOf(".")));
46973
- const inputsPath = [enclosing, ...pointer].filter(Boolean).join(".");
46974
- const values = navigateConcretePath2(manifest, inputsPath);
46975
- if (!values || typeof values !== "object" || Array.isArray(values)) continue;
46976
- out.push(
46977
- callSite("slot", inputsPath, values, invoke, manifest, ctx)
46978
- );
46979
- }
46478
+ return out;
46479
+ }
46480
+ function slotCallSites(manifest, fieldMap, ctx) {
46481
+ const out = [];
46482
+ if (!fieldMap) return out;
46483
+ for (const [fieldPath, entry] of fieldMap) {
46484
+ if (!isRefEntry(entry) || !entry.inputs) continue;
46485
+ const pointer = pointerSegments(entry.inputs);
46486
+ if (!pointer) continue;
46487
+ for (const { value: invoke, path: slotPath } of resolveFieldEntries(manifest, fieldPath)) {
46488
+ if (!invoke || typeof invoke !== "object" || Array.isArray(invoke)) continue;
46489
+ const enclosing = slotPath.slice(0, Math.max(0, slotPath.lastIndexOf(".")));
46490
+ const inputsPath = [enclosing, ...pointer].filter(Boolean).join(".");
46491
+ const values = navigateConcretePath2(manifest, inputsPath);
46492
+ if (!values || typeof values !== "object" || Array.isArray(values)) continue;
46493
+ out.push(
46494
+ callSite("slot", inputsPath, values, invoke, manifest, ctx)
46495
+ );
46496
+ }
46497
+ }
46498
+ return out;
46499
+ }
46500
+ function pointerSegments(pointer) {
46501
+ if (!pointer.startsWith("/")) return void 0;
46502
+ const segments = pointer.slice(1).split("/").map((s) => s.replace(/~1/g, "/").replace(/~0/g, "~"));
46503
+ return segments.every((s) => s.length > 0 && !/^\d+$/.test(s)) ? segments : void 0;
46504
+ }
46505
+ function templateCallSite(manifest, ctx) {
46506
+ if (manifest.kind !== "Telo.Definition") return void 0;
46507
+ const values = manifest.inputs;
46508
+ if (!values || typeof values !== "object" || Array.isArray(values)) return void 0;
46509
+ const scope = contractScopeOf(ctx);
46510
+ const module = manifest.metadata?.module;
46511
+ for (const slot of ["invoke", "provide"]) {
46512
+ const target = dispatchTargetOf(manifest, slot);
46513
+ if (!target) continue;
46514
+ const definition2 = scope.resolveIn(target.kind, module);
46515
+ const aliases = moduleAliasScope(manifest.metadata, ctx.aliases, ctx.aliasesByModule);
46516
+ const entry = target.entry && {
46517
+ ...target.entry,
46518
+ inputType: withCanonicalRefSentinels(
46519
+ target.entry.inputType,
46520
+ module,
46521
+ (alias) => aliases?.moduleForAlias(alias)
46522
+ )
46523
+ };
46524
+ return {
46525
+ origin: "template",
46526
+ path: "inputs",
46527
+ values,
46528
+ invoke: { kind: target.kind, name: target.name },
46529
+ invokedManifest: entry,
46530
+ invokedDefinition: definition2,
46531
+ contract: resolveContract("inputType", entry, definition2, scope)
46532
+ };
46533
+ }
46534
+ const dispatch = [manifest.invoke, manifest.provide].find(
46535
+ (d) => d && typeof d === "object" && !Array.isArray(d) && typeof d.kind === "string"
46536
+ );
46537
+ if (!dispatch) return void 0;
46538
+ const definition = scope.resolveIn(dispatch.kind, module);
46539
+ return {
46540
+ origin: "template",
46541
+ path: "inputs",
46542
+ values,
46543
+ invoke: { kind: dispatch.kind },
46544
+ invokedManifest: void 0,
46545
+ invokedDefinition: definition,
46546
+ contract: resolveContract("inputType", void 0, definition, scope)
46547
+ };
46548
+ }
46549
+ function valueSchemaSites(manifest, defSchema, ctx) {
46550
+ if (!defSchema) return [];
46551
+ const out = [];
46552
+ for (const { scope, from } of valueSchemaSlots(defSchema)) {
46553
+ const schema4 = resolveTypeFieldToSchema(manifest[from], ctx.typeManifests);
46554
+ if (!schema4 || typeof schema4 !== "object") continue;
46555
+ for (const { path, value } of resolveScopeValues(manifest, scope)) {
46556
+ out.push({ path, value, schema: schema4, from });
46557
+ }
46558
+ }
46559
+ return out;
46560
+ }
46561
+ function derivedSlotsOf(resource, ctx) {
46562
+ const manifest = resource;
46563
+ const moduleScope = moduleAliasScope(resource.metadata, ctx.aliases, ctx.aliasesByModule);
46564
+ const resolvedKind = moduleScope.resolveKind(resource.kind);
46565
+ const definition = ctx.defs.resolve(resource.kind) ?? (resolvedKind ? ctx.defs.resolve(resolvedKind) : void 0);
46566
+ const schema4 = ctx.defs.effectiveSchemaOf(definition);
46567
+ const out = [];
46568
+ const at2 = (path, value, schema5) => out.push({ path, value, schema: schema5, replace: (next) => assignConcretePath(manifest, path, next) });
46569
+ const calls = [
46570
+ ...schema4 ? stepCallSites(manifest, schema4, ctx) : [],
46571
+ ...slotCallSites(
46572
+ manifest,
46573
+ ctx.defs.expandedFieldMapForResource(resource, ctx.aliases, ctx.aliasesByModule),
46574
+ ctx
46575
+ )
46576
+ ];
46577
+ const template = templateCallSite(manifest, ctx);
46578
+ if (template) calls.push(template);
46579
+ for (const call of calls) {
46580
+ if (call.contract) at2(call.path, call.values, call.contract.schema);
46581
+ }
46582
+ for (const site of valueSchemaSites(manifest, schema4, ctx)) {
46583
+ at2(site.path, site.value, site.schema);
46584
+ }
46585
+ visitManifest(
46586
+ [resource],
46587
+ ctx.defs,
46588
+ {
46589
+ onSchemaFrom: (e) => {
46590
+ for (const site of schemaFromSites(manifest, e.fieldPath, e.entry.schemaFrom, ctx)) {
46591
+ if (isSchemaFromSite(site)) at2(site.path, site.value, site.schema);
46592
+ }
46593
+ }
46594
+ },
46595
+ {
46596
+ aliases: ctx.aliases,
46597
+ aliasesByModule: ctx.aliasesByModule,
46598
+ skipKinds: REF_VALIDATION_SKIP_KINDS,
46599
+ expand: false
46600
+ }
46601
+ );
46602
+ return out;
46603
+ }
46604
+
46605
+ // ../../analyzer/nodejs/src/validate-step-inputs.ts
46606
+ var missingRequired = (issue) => /is missing required property/.test(issue.message);
46607
+ function containerOf(path) {
46608
+ const dot = path.lastIndexOf(".");
46609
+ return dot === -1 ? "" : path.slice(0, dot);
46610
+ }
46611
+ var declarationsByName = /* @__PURE__ */ new WeakMap();
46612
+ function byNameOf(allManifests) {
46613
+ let byName = declarationsByName.get(allManifests);
46614
+ if (!byName) {
46615
+ byName = /* @__PURE__ */ new Map();
46616
+ for (const m of allManifests) {
46617
+ const name = m.metadata?.name;
46618
+ if (typeof name !== "string" || m.kind === "Telo.Import") continue;
46619
+ const list2 = byName.get(name);
46620
+ if (list2) list2.push(m);
46621
+ else byName.set(name, [m]);
46622
+ }
46623
+ declarationsByName.set(allManifests, byName);
46624
+ }
46625
+ return byName;
46626
+ }
46627
+ function callSiteContext(manifest, allManifests, defs, aliases, scopes) {
46628
+ const fromModule = manifest.metadata?.module;
46629
+ const scope = moduleAliasScope({ module: fromModule }, aliases, scopes.aliasesByModule);
46630
+ return {
46631
+ defs,
46632
+ aliases,
46633
+ aliasesByModule: scopes.aliasesByModule,
46634
+ rootModules: scopes.rootModules,
46635
+ typeManifests: allManifests,
46636
+ resolveTarget: (ref) => typeof ref.name === "string" ? resolveReferenceTarget(
46637
+ byNameOf(allManifests),
46638
+ { name: ref.name, ...typeof ref.alias === "string" ? { alias: ref.alias } : {} },
46639
+ fromModule,
46640
+ (alias) => scope.moduleForAlias(alias)
46641
+ ) : void 0
46642
+ };
46643
+ }
46644
+ function collectStepInputIssues(manifest, defSchema, allManifests, defs, aliases, scopes, stepContext) {
46645
+ const ctx = callSiteContext(manifest, allManifests, defs, aliases, scopes);
46646
+ return stepCallSites(manifest, defSchema, ctx).flatMap(
46647
+ (site) => checkCallSite(site, manifest, allManifests, defs, aliases, scopes, stepContext)
46648
+ );
46649
+ }
46650
+ function collectRefInputIssues(manifest, fieldMap, allManifests, defs, aliases, scopes) {
46651
+ const ctx = callSiteContext(manifest, allManifests, defs, aliases, scopes);
46652
+ return slotCallSites(manifest, fieldMap, ctx).flatMap(
46653
+ (site) => checkCallSite(site, manifest, allManifests, defs, aliases, scopes)
46654
+ );
46655
+ }
46656
+ function checkCallSite(site, manifest, allManifests, defs, aliases, scopes, stepContext) {
46657
+ const out = [];
46658
+ const { contract, values, invoke, invokedManifest, invokedDefinition } = site;
46659
+ if (!contract) return out;
46660
+ const targetLabel2 = invoke.name ?? invoke.kind ?? "the invoked resource";
46661
+ const contractScope = analyzerContractScope(defs, aliases, scopes, allManifests);
46662
+ const readingModule = manifest.metadata?.module;
46663
+ const celPaths = /* @__PURE__ */ new Set();
46664
+ const substituted = substituteDecodedCelFields(values, contract.schema, void 0, {
46665
+ onSubstitute: (p) => celPaths.add(p),
46666
+ // A contract may name a shape declared elsewhere. Both halves need the
46667
+ // resolver or they disagree about the same slot.
46668
+ external: (ref) => defs.schemaForId(ref)
46669
+ });
46670
+ const roots = [];
46671
+ if (stepContext) roots.push(["steps.", stepContext]);
46672
+ const ownContract = resolveContract(
46673
+ "inputType",
46674
+ manifest,
46675
+ contractScope.resolveIn(manifest.kind, readingModule),
46676
+ contractScope
46677
+ );
46678
+ if (ownContract) roots.push(["inputs.", ownContract.schema]);
46679
+ if (roots.length > 0) {
46680
+ for (const [inputName, inputValue] of Object.entries(values)) {
46681
+ const chain = plainChainOf(inputValue);
46682
+ const root = chain ? roots.find(([prefix]) => chain.startsWith(prefix)) : void 0;
46683
+ if (!chain || !root) continue;
46684
+ const produced = navigateSchemaToExprPath(root[1], chain.slice(root[0].length));
46685
+ const slotSchema = contract.schema.properties?.[inputName];
46686
+ if (!produced || !slotSchema) continue;
46687
+ if (isLiveSlot(produced)) {
46688
+ const retry = site.step ? declaredRetry(site.step.value, site.step.schema, invokedManifest, invokedDefinition) : void 0;
46689
+ if (retry !== void 0) {
46690
+ out.push({
46691
+ path: `${site.path}.${inputName}`,
46692
+ targetLabel: targetLabel2,
46693
+ message: `'${inputName}' is a live value, which is consumed by reading and so exists once \u2014 but ${retry} re-attempts the dispatch, and a re-attempt would pass nothing. Collect it to a value first, or chunk the work so each attempt carries its own replayable piece.`,
46694
+ code: "LIVE_VALUE_RETRIED"
46695
+ });
46696
+ continue;
46697
+ }
46698
+ }
46699
+ if (!valueTypeOf(produced) || !valueTypeOf(slotSchema)) continue;
46700
+ const { compatible, issues } = checkSchemaCompatibility(
46701
+ produced,
46702
+ slotSchema,
46703
+ (ref) => defs.schemaForId(ref)
46704
+ );
46705
+ if (compatible) continue;
46706
+ out.push({
46707
+ path: `${site.path}.${inputName}`,
46708
+ targetLabel: targetLabel2,
46709
+ message: issues.join("; "),
46710
+ code: "CEL_TYPE_ARGUMENT_MISMATCH"
46711
+ });
46712
+ }
46713
+ }
46714
+ for (const issue of defs.validateResourceConfig(substituted, contract.schema)) {
46715
+ if (celPaths.has(issue.path)) continue;
46716
+ const anchor = missingRequired(issue) ? containerOf(issue.path) : issue.path;
46717
+ out.push({
46718
+ path: anchor ? `${site.path}.${anchor}` : site.path,
46719
+ targetLabel: targetLabel2,
46720
+ message: issue.message
46721
+ });
46722
+ }
46723
+ return out;
46724
+ }
46725
+ function declaredRetry(step, stepItemSchema, invokedManifest, invokedDef) {
46726
+ for (const [field, budget] of retryFields(stepItemSchema)) {
46727
+ if (budget(step?.[field]) > 0) return `the step's \`${field}\``;
46728
+ }
46729
+ for (const [field, budget] of retryFields(invokedDef?.schema)) {
46730
+ if (budget(invokedManifest?.[field]) > 0) return `the target's \`${field}\``;
46731
+ }
46732
+ return void 0;
46733
+ }
46734
+ var RETRY_BUDGET = {
46735
+ RetryPolicy: (value) => {
46736
+ if (!value || typeof value !== "object") return 0;
46737
+ const attempts = value.attempts;
46738
+ return typeof attempts === "number" ? attempts : 0;
46739
+ },
46740
+ RetryAttempts: (value) => typeof value === "number" ? value : 0
46741
+ };
46742
+ function retryFields(schema4) {
46743
+ if (!schema4) return [];
46744
+ const out = [];
46745
+ for (const [key, sub] of gatherPropertySchemas(schema4)) {
46746
+ const budget = RETRY_BUDGET[manifestFragmentOf(sub) ?? ""];
46747
+ if (budget) out.push([key, budget]);
46748
+ }
46749
+ return out;
46750
+ }
46751
+
46752
+ // ../../analyzer/nodejs/src/validate-reference-output.ts
46753
+ function producedOutputContract(target, definition, scope, defs) {
46754
+ return resolveContract("outputType", target, definition, scope)?.schema ?? valueDerivedContract(
46755
+ target,
46756
+ defs.effectiveSchemaOf(definition),
46757
+ "outputType"
46758
+ );
46759
+ }
46760
+ var OUTPUT_ROLES = { source: "the target's output", target: "this slot" };
46761
+ function referenceOutputRefusal(slot, produced, defs) {
46762
+ if (!slot.outputType || !produced) return [];
46763
+ const { compatible, issues } = checkSchemaCompatibility(
46764
+ produced,
46765
+ slot.outputType,
46766
+ (id) => defs.schemaForId(id),
46767
+ OUTPUT_ROLES
46768
+ );
46769
+ return compatible ? [] : issues;
46770
+ }
46771
+ function targetLabel(target) {
46772
+ const metadata = target.metadata;
46773
+ if (metadata?.xTeloOrigin !== void 0 || typeof metadata?.name !== "string") {
46774
+ return `the inline ${String(target.kind)}`;
46775
+ }
46776
+ return `${String(target.kind)} '${metadata.name}'`;
46777
+ }
46778
+ function collectReferenceOutputIssues(manifest, fieldMap, allManifests, defs, aliases, scopes) {
46779
+ const out = [];
46780
+ if (!fieldMap) return out;
46781
+ const ctx = callSiteContext(manifest, allManifests, defs, aliases, scopes);
46782
+ const contractScope = analyzerContractScope(defs, aliases, scopes, allManifests);
46783
+ const readingModule = manifest.metadata?.module;
46784
+ for (const [fieldPath, entry] of fieldMap) {
46785
+ if (!isRefEntry(entry) || !entry.outputType) continue;
46786
+ const slot = refSlotOfEntry(entry);
46787
+ for (const { value: ref, path } of resolveFieldEntries(manifest, fieldPath)) {
46788
+ if (!ref || typeof ref !== "object" || Array.isArray(ref)) continue;
46789
+ const reference = ref;
46790
+ if (typeof reference.kind !== "string") continue;
46791
+ const target = typeof reference.name === "string" ? ctx.resolveTarget(reference) : reference;
46792
+ if (!target) continue;
46793
+ const definition = contractScope.resolveIn(reference.kind, readingModule);
46794
+ const issues = referenceOutputRefusal(
46795
+ slot,
46796
+ producedOutputContract(target, definition, contractScope, defs),
46797
+ defs
46798
+ );
46799
+ if (issues.length === 0) continue;
46800
+ out.push({
46801
+ path,
46802
+ message: `${targetLabel(target)} does not return what this slot reads from it. The slot requires an output of ${JSON.stringify(slot.outputType)}; its declared output contract disagrees: ${issues.join("; ")}.`
46803
+ });
46804
+ }
46805
+ }
46806
+ return out;
46807
+ }
46808
+
46809
+ // ../../analyzer/nodejs/src/manifest-analysis.ts
46810
+ var ManifestAnalysis = class {
46811
+ constructor(manifests, ctx) {
46812
+ this.manifests = manifests;
46813
+ this.ctx = ctx;
46814
+ const rootModules = /* @__PURE__ */ new Set();
46815
+ for (const m of manifests) {
46816
+ if (isModuleKind(m.kind) && m.metadata?.name) rootModules.add(m.metadata.name);
46817
+ }
46818
+ this.scopes = { aliasesByModule: ctx.aliasesByModule, rootModules };
46819
+ }
46820
+ scopes;
46821
+ celScopeQuery;
46822
+ callGraphMemo;
46823
+ moduleGraphMemo;
46824
+ /** What CEL sees, per site. Built on first use — its indices are a function of
46825
+ * the whole set, and a host that never opens a CEL body should not pay for
46826
+ * them. */
46827
+ get celScope() {
46828
+ return this.celScopeQuery ??= new CelScopeQuery(this.manifests, this.ctx);
46829
+ }
46830
+ /** What calls what. Built on first use and kept, so the projection and any
46831
+ * other consumer over this set share one. */
46832
+ get callGraph() {
46833
+ return this.callGraphMemo ??= buildCallGraph(this.manifests, this.ctx.defs, {
46834
+ aliases: this.ctx.aliases,
46835
+ aliasesByModule: this.ctx.aliasesByModule
46836
+ });
46837
+ }
46838
+ /**
46839
+ * The module graph: boxes, ordered rows and classed edges — the projection an
46840
+ * editor draws.
46841
+ *
46842
+ * Here rather than on the registry because it is the registry AND a manifest
46843
+ * set, which is the pairing this class exists to name; a fifth
46844
+ * `registry.x(manifests, …)` factory is the accretion naming it stopped. It
46845
+ * shares this analysis's call graph rather than building one of its own.
46846
+ *
46847
+ * `deps` is supplied by the registry, which owns capability resolution — this
46848
+ * class holds the manifests, not the rules for reading a kind.
46849
+ */
46850
+ moduleGraph(deps, options = {}) {
46851
+ return this.moduleGraphMemo ??= buildModuleGraph(
46852
+ this.manifests,
46853
+ this.callGraph,
46854
+ deps,
46855
+ options
46856
+ );
46857
+ }
46858
+ /** Whether a resource's name denotes a type or a value — the rule the naming
46859
+ * check applies to it, so a rename proposes only a name `telo check` accepts. */
46860
+ nameLevel(manifest) {
46861
+ const resolveDef = moduleScopedDefResolver(this.ctx.defs, this.ctx.aliases, this.scopes);
46862
+ return levelFor(manifest, resolveDef, manifest.metadata?.module);
46863
+ }
46864
+ /** The manifest a `(kind, name)` pair addresses. */
46865
+ resourceFor(kind, name) {
46866
+ return findManifest(this.manifests, kind, name);
46867
+ }
46868
+ /**
46869
+ * The invocation contract of the resource a reference names.
46870
+ *
46871
+ * The shared resolver, so an editor offering a target's input keys is offering
46872
+ * exactly what `telo check` validates that call site against and what the
46873
+ * kernel binds at dispatch. Layered instance-first: a resource declaring its
46874
+ * own `inputType:` narrows the kind's, which is the common case for a
46875
+ * `Run.Sequence` used as a handler.
46876
+ */
46877
+ contractFor(ref, direction) {
46878
+ const target = this.resolveRef(ref);
46879
+ const definition = ref.kind ? this.definitionFor(ref.kind) : void 0;
46880
+ if (!target && !definition) return void 0;
46881
+ return resolveContract(
46882
+ direction,
46883
+ target,
46884
+ definition,
46885
+ analyzerContractScope(
46886
+ this.ctx.defs,
46887
+ this.ctx.aliases,
46888
+ this.scopes,
46889
+ this.manifests
46890
+ )
46891
+ )?.schema;
46892
+ }
46893
+ /**
46894
+ * Why the resource `ref` names cannot fill `slot`, by what it RETURNS — one
46895
+ * line per definite mismatch against the slot's `x-telo-ref` `outputType`,
46896
+ * empty when it can. The verdict `telo check` reports as
46897
+ * `REFERENCE_OUTPUT_MISMATCH`, so an editor never offers a target the checker
46898
+ * then refuses.
46899
+ */
46900
+ outputRefusal(slot, ref) {
46901
+ if (!slot.outputType) return [];
46902
+ const target = this.resolveRef(ref);
46903
+ const definition = ref.kind ? this.definitionFor(ref.kind) : void 0;
46904
+ if (!target && !definition) return [];
46905
+ const scope = analyzerContractScope(
46906
+ this.ctx.defs,
46907
+ this.ctx.aliases,
46908
+ this.scopes,
46909
+ this.manifests
46910
+ );
46911
+ return referenceOutputRefusal(
46912
+ slot,
46913
+ producedOutputContract(target, definition, scope, this.ctx.defs),
46914
+ this.ctx.defs
46915
+ );
46916
+ }
46917
+ /**
46918
+ * The manifest a reference names.
46919
+ *
46920
+ * An ALIAS narrows before the name does: a flattened set carries every
46921
+ * imported library's exported instances, so two libraries exporting a `store`
46922
+ * are two manifests with one name. Matching the alias to its target module
46923
+ * picks the right one; where the alias resolves to nothing the name alone is
46924
+ * used, which is what a local reference needs anyway.
46925
+ */
46926
+ resolveRef(ref) {
46927
+ if (!ref.name) return void 0;
46928
+ const byName = this.manifests.filter(
46929
+ (m) => m.metadata?.name === ref.name
46930
+ );
46931
+ if (byName.length === 0) return void 0;
46932
+ if (byName.length === 1) return byName[0];
46933
+ const targetModule = ref.alias ? this.ctx.aliases.moduleForAlias?.(ref.alias) : void 0;
46934
+ if (targetModule) {
46935
+ const scoped = byName.find(
46936
+ (m) => m.metadata?.module === targetModule
46937
+ );
46938
+ if (scoped) return scoped;
46939
+ }
46940
+ return ref.kind ? byName.find((m) => m.kind === ref.kind) : void 0;
46941
+ }
46942
+ definitionFor(kind) {
46943
+ const canonical = this.ctx.aliases.resolveKind(kind);
46944
+ return this.ctx.defs.resolve(kind) ?? (canonical ? this.ctx.defs.resolve(canonical) : void 0);
46945
+ }
46946
+ };
46947
+
46948
+ // ../../analyzer/nodejs/src/schema-tag-text.ts
46949
+ function schemaWithTagsAsText(value) {
46950
+ if (isCompiledValue(value) || isTaggedSentinel(value)) {
46951
+ return typeof value.source === "string" ? value.source : "";
46952
+ }
46953
+ if (Array.isArray(value)) return value.map(schemaWithTagsAsText);
46954
+ if (value && typeof value === "object") {
46955
+ const out = {};
46956
+ for (const [k, v] of Object.entries(value)) {
46957
+ out[k] = schemaWithTagsAsText(v);
46958
+ }
46959
+ return out;
46960
+ }
46961
+ return value;
46962
+ }
46963
+
46964
+ // ../../analyzer/nodejs/src/definition-registry.ts
46965
+ var DefinitionRegistry = class {
46966
+ constructor() {
46967
+ for (const def of KERNEL_BUILTINS) this.add(def, true);
46968
+ }
46969
+ /** Per-instance AJV for cross-module $ref resolution. Isolated so each registry
46970
+ * (and thus each AnalysisContext) has its own schema store — no stale schemas
46971
+ * across analyze() calls and no unbounded growth across the process lifetime. */
46972
+ ajv = createAjv();
46973
+ registeredSchemaIds = /* @__PURE__ */ new Set();
46974
+ compiledValidators = /* @__PURE__ */ new WeakMap();
46975
+ /** The subset of `registeredSchemaIds` claimed by a kind's schema. Kinds and
46976
+ * named `Telo.Type`s share one `telo://<module>/<Name>` id space, so this is
46977
+ * what lets a colliding type name be reported instead of silently dropped. */
46978
+ definitionSchemaIds = /* @__PURE__ */ new Set();
46979
+ defs = /* @__PURE__ */ new Map();
46980
+ fieldMaps = /* @__PURE__ */ new Map();
46981
+ /** Reverse inheritance index: parent kind → direct child kinds. */
46982
+ extendedBy = /* @__PURE__ */ new Map();
46983
+ /** Memoized inheritance-resolved schemas. `null` records "resolved to
46984
+ * nothing", so a kind with no schema is not re-walked on every resource. */
46985
+ effectiveSchemas = /* @__PURE__ */ new Map();
46986
+ /** DEPRECATED module identity table: identity string → canonical module name
46987
+ * ("std/pipeline" → "pipeline"). Serves only the legacy
46988
+ * `<namespace>/<module>#<Kind>` form of `x-telo-ref`, kept resolvable for
46989
+ * module versions published before constraints named their target by import
46990
+ * alias. Fed by `metadata.namespace`, which nothing else reads. */
46991
+ identityMap = /* @__PURE__ */ new Map();
46992
+ register(definition) {
46993
+ this.add(definition, false);
46994
+ }
46995
+ /** `trustedSchema` skips meta-validating the schema — only for the built-ins,
46996
+ * whose validity a test asserts once. */
46997
+ add(definition, trustedSchema) {
46998
+ const { name, module: mod } = definition.metadata;
46999
+ const key = mod ? `${mod}.${name}` : name;
47000
+ this.defs.set(key, definition);
47001
+ this.fieldMaps.clear();
47002
+ this.effectiveSchemas.clear();
47003
+ if (definition.capability) {
47004
+ this.addExtendedBy(definition.capability, key);
47005
+ }
47006
+ if (definition.extends) {
47007
+ this.addExtendedBy(definition.extends, key);
47008
+ }
47009
+ if (definition.kind === "Telo.Abstract" && mod === "Telo") {
47010
+ this.identityMap.set("telo", "Telo");
47011
+ }
47012
+ if (mod && definition.schema) {
47013
+ this.tryRegisterSchema(
47014
+ mod,
47015
+ name,
47016
+ definition.schema,
47017
+ trustedSchema
47018
+ );
47019
+ }
47020
+ }
47021
+ addExtendedBy(parent, child) {
47022
+ const children = this.extendedBy.get(parent);
47023
+ if (children) {
47024
+ if (!children.includes(child)) children.push(child);
47025
+ } else {
47026
+ this.extendedBy.set(parent, [child]);
47027
+ }
47028
+ }
47029
+ /** DEPRECATED. Register a module identity so the legacy
47030
+ * `<namespace>/<module>#<Kind>` form of `x-telo-ref` still resolves for module
47031
+ * versions published before constraints named their target by import alias.
47032
+ * New manifests declare no namespace and need no identity — their constraints
47033
+ * are canonicalized to `<module>.<Kind>` before registration.
47034
+ *
47035
+ * The "telo" identity is reserved for the built-in module and is populated
47036
+ * automatically when a `Telo.Abstract` registers. A namespace-less module must
47037
+ * not claim it: overwriting the entry would repoint every legacy `telo#…`
47038
+ * constraint at a module that declares no such kind, and the resulting
47039
+ * unresolvable ref reads as partial context rather than an error.
47040
+ *
47041
+ * @param namespace The module's `metadata.namespace`, or null when it declares none.
47042
+ * @param moduleName The module's `metadata.name` (e.g. "pipeline", "http-server"). */
47043
+ registerModuleIdentity(namespace, moduleName2) {
47044
+ if (!namespace || moduleName2 === "Telo") return;
47045
+ this.identityMap.set(`${namespace}/${moduleName2}`, moduleName2);
47046
+ }
47047
+ /** Registers a named `Telo.Type` resource's schema under its canonical
47048
+ * module-scoped URI `$id` (`telo://<module>/<name>`), so a sibling schema's
47049
+ * `$ref: "telo://Self/<name>"` (rewritten to the canonical form by
47050
+ * `resolveSchemaTypeRefs`) resolves during AJV compilation. Mirrors the
47051
+ * kernel type controller's `registerSchema(canonicalTypeSchemaId(...))`.
47052
+ *
47053
+ * Returns `false` when a kind schema in the same module already owns the id —
47054
+ * a name collision between a kind and a named type. Definitions register
47055
+ * first, so the type is the one that would be dropped, and every
47056
+ * `$ref: "telo://<module>/<Name>"` would then silently validate against the
47057
+ * kind's schema instead. The caller reports it; nothing is overwritten. */
47058
+ registerNamedTypeSchema(id, schema4) {
47059
+ if (this.definitionSchemaIds.has(id)) return false;
47060
+ if (this.registeredSchemaIds.has(id) || this.ajv.getSchema(id)) return true;
47061
+ if (!this.tryAddSchema(schema4, id)) return true;
47062
+ this.registeredSchemaIds.add(id);
47063
+ return true;
47064
+ }
47065
+ /**
47066
+ * Register a schema, surviving one AJV refuses.
47067
+ *
47068
+ * `addSchema` META-VALIDATES and THROWS, and a throw here escapes the whole
47069
+ * analyze pass: one author schema with `minimum: "3"` in it aborted the run
47070
+ * with AJV's own unanchored text and took every other diagnostic in the file
47071
+ * down with it — including the anchored one that says exactly which keyword is
47072
+ * wrong. Registration is a lookup table for `$ref` resolution, so failing to
47073
+ * fill one entry costs a reference that could not have resolved anyway.
47074
+ *
47075
+ * Nothing is swallowed: an unregisterable schema is invalid, and the two
47076
+ * checks that report it both run afterwards and both anchor on the offending
47077
+ * line — `SCHEMA_VIOLATION` from the `KindSchema` / `JsonSchema7` fragment the
47078
+ * slot points at, and `SCHEMA_COMPILE_ERROR` from {@link schemaCompileError},
47079
+ * which wraps `compile` for this same reason.
47080
+ */
47081
+ tryAddSchema(schema4, id, trusted = false) {
47082
+ try {
47083
+ this.ajv.addSchema(schemaWithTagsAsText(schema4), id, void 0, !trusted);
47084
+ return true;
47085
+ } catch {
47086
+ return false;
47087
+ }
47088
+ }
47089
+ /** True when a schema is registered under `id` (a canonical `telo://` type id
47090
+ * or a definition `$id`). Used to flag schema `$ref`s that resolve to nothing. */
47091
+ hasSchemaId(id) {
47092
+ return this.registeredSchemaIds.has(id) || this.ajv.getSchema(id) !== void 0;
47093
+ }
47094
+ /** The schema registered under `id`, for a structural comparison that must see
47095
+ * THROUGH a named shape. Declaring a shape once and referencing it is the
47096
+ * sanctioned way to reuse one, so a comparator that cannot follow the
47097
+ * reference judges two opaque nodes and learns nothing.
47098
+ *
47099
+ * A registered id is READ, never compiled: this is a structural lookup, and
47100
+ * compiling a shape the registry holds but cannot compile throws out of
47101
+ * whichever walk asked. That shape's compile failure is reported where the
47102
+ * shape is validated, not here. */
47103
+ schemaForId(id) {
47104
+ let entry = this.ajv.schemas[id] ?? this.ajv.refs[id];
47105
+ while (typeof entry === "string") entry = this.ajv.schemas[entry] ?? this.ajv.refs[entry];
47106
+ const schema4 = entry ? entry.schema : this.ajv.getSchema(id)?.schema;
47107
+ return schema4 && typeof schema4 === "object" ? schema4 : void 0;
47108
+ }
47109
+ /**
47110
+ * Validates a resource's configuration against its kind's schema, with the
47111
+ * offending field's path — what a `SCHEMA_VIOLATION` diagnostic is built from.
47112
+ *
47113
+ * On THIS registry's AJV, which is the point: it holds every registered
47114
+ * definition schema and every named `Telo.Type`, so a kind whose schema
47115
+ * references a shape declared elsewhere is checked rather than skipped. The
47116
+ * module-level instance this used to run on had none of them registered, so
47117
+ * such a schema failed to compile and the failure was swallowed — a resource
47118
+ * could be arbitrarily wrong and `telo check` reported nothing, while the
47119
+ * kernel (whose validator does resolve the reference) rejected it at boot.
47120
+ * Two AJVs answering one question is what made that possible; there is now
47121
+ * one, and it is the same one `schemaCompileError` reports through.
47122
+ */
47123
+ validateWithRefs(data, schema4) {
47124
+ const validate2 = this.compiledFor(schema4);
47125
+ if (!validate2 || validate2(data)) return [];
47126
+ return reduceSchemaErrors(explainFormatErrors(validate2.errors, data)).map(formatSingleError);
47127
+ }
47128
+ /** {@link validateWithRefs}, with the path each issue is anchored at. */
47129
+ validateResourceConfig(data, schema4) {
47130
+ const validate2 = this.compiledFor(schema4);
47131
+ if (!validate2 || validate2(data)) return [];
47132
+ return schemaIssues(validate2.errors, data);
47133
+ }
47134
+ /** Memoized per schema OBJECT — the analyzer validates every resource of a
47135
+ * kind against the same one, and this runs at keystroke time in an editor.
47136
+ * A schema AJV refuses compiles to `undefined`; that is reported once,
47137
+ * anchored on the owning definition, by `schemaCompileError`. */
47138
+ compiledFor(schema4) {
47139
+ const cached = this.compiledValidators.get(schema4);
47140
+ if (cached) return cached;
47141
+ try {
47142
+ const validate2 = this.ajv.compile(schemaWithTagsAsText(schema4));
47143
+ this.compiledValidators.set(schema4, validate2);
47144
+ return validate2;
47145
+ } catch {
47146
+ return void 0;
47147
+ }
47148
+ }
47149
+ /** Returns the AJV compile error for `schema`, or `undefined` when it compiles.
47150
+ * Compiles on this registry's instance, which has every loaded module schema
47151
+ * plus the manifest root registered, so local `#/$defs`, `telo://manifest`,
47152
+ * and cross-module `$ref`s all resolve. Used to fail loud on a definition
47153
+ * schema that AJV cannot compile — otherwise `validateAgainstSchema` /
47154
+ * `validateWithRefs` would swallow the failure and silently skip every
47155
+ * resource of that kind. */
47156
+ schemaCompileError(schema4) {
47157
+ try {
47158
+ this.ajv.compile(schemaWithTagsAsText(schema4));
47159
+ return void 0;
47160
+ } catch (err) {
47161
+ return err instanceof Error ? err.message : String(err);
47162
+ }
47163
+ }
47164
+ /** Registers a definition schema under the same module-scoped `telo://` id a
47165
+ * named `Telo.Type` uses, so a kind schema and a type schema are addressable
47166
+ * the same way and a `$ref` between them resolves at AJV compile time. One id
47167
+ * space per module: a kind and a named type may not share a name, which
47168
+ * `registerNamedTypeSchema` reports rather than resolving silently. */
47169
+ tryRegisterSchema(moduleName2, typeName2, schema4, trusted) {
47170
+ const id = canonicalTypeSchemaId(moduleName2, typeName2);
47171
+ if (this.registeredSchemaIds.has(id)) {
47172
+ this.definitionSchemaIds.add(id);
47173
+ return;
47174
+ }
47175
+ if (this.ajv.getSchema(id)) {
47176
+ throw new Error(`Duplicate definition schema $id: "${id}" is already registered`);
47177
+ }
47178
+ this.tryAddSchema(schema4, id, trusted);
47179
+ this.registeredSchemaIds.add(id);
47180
+ this.definitionSchemaIds.add(id);
47181
+ }
47182
+ /** Resolves an `x-telo-ref` constraint to a canonical registry kind key.
47183
+ *
47184
+ * The constraint is already canonical `<module>.<Kind>`: alias-form values
47185
+ * (`KvStore.Store`, `Self.Store`, `Telo.Invocable`) are rewritten in the
47186
+ * declaring module's scope by `resolveSchemaRefKinds` before registration, so
47187
+ * no module context is needed here.
47188
+ *
47189
+ * The legacy `<namespace>/<module>#<Kind>` form still resolves through the
47190
+ * identity table for module versions published before the alias form existed:
47191
+ *
47192
+ * "telo#Invocable" → "Telo.Invocable"
47193
+ * "std/http-server#Server" → "http-server.Server"
47194
+ *
47195
+ * Returns undefined when a legacy string is malformed or its identity was
47196
+ * never registered. */
47197
+ resolveRef(xTeloRef) {
47198
+ const hash = xTeloRef.indexOf("#");
47199
+ if (hash === -1) return xTeloRef;
47200
+ if (hash === xTeloRef.length - 1) return void 0;
47201
+ const moduleName2 = this.identityMap.get(xTeloRef.slice(0, hash));
47202
+ if (!moduleName2) return void 0;
47203
+ return `${moduleName2}.${xTeloRef.slice(hash + 1)}`;
47204
+ }
47205
+ resolve(kind) {
47206
+ return this.defs.get(kind);
47207
+ }
47208
+ /**
47209
+ * The kind's AUTHOR-FACING schema — its own merged with everything it
47210
+ * inherits along `extends` — memoized per kind.
47211
+ *
47212
+ * This is what every consumer reading a kind's schema to interpret an
47213
+ * INSTANCE must use, because that is the schema the instance was authored
47214
+ * against and the one the kernel stamps at definition registration. Reading
47215
+ * `resolve(kind).schema` instead sees only what the leaf declared, so an
47216
+ * annotation on a parent — a CEL context region, a step body, an error
47217
+ * context — is invisible on every child, silently and per consumer. Sharing
47218
+ * one memo is what keeps those consumers from drifting apart.
47219
+ *
47220
+ * Lazy for the same reason `getFieldMap` is: a child registered before its
47221
+ * parent still sees the inherited half once both are present.
47222
+ */
47223
+ effectiveSchema(kind) {
47224
+ const cached = this.effectiveSchemas.get(kind);
47225
+ if (cached !== void 0) return cached ?? void 0;
47226
+ const def = this.defs.get(kind);
47227
+ if (!def) return void 0;
47228
+ const schema4 = effectiveAuthorSchema(def, (k) => this.resolve(k));
47229
+ this.effectiveSchemas.set(kind, schema4 ?? null);
47230
+ return schema4;
47231
+ }
47232
+ /** The effective schema for a definition, by the kind it is registered under.
47233
+ * Falls back to the definition's own schema when it is not in this registry
47234
+ * (a synthesized or not-yet-registered kind), which is the pre-inheritance
47235
+ * behaviour and the safe direction: it types less, never wrongly. */
47236
+ effectiveSchemaOf(definition) {
47237
+ if (!definition) return void 0;
47238
+ const { name, module: mod } = definition.metadata;
47239
+ const resolved = this.effectiveSchema(mod ? `${mod}.${name}` : name);
47240
+ return resolved ?? definition.schema;
46980
47241
  }
46981
- return out;
46982
- }
46983
- function pointerSegments(pointer) {
46984
- if (!pointer.startsWith("/")) return void 0;
46985
- const segments = pointer.slice(1).split("/").map((s) => s.replace(/~1/g, "/").replace(/~0/g, "~"));
46986
- return segments.every((s) => s.length > 0 && !/^\d+$/.test(s)) ? segments : void 0;
46987
- }
46988
- function templateCallSite(manifest, ctx) {
46989
- if (manifest.kind !== "Telo.Definition") return void 0;
46990
- const values = manifest.inputs;
46991
- if (!values || typeof values !== "object" || Array.isArray(values)) return void 0;
46992
- const scope = contractScopeOf(ctx);
46993
- const module = manifest.metadata?.module;
46994
- for (const slot of ["invoke", "provide"]) {
46995
- const target = dispatchTargetOf(manifest, slot);
46996
- if (!target) continue;
46997
- const definition2 = scope.resolveIn(target.kind, module);
46998
- const aliases = moduleAliasScope(manifest.metadata, ctx.aliases, ctx.aliasesByModule);
46999
- const entry = target.entry && {
47000
- ...target.entry,
47001
- inputType: withCanonicalRefSentinels(
47002
- target.entry.inputType,
47003
- module,
47004
- (alias) => aliases?.moduleForAlias(alias)
47005
- )
47006
- };
47007
- return {
47008
- origin: "template",
47009
- path: "inputs",
47010
- values,
47011
- invoke: { kind: target.kind, name: target.name },
47012
- invokedManifest: entry,
47013
- invokedDefinition: definition2,
47014
- contract: resolveContract("inputType", entry, definition2, scope)
47015
- };
47242
+ /** Returns the reference field map for the given kind, computed lazily from the
47243
+ * kind's AUTHOR-FACING (inheritance-resolved) schema and memoized. Lazy so a
47244
+ * child registered before its parent still sees the parent's inherited ref
47245
+ * slots once both are present. */
47246
+ getFieldMap(kind) {
47247
+ const cached = this.fieldMaps.get(kind);
47248
+ if (cached) return cached;
47249
+ const def = this.defs.get(kind);
47250
+ if (!def) return void 0;
47251
+ const schema4 = effectiveAuthorSchema(def, (k) => this.resolve(k));
47252
+ const map3 = buildReferenceFieldMap(schema4 ?? {});
47253
+ this.fieldMaps.set(kind, map3);
47254
+ return map3;
47016
47255
  }
47017
- const dispatch = [manifest.invoke, manifest.provide].find(
47018
- (d) => d && typeof d === "object" && !Array.isArray(d) && typeof d.kind === "string"
47019
- );
47020
- if (!dispatch) return void 0;
47021
- const definition = scope.resolveIn(dispatch.kind, module);
47022
- return {
47023
- origin: "template",
47024
- path: "inputs",
47025
- values,
47026
- invoke: { kind: dispatch.kind },
47027
- invokedManifest: void 0,
47028
- invokedDefinition: definition,
47029
- contract: resolveContract("inputType", void 0, definition, scope)
47030
- };
47031
- }
47032
- function valueSchemaSites(manifest, defSchema, ctx) {
47033
- if (!defSchema) return [];
47034
- const out = [];
47035
- for (const { scope, from } of valueSchemaSlots(defSchema)) {
47036
- const schema4 = resolveTypeFieldToSchema(manifest[from], ctx.typeManifests);
47037
- if (!schema4 || typeof schema4 !== "object") continue;
47038
- for (const { path, value } of resolveScopeValues(manifest, scope)) {
47039
- out.push({ path, value, schema: schema4, from });
47256
+ /** Returns the field map for `kind`, falling back to the alias-resolved kind when not found. */
47257
+ getFieldMapForKind(kind, aliases) {
47258
+ const fm = this.getFieldMap(kind);
47259
+ if (fm) return fm;
47260
+ const resolved = aliases?.resolveKind(kind);
47261
+ return resolved ? this.getFieldMap(resolved) : void 0;
47262
+ }
47263
+ /** Returns the field map for `resource.kind` with x-telo-schema-from entries replaced
47264
+ * by their nested ref/scope slots — so Phase 2 inline normalization and Phase 5
47265
+ * injection see encoders nested behind a schema-from indirection (e.g.
47266
+ * http-server `Server.notFoundHandler.returns[].content[mime].encoder`).
47267
+ *
47268
+ * Only static absolute schema-from paths with a dotted alias anchor are expanded
47269
+ * (e.g. "HttpDispatch.Outcomes/$defs/Returns"). Relative and unqualified absolute
47270
+ * anchors depend on a sibling property at runtime and stay unexpanded; the
47271
+ * analyzer's reference validation phase already flags the cases that matter. */
47272
+ expandedFieldMapForResource(resource, aliases, aliasesByModule) {
47273
+ const moduleScope = moduleAliasScope(resource.metadata, aliases, aliasesByModule);
47274
+ const baseMap = this.getFieldMapForKind(resource.kind, moduleScope);
47275
+ if (!baseMap) return void 0;
47276
+ const resolvedKind = moduleScope.resolveKind(resource.kind) ?? resource.kind;
47277
+ const def = this.resolve(resource.kind) ?? this.resolve(resolvedKind);
47278
+ const ownerScope = moduleAliasScope(def?.metadata, aliases, aliasesByModule);
47279
+ const expanded = /* @__PURE__ */ new Map();
47280
+ for (const [path, entry] of baseMap) {
47281
+ if (!isSchemaFromEntry(entry)) {
47282
+ expanded.set(path, entry);
47283
+ continue;
47284
+ }
47285
+ const sub = this.resolveSchemaFromSubMap(entry.schemaFrom, path, ownerScope);
47286
+ if (!sub) continue;
47287
+ for (const [subPath, subEntry] of sub) expanded.set(subPath, subEntry);
47040
47288
  }
47289
+ return expanded;
47041
47290
  }
47042
- return out;
47043
- }
47044
- function derivedSlotsOf(resource, ctx) {
47045
- const manifest = resource;
47046
- const moduleScope = moduleAliasScope(resource.metadata, ctx.aliases, ctx.aliasesByModule);
47047
- const resolvedKind = moduleScope.resolveKind(resource.kind);
47048
- const definition = ctx.defs.resolve(resource.kind) ?? (resolvedKind ? ctx.defs.resolve(resolvedKind) : void 0);
47049
- const schema4 = ctx.defs.effectiveSchemaOf(definition);
47050
- const out = [];
47051
- const at2 = (path, value, schema5) => out.push({ path, value, schema: schema5, replace: (next) => assignConcretePath(manifest, path, next) });
47052
- const calls = [
47053
- ...schema4 ? stepCallSites(manifest, schema4, ctx) : [],
47054
- ...slotCallSites(
47055
- manifest,
47056
- ctx.defs.expandedFieldMapForResource(resource, ctx.aliases, ctx.aliasesByModule),
47057
- ctx
47058
- )
47059
- ];
47060
- const template = templateCallSite(manifest, ctx);
47061
- if (template) calls.push(template);
47062
- for (const call of calls) {
47063
- if (call.contract) at2(call.path, call.values, call.contract.schema);
47291
+ /**
47292
+ * The schema an `x-telo-schema-from` slot derives its shape from.
47293
+ *
47294
+ * Only the STATIC form resolves: an anchor that is a dotted alias-qualified
47295
+ * kind (`HttpDispatch.Request/$defs/Matcher`). The polymorphic forms — a
47296
+ * relative anchor, or a single-segment absolute one — name a sibling property
47297
+ * whose value is known per resource, so a definition-level lookup would be
47298
+ * guessing at one instance's shape.
47299
+ *
47300
+ * Its own method because a schema-from slot is otherwise INVISIBLE to anything
47301
+ * reading `properties`: the field map needs the nested ref slots, and an IDE
47302
+ * needs the very same node to offer a key or describe one. Two resolutions of
47303
+ * one annotation would eventually disagree about which anchors are static.
47304
+ */
47305
+ resolveSchemaFromNode(schemaFrom, ownerScope) {
47306
+ const isAbsolute = schemaFrom.startsWith("/");
47307
+ const expr = isAbsolute ? schemaFrom.slice(1) : schemaFrom;
47308
+ const slashIdx = expr.indexOf("/");
47309
+ if (slashIdx === -1) return void 0;
47310
+ const anchorName = expr.slice(0, slashIdx);
47311
+ const jsonPointer = "/" + expr.slice(slashIdx + 1);
47312
+ if (!anchorName.includes(".")) return void 0;
47313
+ const targetKind = ownerScope.resolveKind(anchorName);
47314
+ if (!targetKind) return void 0;
47315
+ const targetDef = this.resolve(targetKind);
47316
+ if (!targetDef?.schema) return void 0;
47317
+ const subSchema = navigateJsonPointer(
47318
+ targetDef.schema,
47319
+ jsonPointer
47320
+ );
47321
+ if (!subSchema || typeof subSchema !== "object") return void 0;
47322
+ return subSchema;
47064
47323
  }
47065
- for (const site of valueSchemaSites(manifest, schema4, ctx)) {
47066
- at2(site.path, site.value, site.schema);
47324
+ resolveSchemaFromSubMap(schemaFrom, fieldPath, ownerScope) {
47325
+ const subSchema = this.resolveSchemaFromNode(schemaFrom, ownerScope);
47326
+ if (!subSchema) return null;
47327
+ return buildFieldMapAtPath(subSchema, fieldPath);
47067
47328
  }
47068
- visitManifest(
47069
- [resource],
47070
- ctx.defs,
47071
- {
47072
- onSchemaFrom: (e) => {
47073
- for (const site of schemaFromSites(manifest, e.fieldPath, e.entry.schemaFrom, ctx)) {
47074
- if (isSchemaFromSite(site)) at2(site.path, site.value, site.schema);
47075
- }
47329
+ /** The kinds a definition descends from DIRECTLY — the same two edges
47330
+ * `register` feeds into `extendedBy`, read the other way.
47331
+ *
47332
+ * Both spellings, because either can carry the edge that satisfies a slot:
47333
+ * `capability:` is the legacy implements-this form, `extends:` the canonical
47334
+ * one. A consumer walking these by hand would be a second reading of what an
47335
+ * inheritance edge IS, and the downward index and the upward walk have to
47336
+ * agree about that forever. */
47337
+ parentsOf(kind) {
47338
+ const def = this.defs.get(kind);
47339
+ if (!def) return [];
47340
+ return [def.capability, def.extends].filter(
47341
+ (parent) => typeof parent === "string" && parent.length > 0
47342
+ );
47343
+ }
47344
+ /** Whether every kind this one descends from is registered.
47345
+ *
47346
+ * When it is, what the kind implements is FULLY KNOWN: a target it does not
47347
+ * reach is one it genuinely does not implement, so a mismatch is a verdict.
47348
+ * When a hop is missing — an unimported abstract, an alias the declaring file
47349
+ * could not resolve — a mismatch cannot be told from a missing dependency,
47350
+ * which is the partial context every reference check stays lenient in. */
47351
+ ancestryResolved(kind) {
47352
+ const queue = [kind];
47353
+ const seen = /* @__PURE__ */ new Set();
47354
+ while (queue.length > 0) {
47355
+ const current = queue.shift();
47356
+ if (seen.has(current)) continue;
47357
+ seen.add(current);
47358
+ if (!this.defs.has(current)) return false;
47359
+ queue.push(...this.parentsOf(current));
47360
+ }
47361
+ return true;
47362
+ }
47363
+ /** Returns all definitions that transitively extend the given abstract kind.
47364
+ * Follows the capability chain to any depth (equivalent to instanceof in OOP).
47365
+ * Definitions are included regardless of registration order. */
47366
+ getByExtends(abstractKind) {
47367
+ const result = [];
47368
+ const queue = [abstractKind];
47369
+ while (queue.length > 0) {
47370
+ const parent = queue.shift();
47371
+ const children = this.extendedBy.get(parent);
47372
+ if (!children) continue;
47373
+ for (const child of children) {
47374
+ const def = this.defs.get(child);
47375
+ if (def) result.push(def);
47376
+ queue.push(child);
47076
47377
  }
47077
- },
47078
- {
47079
- aliases: ctx.aliases,
47080
- aliasesByModule: ctx.aliasesByModule,
47081
- skipKinds: REF_VALIDATION_SKIP_KINDS,
47082
- expand: false
47083
47378
  }
47084
- );
47085
- return out;
47086
- }
47379
+ return result;
47380
+ }
47381
+ kinds() {
47382
+ return Array.from(this.defs.keys());
47383
+ }
47384
+ };
47087
47385
 
47088
47386
  // ../../analyzer/nodejs/src/instantiable-kind.ts
47089
47387
  function isInstantiableDefinition(def) {
@@ -47782,6 +48080,32 @@ var AnalysisRegistry = class _AnalysisRegistry {
47782
48080
  typeManifestsFor: () => []
47783
48081
  })?.schema;
47784
48082
  }
48083
+ /**
48084
+ * Why a resource of `candidate.kind` — configured as `candidate.config`, when
48085
+ * the caller holds its configuration — cannot fill `slot`, by what it RETURNS:
48086
+ * one line per definite mismatch against the slot's `x-telo-ref`
48087
+ * `outputType`, empty when it can. The same derivation and comparator
48088
+ * `telo check`'s `REFERENCE_OUTPUT_MISMATCH` runs, for a host that has a kind
48089
+ * (and perhaps its fields) but not the analyzed manifest set. A named shape an
48090
+ * instance's own `outputType` points at resolves through no manifest here, so
48091
+ * that layer gives no verdict and the kind's contract decides.
48092
+ */
48093
+ outputRefusal(slot, candidate) {
48094
+ if (!slot.outputType) return [];
48095
+ const definition = this.resolveDefinition(candidate.kind);
48096
+ if (!definition) return [];
48097
+ const target = candidate.config ? { kind: candidate.kind, ...candidate.config } : void 0;
48098
+ return referenceOutputRefusal(
48099
+ slot,
48100
+ producedOutputContract(
48101
+ target,
48102
+ definition,
48103
+ { resolveDefinition: this.scopedDefResolver(), typeManifestsFor: () => [] },
48104
+ this.defs
48105
+ ),
48106
+ this.defs
48107
+ );
48108
+ }
47785
48109
  /** A resolver that re-scopes at every hop of an `extends` chain: each kind is
47786
48110
  * resolved in the module that DECLARED the definition it was read off, since
47787
48111
  * `extends` aliases are lexical. `resolveParent` always passes that definition
@@ -51237,6 +51561,14 @@ function checkAnnotation(annotation, manifest, path, issues) {
51237
51561
  message: `x-telo-ref at '${path}' declares 'throwsThrough: ${JSON.stringify(obj.throwsThrough)}', which is not a boolean. Only 'true' declares that throws from this slot's target surface through the declaring resource; anything else reads as absent, which silently stops its catch list from enclosing what it holds.`
51238
51562
  });
51239
51563
  }
51564
+ if (obj.outputType !== void 0 && (!obj.outputType || typeof obj.outputType !== "object" || Array.isArray(obj.outputType))) {
51565
+ issues.push({
51566
+ code: "X_TELO_REF_INVALID_OUTPUT_TYPE",
51567
+ manifest,
51568
+ path,
51569
+ message: `x-telo-ref at '${path}' declares 'outputType: ${JSON.stringify(obj.outputType)}', which is not a JSON Schema object. It names the shape the target's output must have (e.g. '{ type: object, required: [output] }'); anything else reads as absent, so the slot would accept a target returning anything.`
51570
+ });
51571
+ }
51240
51572
  for (const key of Object.keys(obj)) {
51241
51573
  if (REF_ANNOTATION_KEYS.has(key)) continue;
51242
51574
  issues.push({
@@ -51248,7 +51580,7 @@ function checkAnnotation(annotation, manifest, path, issues) {
51248
51580
  }
51249
51581
  return declaredUses(use);
51250
51582
  }
51251
- var REF_ANNOTATION_KEYS = /* @__PURE__ */ new Set(["kind", "use", "inputs", "throwsThrough"]);
51583
+ var REF_ANNOTATION_KEYS = /* @__PURE__ */ new Set(["kind", "use", "inputs", "throwsThrough", "outputType"]);
51252
51584
  function carriesRefAnnotation(obj) {
51253
51585
  if (obj["x-telo-ref"] !== void 0) return true;
51254
51586
  for (const key of ["anyOf", "oneOf"]) {
@@ -51705,64 +52037,6 @@ function formatSegments(segments) {
51705
52037
  return out;
51706
52038
  }
51707
52039
 
51708
- // ../../analyzer/nodejs/src/plain-literal-decoding.ts
51709
- function decodePlainLiterals(value, schema4, external, rootSchema = schema4) {
51710
- return mapTextLeaves(value, schema4, decodePlainText, external, rootSchema);
51711
- }
51712
- function mapTextLeaves(value, schema4, leaf, external, rootSchema = schema4, visit4) {
51713
- const walk4 = (node, raw, base, pointer) => {
51714
- const entered = resolveRefIn(raw, base, external);
51715
- const selected = selectUnionBranch(entered.schema, node, entered.root, external);
51716
- const { schema: here, root } = resolveRefIn(selected, entered.root, external);
51717
- visit4?.(here, node, pointer);
51718
- if (typeof node === "string") return leaf(here, node, pointer);
51719
- if (!node || typeof node !== "object") return node;
51720
- if (here["x-telo-ref"] !== void 0) return node;
51721
- if (isCompiledValue(node) || isTaggedSentinel(node)) return node;
51722
- if (Array.isArray(node)) {
51723
- const item = resolveRefIn(here.items ?? {}, root, external);
51724
- for (let i = 0; i < node.length; i++) node[i] = walk4(node[i], item.schema, item.root, `${pointer}/${i}`);
51725
- return node;
51726
- }
51727
- const proto = Object.getPrototypeOf(node);
51728
- if (proto !== Object.prototype && proto !== null) return node;
51729
- const properties = collectProperties(here);
51730
- const additional = here.additionalProperties && typeof here.additionalProperties === "object" ? here.additionalProperties : void 0;
51731
- const record = node;
51732
- for (const key of Object.keys(record)) {
51733
- const child = properties[key] ?? additional;
51734
- if (child) {
51735
- record[key] = walk4(record[key], child, root, `${pointer}/${key.replace(/~/g, "~0").replace(/\//g, "~1")}`);
51736
- }
51737
- }
51738
- return node;
51739
- };
51740
- return walk4(value, schema4, rootSchema, "");
51741
- }
51742
- function substituteDecodedCelFields(data, schema4, rootSchema, options = {}) {
51743
- const decoded = decodePlainLiterals(
51744
- copyPlainContainers(data),
51745
- schema4,
51746
- options.external,
51747
- rootSchema ?? schema4
51748
- );
51749
- return substituteCelFields(decoded, schema4, rootSchema, options);
51750
- }
51751
- function copyPlainContainers(value) {
51752
- if (Array.isArray(value)) return value.map(copyPlainContainers);
51753
- if (!value || typeof value !== "object" || isCompiledValue(value) || isTaggedSentinel(value)) {
51754
- return value;
51755
- }
51756
- const proto = Object.getPrototypeOf(value);
51757
- if (proto !== Object.prototype && proto !== null) return value;
51758
- return Object.fromEntries(
51759
- Object.entries(value).map(([key, item]) => [
51760
- key,
51761
- copyPlainContainers(item)
51762
- ])
51763
- );
51764
- }
51765
-
51766
52040
  // ../../analyzer/nodejs/src/validate-value-schema.ts
51767
52041
  function collectValueSchemaIssues(manifest, defSchema, allManifests, validator) {
51768
52042
  const issues = [];
@@ -58704,7 +58978,7 @@ function describe3(value) {
58704
58978
  }
58705
58979
 
58706
58980
  // ../../analyzer/nodejs/src/telo-version.ts
58707
- var TELO_SURFACE_VERSION = "0.102.0";
58981
+ var TELO_SURFACE_VERSION = "0.103.1";
58708
58982
 
58709
58983
  // ../../analyzer/nodejs/src/validate-requires.ts
58710
58984
  var SOURCE19 = "telo-analyzer";
@@ -59259,153 +59533,6 @@ function findInModule(manifests, name, module) {
59259
59533
  return byName.length === 1 ? byName[0] : void 0;
59260
59534
  }
59261
59535
 
59262
- // ../../analyzer/nodejs/src/validate-step-inputs.ts
59263
- var missingRequired = (issue) => /is missing required property/.test(issue.message);
59264
- function containerOf(path) {
59265
- const dot = path.lastIndexOf(".");
59266
- return dot === -1 ? "" : path.slice(0, dot);
59267
- }
59268
- var declarationsByName = /* @__PURE__ */ new WeakMap();
59269
- function byNameOf(allManifests) {
59270
- let byName = declarationsByName.get(allManifests);
59271
- if (!byName) {
59272
- byName = /* @__PURE__ */ new Map();
59273
- for (const m of allManifests) {
59274
- const name = m.metadata?.name;
59275
- if (typeof name !== "string" || m.kind === "Telo.Import") continue;
59276
- const list2 = byName.get(name);
59277
- if (list2) list2.push(m);
59278
- else byName.set(name, [m]);
59279
- }
59280
- declarationsByName.set(allManifests, byName);
59281
- }
59282
- return byName;
59283
- }
59284
- function callSiteContext(manifest, allManifests, defs, aliases, scopes) {
59285
- const fromModule = manifest.metadata?.module;
59286
- const scope = moduleAliasScope({ module: fromModule }, aliases, scopes.aliasesByModule);
59287
- return {
59288
- defs,
59289
- aliases,
59290
- aliasesByModule: scopes.aliasesByModule,
59291
- rootModules: scopes.rootModules,
59292
- typeManifests: allManifests,
59293
- resolveTarget: (ref) => typeof ref.name === "string" ? resolveReferenceTarget(
59294
- byNameOf(allManifests),
59295
- { name: ref.name, ...typeof ref.alias === "string" ? { alias: ref.alias } : {} },
59296
- fromModule,
59297
- (alias) => scope.moduleForAlias(alias)
59298
- ) : void 0
59299
- };
59300
- }
59301
- function collectStepInputIssues(manifest, defSchema, allManifests, defs, aliases, scopes, stepContext) {
59302
- const ctx = callSiteContext(manifest, allManifests, defs, aliases, scopes);
59303
- return stepCallSites(manifest, defSchema, ctx).flatMap(
59304
- (site) => checkCallSite(site, manifest, allManifests, defs, aliases, scopes, stepContext)
59305
- );
59306
- }
59307
- function collectRefInputIssues(manifest, fieldMap, allManifests, defs, aliases, scopes) {
59308
- const ctx = callSiteContext(manifest, allManifests, defs, aliases, scopes);
59309
- return slotCallSites(manifest, fieldMap, ctx).flatMap(
59310
- (site) => checkCallSite(site, manifest, allManifests, defs, aliases, scopes)
59311
- );
59312
- }
59313
- function checkCallSite(site, manifest, allManifests, defs, aliases, scopes, stepContext) {
59314
- const out = [];
59315
- const { contract, values, invoke, invokedManifest, invokedDefinition } = site;
59316
- if (!contract) return out;
59317
- const targetLabel = invoke.name ?? invoke.kind ?? "the invoked resource";
59318
- const contractScope = analyzerContractScope(defs, aliases, scopes, allManifests);
59319
- const readingModule = manifest.metadata?.module;
59320
- const celPaths = /* @__PURE__ */ new Set();
59321
- const substituted = substituteDecodedCelFields(values, contract.schema, void 0, {
59322
- onSubstitute: (p) => celPaths.add(p),
59323
- // A contract may name a shape declared elsewhere. Both halves need the
59324
- // resolver or they disagree about the same slot.
59325
- external: (ref) => defs.schemaForId(ref)
59326
- });
59327
- const roots = [];
59328
- if (stepContext) roots.push(["steps.", stepContext]);
59329
- const ownContract = resolveContract(
59330
- "inputType",
59331
- manifest,
59332
- contractScope.resolveIn(manifest.kind, readingModule),
59333
- contractScope
59334
- );
59335
- if (ownContract) roots.push(["inputs.", ownContract.schema]);
59336
- if (roots.length > 0) {
59337
- for (const [inputName, inputValue] of Object.entries(values)) {
59338
- const chain = plainChainOf(inputValue);
59339
- const root = chain ? roots.find(([prefix]) => chain.startsWith(prefix)) : void 0;
59340
- if (!chain || !root) continue;
59341
- const produced = navigateSchemaToExprPath(root[1], chain.slice(root[0].length));
59342
- const slotSchema = contract.schema.properties?.[inputName];
59343
- if (!produced || !slotSchema) continue;
59344
- if (isLiveSlot(produced)) {
59345
- const retry = site.step ? declaredRetry(site.step.value, site.step.schema, invokedManifest, invokedDefinition) : void 0;
59346
- if (retry !== void 0) {
59347
- out.push({
59348
- path: `${site.path}.${inputName}`,
59349
- targetLabel,
59350
- message: `'${inputName}' is a live value, which is consumed by reading and so exists once \u2014 but ${retry} re-attempts the dispatch, and a re-attempt would pass nothing. Collect it to a value first, or chunk the work so each attempt carries its own replayable piece.`,
59351
- code: "LIVE_VALUE_RETRIED"
59352
- });
59353
- continue;
59354
- }
59355
- }
59356
- if (!valueTypeOf(produced) || !valueTypeOf(slotSchema)) continue;
59357
- const { compatible, issues } = checkSchemaCompatibility(
59358
- produced,
59359
- slotSchema,
59360
- (ref) => defs.schemaForId(ref)
59361
- );
59362
- if (compatible) continue;
59363
- out.push({
59364
- path: `${site.path}.${inputName}`,
59365
- targetLabel,
59366
- message: issues.join("; "),
59367
- code: "CEL_TYPE_ARGUMENT_MISMATCH"
59368
- });
59369
- }
59370
- }
59371
- for (const issue of defs.validateResourceConfig(substituted, contract.schema)) {
59372
- if (celPaths.has(issue.path)) continue;
59373
- const anchor = missingRequired(issue) ? containerOf(issue.path) : issue.path;
59374
- out.push({
59375
- path: anchor ? `${site.path}.${anchor}` : site.path,
59376
- targetLabel,
59377
- message: issue.message
59378
- });
59379
- }
59380
- return out;
59381
- }
59382
- function declaredRetry(step, stepItemSchema, invokedManifest, invokedDef) {
59383
- for (const [field, budget] of retryFields(stepItemSchema)) {
59384
- if (budget(step?.[field]) > 0) return `the step's \`${field}\``;
59385
- }
59386
- for (const [field, budget] of retryFields(invokedDef?.schema)) {
59387
- if (budget(invokedManifest?.[field]) > 0) return `the target's \`${field}\``;
59388
- }
59389
- return void 0;
59390
- }
59391
- var RETRY_BUDGET = {
59392
- RetryPolicy: (value) => {
59393
- if (!value || typeof value !== "object") return 0;
59394
- const attempts = value.attempts;
59395
- return typeof attempts === "number" ? attempts : 0;
59396
- },
59397
- RetryAttempts: (value) => typeof value === "number" ? value : 0
59398
- };
59399
- function retryFields(schema4) {
59400
- if (!schema4) return [];
59401
- const out = [];
59402
- for (const [key, sub] of gatherPropertySchemas(schema4)) {
59403
- const budget = RETRY_BUDGET[manifestFragmentOf(sub) ?? ""];
59404
- if (budget) out.push([key, budget]);
59405
- }
59406
- return out;
59407
- }
59408
-
59409
59536
  // ../../analyzer/nodejs/src/validate-nested-inline.ts
59410
59537
  var SOURCE22 = "telo-analyzer";
59411
59538
  function validateNestedInlineResources(manifest, rootSchema, lookupDefinition, allManifests, validator, implementationsOf) {
@@ -63448,10 +63575,31 @@ var StaticAnalyzer = class {
63448
63575
  if (effectiveSchema) {
63449
63576
  const stepName = m.metadata?.name;
63450
63577
  const stepFile = m.metadata?.source;
63578
+ const expandedFieldMap = defs.expandedFieldMapForResource(m, aliases, aliasesByModule);
63579
+ for (const issue of collectReferenceOutputIssues(
63580
+ m,
63581
+ expandedFieldMap,
63582
+ allManifests,
63583
+ defs,
63584
+ aliases,
63585
+ { aliasesByModule, rootModules }
63586
+ )) {
63587
+ diagnostics.push({
63588
+ severity: DiagnosticSeverity2.Error,
63589
+ code: "REFERENCE_OUTPUT_MISMATCH",
63590
+ source: SOURCE35,
63591
+ message: `${m.kind}/${stepName}: at '${issue.path}', ${issue.message}`,
63592
+ data: {
63593
+ resource: { kind: m.kind, name: stepName ?? "" },
63594
+ filePath: stepFile,
63595
+ path: issue.path
63596
+ }
63597
+ });
63598
+ }
63451
63599
  const inputIssues = [
63452
63600
  ...collectRefInputIssues(
63453
63601
  m,
63454
- defs.expandedFieldMapForResource(m, aliases, aliasesByModule),
63602
+ expandedFieldMap,
63455
63603
  allManifests,
63456
63604
  defs,
63457
63605
  aliases,
@@ -67160,9 +67308,11 @@ function unionLeaves(parent, leaves) {
67160
67308
  return out;
67161
67309
  }
67162
67310
  function lookupRefConstraints(definitionSchema, yamlPath, schemaFrom) {
67311
+ return lookupRefSlot(definitionSchema, yamlPath, schemaFrom)?.kinds ?? [];
67312
+ }
67313
+ function lookupRefSlot(definitionSchema, yamlPath, schemaFrom) {
67163
67314
  const node = navigateSchema2(definitionSchema, yamlPath, schemaFrom);
67164
- if (!node) return [];
67165
- return readRefSlot(node)?.kinds ?? [];
67315
+ return node ? readRefSlot(node) : void 0;
67166
67316
  }
67167
67317
  function detectContext(text, line, character, docs) {
67168
67318
  const resolved = resolveNodeAtPosition(text, docs ?? parseToAst(text), line, character);
@@ -67905,8 +68055,9 @@ function extractInFileResources(docs) {
67905
68055
  }
67906
68056
  return out;
67907
68057
  }
67908
- function refNameCompletions(docs, refKind, refConstraints, registry, replaceRange) {
68058
+ function refNameCompletions(docs, refKind, slot, registry, analysis, replaceRange) {
67909
68059
  const resources = extractInFileResources(docs);
68060
+ const refConstraints = slot?.kinds ?? [];
67910
68061
  let acceptable;
67911
68062
  if (refKind) {
67912
68063
  acceptable = /* @__PURE__ */ new Set([refKind]);
@@ -67920,6 +68071,7 @@ function refNameCompletions(docs, refKind, refConstraints, registry, replaceRang
67920
68071
  const out = [];
67921
68072
  for (const r of resources) {
67922
68073
  if (acceptable && !acceptable.has(r.kind)) continue;
68074
+ if (slot && outputRefused(slot, r, registry, analysis)) continue;
67923
68075
  if (seen.has(r.name)) continue;
67924
68076
  seen.add(r.name);
67925
68077
  out.push({
@@ -67933,18 +68085,25 @@ function refNameCompletions(docs, refKind, refConstraints, registry, replaceRang
67933
68085
  }
67934
68086
  return out;
67935
68087
  }
68088
+ function outputRefused(slot, candidate, registry, analysis) {
68089
+ if (!slot.outputType) return false;
68090
+ const refusal = analysis ? analysis.outputRefusal(slot, { kind: candidate.kind, name: candidate.name }) : registry?.outputRefusal(slot, { kind: candidate.kind }) ?? [];
68091
+ return refusal.length > 0;
68092
+ }
67936
68093
  function refConstrainedKinds(registry, parentDocKind, parentYamlPath) {
67937
68094
  const definition = registry.resolveDefinition(parentDocKind);
67938
68095
  if (!definition?.schema) return void 0;
67939
- const constraints = lookupRefConstraints(
68096
+ const slot = lookupRefSlot(
67940
68097
  definition.schema,
67941
68098
  parentYamlPath,
67942
68099
  (from) => registry.resolveSchemaFrom(from, parentDocKind)
67943
68100
  );
67944
- if (constraints.length === 0) return void 0;
67945
- const resolved = constraints.map((c) => registry.userFacingKindsForRef(c));
68101
+ if (!slot || slot.kinds.length === 0) return void 0;
68102
+ const resolved = slot.kinds.map((c) => registry.userFacingKindsForRef(c));
67946
68103
  if (!resolved.some(Boolean)) return void 0;
67947
- return [...new Set(resolved.flatMap((kinds) => kinds ?? []))];
68104
+ return [...new Set(resolved.flatMap((kinds) => kinds ?? []))].filter(
68105
+ (kind) => !outputRefused(slot, { kind }, registry, void 0)
68106
+ );
67948
68107
  }
67949
68108
  function kindCompletions(registry, docKind, yamlPath, replaceRange) {
67950
68109
  let kinds;
@@ -68019,12 +68178,12 @@ async function buildCompletions(text, line, character, registry, adapter, docs,
68019
68178
  }
68020
68179
  if (ctx.type === "ref-name") {
68021
68180
  const definition = registry?.resolveDefinition(ctx.docKind);
68022
- const refConstraints = registry && definition?.schema ? lookupRefConstraints(
68181
+ const slot = registry && definition?.schema ? lookupRefSlot(
68023
68182
  definition.schema,
68024
68183
  ctx.yamlPath,
68025
68184
  (from) => registry.resolveSchemaFrom(from, ctx.docKind)
68026
- ) : [];
68027
- return refNameCompletions(astDocs, ctx.refKind, refConstraints, registry, ctx.replaceRange);
68185
+ ) : void 0;
68186
+ return refNameCompletions(astDocs, ctx.refKind, slot, registry, analysis, ctx.replaceRange);
68028
68187
  }
68029
68188
  if (ctx.type === "field-value") {
68030
68189
  if (ctx.field === "import-source") {