@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;
|
|
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,
|
|
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
|
-
//
|
|
65
|
-
//
|
|
66
|
-
|
|
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
|
|
69
|
-
|
|
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
|
@@ -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
|
-
//
|
|
77
|
-
//
|
|
78
|
-
|
|
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
|
|
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;
|