@telorun/analyzer 0.26.0 → 0.28.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 -140
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +52 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/loaded-types.d.ts +15 -2
- package/dist/loaded-types.d.ts.map +1 -1
- package/dist/manifest-loader.d.ts +6 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +21 -17
- package/dist/reconcile-module-versions.d.ts +25 -0
- package/dist/reconcile-module-versions.d.ts.map +1 -0
- package/dist/reconcile-module-versions.js +215 -0
- package/dist/sources/default-sources.d.ts +8 -0
- package/dist/sources/default-sources.d.ts.map +1 -0
- package/dist/sources/default-sources.js +10 -0
- package/dist/types.d.ts +0 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-provider-coherence.d.ts +8 -0
- package/dist/validate-provider-coherence.d.ts.map +1 -1
- package/dist/validate-provider-coherence.js +81 -0
- package/package.json +2 -2
- package/src/builtins.ts +52 -1
- package/src/index.ts +3 -0
- package/src/loaded-types.ts +15 -2
- package/src/manifest-loader.ts +22 -19
- package/src/reconcile-module-versions.ts +253 -0
- package/src/sources/default-sources.ts +12 -0
- package/src/types.ts +0 -8
- package/src/validate-provider-coherence.ts +85 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { HttpSource } from "./http-source.js";
|
|
2
|
+
import { RegistrySource } from "./registry-source.js";
|
|
3
|
+
/** The browser-safe built-in sources, in resolution order: HTTP fetch then
|
|
4
|
+
* registry. Node-specific sources (local filesystem) are supplied by the
|
|
5
|
+
* consuming package and passed alongside these into the `Loader` constructor.
|
|
6
|
+
* Callers that only want a subset (e.g. the editor, which brings its own
|
|
7
|
+
* registry adapters) construct the individual sources directly. */
|
|
8
|
+
export function defaultSources(registryUrl) {
|
|
9
|
+
return [new HttpSource(), new RegistrySource(registryUrl)];
|
|
10
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -68,14 +68,6 @@ export interface LoadOptions {
|
|
|
68
68
|
desugarImports?: boolean;
|
|
69
69
|
}
|
|
70
70
|
export interface LoaderInitOptions {
|
|
71
|
-
/** Sources inserted with highest priority before built-ins. */
|
|
72
|
-
extraSources?: ManifestSource[];
|
|
73
|
-
/** Include built-in HttpSource. Defaults to true. */
|
|
74
|
-
includeHttpSource?: boolean;
|
|
75
|
-
/** Include built-in RegistrySource. Defaults to true. */
|
|
76
|
-
includeRegistrySource?: boolean;
|
|
77
|
-
/** Base URL used by built-in RegistrySource when enabled. */
|
|
78
|
-
registryUrl?: string;
|
|
79
71
|
/** Handlers for CEL stdlib functions (e.g. `sha256`). Analyzer-only callers may
|
|
80
72
|
* omit this and get throwing stubs; runtime callers (kernel) must supply real impls. */
|
|
81
73
|
celHandlers?: import("./cel-environment.js").CelHandlers;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;qHACqH;AACrH,eAAO,MAAM,kBAAkB;;;;;CAKrB,CAAC;AACX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE9F,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,cAAc,CAAC;AAErD,MAAM,WAAW,QAAQ;IACvB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,QAAQ,CAAC;CACf;AAED;;oDAEoD;AACpD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE/C;6EAC6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAExD;;qEAEiE;IACjE,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEjE;;qEAEiE;IACjE,cAAc,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IAC1B;;;+EAG2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;mEAO+D;IAC/D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;qHACqH;AACrH,eAAO,MAAM,kBAAkB;;;;;CAKrB,CAAC;AACX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAE9F,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,cAAc,CAAC;AAErD,MAAM,WAAW,QAAQ;IACvB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,+BAA+B;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,QAAQ,CAAC;IAChB,GAAG,EAAE,QAAQ,CAAC;CACf;AAED;;oDAEoD;AACpD,MAAM,MAAM,aAAa,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAE/C;6EAC6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,2BAA2B;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7D,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAExD;;qEAEiE;IACjE,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAEjE;;qEAEiE;IACjE,cAAc,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC1D;AAED,MAAM,WAAW,WAAW;IAC1B;;;+EAG2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;mEAO+D;IAC/D,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC;6FACyF;IACzF,WAAW,CAAC,EAAE,OAAO,sBAAsB,EAAE,WAAW,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;;;;;;sDAUkD;IAClD,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;gEAKgE;AAChE,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,OAAO,qBAAqB,EAAE,aAAa,CAAC;IACtD,WAAW,CAAC,EAAE,OAAO,0BAA0B,EAAE,kBAAkB,CAAC;IACpE;;;;+EAI2E;IAC3E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,qBAAqB,EAAE,aAAa,CAAC,CAAC;CAC5E"}
|
|
@@ -18,6 +18,14 @@ import { type AnalysisDiagnostic } from "./types.js";
|
|
|
18
18
|
* kind is registered but not a `Telo.Invocable`.
|
|
19
19
|
* - PROVIDER_MISSING_IMPLEMENTATION: definition with `capability: Telo.Provider`
|
|
20
20
|
* declares neither `controllers:` (TS-backed) nor `provide:` (template-backed).
|
|
21
|
+
* - MOUNT_ON_NON_MOUNT: `mount:` declared on a definition whose `capability` is
|
|
22
|
+
* not `Telo.Mount`.
|
|
23
|
+
* - MOUNT_DISPATCHER_CONFLICT: `mount:` co-exists with another dispatch
|
|
24
|
+
* entry-point (`invoke:` / `run:` / `provide:`).
|
|
25
|
+
* - MOUNT_TARGET_UNKNOWN: `mount.name` does not resolve to an entry in
|
|
26
|
+
* `resources:`.
|
|
27
|
+
* - MOUNT_TARGET_NOT_MOUNTABLE: `mount.name` resolves to a resource whose kind
|
|
28
|
+
* is registered but not a `Telo.Mount`.
|
|
21
29
|
*/
|
|
22
30
|
export declare function validateProviderCoherence(manifests: ResourceManifest[], registry: DefinitionRegistry, aliases: AliasResolver): AnalysisDiagnostic[];
|
|
23
31
|
//# sourceMappingURL=validate-provider-coherence.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-provider-coherence.d.ts","sourceRoot":"","sources":["../src/validate-provider-coherence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE
|
|
1
|
+
{"version":3,"file":"validate-provider-coherence.d.ts","sourceRoot":"","sources":["../src/validate-provider-coherence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,aAAa,GACrB,kBAAkB,EAAE,CAsNtB"}
|
|
@@ -16,6 +16,14 @@ const SOURCE = "telo-analyzer";
|
|
|
16
16
|
* kind is registered but not a `Telo.Invocable`.
|
|
17
17
|
* - PROVIDER_MISSING_IMPLEMENTATION: definition with `capability: Telo.Provider`
|
|
18
18
|
* declares neither `controllers:` (TS-backed) nor `provide:` (template-backed).
|
|
19
|
+
* - MOUNT_ON_NON_MOUNT: `mount:` declared on a definition whose `capability` is
|
|
20
|
+
* not `Telo.Mount`.
|
|
21
|
+
* - MOUNT_DISPATCHER_CONFLICT: `mount:` co-exists with another dispatch
|
|
22
|
+
* entry-point (`invoke:` / `run:` / `provide:`).
|
|
23
|
+
* - MOUNT_TARGET_UNKNOWN: `mount.name` does not resolve to an entry in
|
|
24
|
+
* `resources:`.
|
|
25
|
+
* - MOUNT_TARGET_NOT_MOUNTABLE: `mount.name` resolves to a resource whose kind
|
|
26
|
+
* is registered but not a `Telo.Mount`.
|
|
19
27
|
*/
|
|
20
28
|
export function validateProviderCoherence(manifests, registry, aliases) {
|
|
21
29
|
const diagnostics = [];
|
|
@@ -45,11 +53,13 @@ export function validateProviderCoherence(manifests, registry, aliases) {
|
|
|
45
53
|
const provide = md.provide;
|
|
46
54
|
const invoke = md.invoke;
|
|
47
55
|
const run = md.run;
|
|
56
|
+
const mount = md.mount;
|
|
48
57
|
const controllers = md.controllers;
|
|
49
58
|
const resources = md.resources;
|
|
50
59
|
const hasProvide = provide !== undefined && provide !== null;
|
|
51
60
|
const hasInvoke = invoke !== undefined && invoke !== null;
|
|
52
61
|
const hasRun = run !== undefined && run !== null;
|
|
62
|
+
const hasMount = mount !== undefined && mount !== null;
|
|
53
63
|
const hasControllers = Array.isArray(controllers) && controllers.length > 0;
|
|
54
64
|
if (hasProvide && capability !== "Telo.Provider") {
|
|
55
65
|
diagnostics.push({
|
|
@@ -133,6 +143,77 @@ export function validateProviderCoherence(manifests, registry, aliases) {
|
|
|
133
143
|
}
|
|
134
144
|
}
|
|
135
145
|
}
|
|
146
|
+
if (hasMount && capability !== "Telo.Mount") {
|
|
147
|
+
diagnostics.push({
|
|
148
|
+
severity: DiagnosticSeverity.Error,
|
|
149
|
+
code: "MOUNT_ON_NON_MOUNT",
|
|
150
|
+
source: SOURCE,
|
|
151
|
+
message: `${label}: 'mount:' is only valid on definitions with 'capability: Telo.Mount' ` +
|
|
152
|
+
`(found '${capability ?? "<unset>"}'). Use 'invoke:' / 'run:' / 'provide:' for other capabilities.`,
|
|
153
|
+
data: { resource, filePath, path: "mount" },
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
if (hasMount && (hasInvoke || hasRun || hasProvide)) {
|
|
157
|
+
const conflict = hasInvoke ? "invoke" : hasRun ? "run" : "provide";
|
|
158
|
+
diagnostics.push({
|
|
159
|
+
severity: DiagnosticSeverity.Error,
|
|
160
|
+
code: "MOUNT_DISPATCHER_CONFLICT",
|
|
161
|
+
source: SOURCE,
|
|
162
|
+
message: `${label}: 'mount:' cannot co-exist with '${conflict}:'. ` +
|
|
163
|
+
`A definition declares exactly one dispatch entry-point.`,
|
|
164
|
+
data: { resource, filePath, path: "mount" },
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (hasMount) {
|
|
168
|
+
// Resolve the target's name from either form: the bare string (the
|
|
169
|
+
// primary, documented form — `mount: api`) or the object's `name`. A CEL
|
|
170
|
+
// target (`${{ … }}`) can only be checked at runtime, so skip those.
|
|
171
|
+
let mountedName;
|
|
172
|
+
if (typeof mount === "string") {
|
|
173
|
+
if (!mount.includes("${{"))
|
|
174
|
+
mountedName = mount;
|
|
175
|
+
}
|
|
176
|
+
else if (typeof mount === "object" && !Array.isArray(mount)) {
|
|
177
|
+
const mountObj = mount;
|
|
178
|
+
if (typeof mountObj.name === "string" && !mountObj.name.includes("${{")) {
|
|
179
|
+
mountedName = mountObj.name;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const mountPath = typeof mount === "string" ? "mount" : "mount.name";
|
|
183
|
+
if (mountedName && Array.isArray(resources)) {
|
|
184
|
+
const match = resources.find((r) => {
|
|
185
|
+
const meta = r?.metadata;
|
|
186
|
+
return typeof meta?.name === "string" && meta.name === mountedName;
|
|
187
|
+
});
|
|
188
|
+
if (!match) {
|
|
189
|
+
diagnostics.push({
|
|
190
|
+
severity: DiagnosticSeverity.Error,
|
|
191
|
+
code: "MOUNT_TARGET_UNKNOWN",
|
|
192
|
+
source: SOURCE,
|
|
193
|
+
message: `${label}: '${mountPath}: ${mountedName}' does not match any entry's ` +
|
|
194
|
+
`metadata.name in 'resources:'.`,
|
|
195
|
+
data: { resource, filePath, path: mountPath },
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
else if (typeof match.kind === "string") {
|
|
199
|
+
const resolvedKind = aliases.resolveKind(match.kind) ?? match.kind;
|
|
200
|
+
const targetDef = registry.resolve(resolvedKind) ?? registry.resolve(match.kind);
|
|
201
|
+
if (targetDef && targetDef.kind === "Telo.Definition") {
|
|
202
|
+
const targetCap = targetDef.capability;
|
|
203
|
+
if (typeof targetCap === "string" && targetCap !== "Telo.Mount") {
|
|
204
|
+
diagnostics.push({
|
|
205
|
+
severity: DiagnosticSeverity.Error,
|
|
206
|
+
code: "MOUNT_TARGET_NOT_MOUNTABLE",
|
|
207
|
+
source: SOURCE,
|
|
208
|
+
message: `${label}: '${mountPath}: ${mountedName}' resolves to a ${match.kind} ` +
|
|
209
|
+
`(capability '${targetCap}'); 'mount:' requires a Telo.Mount target.`,
|
|
210
|
+
data: { resource, filePath, path: mountPath },
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
136
217
|
if (capability === "Telo.Provider" && !hasControllers && !hasProvide) {
|
|
137
218
|
diagnostics.push({
|
|
138
219
|
severity: DiagnosticSeverity.Error,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/analyzer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.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.36.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
54
|
"@telorun/sdk": "*"
|
package/src/builtins.ts
CHANGED
|
@@ -61,12 +61,28 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
|
|
|
61
61
|
items: {
|
|
62
62
|
type: "object",
|
|
63
63
|
additionalProperties: true,
|
|
64
|
+
// Resource bodies are `self`-only for config: per-call `inputs` is
|
|
65
|
+
// NOT in scope here. Each entry is a persistent child created once at
|
|
66
|
+
// init() and reused, so its config cannot depend on call-time data —
|
|
67
|
+
// that flows through the top-level `inputs:` sibling into the dispatch
|
|
68
|
+
// target's invoke().
|
|
69
|
+
//
|
|
70
|
+
// The exception is CEL the child's OWN controller evaluates later
|
|
71
|
+
// against a runtime context it owns (e.g. an Http.Api evaluating route
|
|
72
|
+
// CEL per request). Those `request` / `result` / `steps` / `error`
|
|
73
|
+
// variables are deferred — the template controller preserves them
|
|
74
|
+
// untouched (see resource-template-controller.ts) — so they are
|
|
75
|
+
// exposed here permissively. Their deep shape is the child kind's
|
|
76
|
+
// concern, not the template's, so they type as open values.
|
|
64
77
|
"x-telo-context": {
|
|
65
78
|
type: "object",
|
|
66
79
|
additionalProperties: false,
|
|
67
80
|
properties: {
|
|
68
81
|
self: { "x-telo-context-from-root": "schema" },
|
|
69
|
-
|
|
82
|
+
request: {},
|
|
83
|
+
result: {},
|
|
84
|
+
steps: {},
|
|
85
|
+
error: {},
|
|
70
86
|
},
|
|
71
87
|
},
|
|
72
88
|
},
|
|
@@ -129,6 +145,41 @@ export const KERNEL_BUILTINS: ResourceDefinition[] = [
|
|
|
129
145
|
},
|
|
130
146
|
},
|
|
131
147
|
},
|
|
148
|
+
// Mount dispatch: names the `resources:` entry (a Telo.Mount, e.g. an
|
|
149
|
+
// Http.Api) whose `register()` this definition delegates to. Same
|
|
150
|
+
// string / { kind, name } grammar as `invoke:`. The named child stays
|
|
151
|
+
// persistent so the produced mount's routes can `!ref` its siblings.
|
|
152
|
+
mount: {
|
|
153
|
+
oneOf: [
|
|
154
|
+
{
|
|
155
|
+
type: "string",
|
|
156
|
+
"x-telo-context": {
|
|
157
|
+
type: "object",
|
|
158
|
+
additionalProperties: false,
|
|
159
|
+
properties: {
|
|
160
|
+
self: { "x-telo-context-from-root": "schema" },
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
type: "object",
|
|
166
|
+
additionalProperties: true,
|
|
167
|
+
properties: {
|
|
168
|
+
kind: { type: "string" },
|
|
169
|
+
name: {
|
|
170
|
+
type: "string",
|
|
171
|
+
"x-telo-context": {
|
|
172
|
+
type: "object",
|
|
173
|
+
additionalProperties: false,
|
|
174
|
+
properties: {
|
|
175
|
+
self: { "x-telo-context-from-root": "schema" },
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
132
183
|
inputs: {
|
|
133
184
|
type: "object",
|
|
134
185
|
additionalProperties: true,
|
package/src/index.ts
CHANGED
|
@@ -39,6 +39,8 @@ export { parseLoadedFile } from "./parse-loaded-file.js";
|
|
|
39
39
|
export type { ParseOptions } from "./parse-loaded-file.js";
|
|
40
40
|
export { desugarLoadedFile, inlineImportManifests } from "./inline-imports.js";
|
|
41
41
|
export type { SyntheticImport } from "./inline-imports.js";
|
|
42
|
+
export { reconcileModuleVersions } from "./reconcile-module-versions.js";
|
|
43
|
+
export type { VersionReconciliation } from "./reconcile-module-versions.js";
|
|
42
44
|
export { residualEntrySchema, residualEntrySchemaMap } from "./residual-schema.js";
|
|
43
45
|
export {
|
|
44
46
|
buildDocumentPositions,
|
|
@@ -49,6 +51,7 @@ export {
|
|
|
49
51
|
export type { DocumentPosition } from "./position-metadata.js";
|
|
50
52
|
export { HttpSource } from "./sources/http-source.js";
|
|
51
53
|
export { RegistrySource } from "./sources/registry-source.js";
|
|
54
|
+
export { defaultSources } from "./sources/default-sources.js";
|
|
52
55
|
export { withSyntheticPositions } from "./with-synthetic-positions.js";
|
|
53
56
|
export { DEFAULT_MANIFEST_FILENAME, DiagnosticSeverity } from "./types.js";
|
|
54
57
|
export type {
|
package/src/loaded-types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
2
|
import type { Document } from "yaml";
|
|
3
3
|
import type { DocumentPosition } from "./position-metadata.js";
|
|
4
|
-
import type { Range } from "./types.js";
|
|
4
|
+
import type { AnalysisDiagnostic, Range } from "./types.js";
|
|
5
5
|
|
|
6
6
|
/** One physical file's parsed result. Returned for the owner manifest, for
|
|
7
7
|
* each `include:` partial, and for each external import target.
|
|
@@ -70,8 +70,21 @@ export interface LoadedGraph {
|
|
|
70
70
|
* its partials. */
|
|
71
71
|
modules: Map<string, LoadedModule>;
|
|
72
72
|
/** Per-Telo.Import resolution. Keyed by the resolved URL of the file the
|
|
73
|
-
* Telo.Import was declared in, then by the import's PascalCase alias.
|
|
73
|
+
* Telo.Import was declared in, then by the import's PascalCase alias.
|
|
74
|
+
* Version reconciliation repoints losing edges at their winner here, so a
|
|
75
|
+
* consumer walking these edges (`flattenForAnalyzer`) sees one version per
|
|
76
|
+
* module identity. */
|
|
74
77
|
importEdges: Map<string, Map<string, ImportEdge>>;
|
|
78
|
+
/** Version-reconciliation redirects: a losing module's canonical source URL →
|
|
79
|
+
* the winning version's canonical source URL. The runtime consults this when
|
|
80
|
+
* it independently re-resolves an import (the analyzer already sees repointed
|
|
81
|
+
* `importEdges`). Empty when no module identity appeared at two sources. */
|
|
82
|
+
overrides: Map<string, string>;
|
|
83
|
+
/** Diagnostics produced while reconciling module versions — one per import
|
|
84
|
+
* edge redirected to a different version (warning for a same-major hoist,
|
|
85
|
+
* error for a major mismatch). Surfaced alongside `analyze()` diagnostics by
|
|
86
|
+
* every consumer (CLI, editor, VS Code). */
|
|
87
|
+
versionDiagnostics: AnalysisDiagnostic[];
|
|
75
88
|
/** Surface-level errors that did not abort the graph load (e.g. an import
|
|
76
89
|
* whose target failed to fetch). */
|
|
77
90
|
errors: GraphLoadError[];
|
package/src/manifest-loader.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { Environment } from "@marcbachmann/cel-js";
|
|
2
2
|
import type { ResourceManifest } from "@telorun/sdk";
|
|
3
|
-
import { HttpSource } from "./sources/http-source.js";
|
|
4
|
-
import { RegistrySource } from "./sources/registry-source.js";
|
|
5
3
|
import { buildCelEnvironment } from "./cel-environment.js";
|
|
6
4
|
import type {
|
|
7
5
|
GraphLoadError,
|
|
@@ -13,6 +11,7 @@ import type {
|
|
|
13
11
|
import { desugarLoadedFile } from "./inline-imports.js";
|
|
14
12
|
import { isModuleKind } from "./module-kinds.js";
|
|
15
13
|
import { parseLoadedFile } from "./parse-loaded-file.js";
|
|
14
|
+
import { reconcileModuleVersions } from "./reconcile-module-versions.js";
|
|
16
15
|
import {
|
|
17
16
|
DEFAULT_MANIFEST_FILENAME,
|
|
18
17
|
type LoadOptions,
|
|
@@ -53,22 +52,13 @@ export class Loader {
|
|
|
53
52
|
protected sources: ManifestSource[];
|
|
54
53
|
private readonly celEnv: Environment;
|
|
55
54
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
this.sources = [];
|
|
65
|
-
if (includeHttpSource) this.sources.push(new HttpSource());
|
|
66
|
-
if (includeRegistrySource) this.sources.push(new RegistrySource(options.registryUrl));
|
|
67
|
-
|
|
68
|
-
if (options.extraSources?.length) {
|
|
69
|
-
this.sources.unshift(...options.extraSources);
|
|
70
|
-
}
|
|
71
|
-
|
|
55
|
+
/** Sources are resolved in order — the first whose `supports(url)` matches
|
|
56
|
+
* wins. The caller (composition root) decides which concrete sources exist
|
|
57
|
+
* and supplies them; `defaultSources()` bundles the browser-safe built-ins
|
|
58
|
+
* (HTTP + registry) for the common case. `register()` prepends a source at
|
|
59
|
+
* runtime. */
|
|
60
|
+
constructor(sources: ManifestSource[] = [], options: LoaderInitOptions = {}) {
|
|
61
|
+
this.sources = [...sources];
|
|
72
62
|
this.celEnv = buildCelEnvironment(options.celHandlers);
|
|
73
63
|
}
|
|
74
64
|
|
|
@@ -307,7 +297,20 @@ export class Loader {
|
|
|
307
297
|
}
|
|
308
298
|
}
|
|
309
299
|
|
|
310
|
-
|
|
300
|
+
// Collapse multiple versions of the same module identity onto one version
|
|
301
|
+
// before any consumer walks the edges: repoints losing `importEdges` in
|
|
302
|
+
// place and yields the runtime override map + hoist/conflict diagnostics.
|
|
303
|
+
const { overrides, diagnostics } = reconcileModuleVersions(modules, importEdges);
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
rootSource,
|
|
307
|
+
entry,
|
|
308
|
+
modules,
|
|
309
|
+
importEdges,
|
|
310
|
+
overrides,
|
|
311
|
+
versionDiagnostics: diagnostics,
|
|
312
|
+
errors,
|
|
313
|
+
};
|
|
311
314
|
}
|
|
312
315
|
|
|
313
316
|
/** Resolve an `import` URL against the file it appears in. Relative /
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import type { ImportEdge, LoadedModule } from "./loaded-types.js";
|
|
2
|
+
import { isModuleKind } from "./module-kinds.js";
|
|
3
|
+
import { DiagnosticSeverity, type AnalysisDiagnostic } from "./types.js";
|
|
4
|
+
|
|
5
|
+
const SOURCE = "telo-analyzer";
|
|
6
|
+
|
|
7
|
+
/** Outcome of reconciling a module name that appears at more than one resolved
|
|
8
|
+
* source in a single import graph. The `overrides` map redirects each losing
|
|
9
|
+
* canonical URL to the winner's canonical URL — consulted by the runtime when
|
|
10
|
+
* it independently re-resolves an import (the analyzer side is handled by
|
|
11
|
+
* repointing `importEdges` in place). */
|
|
12
|
+
export interface VersionReconciliation {
|
|
13
|
+
/** Loser canonical source URL → winner canonical source URL. */
|
|
14
|
+
overrides: Map<string, string>;
|
|
15
|
+
/** One diagnostic per import edge that pointed at a non-winner: a warning for
|
|
16
|
+
* a same-major hoist, an error for an incompatible major mismatch. */
|
|
17
|
+
diagnostics: AnalysisDiagnostic[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ParsedVersion {
|
|
21
|
+
major: number;
|
|
22
|
+
minor: number;
|
|
23
|
+
patch: number;
|
|
24
|
+
/** Dot-separated prerelease identifiers, or `null` for a release version. */
|
|
25
|
+
pre: string[] | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface ModuleIdentity {
|
|
29
|
+
source: string;
|
|
30
|
+
identity: string;
|
|
31
|
+
version: string;
|
|
32
|
+
parsed: ParsedVersion | null;
|
|
33
|
+
text: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Parse `X.Y.Z`, `vX.Y.Z`, or `X.Y.Z-pre.1`. Returns `null` for anything that
|
|
37
|
+
* isn't a plain three-part numeric core — an unparseable version forces the
|
|
38
|
+
* group onto the conflict path (we never silently hoist across a version we
|
|
39
|
+
* can't reason about). Pure: no dependency on the `semver` package, so the
|
|
40
|
+
* analyzer stays browser-safe and dependency-free. */
|
|
41
|
+
function parseVersion(raw: string | undefined): ParsedVersion | null {
|
|
42
|
+
if (typeof raw !== "string") return null;
|
|
43
|
+
const v = raw.startsWith("v") ? raw.slice(1) : raw;
|
|
44
|
+
const [core, ...preParts] = v.split("-");
|
|
45
|
+
const pre = preParts.length > 0 ? preParts.join("-") : null;
|
|
46
|
+
const segments = core.split(".");
|
|
47
|
+
if (segments.length !== 3) return null;
|
|
48
|
+
const [major, minor, patch] = segments.map((s) => {
|
|
49
|
+
if (!/^\d+$/.test(s)) return NaN;
|
|
50
|
+
return Number(s);
|
|
51
|
+
});
|
|
52
|
+
if ([major, minor, patch].some((n) => Number.isNaN(n))) return null;
|
|
53
|
+
return { major, minor, patch, pre: pre === null ? null : pre.split(".") };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** SemVer precedence: numeric core, then a release outranks a prerelease, then
|
|
57
|
+
* prerelease identifiers compared field-by-field (numeric < non-numeric per
|
|
58
|
+
* spec, shorter set loses when all shared fields are equal). */
|
|
59
|
+
function compareVersions(a: ParsedVersion, b: ParsedVersion): number {
|
|
60
|
+
if (a.major !== b.major) return a.major - b.major;
|
|
61
|
+
if (a.minor !== b.minor) return a.minor - b.minor;
|
|
62
|
+
if (a.patch !== b.patch) return a.patch - b.patch;
|
|
63
|
+
if (a.pre === null && b.pre === null) return 0;
|
|
64
|
+
if (a.pre === null) return 1;
|
|
65
|
+
if (b.pre === null) return -1;
|
|
66
|
+
const len = Math.max(a.pre.length, b.pre.length);
|
|
67
|
+
for (let i = 0; i < len; i++) {
|
|
68
|
+
const ai = a.pre[i];
|
|
69
|
+
const bi = b.pre[i];
|
|
70
|
+
if (ai === undefined) return -1;
|
|
71
|
+
if (bi === undefined) return 1;
|
|
72
|
+
const an = /^\d+$/.test(ai);
|
|
73
|
+
const bn = /^\d+$/.test(bi);
|
|
74
|
+
if (an && bn) {
|
|
75
|
+
const d = Number(ai) - Number(bi);
|
|
76
|
+
if (d !== 0) return d;
|
|
77
|
+
} else if (an !== bn) {
|
|
78
|
+
return an ? -1 : 1;
|
|
79
|
+
} else if (ai !== bi) {
|
|
80
|
+
return ai < bi ? -1 : 1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return 0;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Read a loaded module's `namespace/name` identity, version, and raw owner
|
|
87
|
+
* text. Returns `null` for modules without a namespace: only a registry
|
|
88
|
+
* identity (`<namespace>/<name>`) is a stable cross-import key. Two namespace-
|
|
89
|
+
* less local libraries that merely share a `metadata.name` are distinct modules
|
|
90
|
+
* reached via distinct source URLs — reconciling them would drop one and break
|
|
91
|
+
* its kinds; the same local file reached via two paths is already collapsed by
|
|
92
|
+
* canonical-source dedup, so there is nothing left to reconcile here. */
|
|
93
|
+
function moduleIdentityOf(mod: LoadedModule): ModuleIdentity | null {
|
|
94
|
+
const doc = mod.owner.manifests.find((m) => m && isModuleKind(m.kind));
|
|
95
|
+
if (!doc) return null;
|
|
96
|
+
const meta = doc.metadata as { name?: string; namespace?: string | null; version?: string };
|
|
97
|
+
const name = meta?.name;
|
|
98
|
+
if (typeof name !== "string" || name.length === 0) return null;
|
|
99
|
+
if (typeof meta.namespace !== "string" || meta.namespace.length === 0) return null;
|
|
100
|
+
const version = typeof meta.version === "string" ? meta.version : "";
|
|
101
|
+
return {
|
|
102
|
+
source: mod.owner.source,
|
|
103
|
+
identity: `${meta.namespace}/${name}`,
|
|
104
|
+
version,
|
|
105
|
+
parsed: parseVersion(version),
|
|
106
|
+
text: mod.owner.text,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface GroupResolution {
|
|
111
|
+
winner: ModuleIdentity;
|
|
112
|
+
/** True when members disagree on major version (or a version is unparseable). */
|
|
113
|
+
conflict: boolean;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Pick the winning member of a same-identity group and classify it. The winner
|
|
117
|
+
* is the highest version (deterministic tiebreak on source URL for equal
|
|
118
|
+
* versions / same-version-different-source). A major disagreement — or any
|
|
119
|
+
* unparseable version — marks the group a conflict; we still pick a winner so
|
|
120
|
+
* the rest of analysis proceeds against a single version instead of cascading
|
|
121
|
+
* duplicate-kind errors. */
|
|
122
|
+
function resolveGroup(members: ModuleIdentity[]): GroupResolution {
|
|
123
|
+
const majors = new Set<number | null>();
|
|
124
|
+
for (const m of members) majors.add(m.parsed ? m.parsed.major : null);
|
|
125
|
+
const conflict = majors.has(null) || majors.size > 1;
|
|
126
|
+
|
|
127
|
+
const winner = members.reduce((best, cur) => {
|
|
128
|
+
if (!cur.parsed) return best;
|
|
129
|
+
if (!best.parsed) return cur;
|
|
130
|
+
const cmp = compareVersions(cur.parsed, best.parsed);
|
|
131
|
+
if (cmp > 0) return cur;
|
|
132
|
+
if (cmp === 0 && cur.source < best.source) return cur;
|
|
133
|
+
return best;
|
|
134
|
+
}, members[0]);
|
|
135
|
+
|
|
136
|
+
return { winner, conflict };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** The diagnostic for a redirected edge, or `null` when the redirect is a
|
|
140
|
+
* silent dedupe (the same version resolved from two sources with identical
|
|
141
|
+
* content — no decision was made, so nothing to report). */
|
|
142
|
+
function hoistDiagnostic(
|
|
143
|
+
identity: string,
|
|
144
|
+
importerSource: string,
|
|
145
|
+
alias: string,
|
|
146
|
+
loser: ModuleIdentity,
|
|
147
|
+
winner: ModuleIdentity,
|
|
148
|
+
conflict: boolean,
|
|
149
|
+
): AnalysisDiagnostic | null {
|
|
150
|
+
const data = { filePath: importerSource, path: `imports.${alias}` };
|
|
151
|
+
if (conflict) {
|
|
152
|
+
return {
|
|
153
|
+
severity: DiagnosticSeverity.Error,
|
|
154
|
+
code: "MODULE_VERSION_CONFLICT",
|
|
155
|
+
source: SOURCE,
|
|
156
|
+
message:
|
|
157
|
+
`Module '${identity}' is imported at incompatible major versions: ` +
|
|
158
|
+
`${loser.version || "<unknown>"} here and ${winner.version} elsewhere in the same graph. ` +
|
|
159
|
+
`Major versions can carry breaking changes and cannot be reconciled automatically — ` +
|
|
160
|
+
`align every importer on one major.`,
|
|
161
|
+
data,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (loser.version === winner.version) {
|
|
165
|
+
// Same version, two sources. Identical content is a no-op dedupe; differing
|
|
166
|
+
// content means one is masquerading as the other (e.g. a local checkout vs
|
|
167
|
+
// the published version) — worth surfacing.
|
|
168
|
+
if (loser.text === winner.text) return null;
|
|
169
|
+
return {
|
|
170
|
+
severity: DiagnosticSeverity.Warning,
|
|
171
|
+
code: "MODULE_VERSION_HOISTED",
|
|
172
|
+
source: SOURCE,
|
|
173
|
+
message:
|
|
174
|
+
`Module '${identity}@${winner.version}' is imported from two sources whose contents ` +
|
|
175
|
+
`differ ('${loser.source}' and '${winner.source}'). Using '${winner.source}' for every ` +
|
|
176
|
+
`importer — pin a single source to remove the ambiguity.`,
|
|
177
|
+
data,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
severity: DiagnosticSeverity.Warning,
|
|
182
|
+
code: "MODULE_VERSION_HOISTED",
|
|
183
|
+
source: SOURCE,
|
|
184
|
+
message:
|
|
185
|
+
`Module '${identity}@${loser.version || "<unknown>"}' was hoisted to '${winner.version}' ` +
|
|
186
|
+
`because the same module is imported at the higher version elsewhere in the graph. ` +
|
|
187
|
+
`Pre-1.0 versions are additive, so the higher version is used for every importer.`,
|
|
188
|
+
data,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Reconcile a loaded import graph so each module identity (`namespace/name`)
|
|
194
|
+
* resolves to a single version. Within a shared major the highest version wins
|
|
195
|
+
* (a non-lossy hoist, given Telo's additive-only pre-1.0 policy); a major
|
|
196
|
+
* mismatch is a hard conflict. Mutates `importEdges` in place — every edge that
|
|
197
|
+
* pointed at a losing source is repointed at the winner — so `flattenForAnalyzer`
|
|
198
|
+
* walks a deduplicated graph and the runtime collision (two definitions of the
|
|
199
|
+
* same kind) cannot occur. Pure and browser-safe: no I/O, no Node built-ins.
|
|
200
|
+
*/
|
|
201
|
+
export function reconcileModuleVersions(
|
|
202
|
+
modules: Map<string, LoadedModule>,
|
|
203
|
+
importEdges: Map<string, Map<string, ImportEdge>>,
|
|
204
|
+
): VersionReconciliation {
|
|
205
|
+
const overrides = new Map<string, string>();
|
|
206
|
+
const diagnostics: AnalysisDiagnostic[] = [];
|
|
207
|
+
|
|
208
|
+
const groups = new Map<string, ModuleIdentity[]>();
|
|
209
|
+
const infoBySource = new Map<string, ModuleIdentity>();
|
|
210
|
+
for (const mod of modules.values()) {
|
|
211
|
+
const info = moduleIdentityOf(mod);
|
|
212
|
+
if (!info) continue;
|
|
213
|
+
infoBySource.set(info.source, info);
|
|
214
|
+
const list = groups.get(info.identity);
|
|
215
|
+
if (list) list.push(info);
|
|
216
|
+
else groups.set(info.identity, [info]);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const conflictByIdentity = new Map<string, boolean>();
|
|
220
|
+
for (const [identity, members] of groups) {
|
|
221
|
+
if (members.length < 2) continue;
|
|
222
|
+
const { winner, conflict } = resolveGroup(members);
|
|
223
|
+
conflictByIdentity.set(identity, conflict);
|
|
224
|
+
for (const member of members) {
|
|
225
|
+
if (member.source !== winner.source) overrides.set(member.source, winner.source);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (overrides.size === 0) return { overrides, diagnostics };
|
|
230
|
+
|
|
231
|
+
for (const [importerSource, aliasMap] of importEdges) {
|
|
232
|
+
for (const [alias, edge] of aliasMap) {
|
|
233
|
+
const winnerSource = overrides.get(edge.targetSource);
|
|
234
|
+
if (!winnerSource) continue;
|
|
235
|
+
const loser = infoBySource.get(edge.targetSource);
|
|
236
|
+
const winner = infoBySource.get(winnerSource);
|
|
237
|
+
if (loser && winner) {
|
|
238
|
+
const diag = hoistDiagnostic(
|
|
239
|
+
loser.identity,
|
|
240
|
+
importerSource,
|
|
241
|
+
alias,
|
|
242
|
+
loser,
|
|
243
|
+
winner,
|
|
244
|
+
conflictByIdentity.get(loser.identity) ?? false,
|
|
245
|
+
);
|
|
246
|
+
if (diag) diagnostics.push(diag);
|
|
247
|
+
}
|
|
248
|
+
edge.targetSource = winnerSource;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return { overrides, diagnostics };
|
|
253
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ManifestSource } from "../types.js";
|
|
2
|
+
import { HttpSource } from "./http-source.js";
|
|
3
|
+
import { RegistrySource } from "./registry-source.js";
|
|
4
|
+
|
|
5
|
+
/** The browser-safe built-in sources, in resolution order: HTTP fetch then
|
|
6
|
+
* registry. Node-specific sources (local filesystem) are supplied by the
|
|
7
|
+
* consuming package and passed alongside these into the `Loader` constructor.
|
|
8
|
+
* Callers that only want a subset (e.g. the editor, which brings its own
|
|
9
|
+
* registry adapters) construct the individual sources directly. */
|
|
10
|
+
export function defaultSources(registryUrl?: string): ManifestSource[] {
|
|
11
|
+
return [new HttpSource(), new RegistrySource(registryUrl)];
|
|
12
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -75,14 +75,6 @@ export interface LoadOptions {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
export interface LoaderInitOptions {
|
|
78
|
-
/** Sources inserted with highest priority before built-ins. */
|
|
79
|
-
extraSources?: ManifestSource[];
|
|
80
|
-
/** Include built-in HttpSource. Defaults to true. */
|
|
81
|
-
includeHttpSource?: boolean;
|
|
82
|
-
/** Include built-in RegistrySource. Defaults to true. */
|
|
83
|
-
includeRegistrySource?: boolean;
|
|
84
|
-
/** Base URL used by built-in RegistrySource when enabled. */
|
|
85
|
-
registryUrl?: string;
|
|
86
78
|
/** Handlers for CEL stdlib functions (e.g. `sha256`). Analyzer-only callers may
|
|
87
79
|
* omit this and get throwing stubs; runtime callers (kernel) must supply real impls. */
|
|
88
80
|
celHandlers?: import("./cel-environment.js").CelHandlers;
|