@telorun/analyzer 0.43.0 → 0.44.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/dist/analysis-registry.d.ts +4 -4
- package/dist/analysis-registry.d.ts.map +1 -1
- package/dist/analysis-registry.js +3 -3
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +84 -1
- package/dist/builtins.js +7 -7
- package/dist/definition-registry.d.ts +49 -22
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +67 -61
- package/dist/loaded-types.d.ts +13 -6
- package/dist/loaded-types.d.ts.map +1 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +1 -0
- package/dist/reconcile-module-versions.d.ts +2 -2
- package/dist/reconcile-module-versions.d.ts.map +1 -1
- package/dist/reconcile-module-versions.js +69 -17
- package/dist/reference-field-map.d.ts +2 -1
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/resolve-schema-ref-kinds.d.ts +62 -0
- package/dist/resolve-schema-ref-kinds.d.ts.map +1 -0
- package/dist/resolve-schema-ref-kinds.js +81 -0
- package/dist/sources/manifest-cache.d.ts +14 -4
- package/dist/sources/manifest-cache.d.ts.map +1 -1
- package/dist/sources/manifest-cache.js +15 -6
- package/package.json +2 -2
- package/src/analysis-registry.ts +4 -4
- package/src/analyzer.ts +85 -2
- package/src/builtins.ts +7 -7
- package/src/definition-registry.ts +67 -66
- package/src/loaded-types.ts +13 -6
- package/src/manifest-loader.ts +1 -0
- package/src/reconcile-module-versions.ts +69 -17
- package/src/reference-field-map.ts +2 -1
- package/src/resolve-schema-ref-kinds.ts +118 -0
- package/src/sources/manifest-cache.ts +26 -8
|
@@ -13,8 +13,8 @@ export interface VersionReconciliation {
|
|
|
13
13
|
diagnostics: AnalysisDiagnostic[];
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
|
-
* Reconcile a loaded import graph so each module
|
|
17
|
-
* resolves to a single version. Within a shared major the highest version wins
|
|
16
|
+
* Reconcile a loaded import graph so each module location (an import ref minus
|
|
17
|
+
* its version) resolves to a single version. Within a shared major the highest version wins
|
|
18
18
|
* (a non-lossy hoist, given Telo's additive-only pre-1.0 policy); a major
|
|
19
19
|
* mismatch is a hard conflict. Mutates `importEdges` in place — every edge that
|
|
20
20
|
* pointed at a losing source is repointed at the winner — so `flattenForAnalyzer`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reconcile-module-versions.d.ts","sourceRoot":"","sources":["../src/reconcile-module-versions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;0CAI0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;2EACuE;IACvE,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;
|
|
1
|
+
{"version":3,"file":"reconcile-module-versions.d.ts","sourceRoot":"","sources":["../src/reconcile-module-versions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAElE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;0CAI0C;AAC1C,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B;2EACuE;IACvE,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC;AAuMD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAClC,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,GAChD,qBAAqB,CAsEvB"}
|
|
@@ -64,27 +64,56 @@ function compareVersions(a, b) {
|
|
|
64
64
|
}
|
|
65
65
|
return 0;
|
|
66
66
|
}
|
|
67
|
-
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
|
|
67
|
+
/** The location identity of an import ref: the ref with its version stripped.
|
|
68
|
+
* Two refs share an identity when they address the same module at different
|
|
69
|
+
* versions, whatever transport owns them:
|
|
70
|
+
*
|
|
71
|
+
* "std/kv-store@0.3.0" → "std/kv-store"
|
|
72
|
+
* "oci://ghcr.io/acme/s3@1.2.0" → "oci://ghcr.io/acme/s3"
|
|
73
|
+
* "https://x.com/lib/telo.yaml" → itself (a URL carries no version)
|
|
74
|
+
*
|
|
75
|
+
* Returns `null` for a relative path, which addresses one file on the
|
|
76
|
+
* publisher's disk and is therefore not a cross-import key: two local libraries
|
|
77
|
+
* that merely agree on `metadata.name` are distinct modules, and reconciling
|
|
78
|
+
* them would drop one and break its kinds. The same local file reached via two
|
|
79
|
+
* paths is already collapsed by canonical-source dedup.
|
|
80
|
+
*
|
|
81
|
+
* **What this key cannot relate.** It compares ref *spellings*, so it groups by
|
|
82
|
+
* origin exactly and nothing else. Two consequences, both accepted:
|
|
83
|
+
*
|
|
84
|
+
* - A module imported once by a registry ref and once by a relative path is two
|
|
85
|
+
* groups, so a version skew between them is not hoisted. Keying on what the
|
|
86
|
+
* module declares about itself would catch that case, but only by trusting a
|
|
87
|
+
* self-declared identity — which is what this change removes, and which
|
|
88
|
+
* cannot tell two same-named modules from different origins apart.
|
|
89
|
+
* - A bare `std/kv-store@0.4.0` and the equivalent direct
|
|
90
|
+
* `https://<registry>/std/kv-store/0.4.0/telo.yaml` are two groups. Relating
|
|
91
|
+
* them needs the configured registry base, which this pure, browser-safe
|
|
92
|
+
* function does not have. */
|
|
93
|
+
function refIdentity(ref) {
|
|
94
|
+
const base = ref.split("#")[0];
|
|
95
|
+
if (!base || base.startsWith(".") || base.startsWith("/") || base.startsWith("file:")) {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
const lastSlash = base.lastIndexOf("/");
|
|
99
|
+
const at = base.lastIndexOf("@");
|
|
100
|
+
return at > lastSlash && at > 0 ? base.slice(0, at) : base;
|
|
101
|
+
}
|
|
102
|
+
/** Read a loaded module's version and raw owner text under the location
|
|
103
|
+
* identity the import edge reached it by. The identity comes from the ref, not
|
|
104
|
+
* from anything the module declares about itself — a module's own metadata
|
|
105
|
+
* cannot distinguish two same-named modules published to different origins. */
|
|
106
|
+
function moduleIdentityOf(mod, identity) {
|
|
75
107
|
const doc = mod.owner.manifests.find((m) => m && isModuleKind(m.kind));
|
|
76
108
|
if (!doc)
|
|
77
109
|
return null;
|
|
78
110
|
const meta = doc.metadata;
|
|
79
|
-
|
|
80
|
-
if (typeof name !== "string" || name.length === 0)
|
|
81
|
-
return null;
|
|
82
|
-
if (typeof meta.namespace !== "string" || meta.namespace.length === 0)
|
|
111
|
+
if (typeof meta?.name !== "string" || meta.name.length === 0)
|
|
83
112
|
return null;
|
|
84
113
|
const version = typeof meta.version === "string" ? meta.version : "";
|
|
85
114
|
return {
|
|
86
115
|
source: mod.owner.source,
|
|
87
|
-
identity
|
|
116
|
+
identity,
|
|
88
117
|
version,
|
|
89
118
|
parsed: parseVersion(version),
|
|
90
119
|
text: mod.owner.text,
|
|
@@ -154,8 +183,8 @@ function hoistDiagnostic(identity, importerSource, alias, loser, winner, conflic
|
|
|
154
183
|
return null;
|
|
155
184
|
}
|
|
156
185
|
/**
|
|
157
|
-
* Reconcile a loaded import graph so each module
|
|
158
|
-
* resolves to a single version. Within a shared major the highest version wins
|
|
186
|
+
* Reconcile a loaded import graph so each module location (an import ref minus
|
|
187
|
+
* its version) resolves to a single version. Within a shared major the highest version wins
|
|
159
188
|
* (a non-lossy hoist, given Telo's additive-only pre-1.0 policy); a major
|
|
160
189
|
* mismatch is a hard conflict. Mutates `importEdges` in place — every edge that
|
|
161
190
|
* pointed at a losing source is repointed at the winner — so `flattenForAnalyzer`
|
|
@@ -165,10 +194,33 @@ function hoistDiagnostic(identity, importerSource, alias, loser, winner, conflic
|
|
|
165
194
|
export function reconcileModuleVersions(modules, importEdges) {
|
|
166
195
|
const overrides = new Map();
|
|
167
196
|
const diagnostics = [];
|
|
197
|
+
// Location identity per resolved module, taken from the ref that reached it.
|
|
198
|
+
// Two refs at different versions resolve to different canonical sources, so a
|
|
199
|
+
// source normally maps to exactly one identity; the entry module has no
|
|
200
|
+
// inbound edge and needs none (it is never reconciled against itself).
|
|
201
|
+
//
|
|
202
|
+
// One source CAN be reached by two spellings of the same location (a bare
|
|
203
|
+
// registry ref and the direct URL it resolves to). Both name the same module
|
|
204
|
+
// at the same version, so either identity groups it correctly — but the choice
|
|
205
|
+
// must not depend on edge iteration order, or the same graph could reconcile
|
|
206
|
+
// differently across runs. First edge wins.
|
|
207
|
+
const identityBySource = new Map();
|
|
208
|
+
for (const aliasMap of importEdges.values()) {
|
|
209
|
+
for (const edge of aliasMap.values()) {
|
|
210
|
+
if (identityBySource.has(edge.targetSource))
|
|
211
|
+
continue;
|
|
212
|
+
const identity = refIdentity(edge.targetRef);
|
|
213
|
+
if (identity)
|
|
214
|
+
identityBySource.set(edge.targetSource, identity);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
168
217
|
const groups = new Map();
|
|
169
218
|
const infoBySource = new Map();
|
|
170
|
-
for (const mod of modules
|
|
171
|
-
const
|
|
219
|
+
for (const [source, mod] of modules) {
|
|
220
|
+
const identity = identityBySource.get(source);
|
|
221
|
+
if (!identity)
|
|
222
|
+
continue;
|
|
223
|
+
const info = moduleIdentityOf(mod, identity);
|
|
172
224
|
if (!info)
|
|
173
225
|
continue;
|
|
174
226
|
infoBySource.set(info.source, info);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** An entry for a field that carries one or more x-telo-ref constraints. */
|
|
2
2
|
export interface RefFieldEntry {
|
|
3
|
-
/** One or more canonical
|
|
3
|
+
/** One or more canonical kind keys ("<module>.<Kind>"), or the deprecated
|
|
4
|
+
* identity form ("<namespace>/<module>#<Kind>") for a legacy published module.
|
|
4
5
|
* Multiple entries arise from anyOf branches. */
|
|
5
6
|
refs: string[];
|
|
6
7
|
/** True when the field path traversed through at least one array (path contains "[]"). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reference-field-map.d.ts","sourceRoot":"","sources":["../src/reference-field-map.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B;
|
|
1
|
+
{"version":3,"file":"reference-field-map.d.ts","sourceRoot":"","sources":["../src/reference-field-map.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,MAAM,WAAW,aAAa;IAC5B;;sDAEkD;IAClD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,0FAA0F;IAC1F,OAAO,EAAE,OAAO,CAAC;IACjB;8DAC0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B;;;;;;;;;;kCAU8B;IAC9B,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,4EAA4E;AAC5E,MAAM,WAAW,eAAe;IAC9B;2CACuC;IACvC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC1B;AAED;8CAC8C;AAC9C,MAAM,WAAW,oBAAoB;IACnC;;qFAEiF;IACjF,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,aAAa,GAAG,aAAa,GAAG,eAAe,GAAG,oBAAoB,CAAC;AAEnF;0FAC0F;AAC1F,MAAM,MAAM,iBAAiB,GAAG,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,aAAa,CAEvE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,eAAe,CAE3E;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,aAAa,GAAG,KAAK,IAAI,oBAAoB,CAErF;AAED,oGAAoG;AACpG,eAAO,MAAM,cAAc,aAAwC,CAAC;AAEpE;;;;;;;;;2EAS2E;AAC3E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAItE;AAED;;;;sEAIsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;wDAUwD;AACxD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,kBAAkB,EAAE,CAoCpF;AAED;+DAC+D;AAC/D,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,EAAE,CAExE;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,iBAAiB,CAQrF;AASD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,EAAE,CAa/D;AAED;;;8CAG8C;AAC9C,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,UAAU,EAAE,MAAM,GACjB,iBAAiB,CAInB"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
import type { AliasResolver } from "./alias-resolver.js";
|
|
3
|
+
/** Why an `x-telo-ref` constraint did not canonicalize.
|
|
4
|
+
*
|
|
5
|
+
* - `legacy` — the deprecated `<namespace>/<module>#<Kind>` identity form,
|
|
6
|
+
* still resolved through the identity table.
|
|
7
|
+
* - `unknown` — the prefix is not an alias in the declaring module's scope: a
|
|
8
|
+
* typo, a missing `imports:` entry — or a value that was already
|
|
9
|
+
* canonical, which the caller separates by asking the registry.
|
|
10
|
+
* - `gated` — the alias is known and the target owns the kind, but its
|
|
11
|
+
* `exports.kinds` does not list it. */
|
|
12
|
+
export type RefConstraintReason = "legacy" | "unknown" | "gated";
|
|
13
|
+
/** An `x-telo-ref` constraint that did not canonicalize. */
|
|
14
|
+
export interface RefConstraintIssue {
|
|
15
|
+
/** The constraint string exactly as authored. */
|
|
16
|
+
ref: string;
|
|
17
|
+
/** Dotted path to the annotated schema node within the doc, e.g.
|
|
18
|
+
* `schema.properties.store`. Points the author at the slot, not just the doc. */
|
|
19
|
+
path: string;
|
|
20
|
+
/** The `Telo.Definition` / `Telo.Abstract` doc that declares the slot. */
|
|
21
|
+
manifest: ResourceManifest;
|
|
22
|
+
reason: RefConstraintReason;
|
|
23
|
+
/** For `gated`: the target module and the kinds it does export. */
|
|
24
|
+
gate?: {
|
|
25
|
+
module: string;
|
|
26
|
+
exported: string[];
|
|
27
|
+
};
|
|
28
|
+
/** Aliases the declaring scope does know — the "did you mean" material for
|
|
29
|
+
* an `unknown` prefix. */
|
|
30
|
+
knownAliases?: string[];
|
|
31
|
+
}
|
|
32
|
+
/** True for the legacy identity form, which is split on `#` by the identity table. */
|
|
33
|
+
export declare function isLegacyRefIdentity(ref: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Rewrites the `x-telo-ref` constraints on one definition doc from the alias
|
|
36
|
+
* form (`KvStore.Store`, `Self.Store`, `Telo.Invocable`) to the canonical
|
|
37
|
+
* `<module>.<Kind>` key the definition registry is keyed by.
|
|
38
|
+
*
|
|
39
|
+
* `resolver` must be the scope of the module that DECLARES the definition, not
|
|
40
|
+
* the consumer's: an imported library names its dependencies by its own aliases.
|
|
41
|
+
* This mirrors how `extends:` and `capability:` are pre-resolved before
|
|
42
|
+
* registration, so the registry never needs module context to answer a ref query.
|
|
43
|
+
*
|
|
44
|
+
* Every constraint that does not canonicalize is returned, tagged with why. That
|
|
45
|
+
* matters more than it looks: an unresolved constraint leaves a string naming no
|
|
46
|
+
* registered kind, and the reference check treats an unknown target as partial
|
|
47
|
+
* context and skips it — so an unreported one would silently let the slot accept
|
|
48
|
+
* anything. The authored value is left in place either way, so a diagnostic
|
|
49
|
+
* quotes what the author actually wrote.
|
|
50
|
+
*
|
|
51
|
+
* An `unknown` result also covers an already-canonical value (`kv-store.Store`
|
|
52
|
+
* names a module, not an alias), which is what keeps the rewrite idempotent —
|
|
53
|
+
* the caller drops those by checking the definition registry once every kind is
|
|
54
|
+
* registered.
|
|
55
|
+
*
|
|
56
|
+
* The walk covers the whole doc rather than a fixed field list: a constraint can
|
|
57
|
+
* sit in `schema`, `inputType`, `outputType`, or a `$defs` entry nested in any of
|
|
58
|
+
* them, and rewriting one that appears in a template body's inline schema is
|
|
59
|
+
* correct too.
|
|
60
|
+
*/
|
|
61
|
+
export declare function resolveSchemaRefKinds(definition: ResourceManifest, resolver: Pick<AliasResolver, "resolveKindResult" | "knownAliases">): RefConstraintIssue[];
|
|
62
|
+
//# sourceMappingURL=resolve-schema-ref-kinds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve-schema-ref-kinds.d.ts","sourceRoot":"","sources":["../src/resolve-schema-ref-kinds.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD;;;;;;;;sDAQsD;AACtD,MAAM,MAAM,mBAAmB,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAEjE,4DAA4D;AAC5D,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAC;IACZ;sFACkF;IAClF,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,mEAAmE;IACnE,IAAI,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC9C;+BAC2B;IAC3B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,sFAAsF;AACtF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,gBAAgB,EAC5B,QAAQ,EAAE,IAAI,CAAC,aAAa,EAAE,mBAAmB,GAAG,cAAc,CAAC,GAClE,kBAAkB,EAAE,CAiDtB"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const REF_ANNOTATION = "x-telo-ref";
|
|
2
|
+
/** True for the legacy identity form, which is split on `#` by the identity table. */
|
|
3
|
+
export function isLegacyRefIdentity(ref) {
|
|
4
|
+
return ref.includes("#");
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Rewrites the `x-telo-ref` constraints on one definition doc from the alias
|
|
8
|
+
* form (`KvStore.Store`, `Self.Store`, `Telo.Invocable`) to the canonical
|
|
9
|
+
* `<module>.<Kind>` key the definition registry is keyed by.
|
|
10
|
+
*
|
|
11
|
+
* `resolver` must be the scope of the module that DECLARES the definition, not
|
|
12
|
+
* the consumer's: an imported library names its dependencies by its own aliases.
|
|
13
|
+
* This mirrors how `extends:` and `capability:` are pre-resolved before
|
|
14
|
+
* registration, so the registry never needs module context to answer a ref query.
|
|
15
|
+
*
|
|
16
|
+
* Every constraint that does not canonicalize is returned, tagged with why. That
|
|
17
|
+
* matters more than it looks: an unresolved constraint leaves a string naming no
|
|
18
|
+
* registered kind, and the reference check treats an unknown target as partial
|
|
19
|
+
* context and skips it — so an unreported one would silently let the slot accept
|
|
20
|
+
* anything. The authored value is left in place either way, so a diagnostic
|
|
21
|
+
* quotes what the author actually wrote.
|
|
22
|
+
*
|
|
23
|
+
* An `unknown` result also covers an already-canonical value (`kv-store.Store`
|
|
24
|
+
* names a module, not an alias), which is what keeps the rewrite idempotent —
|
|
25
|
+
* the caller drops those by checking the definition registry once every kind is
|
|
26
|
+
* registered.
|
|
27
|
+
*
|
|
28
|
+
* The walk covers the whole doc rather than a fixed field list: a constraint can
|
|
29
|
+
* sit in `schema`, `inputType`, `outputType`, or a `$defs` entry nested in any of
|
|
30
|
+
* them, and rewriting one that appears in a template body's inline schema is
|
|
31
|
+
* correct too.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveSchemaRefKinds(definition, resolver) {
|
|
34
|
+
const issues = [];
|
|
35
|
+
const record = (ref, path) => {
|
|
36
|
+
if (isLegacyRefIdentity(ref)) {
|
|
37
|
+
issues.push({ ref, path, manifest: definition, reason: "legacy" });
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const result = resolver.resolveKindResult(ref);
|
|
41
|
+
if (result.status === "ok")
|
|
42
|
+
return;
|
|
43
|
+
issues.push(result.status === "gated"
|
|
44
|
+
? {
|
|
45
|
+
ref,
|
|
46
|
+
path,
|
|
47
|
+
manifest: definition,
|
|
48
|
+
reason: "gated",
|
|
49
|
+
gate: { module: result.module, exported: result.exported },
|
|
50
|
+
}
|
|
51
|
+
: {
|
|
52
|
+
ref,
|
|
53
|
+
path,
|
|
54
|
+
manifest: definition,
|
|
55
|
+
reason: "unknown",
|
|
56
|
+
knownAliases: resolver.knownAliases(),
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
const walk = (value, path) => {
|
|
60
|
+
if (value === null || typeof value !== "object")
|
|
61
|
+
return;
|
|
62
|
+
if (Array.isArray(value)) {
|
|
63
|
+
value.forEach((item, i) => walk(item, `${path}[${i}]`));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const obj = value;
|
|
67
|
+
const ref = obj[REF_ANNOTATION];
|
|
68
|
+
if (typeof ref === "string" && ref) {
|
|
69
|
+
const result = isLegacyRefIdentity(ref) ? null : resolver.resolveKindResult(ref);
|
|
70
|
+
if (result?.status === "ok")
|
|
71
|
+
obj[REF_ANNOTATION] = result.kind;
|
|
72
|
+
else
|
|
73
|
+
record(ref, path);
|
|
74
|
+
}
|
|
75
|
+
for (const key of Object.keys(obj)) {
|
|
76
|
+
walk(obj[key], path ? `${path}.${key}` : key);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
walk(definition, "");
|
|
80
|
+
return issues;
|
|
81
|
+
}
|
|
@@ -7,16 +7,26 @@ export declare const MANIFEST_CACHE_BASE_URL = "https://manifests.telo.sh";
|
|
|
7
7
|
* stores per version — `{ transport, host, path, version }` — so the tracker's
|
|
8
8
|
* write key and the editor's read key come from the same function and never
|
|
9
9
|
* drift. `path` is the slash-separated repo/module path (multi-segment OCI
|
|
10
|
-
* repos nest as prefixes). */
|
|
10
|
+
* repos nest as prefixes), empty for a module addressed at a host's root. */
|
|
11
11
|
export interface ManifestCacheCoords {
|
|
12
12
|
transport: string;
|
|
13
13
|
host: string;
|
|
14
14
|
path: string;
|
|
15
|
-
version
|
|
15
|
+
/** Omit only when the ref carries no version to key by — a direct `https://`
|
|
16
|
+
* URL addresses exactly one file, so its path alone is already unambiguous.
|
|
17
|
+
* Every ref whose grammar has a version segment must supply it, or two
|
|
18
|
+
* versions of one module would collide on a single key. */
|
|
19
|
+
version?: string;
|
|
20
|
+
/** File within the version directory. Defaults to the module manifest, which
|
|
21
|
+
* is the only file the hub's bucket stores; the local install cache also
|
|
22
|
+
* holds each `include:` partial, which is named here. */
|
|
23
|
+
file?: string;
|
|
16
24
|
}
|
|
17
25
|
/** Deterministic cache key for one module version:
|
|
18
|
-
* `<transport>/<host>/<path…>/<version
|
|
19
|
-
*
|
|
26
|
+
* `<transport>/<host>/<path…>/<version>/<file>`, where `version` is omitted
|
|
27
|
+
* when the coordinates carry none and `file` defaults to the module manifest.
|
|
28
|
+
* Returns `null` when any coordinate is empty or would traverse out of the key
|
|
29
|
+
* space. */
|
|
20
30
|
export declare function manifestCacheKey(coords: ManifestCacheCoords): string | null;
|
|
21
31
|
/** Cache coordinates for an `oci://host/repo@tag` ref. Returns `null` when the
|
|
22
32
|
* ref carries no explicit tag (a defaulted `latest` or a `sha256:` digest is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-cache.d.ts","sourceRoot":"","sources":["../../src/sources/manifest-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD;;iFAEiF;AACjF,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE
|
|
1
|
+
{"version":3,"file":"manifest-cache.d.ts","sourceRoot":"","sources":["../../src/sources/manifest-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKlD;;iFAEiF;AACjF,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE;;;;8EAI8E;AAC9E,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;gEAG4D;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;8DAE0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAOD;;;;aAIa;AACb,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,GAAG,IAAI,CAe3E;AAED;;;6EAG6E;AAC7E,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAY9E;AAID;;2DAE2D;AAC3D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;;;;;;oEASoE;AACpE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAuB/F;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAC9B,WAAW,EAAE,MAAM,GAAG,mBAAmB,EACzC,OAAO,GAAE,MAAgC,GACxC,MAAM,GAAG,IAAI,CAKf;AAED;;;;;+DAK+D;AAC/D,qBAAa,mBAAoB,YAAW,cAAc;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,MAAgC;IAEtE,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAQxB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAalE;;;iEAG6D;IAC7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;CAOxD"}
|
|
@@ -10,15 +10,24 @@ function invalidSegment(segment) {
|
|
|
10
10
|
return segment === "" || segment === "." || segment === ".." || segment.includes("\\");
|
|
11
11
|
}
|
|
12
12
|
/** Deterministic cache key for one module version:
|
|
13
|
-
* `<transport>/<host>/<path…>/<version
|
|
14
|
-
*
|
|
13
|
+
* `<transport>/<host>/<path…>/<version>/<file>`, where `version` is omitted
|
|
14
|
+
* when the coordinates carry none and `file` defaults to the module manifest.
|
|
15
|
+
* Returns `null` when any coordinate is empty or would traverse out of the key
|
|
16
|
+
* space. */
|
|
15
17
|
export function manifestCacheKey(coords) {
|
|
16
|
-
const { transport, host, path, version } = coords;
|
|
17
|
-
const segments = [transport, host, ...path.split("/")
|
|
18
|
-
if (
|
|
18
|
+
const { transport, host, path, version, file } = coords;
|
|
19
|
+
const segments = [transport, host, ...(path ? path.split("/") : [])];
|
|
20
|
+
if (version !== undefined)
|
|
21
|
+
segments.push(version);
|
|
22
|
+
segments.push(file ?? DEFAULT_MANIFEST_FILENAME);
|
|
23
|
+
if (segments.some(invalidSegment) ||
|
|
24
|
+
transport.includes("/") ||
|
|
25
|
+
host.includes("/") ||
|
|
26
|
+
(version !== undefined && version.includes("/")) ||
|
|
27
|
+
(file !== undefined && file.includes("/"))) {
|
|
19
28
|
return null;
|
|
20
29
|
}
|
|
21
|
-
return
|
|
30
|
+
return segments.join("/");
|
|
22
31
|
}
|
|
23
32
|
/** Cache coordinates for an `oci://host/repo@tag` ref. Returns `null` when the
|
|
24
33
|
* ref carries no explicit tag (a defaulted `latest` or a `sha256:` digest is
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/analyzer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.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.56.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@telorun/sdk": "*"
|
package/src/analysis-registry.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface RefFieldInfo {
|
|
|
17
17
|
path: string;
|
|
18
18
|
/** True when the path traverses at least one array. */
|
|
19
19
|
isArray: boolean;
|
|
20
|
-
/** Accepted `x-telo-ref` constraint strings (e.g. `
|
|
20
|
+
/** Accepted `x-telo-ref` constraint strings, canonicalized (e.g. `Telo.Runnable`). */
|
|
21
21
|
refs: string[];
|
|
22
22
|
/** Distinct capabilities the slot may target (`Telo.Runnable`,
|
|
23
23
|
* `Telo.Service`, `Telo.Provider`, …) — one per resolvable constraint. The
|
|
@@ -119,9 +119,9 @@ export class AnalysisRegistry {
|
|
|
119
119
|
|
|
120
120
|
/** Base capability an `x-telo-ref` constraint targets. A definition's declared
|
|
121
121
|
* `capability` is always one of the base capabilities, so it wins — this
|
|
122
|
-
* resolves user-defined abstracts (e.g. `
|
|
122
|
+
* resolves user-defined abstracts (e.g. `ai.Model`, declared
|
|
123
123
|
* `capability: Telo.Invocable`) to the capability instances satisfy, not the
|
|
124
|
-
* abstract kind. Builtin abstracts (`
|
|
124
|
+
* abstract kind. Builtin abstracts (`Telo.Runnable`) carry no `capability`
|
|
125
125
|
* field — there the kind itself *is* the capability. Undefined when
|
|
126
126
|
* unresolvable. */
|
|
127
127
|
capabilityForRef(xTeloRef: string): string | undefined {
|
|
@@ -306,7 +306,7 @@ export class AnalysisRegistry {
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
/** Returns every user-facing (alias-form) kind that satisfies the given
|
|
309
|
-
* `x-telo-ref` constraint string (e.g. `"
|
|
309
|
+
* `x-telo-ref` constraint string, canonicalized (e.g. `"Telo.Invocable"`, `"sql.Connection"`).
|
|
310
310
|
* Resolution mirrors `validateReferences.checkKind`: abstract targets expand to
|
|
311
311
|
* the set of definitions extending them; concrete targets yield just themselves.
|
|
312
312
|
* Returns `undefined` when the ref can't be resolved (e.g. unregistered identity),
|
package/src/analyzer.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { isModuleKind } from "./module-kinds.js";
|
|
|
20
20
|
import { normalizeInlineResources } from "./normalize-inline-resources.js";
|
|
21
21
|
import { REF_VALIDATION_SKIP_KINDS } from "./system-kinds.js";
|
|
22
22
|
import { resolveRefSentinels } from "./resolve-ref-sentinels.js";
|
|
23
|
+
import { resolveSchemaRefKinds, type RefConstraintIssue } from "./resolve-schema-ref-kinds.js";
|
|
23
24
|
import { resolveSchemaTypeRefs } from "./resolve-schema-type-refs.js";
|
|
24
25
|
import { validateSchemaTypeRefs } from "./validate-schema-type-refs.js";
|
|
25
26
|
import { rewriteSyntheticOrigins } from "./rewrite-synthetic-origins.js";
|
|
@@ -1035,6 +1036,7 @@ export class StaticAnalyzer {
|
|
|
1035
1036
|
// declaring scope's resolver, so `extendedBy` is keyed by canonical kind regardless
|
|
1036
1037
|
// of alias choices. `capability` covers the legacy implements-this-abstract overload;
|
|
1037
1038
|
// `extends` is the canonical first-class form.
|
|
1039
|
+
const refConstraintIssues: RefConstraintIssue[] = [];
|
|
1038
1040
|
for (const m of manifests) {
|
|
1039
1041
|
if (m.kind !== "Telo.Definition" && m.kind !== "Telo.Abstract") continue;
|
|
1040
1042
|
const def = m as unknown as ResourceDefinition;
|
|
@@ -1043,6 +1045,15 @@ export class StaticAnalyzer {
|
|
|
1043
1045
|
ownModule && !rootModules.has(ownModule)
|
|
1044
1046
|
? (aliasesByModule.get(ownModule) ?? new AliasResolver())
|
|
1045
1047
|
: aliases;
|
|
1048
|
+
// Canonicalize alias-form `x-telo-ref` constraints in the DECLARING module's
|
|
1049
|
+
// scope, before the schema reaches `register()` and the lazily-built field
|
|
1050
|
+
// maps. Same pre-resolution `capability` / `extends` get below.
|
|
1051
|
+
const issues = resolveSchemaRefKinds(m, scopeResolver);
|
|
1052
|
+
// Report only for definitions the author can edit. A published dependency
|
|
1053
|
+
// still on the deprecated form — or with a constraint that no longer
|
|
1054
|
+
// resolves — is not the consumer's to fix, and every import would
|
|
1055
|
+
// otherwise flood `telo check` with unactionable noise.
|
|
1056
|
+
if (!ownModule || rootModules.has(ownModule)) refConstraintIssues.push(...issues);
|
|
1046
1057
|
const resolvedCapability = def.capability
|
|
1047
1058
|
? (scopeResolver.resolveKind(def.capability) ?? def.capability)
|
|
1048
1059
|
: def.capability;
|
|
@@ -1063,6 +1074,57 @@ export class StaticAnalyzer {
|
|
|
1063
1074
|
// distinguishable from the resolver's own substitution (after Phase 2/2.5
|
|
1064
1075
|
// they are the same object).
|
|
1065
1076
|
if (!options?.skipValidation) {
|
|
1077
|
+
for (const issue of refConstraintIssues) {
|
|
1078
|
+
// An `unknown` prefix is also what an ALREADY-CANONICAL value looks like
|
|
1079
|
+
// (`kv-store.Store` names a module, not an alias). Now that every kind is
|
|
1080
|
+
// registered, the registry separates the two — anything it resolves was
|
|
1081
|
+
// canonical, anything it doesn't names nothing at all.
|
|
1082
|
+
if (issue.reason === "unknown" && defs.resolve(issue.ref)) continue;
|
|
1083
|
+
const resource = {
|
|
1084
|
+
kind: issue.manifest.kind,
|
|
1085
|
+
name: issue.manifest.metadata?.name as string,
|
|
1086
|
+
};
|
|
1087
|
+
const filePath = (issue.manifest.metadata as { source?: string } | undefined)?.source;
|
|
1088
|
+
const data = { resource, filePath, path: issue.path };
|
|
1089
|
+
if (issue.reason === "legacy") {
|
|
1090
|
+
diagnostics.push({
|
|
1091
|
+
severity: DiagnosticSeverity.Warning,
|
|
1092
|
+
code: "X_TELO_REF_LEGACY_IDENTITY",
|
|
1093
|
+
source: SOURCE,
|
|
1094
|
+
message:
|
|
1095
|
+
`x-telo-ref '${issue.ref}' at '${issue.path}' uses the deprecated ` +
|
|
1096
|
+
`'<namespace>/<module>#<Kind>' form. Write the target as an alias-qualified kind ` +
|
|
1097
|
+
`instead — '<Alias>.<Kind>' for a module declared in this file's 'imports:' map, ` +
|
|
1098
|
+
`'Self.<Kind>' for a kind in this library, or 'Telo.<Kind>' for a built-in capability.`,
|
|
1099
|
+
data,
|
|
1100
|
+
});
|
|
1101
|
+
} else if (issue.reason === "gated") {
|
|
1102
|
+
diagnostics.push({
|
|
1103
|
+
severity: DiagnosticSeverity.Error,
|
|
1104
|
+
code: "KIND_NOT_EXPORTED",
|
|
1105
|
+
source: SOURCE,
|
|
1106
|
+
message:
|
|
1107
|
+
`x-telo-ref '${issue.ref}' at '${issue.path}' targets a kind module ` +
|
|
1108
|
+
`'${issue.gate?.module}' does not export. Add ` +
|
|
1109
|
+
`'${issue.ref.slice(issue.ref.indexOf(".") + 1)}' to that module's exports.kinds. ` +
|
|
1110
|
+
`Exported kinds: ${issue.gate?.exported.join(", ") || "(none)"}.`,
|
|
1111
|
+
data,
|
|
1112
|
+
});
|
|
1113
|
+
} else {
|
|
1114
|
+
diagnostics.push({
|
|
1115
|
+
severity: DiagnosticSeverity.Error,
|
|
1116
|
+
code: "X_TELO_REF_UNRESOLVED",
|
|
1117
|
+
source: SOURCE,
|
|
1118
|
+
message:
|
|
1119
|
+
`x-telo-ref '${issue.ref}' at '${issue.path}' names no kind. The prefix must be an ` +
|
|
1120
|
+
`import alias declared in this file's 'imports:' map, 'Self' for a kind in this ` +
|
|
1121
|
+
`library, or 'Telo' for a built-in capability. An unresolvable constraint would ` +
|
|
1122
|
+
`leave the slot accepting any resource. Known aliases: ` +
|
|
1123
|
+
`${issue.knownAliases?.join(", ") || "(none)"}.`,
|
|
1124
|
+
data,
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1066
1128
|
diagnostics.push(...validateReferenceForms(manifests, defs, aliases, aliasesByModule));
|
|
1067
1129
|
}
|
|
1068
1130
|
|
|
@@ -1089,10 +1151,31 @@ export class StaticAnalyzer {
|
|
|
1089
1151
|
rootModules.has(ownModule) ? aliases : (aliasesByModule.get(ownModule) ?? new AliasResolver());
|
|
1090
1152
|
const canonicalKind = scopeResolver.resolveKind(m.kind as string) ?? (m.kind as string);
|
|
1091
1153
|
if (defs.resolve(canonicalKind)?.capability !== "Telo.Type") continue;
|
|
1092
|
-
|
|
1093
|
-
|
|
1154
|
+
const typeName = m.metadata.name as string;
|
|
1155
|
+
const registered = defs.registerNamedTypeSchema(
|
|
1156
|
+
canonicalTypeSchemaId(ownModule, typeName),
|
|
1094
1157
|
m.schema as Record<string, any>,
|
|
1095
1158
|
);
|
|
1159
|
+
// Kinds and named types share one `telo://<module>/<Name>` id space. A
|
|
1160
|
+
// collision would leave every `$ref` to that id resolving to the kind's
|
|
1161
|
+
// schema — validating the wrong shape, silently — so it is an error, not
|
|
1162
|
+
// a last-writer-wins.
|
|
1163
|
+
if (!registered && !options?.skipValidation) {
|
|
1164
|
+
diagnostics.push({
|
|
1165
|
+
severity: DiagnosticSeverity.Error,
|
|
1166
|
+
code: "DUPLICATE_SCHEMA_ID",
|
|
1167
|
+
source: SOURCE,
|
|
1168
|
+
message:
|
|
1169
|
+
`Type '${typeName}' collides with the kind '${ownModule}.${typeName}': both claim the ` +
|
|
1170
|
+
`schema id '${canonicalTypeSchemaId(ownModule, typeName)}'. A '$ref' to it would ` +
|
|
1171
|
+
`resolve to the kind's schema. Rename one of them.`,
|
|
1172
|
+
data: {
|
|
1173
|
+
resource: { kind: m.kind, name: typeName },
|
|
1174
|
+
filePath: (m.metadata as { source?: string } | undefined)?.source,
|
|
1175
|
+
path: "metadata.name",
|
|
1176
|
+
},
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1096
1179
|
}
|
|
1097
1180
|
if (!options?.skipValidation) {
|
|
1098
1181
|
diagnostics.push(
|
package/src/builtins.ts
CHANGED
|
@@ -97,7 +97,7 @@ const ROOT_LOGGING_SCHEMA = {
|
|
|
97
97
|
type: "array",
|
|
98
98
|
items: {
|
|
99
99
|
type: "object",
|
|
100
|
-
"x-telo-ref": "
|
|
100
|
+
"x-telo-ref": "Telo.LogSink",
|
|
101
101
|
"x-telo-inline": true,
|
|
102
102
|
},
|
|
103
103
|
},
|
|
@@ -444,8 +444,8 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
|
|
|
444
444
|
type: "array",
|
|
445
445
|
items: {
|
|
446
446
|
anyOf: [
|
|
447
|
-
{ type: "string", "x-telo-ref": "
|
|
448
|
-
{ type: "string", "x-telo-ref": "
|
|
447
|
+
{ type: "string", "x-telo-ref": "Telo.Runnable" },
|
|
448
|
+
{ type: "string", "x-telo-ref": "Telo.Service" },
|
|
449
449
|
// Post-resolution shape that `resolveRefSentinels`
|
|
450
450
|
// substitutes a `!ref <name>` sentinel into. The
|
|
451
451
|
// adjacent `x-telo-ref` constraints govern the kind
|
|
@@ -469,8 +469,8 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
|
|
|
469
469
|
properties: {
|
|
470
470
|
ref: {
|
|
471
471
|
anyOf: [
|
|
472
|
-
{ type: "string", "x-telo-ref": "
|
|
473
|
-
{ type: "string", "x-telo-ref": "
|
|
472
|
+
{ type: "string", "x-telo-ref": "Telo.Runnable" },
|
|
473
|
+
{ type: "string", "x-telo-ref": "Telo.Service" },
|
|
474
474
|
{
|
|
475
475
|
type: "object",
|
|
476
476
|
required: ["kind", "name"],
|
|
@@ -510,8 +510,8 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
|
|
|
510
510
|
},
|
|
511
511
|
additionalProperties: true,
|
|
512
512
|
anyOf: [
|
|
513
|
-
{ "x-telo-ref": "
|
|
514
|
-
{ "x-telo-ref": "
|
|
513
|
+
{ "x-telo-ref": "Telo.Invocable" },
|
|
514
|
+
{ "x-telo-ref": "Telo.Runnable" },
|
|
515
515
|
],
|
|
516
516
|
},
|
|
517
517
|
inputs: { type: "object", additionalProperties: true },
|