@telorun/analyzer 0.17.0 → 0.19.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.
- package/README.md +1 -1
- package/dist/analysis-registry.d.ts +59 -0
- package/dist/analysis-registry.d.ts.map +1 -1
- package/dist/analysis-registry.js +122 -10
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +4 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/analysis-registry.ts +138 -10
- package/src/analyzer.ts +5 -1
- package/src/index.ts +1 -0
package/README.md
CHANGED
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
import type { ResourceDefinition, ResourceManifest } from "@telorun/sdk";
|
|
2
2
|
import { type ManifestVisitor } from "./manifest-visitor.js";
|
|
3
3
|
import type { AnalysisContext } from "./types.js";
|
|
4
|
+
/** One reference field declared by a resource's definition, derived purely from
|
|
5
|
+
* the schema field map (independent of whether the manifest fills it). Editor
|
|
6
|
+
* hosts render these as ports / adapters on a node. */
|
|
7
|
+
export interface RefFieldInfo {
|
|
8
|
+
/** Field-map path with `[]` / `{}` markers (e.g. `targets[]`,
|
|
9
|
+
* `routes[].handler`, `encoder`). */
|
|
10
|
+
path: string;
|
|
11
|
+
/** True when the path traverses at least one array. */
|
|
12
|
+
isArray: boolean;
|
|
13
|
+
/** Accepted `x-telo-ref` constraint strings (e.g. `telo#Runnable`). */
|
|
14
|
+
refs: string[];
|
|
15
|
+
/** Distinct capabilities the slot may target (`Telo.Runnable`,
|
|
16
|
+
* `Telo.Service`, `Telo.Provider`, …) — one per resolvable constraint. The
|
|
17
|
+
* first classifies the port (node-capability → edge, ambient → picker); the
|
|
18
|
+
* full set validates drag-to-wire endpoints. Empty when none resolve. */
|
|
19
|
+
capabilities: string[];
|
|
20
|
+
}
|
|
4
21
|
/**
|
|
5
22
|
* Accumulates type and alias knowledge for a running kernel or analysis session.
|
|
6
23
|
* Wraps AliasResolver and DefinitionRegistry into a single domain-level interface
|
|
@@ -23,6 +40,38 @@ export declare class AnalysisRegistry {
|
|
|
23
40
|
* sub-schema (e.g. Server.notFoundHandler.returns[].content[mime].encoder).
|
|
24
41
|
*/
|
|
25
42
|
iterateFieldEntries(resource: ResourceManifest, onRef: (fieldPath: string) => void, onScope: (fieldPath: string) => void): void;
|
|
43
|
+
/**
|
|
44
|
+
* Returns every reference field a resource's definition declares, with arity
|
|
45
|
+
* and the capability each slot targets — derived purely from the schema field
|
|
46
|
+
* map, so it lists slots even when the manifest leaves them empty. Editor
|
|
47
|
+
* hosts render these as node ports (drag-to-wire for node-capability targets,
|
|
48
|
+
* inline picker for ambient targets).
|
|
49
|
+
*/
|
|
50
|
+
refFieldsForResource(resource: ResourceManifest): RefFieldInfo[];
|
|
51
|
+
/** Base capability an `x-telo-ref` constraint targets. A definition's declared
|
|
52
|
+
* `capability` is always one of the base capabilities, so it wins — this
|
|
53
|
+
* resolves user-defined abstracts (e.g. `std/ai#Model`, declared
|
|
54
|
+
* `capability: Telo.Invocable`) to the capability instances satisfy, not the
|
|
55
|
+
* abstract kind. Builtin abstracts (`telo#Runnable`) carry no `capability`
|
|
56
|
+
* field — there the kind itself *is* the capability. Undefined when
|
|
57
|
+
* unresolvable. */
|
|
58
|
+
capabilityForRef(xTeloRef: string): string | undefined;
|
|
59
|
+
/** Resolves the JSON Schema for a kind's `invoke()` inputs, for editor hosts
|
|
60
|
+
* that render a typed inputs form. Two-layer fallback mirroring the analyzer's
|
|
61
|
+
* template inputs typing: the definition's own `inputType`, then the
|
|
62
|
+
* `extends`-declared abstract's `inputType`. Resolves the inline
|
|
63
|
+
* (`{ kind: Type.JsonSchema, schema }`) and raw-schema forms; a bare named
|
|
64
|
+
* type reference is left unresolved (returns undefined) so the caller can fall
|
|
65
|
+
* back to a freeform map. Undefined when the kind declares no input contract. */
|
|
66
|
+
inputTypeForKind(kind: string): Record<string, unknown> | undefined;
|
|
67
|
+
/** Resolves the JSON Schema for a kind's `invoke()` / `run()` output, for
|
|
68
|
+
* editor hosts that render a typed output signature. Mirrors
|
|
69
|
+
* {@link inputTypeForKind}: the definition's own `outputType`, then the
|
|
70
|
+
* `extends`-declared abstract's `outputType`. Resolves the inline and
|
|
71
|
+
* raw-schema forms; a bare named type reference is left unresolved. Undefined
|
|
72
|
+
* when the kind declares no output contract. */
|
|
73
|
+
outputTypeForKind(kind: string): Record<string, unknown> | undefined;
|
|
74
|
+
private capabilitiesForRefs;
|
|
26
75
|
/**
|
|
27
76
|
* Walks a manifest's annotation sites (refs, scopes, schema-from, CEL) via
|
|
28
77
|
* the shared manifest visitor, bound to this registry's definitions and
|
|
@@ -54,6 +103,16 @@ export declare class AnalysisRegistry {
|
|
|
54
103
|
/** Returns the closest user-facing kind to `badKind`, or undefined when nothing
|
|
55
104
|
* is close enough (or multiple candidates tie). Case-sensitive. */
|
|
56
105
|
suggestKind(badKind: string): string | undefined;
|
|
106
|
+
/** Returns the **canonical** (`module.Type`) kinds that satisfy an `x-telo-ref`
|
|
107
|
+
* constraint — an abstract target expands to its implementations (via the
|
|
108
|
+
* extends / capability index), a concrete target yields just itself.
|
|
109
|
+
* Resolution mirrors `validateReferences.checkKind`. Unlike
|
|
110
|
+
* {@link userFacingKindsForRef} this is import-independent: it includes
|
|
111
|
+
* locally-defined kinds (no alias), so callers can test whether an existing
|
|
112
|
+
* resource's kind satisfies the ref by canonicalizing it (`resolveKind`) and
|
|
113
|
+
* membership-checking here. Returns `undefined` when the ref can't be
|
|
114
|
+
* resolved (e.g. unregistered identity). */
|
|
115
|
+
acceptedKindsForRef(xTeloRef: string): Set<string> | undefined;
|
|
57
116
|
/** Returns every user-facing (alias-form) kind that satisfies the given
|
|
58
117
|
* `x-telo-ref` constraint string (e.g. `"telo#Invocable"`, `"std/sql#Connection"`).
|
|
59
118
|
* Resolution mirrors `validateReferences.checkKind`: abstract targets expand to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKzE,OAAO,EAAqC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"analysis-registry.d.ts","sourceRoot":"","sources":["../src/analysis-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKzE,OAAO,EAAqC,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAEhG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD;;wDAEwD;AACxD,MAAM,WAAW,YAAY;IAC3B;0CACsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;8EAG0E;IAC1E,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;GAIG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA4B;IACjD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAoC;IAEpE,kBAAkB,CAAC,GAAG,EAAE,kBAAkB,GAAG,IAAI;IAIjD,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIpE,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI;IAIpE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAI7C;;;;;;;OAOG;IACH,mBAAmB,CACjB,QAAQ,EAAE,gBAAgB,EAC1B,KAAK,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,GACnC,IAAI;IAkBP;;;;;;OAMG;IACH,oBAAoB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,YAAY,EAAE;IAoBhE;;;;;;wBAMoB;IACpB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAQtD;;;;;;sFAMkF;IAClF,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAenE;;;;;qDAKiD;IACjD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS;IAepE,OAAO,CAAC,mBAAmB;IAS3B;;;;;;OAMG;IACH,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,EACxB,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAC;QAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAAE,GACzF,IAAI;IAQP;;;;OAIG;IACH,kBAAkB,IAAI,kBAAkB,EAAE;IAI1C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAM/D,QAAQ,IAAI,MAAM,EAAE;IAIpB;mEAC+D;IAC/D,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAIxC;;;gCAG4B;IAC5B,oBAAoB,IAAI,MAAM,EAAE;IAIhC;wEACoE;IACpE,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIhD;;;;;;;;iDAQ6C;IAC7C,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS;IAoB9D;;;;;gEAK4D;IAC5D,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS;IAiB7D,qFAAqF;IACrF,QAAQ,IAAI,eAAe;CAG5B"}
|
|
@@ -4,6 +4,7 @@ import { DefinitionRegistry } from "./definition-registry.js";
|
|
|
4
4
|
import { computeSuggestKind, computeValidUserFacingKinds } from "./kind-suggest.js";
|
|
5
5
|
import { visitManifest as runVisitManifest } from "./manifest-visitor.js";
|
|
6
6
|
import { isRefEntry, isScopeEntry } from "./reference-field-map.js";
|
|
7
|
+
import { resolveTypeFieldToSchema } from "./validate-cel-context.js";
|
|
7
8
|
/**
|
|
8
9
|
* Accumulates type and alias knowledge for a running kernel or analysis session.
|
|
9
10
|
* Wraps AliasResolver and DefinitionRegistry into a single domain-level interface
|
|
@@ -47,6 +48,102 @@ export class AnalysisRegistry {
|
|
|
47
48
|
}
|
|
48
49
|
}
|
|
49
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Returns every reference field a resource's definition declares, with arity
|
|
53
|
+
* and the capability each slot targets — derived purely from the schema field
|
|
54
|
+
* map, so it lists slots even when the manifest leaves them empty. Editor
|
|
55
|
+
* hosts render these as node ports (drag-to-wire for node-capability targets,
|
|
56
|
+
* inline picker for ambient targets).
|
|
57
|
+
*/
|
|
58
|
+
refFieldsForResource(resource) {
|
|
59
|
+
const fieldMap = this.defs.expandedFieldMapForResource(resource, this.aliases, this.aliasesByModule);
|
|
60
|
+
if (!fieldMap)
|
|
61
|
+
return [];
|
|
62
|
+
const out = [];
|
|
63
|
+
for (const [path, entry] of fieldMap) {
|
|
64
|
+
if (!isRefEntry(entry))
|
|
65
|
+
continue;
|
|
66
|
+
out.push({
|
|
67
|
+
path,
|
|
68
|
+
isArray: entry.isArray,
|
|
69
|
+
refs: entry.refs,
|
|
70
|
+
capabilities: this.capabilitiesForRefs(entry.refs),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
/** Base capability an `x-telo-ref` constraint targets. A definition's declared
|
|
76
|
+
* `capability` is always one of the base capabilities, so it wins — this
|
|
77
|
+
* resolves user-defined abstracts (e.g. `std/ai#Model`, declared
|
|
78
|
+
* `capability: Telo.Invocable`) to the capability instances satisfy, not the
|
|
79
|
+
* abstract kind. Builtin abstracts (`telo#Runnable`) carry no `capability`
|
|
80
|
+
* field — there the kind itself *is* the capability. Undefined when
|
|
81
|
+
* unresolvable. */
|
|
82
|
+
capabilityForRef(xTeloRef) {
|
|
83
|
+
const kind = this.defs.resolveRef(xTeloRef);
|
|
84
|
+
if (!kind)
|
|
85
|
+
return undefined;
|
|
86
|
+
const def = this.defs.resolve(kind);
|
|
87
|
+
if (!def)
|
|
88
|
+
return undefined;
|
|
89
|
+
return def.capability ?? kind;
|
|
90
|
+
}
|
|
91
|
+
/** Resolves the JSON Schema for a kind's `invoke()` inputs, for editor hosts
|
|
92
|
+
* that render a typed inputs form. Two-layer fallback mirroring the analyzer's
|
|
93
|
+
* template inputs typing: the definition's own `inputType`, then the
|
|
94
|
+
* `extends`-declared abstract's `inputType`. Resolves the inline
|
|
95
|
+
* (`{ kind: Type.JsonSchema, schema }`) and raw-schema forms; a bare named
|
|
96
|
+
* type reference is left unresolved (returns undefined) so the caller can fall
|
|
97
|
+
* back to a freeform map. Undefined when the kind declares no input contract. */
|
|
98
|
+
inputTypeForKind(kind) {
|
|
99
|
+
const def = this.resolveDefinition(kind);
|
|
100
|
+
if (!def)
|
|
101
|
+
return undefined;
|
|
102
|
+
const own = resolveTypeFieldToSchema(def.inputType, []);
|
|
103
|
+
if (own)
|
|
104
|
+
return own;
|
|
105
|
+
if (def.extends) {
|
|
106
|
+
const abstractDef = this.resolveDefinition(def.extends);
|
|
107
|
+
if (abstractDef) {
|
|
108
|
+
const inherited = resolveTypeFieldToSchema(abstractDef.inputType, []);
|
|
109
|
+
if (inherited)
|
|
110
|
+
return inherited;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
/** Resolves the JSON Schema for a kind's `invoke()` / `run()` output, for
|
|
116
|
+
* editor hosts that render a typed output signature. Mirrors
|
|
117
|
+
* {@link inputTypeForKind}: the definition's own `outputType`, then the
|
|
118
|
+
* `extends`-declared abstract's `outputType`. Resolves the inline and
|
|
119
|
+
* raw-schema forms; a bare named type reference is left unresolved. Undefined
|
|
120
|
+
* when the kind declares no output contract. */
|
|
121
|
+
outputTypeForKind(kind) {
|
|
122
|
+
const def = this.resolveDefinition(kind);
|
|
123
|
+
if (!def)
|
|
124
|
+
return undefined;
|
|
125
|
+
const own = resolveTypeFieldToSchema(def.outputType, []);
|
|
126
|
+
if (own)
|
|
127
|
+
return own;
|
|
128
|
+
if (def.extends) {
|
|
129
|
+
const abstractDef = this.resolveDefinition(def.extends);
|
|
130
|
+
if (abstractDef) {
|
|
131
|
+
const inherited = resolveTypeFieldToSchema(abstractDef.outputType, []);
|
|
132
|
+
if (inherited)
|
|
133
|
+
return inherited;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
capabilitiesForRefs(refs) {
|
|
139
|
+
const out = [];
|
|
140
|
+
for (const ref of refs) {
|
|
141
|
+
const cap = this.capabilityForRef(ref);
|
|
142
|
+
if (cap && !out.includes(cap))
|
|
143
|
+
out.push(cap);
|
|
144
|
+
}
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
50
147
|
/**
|
|
51
148
|
* Walks a manifest's annotation sites (refs, scopes, schema-from, CEL) via
|
|
52
149
|
* the shared manifest visitor, bound to this registry's definitions and
|
|
@@ -94,31 +191,46 @@ export class AnalysisRegistry {
|
|
|
94
191
|
suggestKind(badKind) {
|
|
95
192
|
return computeSuggestKind(badKind, this.aliases, this.defs);
|
|
96
193
|
}
|
|
97
|
-
/** Returns
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
|
|
194
|
+
/** Returns the **canonical** (`module.Type`) kinds that satisfy an `x-telo-ref`
|
|
195
|
+
* constraint — an abstract target expands to its implementations (via the
|
|
196
|
+
* extends / capability index), a concrete target yields just itself.
|
|
197
|
+
* Resolution mirrors `validateReferences.checkKind`. Unlike
|
|
198
|
+
* {@link userFacingKindsForRef} this is import-independent: it includes
|
|
199
|
+
* locally-defined kinds (no alias), so callers can test whether an existing
|
|
200
|
+
* resource's kind satisfies the ref by canonicalizing it (`resolveKind`) and
|
|
201
|
+
* membership-checking here. Returns `undefined` when the ref can't be
|
|
202
|
+
* resolved (e.g. unregistered identity). */
|
|
203
|
+
acceptedKindsForRef(xTeloRef) {
|
|
104
204
|
const targetKind = this.defs.resolveRef(xTeloRef);
|
|
105
205
|
if (!targetKind)
|
|
106
206
|
return undefined;
|
|
107
207
|
const targetDef = this.defs.resolve(targetKind);
|
|
108
208
|
if (!targetDef)
|
|
109
209
|
return undefined;
|
|
110
|
-
const
|
|
210
|
+
const out = new Set();
|
|
111
211
|
if (targetDef.kind === "Telo.Abstract") {
|
|
112
212
|
for (const def of this.defs.getByExtends(targetKind)) {
|
|
113
213
|
const module = def.metadata?.module;
|
|
114
214
|
if (module && def.metadata?.name) {
|
|
115
|
-
|
|
215
|
+
out.add(`${module}.${def.metadata.name}`);
|
|
116
216
|
}
|
|
117
217
|
}
|
|
118
218
|
}
|
|
119
219
|
else {
|
|
120
|
-
|
|
220
|
+
out.add(targetKind);
|
|
121
221
|
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
/** Returns every user-facing (alias-form) kind that satisfies the given
|
|
225
|
+
* `x-telo-ref` constraint string (e.g. `"telo#Invocable"`, `"std/sql#Connection"`).
|
|
226
|
+
* Resolution mirrors `validateReferences.checkKind`: abstract targets expand to
|
|
227
|
+
* the set of definitions extending them; concrete targets yield just themselves.
|
|
228
|
+
* Returns `undefined` when the ref can't be resolved (e.g. unregistered identity),
|
|
229
|
+
* so callers can fall back to the unfiltered kind list. */
|
|
230
|
+
userFacingKindsForRef(xTeloRef) {
|
|
231
|
+
const canonicalKinds = this.acceptedKindsForRef(xTeloRef);
|
|
232
|
+
if (!canonicalKinds)
|
|
233
|
+
return undefined;
|
|
122
234
|
const out = new Set();
|
|
123
235
|
for (const kind of canonicalKinds) {
|
|
124
236
|
const dot = kind.indexOf(".");
|
package/dist/analyzer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAiB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAiB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA2hB/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAypBvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CACP,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,EAI1B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,GACtC,gBAAgB,EAAE;IAqBrB,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
|
package/dist/analyzer.js
CHANGED
|
@@ -442,8 +442,11 @@ function collectCelTypeIssues(data, schema, path, definition, manifest, baseType
|
|
|
442
442
|
}
|
|
443
443
|
else if (data !== null && typeof data === "object") {
|
|
444
444
|
const props = (schema.properties ?? {});
|
|
445
|
+
const mapValueSchema = schema.additionalProperties && typeof schema.additionalProperties === "object"
|
|
446
|
+
? schema.additionalProperties
|
|
447
|
+
: {};
|
|
445
448
|
for (const [k, v] of Object.entries(data)) {
|
|
446
|
-
issues.push(...collectCelTypeIssues(v, (props[k] ??
|
|
449
|
+
issues.push(...collectCelTypeIssues(v, (props[k] ?? mapValueSchema), path ? `${path}.${k}` : k, definition, manifest, baseTypedEnv, rootEnv, rootModuleManifest));
|
|
447
450
|
}
|
|
448
451
|
}
|
|
449
452
|
return issues;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { AnalysisRegistry } from "./analysis-registry.js";
|
|
2
|
+
export type { RefFieldInfo } from "./analysis-registry.js";
|
|
2
3
|
export { StaticAnalyzer } from "./analyzer.js";
|
|
3
4
|
export type { GraphLoadError, ImportEdge, LoadedFile, LoadedGraph, LoadedModule, ParseError, } from "./loaded-types.js";
|
|
4
5
|
export { flattenForAnalyzer, flattenLoadedModule, selectModuleManifestsForAnalysis, } from "./flatten-for-analyzer.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,cAAc,EACd,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACH,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,GACnC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,QAAQ,EACR,aAAa,EACb,KAAK,EACR,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EACR,cAAc,EACd,UAAU,EACV,UAAU,EACV,WAAW,EACX,YAAY,EACZ,UAAU,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACH,kBAAkB,EAClB,mBAAmB,EACnB,gCAAgC,GACnC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,YAAY,EACR,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC/D,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,YAAY,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EACH,sBAAsB,EACtB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,GACtB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AACvE,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC3E,YAAY,EACR,kBAAkB,EAClB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,QAAQ,EACR,aAAa,EACb,KAAK,EACR,MAAM,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/analyzer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Telo Analyzer - Static manifest validator for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"@types/node": "^20.0.0",
|
|
49
49
|
"typescript": "^5.0.0",
|
|
50
50
|
"vitest": "^2.1.8",
|
|
51
|
-
"@telorun/sdk": "0.
|
|
51
|
+
"@telorun/sdk": "0.19.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@telorun/sdk": "*"
|
package/src/analysis-registry.ts
CHANGED
|
@@ -6,6 +6,25 @@ import { computeSuggestKind, computeValidUserFacingKinds } from "./kind-suggest.
|
|
|
6
6
|
import { visitManifest as runVisitManifest, type ManifestVisitor } from "./manifest-visitor.js";
|
|
7
7
|
import { isRefEntry, isScopeEntry } from "./reference-field-map.js";
|
|
8
8
|
import type { AnalysisContext } from "./types.js";
|
|
9
|
+
import { resolveTypeFieldToSchema } from "./validate-cel-context.js";
|
|
10
|
+
|
|
11
|
+
/** One reference field declared by a resource's definition, derived purely from
|
|
12
|
+
* the schema field map (independent of whether the manifest fills it). Editor
|
|
13
|
+
* hosts render these as ports / adapters on a node. */
|
|
14
|
+
export interface RefFieldInfo {
|
|
15
|
+
/** Field-map path with `[]` / `{}` markers (e.g. `targets[]`,
|
|
16
|
+
* `routes[].handler`, `encoder`). */
|
|
17
|
+
path: string;
|
|
18
|
+
/** True when the path traverses at least one array. */
|
|
19
|
+
isArray: boolean;
|
|
20
|
+
/** Accepted `x-telo-ref` constraint strings (e.g. `telo#Runnable`). */
|
|
21
|
+
refs: string[];
|
|
22
|
+
/** Distinct capabilities the slot may target (`Telo.Runnable`,
|
|
23
|
+
* `Telo.Service`, `Telo.Provider`, …) — one per resolvable constraint. The
|
|
24
|
+
* first classifies the port (node-capability → edge, ambient → picker); the
|
|
25
|
+
* full set validates drag-to-wire endpoints. Empty when none resolve. */
|
|
26
|
+
capabilities: string[];
|
|
27
|
+
}
|
|
9
28
|
|
|
10
29
|
/**
|
|
11
30
|
* Accumulates type and alias knowledge for a running kernel or analysis session.
|
|
@@ -63,6 +82,100 @@ export class AnalysisRegistry {
|
|
|
63
82
|
}
|
|
64
83
|
}
|
|
65
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Returns every reference field a resource's definition declares, with arity
|
|
87
|
+
* and the capability each slot targets — derived purely from the schema field
|
|
88
|
+
* map, so it lists slots even when the manifest leaves them empty. Editor
|
|
89
|
+
* hosts render these as node ports (drag-to-wire for node-capability targets,
|
|
90
|
+
* inline picker for ambient targets).
|
|
91
|
+
*/
|
|
92
|
+
refFieldsForResource(resource: ResourceManifest): RefFieldInfo[] {
|
|
93
|
+
const fieldMap = this.defs.expandedFieldMapForResource(
|
|
94
|
+
resource,
|
|
95
|
+
this.aliases,
|
|
96
|
+
this.aliasesByModule,
|
|
97
|
+
);
|
|
98
|
+
if (!fieldMap) return [];
|
|
99
|
+
const out: RefFieldInfo[] = [];
|
|
100
|
+
for (const [path, entry] of fieldMap) {
|
|
101
|
+
if (!isRefEntry(entry)) continue;
|
|
102
|
+
out.push({
|
|
103
|
+
path,
|
|
104
|
+
isArray: entry.isArray,
|
|
105
|
+
refs: entry.refs,
|
|
106
|
+
capabilities: this.capabilitiesForRefs(entry.refs),
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Base capability an `x-telo-ref` constraint targets. A definition's declared
|
|
113
|
+
* `capability` is always one of the base capabilities, so it wins — this
|
|
114
|
+
* resolves user-defined abstracts (e.g. `std/ai#Model`, declared
|
|
115
|
+
* `capability: Telo.Invocable`) to the capability instances satisfy, not the
|
|
116
|
+
* abstract kind. Builtin abstracts (`telo#Runnable`) carry no `capability`
|
|
117
|
+
* field — there the kind itself *is* the capability. Undefined when
|
|
118
|
+
* unresolvable. */
|
|
119
|
+
capabilityForRef(xTeloRef: string): string | undefined {
|
|
120
|
+
const kind = this.defs.resolveRef(xTeloRef);
|
|
121
|
+
if (!kind) return undefined;
|
|
122
|
+
const def = this.defs.resolve(kind);
|
|
123
|
+
if (!def) return undefined;
|
|
124
|
+
return def.capability ?? kind;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Resolves the JSON Schema for a kind's `invoke()` inputs, for editor hosts
|
|
128
|
+
* that render a typed inputs form. Two-layer fallback mirroring the analyzer's
|
|
129
|
+
* template inputs typing: the definition's own `inputType`, then the
|
|
130
|
+
* `extends`-declared abstract's `inputType`. Resolves the inline
|
|
131
|
+
* (`{ kind: Type.JsonSchema, schema }`) and raw-schema forms; a bare named
|
|
132
|
+
* type reference is left unresolved (returns undefined) so the caller can fall
|
|
133
|
+
* back to a freeform map. Undefined when the kind declares no input contract. */
|
|
134
|
+
inputTypeForKind(kind: string): Record<string, unknown> | undefined {
|
|
135
|
+
const def = this.resolveDefinition(kind);
|
|
136
|
+
if (!def) return undefined;
|
|
137
|
+
const own = resolveTypeFieldToSchema(def.inputType, []);
|
|
138
|
+
if (own) return own;
|
|
139
|
+
if (def.extends) {
|
|
140
|
+
const abstractDef = this.resolveDefinition(def.extends);
|
|
141
|
+
if (abstractDef) {
|
|
142
|
+
const inherited = resolveTypeFieldToSchema(abstractDef.inputType, []);
|
|
143
|
+
if (inherited) return inherited;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return undefined;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Resolves the JSON Schema for a kind's `invoke()` / `run()` output, for
|
|
150
|
+
* editor hosts that render a typed output signature. Mirrors
|
|
151
|
+
* {@link inputTypeForKind}: the definition's own `outputType`, then the
|
|
152
|
+
* `extends`-declared abstract's `outputType`. Resolves the inline and
|
|
153
|
+
* raw-schema forms; a bare named type reference is left unresolved. Undefined
|
|
154
|
+
* when the kind declares no output contract. */
|
|
155
|
+
outputTypeForKind(kind: string): Record<string, unknown> | undefined {
|
|
156
|
+
const def = this.resolveDefinition(kind);
|
|
157
|
+
if (!def) return undefined;
|
|
158
|
+
const own = resolveTypeFieldToSchema(def.outputType, []);
|
|
159
|
+
if (own) return own;
|
|
160
|
+
if (def.extends) {
|
|
161
|
+
const abstractDef = this.resolveDefinition(def.extends);
|
|
162
|
+
if (abstractDef) {
|
|
163
|
+
const inherited = resolveTypeFieldToSchema(abstractDef.outputType, []);
|
|
164
|
+
if (inherited) return inherited;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private capabilitiesForRefs(refs: string[]): string[] {
|
|
171
|
+
const out: string[] = [];
|
|
172
|
+
for (const ref of refs) {
|
|
173
|
+
const cap = this.capabilityForRef(ref);
|
|
174
|
+
if (cap && !out.includes(cap)) out.push(cap);
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
|
|
66
179
|
/**
|
|
67
180
|
* Walks a manifest's annotation sites (refs, scopes, schema-from, CEL) via
|
|
68
181
|
* the shared manifest visitor, bound to this registry's definitions and
|
|
@@ -121,29 +234,44 @@ export class AnalysisRegistry {
|
|
|
121
234
|
return computeSuggestKind(badKind, this.aliases, this.defs);
|
|
122
235
|
}
|
|
123
236
|
|
|
124
|
-
/** Returns
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
|
|
237
|
+
/** Returns the **canonical** (`module.Type`) kinds that satisfy an `x-telo-ref`
|
|
238
|
+
* constraint — an abstract target expands to its implementations (via the
|
|
239
|
+
* extends / capability index), a concrete target yields just itself.
|
|
240
|
+
* Resolution mirrors `validateReferences.checkKind`. Unlike
|
|
241
|
+
* {@link userFacingKindsForRef} this is import-independent: it includes
|
|
242
|
+
* locally-defined kinds (no alias), so callers can test whether an existing
|
|
243
|
+
* resource's kind satisfies the ref by canonicalizing it (`resolveKind`) and
|
|
244
|
+
* membership-checking here. Returns `undefined` when the ref can't be
|
|
245
|
+
* resolved (e.g. unregistered identity). */
|
|
246
|
+
acceptedKindsForRef(xTeloRef: string): Set<string> | undefined {
|
|
131
247
|
const targetKind = this.defs.resolveRef(xTeloRef);
|
|
132
248
|
if (!targetKind) return undefined;
|
|
133
249
|
const targetDef = this.defs.resolve(targetKind);
|
|
134
250
|
if (!targetDef) return undefined;
|
|
135
251
|
|
|
136
|
-
const
|
|
252
|
+
const out = new Set<string>();
|
|
137
253
|
if (targetDef.kind === "Telo.Abstract") {
|
|
138
254
|
for (const def of this.defs.getByExtends(targetKind)) {
|
|
139
255
|
const module = (def.metadata as { module?: string } | undefined)?.module;
|
|
140
256
|
if (module && def.metadata?.name) {
|
|
141
|
-
|
|
257
|
+
out.add(`${module}.${def.metadata.name as string}`);
|
|
142
258
|
}
|
|
143
259
|
}
|
|
144
260
|
} else {
|
|
145
|
-
|
|
261
|
+
out.add(targetKind);
|
|
146
262
|
}
|
|
263
|
+
return out;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/** Returns every user-facing (alias-form) kind that satisfies the given
|
|
267
|
+
* `x-telo-ref` constraint string (e.g. `"telo#Invocable"`, `"std/sql#Connection"`).
|
|
268
|
+
* Resolution mirrors `validateReferences.checkKind`: abstract targets expand to
|
|
269
|
+
* the set of definitions extending them; concrete targets yield just themselves.
|
|
270
|
+
* Returns `undefined` when the ref can't be resolved (e.g. unregistered identity),
|
|
271
|
+
* so callers can fall back to the unfiltered kind list. */
|
|
272
|
+
userFacingKindsForRef(xTeloRef: string): string[] | undefined {
|
|
273
|
+
const canonicalKinds = this.acceptedKindsForRef(xTeloRef);
|
|
274
|
+
if (!canonicalKinds) return undefined;
|
|
147
275
|
|
|
148
276
|
const out = new Set<string>();
|
|
149
277
|
for (const kind of canonicalKinds) {
|
package/src/analyzer.ts
CHANGED
|
@@ -540,11 +540,15 @@ function collectCelTypeIssues(
|
|
|
540
540
|
}
|
|
541
541
|
} else if (data !== null && typeof data === "object") {
|
|
542
542
|
const props = (schema.properties ?? {}) as Record<string, any>;
|
|
543
|
+
const mapValueSchema =
|
|
544
|
+
schema.additionalProperties && typeof schema.additionalProperties === "object"
|
|
545
|
+
? (schema.additionalProperties as Record<string, any>)
|
|
546
|
+
: {};
|
|
543
547
|
for (const [k, v] of Object.entries(data as Record<string, unknown>)) {
|
|
544
548
|
issues.push(
|
|
545
549
|
...collectCelTypeIssues(
|
|
546
550
|
v,
|
|
547
|
-
(props[k] ??
|
|
551
|
+
(props[k] ?? mapValueSchema) as Record<string, any>,
|
|
548
552
|
path ? `${path}.${k}` : k,
|
|
549
553
|
definition,
|
|
550
554
|
manifest,
|