@telorun/analyzer 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAGlC,+EAA+E;AAC/E,qBAAa,kBAAkB;;IAK7B;;sFAEkF;IAClF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IAEzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAClE,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D;6DACyD;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD;;0EAEsE;IACtE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA6B;IAEhE,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI;IAiC9C,OAAO,CAAC,aAAa;IASrB;;;yFAGqF;IACrF,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAgB1E;4EACwE;IACxE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMnE;wFACoF;IACpF,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE;IAWtE,OAAO,CAAC,iBAAiB;IAczB;;;;;;;;4FAQwF;IACxF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAUhD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIrD,+FAA+F;IAC/F,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIxD,gGAAgG;IAChG,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GACvD,iBAAiB,GAAG,SAAS;IAOhC;;;;;;;;qFAQiF;IACjF,2BAA2B,CACzB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC1C,iBAAiB,GAAG,SAAS;IAuBhC,OAAO,CAAC,uBAAuB;IA+B/B;;qEAEiE;IACjE,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAgBxD,KAAK,IAAI,MAAM,EAAE;CAGlB"}
1
+ {"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAGlC,+EAA+E;AAC/E,qBAAa,kBAAkB;;IAK7B;;sFAEkF;IAClF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IAEzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAClE,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D;6DACyD;IACzD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IACzD;;0EAEsE;IACtE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA6B;IAEhE,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI;IAiC9C,OAAO,CAAC,aAAa;IASrB;;;yFAGqF;IACrF,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IA+B1E;4EACwE;IACxE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAMnE;wFACoF;IACpF,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE;IAWtE,OAAO,CAAC,iBAAiB;IAczB;;;;;;;;4FAQwF;IACxF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAUhD,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAIrD,+FAA+F;IAC/F,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIxD,gGAAgG;IAChG,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QAAE,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAAE,GACvD,iBAAiB,GAAG,SAAS;IAOhC;;;;;;;;qFAQiF;IACjF,2BAA2B,CACzB,QAAQ,EAAE,gBAAgB,EAC1B,OAAO,EAAE,aAAa,EACtB,eAAe,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC1C,iBAAiB,GAAG,SAAS;IAuBhC,OAAO,CAAC,uBAAuB;IA+B/B;;qEAEiE;IACjE,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAgBxD,KAAK,IAAI,MAAM,EAAE;CAGlB"}
@@ -70,6 +70,22 @@ export class DefinitionRegistry {
70
70
  * @param namespace The module's metadata.namespace (e.g. "std"), or null for telo built-ins.
71
71
  * @param moduleName The module's metadata.name (e.g. "pipeline", "http-server"). */
72
72
  registerModuleIdentity(namespace, moduleName) {
73
+ // The "telo" identity is reserved for the Telo built-in module and gets
74
+ // populated automatically when a Telo.Abstract definition registers (see
75
+ // `register` below). A user app / library without a namespace must NOT
76
+ // claim it — silently overwriting the built-in entry breaks every
77
+ // x-telo-ref that resolves through "telo#…". Concretely, the
78
+ // `Http.Api.routes[].handler` slot in the http-server schema carries
79
+ // `x-telo-ref: "telo#Invocable"`. If the entry application is, say,
80
+ // `Telo.Application/HelloApi` (no namespace), this method previously
81
+ // overwrote `"telo" → "Telo"` with `"telo" → "HelloApi"`. The handler's
82
+ // ref then resolved to a nonexistent `HelloApi.Invocable`, the
83
+ // kind-mismatch check inside `validate-references.ts` short-circuited
84
+ // on partial context, and the analyzer reported zero issues for a
85
+ // manifest that explodes at runtime. Skip non-Telo no-namespace modules;
86
+ // they have no x-telo-ref identity to declare anyway.
87
+ if (!namespace && moduleName !== "Telo")
88
+ return;
73
89
  const identity = namespace ? `${namespace}/${moduleName}` : "telo";
74
90
  this.identityMap.set(identity, moduleName);
75
91
  this.reverseIdentityMap.set(moduleName, identity);
@@ -1 +1 @@
1
- {"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKrD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA4C/F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CA2WtB"}
1
+ {"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKrD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA4C/F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CAkZtB"}
@@ -60,14 +60,57 @@ export function validateReferences(resources, context) {
60
60
  const aliasesByModule = context.aliasesByModule;
61
61
  if (!aliases || !registry)
62
62
  return diagnostics;
63
- // Build outer resource lookup by name for resolution check.
64
- // Exclude system kinds (Telo.Definition) they are type blueprints, not instances,
65
- // and their names (e.g. "Server", "Job") would shadow user-defined resource instances.
66
- const byName = new Map();
63
+ // Build outer resource lookup by name for resolution check, collecting
64
+ // every entry per name so we can surface name collisions as diagnostics
65
+ // (the kernel's resource registry shares one namespace across all
66
+ // non-system kinds e.g. `Telo.Application HelloApi` and `Http.Api
67
+ // HelloApi` collide at boot with `ERR_DUPLICATE_RESOURCE`. Catching it
68
+ // statically removes a class of "everything analyzes clean, then the
69
+ // kernel refuses to start" surprises.)
70
+ //
71
+ // Telo.Import is excluded from the duplicate check on top of the
72
+ // SYSTEM_KINDS skip: its `metadata.name` is an alias, not a resource
73
+ // identity (aliases live in a separate namespace from resources, and
74
+ // colliding aliases vs. resource names is benign — the alias is only
75
+ // ever read as a kind prefix).
76
+ const byNameAll = new Map();
67
77
  for (const r of resources) {
68
- if (r.metadata?.name && !SYSTEM_KINDS.has(r.kind))
69
- byName.set(r.metadata.name, r);
78
+ if (!r.metadata?.name || SYSTEM_KINDS.has(r.kind) || r.kind === "Telo.Import")
79
+ continue;
80
+ const name = r.metadata.name;
81
+ const existing = byNameAll.get(name);
82
+ if (existing)
83
+ existing.push(r);
84
+ else
85
+ byNameAll.set(name, [r]);
86
+ }
87
+ for (const [name, list] of byNameAll) {
88
+ if (list.length <= 1)
89
+ continue;
90
+ const [first, ...rest] = list;
91
+ const firstLabel = `${first.kind}/${name}`;
92
+ for (const dup of rest) {
93
+ diagnostics.push({
94
+ severity: DiagnosticSeverity.Error,
95
+ code: "DUPLICATE_RESOURCE_NAME",
96
+ source: SOURCE,
97
+ message: `${dup.kind}/${name}: resource name collides with ${firstLabel} declared earlier (kernel runtime would fail with ERR_DUPLICATE_RESOURCE)`,
98
+ data: {
99
+ resource: { kind: dup.kind, name },
100
+ filePath: dup.metadata?.source,
101
+ path: "metadata.name",
102
+ },
103
+ });
104
+ }
70
105
  }
106
+ // Single-resource map for the resolution / scope lookups below — when a
107
+ // collision exists, falling back to the first occurrence keeps the rest
108
+ // of the pass behaving the same as before the duplicate diagnostic was
109
+ // added (resolution still finds *something*; the duplicate diagnostic
110
+ // is what surfaces the underlying problem to the user).
111
+ const byName = new Map();
112
+ for (const [name, list] of byNameAll)
113
+ byName.set(name, list[0]);
71
114
  for (const r of resources) {
72
115
  if (!r.metadata?.name || !r.kind || SYSTEM_KINDS.has(r.kind))
73
116
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/analyzer",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Telo Analyzer - Static manifest validator for Telo manifests.",
5
5
  "keywords": [
6
6
  "telo",
@@ -80,6 +80,21 @@ export class DefinitionRegistry {
80
80
  * @param namespace The module's metadata.namespace (e.g. "std"), or null for telo built-ins.
81
81
  * @param moduleName The module's metadata.name (e.g. "pipeline", "http-server"). */
82
82
  registerModuleIdentity(namespace: string | null, moduleName: string): void {
83
+ // The "telo" identity is reserved for the Telo built-in module and gets
84
+ // populated automatically when a Telo.Abstract definition registers (see
85
+ // `register` below). A user app / library without a namespace must NOT
86
+ // claim it — silently overwriting the built-in entry breaks every
87
+ // x-telo-ref that resolves through "telo#…". Concretely, the
88
+ // `Http.Api.routes[].handler` slot in the http-server schema carries
89
+ // `x-telo-ref: "telo#Invocable"`. If the entry application is, say,
90
+ // `Telo.Application/HelloApi` (no namespace), this method previously
91
+ // overwrote `"telo" → "Telo"` with `"telo" → "HelloApi"`. The handler's
92
+ // ref then resolved to a nonexistent `HelloApi.Invocable`, the
93
+ // kind-mismatch check inside `validate-references.ts` short-circuited
94
+ // on partial context, and the analyzer reported zero issues for a
95
+ // manifest that explodes at runtime. Skip non-Telo no-namespace modules;
96
+ // they have no x-telo-ref identity to declare anyway.
97
+ if (!namespace && moduleName !== "Telo") return;
83
98
  const identity = namespace ? `${namespace}/${moduleName}` : "telo";
84
99
  this.identityMap.set(identity, moduleName);
85
100
  this.reverseIdentityMap.set(moduleName, identity);
@@ -72,13 +72,52 @@ export function validateReferences(
72
72
  const aliasesByModule = context.aliasesByModule;
73
73
  if (!aliases || !registry) return diagnostics;
74
74
 
75
- // Build outer resource lookup by name for resolution check.
76
- // Exclude system kinds (Telo.Definition) they are type blueprints, not instances,
77
- // and their names (e.g. "Server", "Job") would shadow user-defined resource instances.
78
- const byName = new Map<string, ResourceManifest>();
75
+ // Build outer resource lookup by name for resolution check, collecting
76
+ // every entry per name so we can surface name collisions as diagnostics
77
+ // (the kernel's resource registry shares one namespace across all
78
+ // non-system kinds e.g. `Telo.Application HelloApi` and `Http.Api
79
+ // HelloApi` collide at boot with `ERR_DUPLICATE_RESOURCE`. Catching it
80
+ // statically removes a class of "everything analyzes clean, then the
81
+ // kernel refuses to start" surprises.)
82
+ //
83
+ // Telo.Import is excluded from the duplicate check on top of the
84
+ // SYSTEM_KINDS skip: its `metadata.name` is an alias, not a resource
85
+ // identity (aliases live in a separate namespace from resources, and
86
+ // colliding aliases vs. resource names is benign — the alias is only
87
+ // ever read as a kind prefix).
88
+ const byNameAll = new Map<string, ResourceManifest[]>();
79
89
  for (const r of resources) {
80
- if (r.metadata?.name && !SYSTEM_KINDS.has(r.kind)) byName.set(r.metadata.name as string, r);
90
+ if (!r.metadata?.name || SYSTEM_KINDS.has(r.kind) || r.kind === "Telo.Import") continue;
91
+ const name = r.metadata.name as string;
92
+ const existing = byNameAll.get(name);
93
+ if (existing) existing.push(r);
94
+ else byNameAll.set(name, [r]);
95
+ }
96
+ for (const [name, list] of byNameAll) {
97
+ if (list.length <= 1) continue;
98
+ const [first, ...rest] = list;
99
+ const firstLabel = `${first.kind}/${name}`;
100
+ for (const dup of rest) {
101
+ diagnostics.push({
102
+ severity: DiagnosticSeverity.Error,
103
+ code: "DUPLICATE_RESOURCE_NAME",
104
+ source: SOURCE,
105
+ message: `${dup.kind}/${name}: resource name collides with ${firstLabel} declared earlier (kernel runtime would fail with ERR_DUPLICATE_RESOURCE)`,
106
+ data: {
107
+ resource: { kind: dup.kind, name },
108
+ filePath: (dup.metadata as { source?: string } | undefined)?.source,
109
+ path: "metadata.name",
110
+ },
111
+ });
112
+ }
81
113
  }
114
+ // Single-resource map for the resolution / scope lookups below — when a
115
+ // collision exists, falling back to the first occurrence keeps the rest
116
+ // of the pass behaving the same as before the duplicate diagnostic was
117
+ // added (resolution still finds *something*; the duplicate diagnostic
118
+ // is what surfaces the underlying problem to the user).
119
+ const byName = new Map<string, ResourceManifest>();
120
+ for (const [name, list] of byNameAll) byName.set(name, list[0]);
82
121
 
83
122
  for (const r of resources) {
84
123
  if (!r.metadata?.name || !r.kind || SYSTEM_KINDS.has(r.kind)) continue;