@telorun/analyzer 0.65.0 → 0.67.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/analyzer.d.ts +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +194 -65
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +67 -16
- package/dist/call-graph.d.ts.map +1 -1
- package/dist/call-graph.js +7 -1
- package/dist/cel-environment.d.ts +12 -0
- package/dist/cel-environment.d.ts.map +1 -1
- package/dist/cel-environment.js +35 -19
- package/dist/cel-scope.d.ts +8 -0
- package/dist/cel-scope.d.ts.map +1 -1
- package/dist/cel-scope.js +89 -11
- package/dist/definition-registry.d.ts +41 -0
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +76 -0
- package/dist/dependency-graph.d.ts.map +1 -1
- package/dist/dependency-graph.js +65 -0
- package/dist/eval-paths.d.ts +68 -0
- package/dist/eval-paths.d.ts.map +1 -1
- package/dist/eval-paths.js +138 -0
- package/dist/flatten-for-analyzer.d.ts +37 -1
- package/dist/flatten-for-analyzer.d.ts.map +1 -1
- package/dist/flatten-for-analyzer.js +104 -5
- package/dist/import-resolution-diagnostics.d.ts +1 -1
- package/dist/import-resolution-diagnostics.js +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/inline-imports.d.ts.map +1 -1
- package/dist/inline-imports.js +1 -0
- package/dist/manifest-visitor.d.ts +4 -0
- package/dist/manifest-visitor.d.ts.map +1 -1
- package/dist/manifest-visitor.js +36 -1
- package/dist/migrations/entries/index.d.ts.map +1 -1
- package/dist/migrations/entries/index.js +2 -0
- package/dist/migrations/entries/schema-prepare-bucket.json +27 -0
- package/dist/peer-binding.d.ts +232 -0
- package/dist/peer-binding.d.ts.map +1 -0
- package/dist/peer-binding.js +418 -0
- package/dist/precompile.d.ts.map +1 -1
- package/dist/precompile.js +8 -0
- package/dist/ref-slot.d.ts +13 -0
- package/dist/ref-slot.d.ts.map +1 -1
- package/dist/ref-slot.js +26 -0
- package/dist/reference-field-map.d.ts +28 -0
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +25 -0
- package/dist/referrer-rule.d.ts +17 -0
- package/dist/referrer-rule.d.ts.map +1 -1
- package/dist/referrer-rule.js +4 -1
- package/dist/release/index.d.ts +1 -1
- package/dist/release/index.js +1 -1
- package/dist/resolve-ref-sentinels.d.ts.map +1 -1
- package/dist/resolve-ref-sentinels.js +14 -1
- package/dist/resource-input.d.ts +75 -0
- package/dist/resource-input.d.ts.map +1 -0
- package/dist/resource-input.js +90 -0
- package/dist/resource-rule.d.ts +40 -27
- package/dist/resource-rule.d.ts.map +1 -1
- package/dist/resource-rule.js +61 -7
- package/dist/rule-condition.d.ts +11 -0
- package/dist/rule-condition.d.ts.map +1 -1
- package/dist/rule-condition.js +13 -0
- package/dist/schema-projection.d.ts +119 -4
- package/dist/schema-projection.d.ts.map +1 -1
- package/dist/schema-projection.js +213 -18
- package/dist/system-kinds.d.ts +7 -2
- package/dist/system-kinds.d.ts.map +1 -1
- package/dist/system-kinds.js +7 -2
- package/dist/telo-version.d.ts +1 -1
- package/dist/telo-version.js +1 -1
- package/dist/template-body.d.ts +50 -0
- package/dist/template-body.d.ts.map +1 -0
- package/dist/template-body.js +58 -0
- package/dist/validate-cel-context.d.ts +1 -17
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +73 -78
- package/dist/validate-identifier-names.d.ts.map +1 -1
- package/dist/validate-identifier-names.js +17 -2
- package/dist/validate-reference-forms.d.ts +10 -0
- package/dist/validate-reference-forms.d.ts.map +1 -1
- package/dist/validate-reference-forms.js +23 -0
- package/dist/validate-references.d.ts +17 -0
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +77 -18
- package/dist/validate-referrer-rules.d.ts +38 -4
- package/dist/validate-referrer-rules.d.ts.map +1 -1
- package/dist/validate-referrer-rules.js +139 -31
- package/dist/validate-resource-inputs.d.ts +35 -0
- package/dist/validate-resource-inputs.d.ts.map +1 -0
- package/dist/validate-resource-inputs.js +319 -0
- package/dist/validate-resource-rules.d.ts +11 -2
- package/dist/validate-resource-rules.d.ts.map +1 -1
- package/dist/validate-resource-rules.js +18 -12
- package/dist/validate-schema-projection.d.ts.map +1 -1
- package/dist/validate-schema-projection.js +33 -2
- package/dist/validate-template-dispatch.d.ts +27 -0
- package/dist/validate-template-dispatch.d.ts.map +1 -0
- package/dist/validate-template-dispatch.js +95 -0
- package/dist/with-synthetic-positions.d.ts +1 -1
- package/dist/with-synthetic-positions.js +1 -1
- package/package.json +3 -3
- package/src/analyzer.ts +236 -70
- package/src/builtins.ts +69 -16
- package/src/call-graph.ts +7 -1
- package/src/cel-environment.ts +48 -19
- package/src/cel-scope.ts +113 -19
- package/src/definition-registry.ts +78 -0
- package/src/dependency-graph.ts +66 -0
- package/src/eval-paths.ts +152 -0
- package/src/flatten-for-analyzer.ts +117 -4
- package/src/import-resolution-diagnostics.ts +1 -1
- package/src/index.ts +57 -4
- package/src/inline-imports.ts +1 -0
- package/src/manifest-visitor.ts +41 -1
- package/src/migrations/entries/index.ts +2 -0
- package/src/migrations/entries/schema-prepare-bucket.json +27 -0
- package/src/peer-binding.ts +641 -0
- package/src/precompile.ts +8 -0
- package/src/ref-slot.ts +36 -0
- package/src/reference-field-map.ts +42 -0
- package/src/referrer-rule.ts +20 -1
- package/src/release/index.ts +1 -1
- package/src/resolve-ref-sentinels.ts +12 -1
- package/src/resource-input.ts +132 -0
- package/src/resource-rule.ts +69 -7
- package/src/rule-condition.ts +15 -0
- package/src/schema-projection.ts +302 -20
- package/src/system-kinds.ts +7 -2
- package/src/telo-version.ts +1 -1
- package/src/template-body.ts +104 -0
- package/src/validate-cel-context.ts +73 -78
- package/src/validate-identifier-names.ts +18 -3
- package/src/validate-reference-forms.ts +22 -0
- package/src/validate-references.ts +85 -16
- package/src/validate-referrer-rules.ts +215 -28
- package/src/validate-resource-inputs.ts +367 -0
- package/src/validate-resource-rules.ts +26 -12
- package/src/validate-schema-projection.ts +50 -1
- package/src/validate-template-dispatch.ts +99 -0
- package/src/with-synthetic-positions.ts +1 -1
package/dist/call-graph.js
CHANGED
|
@@ -119,7 +119,13 @@ function resolveUseAtSite(entry, root, concretePath, schemaDefault) {
|
|
|
119
119
|
if (resolved)
|
|
120
120
|
return { use: resolved };
|
|
121
121
|
}
|
|
122
|
-
const slot = {
|
|
122
|
+
const slot = {
|
|
123
|
+
kinds: entry.refs,
|
|
124
|
+
uses: entry.uses,
|
|
125
|
+
useCases: entry.useCases,
|
|
126
|
+
inline: false,
|
|
127
|
+
valueBranches: [],
|
|
128
|
+
};
|
|
123
129
|
const unresolvedReason = isTaggedSentinel(selector)
|
|
124
130
|
? "dynamic"
|
|
125
131
|
: selector === undefined
|
|
@@ -13,6 +13,18 @@ export type { CelHandlers } from "@telorun/templating";
|
|
|
13
13
|
* NOTE: The set of kernel globals registered here must match `KERNEL_GLOBAL_NAMES`
|
|
14
14
|
* in kernel-globals.ts, which is used for chain-access validation. */
|
|
15
15
|
export declare function buildTypedCelEnvironment(baseEnv: Environment, manifest: ResourceManifest, extraContextSchema?: Record<string, any> | null, rootModuleManifest?: ResourceManifest): Environment;
|
|
16
|
+
/**
|
|
17
|
+
* A kind document — whose CEL is written for whoever instantiates the kind, not
|
|
18
|
+
* evaluated in the declaring module's own scope.
|
|
19
|
+
*
|
|
20
|
+
* Its `examples:` show a consumer's route reading `request` and `result`, its
|
|
21
|
+
* `description`s show `${{ secrets.API_KEY }}`, and a rule condition reads the
|
|
22
|
+
* `self` / `referrer` its own evaluator binds. None of those names are in scope
|
|
23
|
+
* where they are WRITTEN, and all of them are correct where they are READ — so
|
|
24
|
+
* every check that asks "is this name in scope here" has to stand down on these
|
|
25
|
+
* documents, or it reports errors nobody can fix without deleting the example.
|
|
26
|
+
*/
|
|
27
|
+
export declare function isKindDocument(manifest: ResourceManifest): boolean;
|
|
16
28
|
/** CEL environment for the `variables:`/`secrets:` expressions on a `Telo.Import`.
|
|
17
29
|
*
|
|
18
30
|
* Import inputs are a config-only contract: their expressions are evaluated
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAcrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAI/C,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,WAAW,
|
|
1
|
+
{"version":3,"file":"cel-environment.d.ts","sourceRoot":"","sources":["../src/cel-environment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAcrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,YAAY,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD;;;;;;;;;uEASuE;AACvE,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,gBAAgB,EAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,EAI/C,kBAAkB,CAAC,EAAE,gBAAgB,GACpC,WAAW,CA8Gb;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,OAAO,CAElE;AAuBD;;;;;;8DAM8D;AAC9D,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,WAAW,EACpB,cAAc,EAAE,gBAAgB,GAAG,SAAS,GAC3C,WAAW,CAkCb"}
|
package/dist/cel-environment.js
CHANGED
|
@@ -35,24 +35,26 @@ rootModuleManifest) {
|
|
|
35
35
|
for (const brand of Object.keys(VALUE_BRAND_BASE)) {
|
|
36
36
|
env.registerType(brand, { fields: {} });
|
|
37
37
|
}
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
38
|
+
// `variables` / `secrets`: the DECLARING module's blocks, which is the
|
|
39
|
+
// contract the resource's CEL is evaluated against at runtime. Read the
|
|
40
|
+
// same three ways `ports` and `module` are, and for the same reason — a
|
|
41
|
+
// resource doc does not carry them, so typing from `manifest` alone left
|
|
42
|
+
// every ordinary resource with an open `variables` and no check at all,
|
|
43
|
+
// while `ports.<typo>` one line away was an error.
|
|
44
|
+
//
|
|
45
|
+
// Order matters: a module-identity doc analyzing itself carries its own
|
|
46
|
+
// block; a resource forwarded from an imported library carries its
|
|
47
|
+
// library's as `metadata.moduleGlobals`, which must win over the consuming
|
|
48
|
+
// application's; everything else is the entry module's own doc.
|
|
49
|
+
const moduleGlobals = manifest.metadata?.moduleGlobals;
|
|
50
|
+
// A KIND document is the exception, and it is not a detail: the CEL inside
|
|
51
|
+
// a `Telo.Definition`'s `schema:` — an `examples:` entry, a `description`
|
|
52
|
+
// showing `${{ secrets.API_KEY }}` — illustrates what a CONSUMER writes, in
|
|
53
|
+
// the consumer's scope. Closing those over the declaring module's blocks
|
|
54
|
+
// reported an error against a name the module never meant to declare, and
|
|
55
|
+
// one nobody could fix without deleting the example.
|
|
56
|
+
const root = (isKindDocument(manifest) ? undefined : rootModuleManifest);
|
|
57
|
+
registerConfigNamespace(env, manifest.variables ?? moduleGlobals?.variables ?? root?.variables, "variables");
|
|
56
58
|
// `ports` namespace: each entry types as the brand its `protocol` selects
|
|
57
59
|
// (tcp → TcpPort, udp → UdpPort), so `${{ ports.http }}` carries a nominal
|
|
58
60
|
// type that consuming fields can check against.
|
|
@@ -73,7 +75,7 @@ rootModuleManifest) {
|
|
|
73
75
|
else {
|
|
74
76
|
env.registerVariable("ports", "map");
|
|
75
77
|
}
|
|
76
|
-
env.
|
|
78
|
+
registerConfigNamespace(env, manifest.secrets ?? moduleGlobals?.secrets ?? root?.secrets, "secrets");
|
|
77
79
|
env.registerVariable("resources", "map");
|
|
78
80
|
// `module` — the declaring module's own `metadata`, so a manifest reads its
|
|
79
81
|
// version instead of restating it. A resource forwarded from an imported
|
|
@@ -110,6 +112,20 @@ rootModuleManifest) {
|
|
|
110
112
|
return baseEnv.clone();
|
|
111
113
|
}
|
|
112
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* A kind document — whose CEL is written for whoever instantiates the kind, not
|
|
117
|
+
* evaluated in the declaring module's own scope.
|
|
118
|
+
*
|
|
119
|
+
* Its `examples:` show a consumer's route reading `request` and `result`, its
|
|
120
|
+
* `description`s show `${{ secrets.API_KEY }}`, and a rule condition reads the
|
|
121
|
+
* `self` / `referrer` its own evaluator binds. None of those names are in scope
|
|
122
|
+
* where they are WRITTEN, and all of them are correct where they are READ — so
|
|
123
|
+
* every check that asks "is this name in scope here" has to stand down on these
|
|
124
|
+
* documents, or it reports errors nobody can fix without deleting the example.
|
|
125
|
+
*/
|
|
126
|
+
export function isKindDocument(manifest) {
|
|
127
|
+
return manifest.kind === "Telo.Definition" || manifest.kind === "Telo.Abstract";
|
|
128
|
+
}
|
|
113
129
|
/** Register a `variables`/`secrets` namespace typed from a module doc's schema map
|
|
114
130
|
* (`{ name: <schema>, … }`), falling back to dyn `map` when absent or untyped. */
|
|
115
131
|
function registerConfigNamespace(env, block, name) {
|
package/dist/cel-scope.d.ts
CHANGED
|
@@ -132,6 +132,14 @@ export declare class CelScopeResolver {
|
|
|
132
132
|
private stepContext;
|
|
133
133
|
private invocationContext;
|
|
134
134
|
private errorScopes;
|
|
135
|
+
/** The same two facts per `resources:` entry of a `Telo.Definition` — a
|
|
136
|
+
* template body is a declaration of ANOTHER kind, so its step body and its
|
|
137
|
+
* error branches are that kind's, not the enclosing definition's. */
|
|
138
|
+
private bodyScopes;
|
|
139
|
+
/** The enclosing definition's `self` schema, resolved once per resource. A
|
|
140
|
+
* template body's contexts resolve against the BODY, so `self` — the one
|
|
141
|
+
* binding anchored on the definition — is substituted before they do. */
|
|
142
|
+
private selfSchema;
|
|
135
143
|
constructor(inputs: CelScopeInputs);
|
|
136
144
|
/** The `steps` context schema for the current resource, or undefined when its
|
|
137
145
|
* kind declares no step body. Exposed because the step-inputs check needs the
|
package/dist/cel-scope.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cel-scope.d.ts","sourceRoot":"","sources":["../src/cel-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAQvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAO9D,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"cel-scope.d.ts","sourceRoot":"","sources":["../src/cel-scope.ts"],"names":[],"mappings":"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"}
|
package/dist/cel-scope.js
CHANGED
|
@@ -5,6 +5,7 @@ import { analyzerContractScope, PERMISSIVE_CONTRACT, resolveContract, } from "./
|
|
|
5
5
|
import { mergeKernelGlobalsIntoContext, } from "./kernel-globals.js";
|
|
6
6
|
import { resolveLocalRef, walkStepArray } from "./schema-walk.js";
|
|
7
7
|
import { readStepSlot } from "./step-slot.js";
|
|
8
|
+
import { bodyForPath, templateBodies } from "./template-body.js";
|
|
8
9
|
import { getManifestItem, resolveContextAnnotations, resolveTypeFieldToSchema, } from "./validate-cel-context.js";
|
|
9
10
|
/** Build a closed JSON Schema for the `self` CEL variable available inside a
|
|
10
11
|
* `Telo.Definition` template body. Mirrors the runtime template controller's
|
|
@@ -268,6 +269,14 @@ export class CelScopeResolver {
|
|
|
268
269
|
stepContext;
|
|
269
270
|
invocationContext;
|
|
270
271
|
errorScopes = new Map();
|
|
272
|
+
/** The same two facts per `resources:` entry of a `Telo.Definition` — a
|
|
273
|
+
* template body is a declaration of ANOTHER kind, so its step body and its
|
|
274
|
+
* error branches are that kind's, not the enclosing definition's. */
|
|
275
|
+
bodyScopes = [];
|
|
276
|
+
/** The enclosing definition's `self` schema, resolved once per resource. A
|
|
277
|
+
* template body's contexts resolve against the BODY, so `self` — the one
|
|
278
|
+
* binding anchored on the definition — is substituted before they do. */
|
|
279
|
+
selfSchema;
|
|
271
280
|
constructor(inputs) {
|
|
272
281
|
this.inputs = inputs;
|
|
273
282
|
}
|
|
@@ -293,10 +302,30 @@ export class CelScopeResolver {
|
|
|
293
302
|
enterResource(m, definition) {
|
|
294
303
|
const { allManifests, defs, aliases, scopes } = this.inputs;
|
|
295
304
|
this.invocationContext = m.metadata?.xTeloInvocationContext;
|
|
296
|
-
|
|
297
|
-
|
|
305
|
+
// The INHERITANCE-RESOLVED schema at both sites, for the reason the CEL
|
|
306
|
+
// context regions use it: an `extends` child is authored against
|
|
307
|
+
// merge(parent, own), so a step body or a `catch:` region the parent
|
|
308
|
+
// declares is in force on the child. All three consumers share one memo so
|
|
309
|
+
// they cannot come to disagree about which schema a kind means.
|
|
310
|
+
const authorSchema = defs.effectiveSchemaOf(definition);
|
|
311
|
+
this.stepContext = authorSchema
|
|
312
|
+
? buildStepContextSchema(m, authorSchema, allManifests, defs, aliases, scopes)
|
|
298
313
|
: undefined;
|
|
299
|
-
this.errorScopes = collectErrorContextScopes(
|
|
314
|
+
this.errorScopes = collectErrorContextScopes(authorSchema);
|
|
315
|
+
this.selfSchema =
|
|
316
|
+
m.kind === "Telo.Definition" ? buildSelfSchema(m, defs, aliases) : undefined;
|
|
317
|
+
this.bodyScopes = templateBodies(m, defs, aliases, scopes).map((body) => {
|
|
318
|
+
const bodySchema = defs.effectiveSchemaOf(body.definition);
|
|
319
|
+
return {
|
|
320
|
+
prefix: body.prefix,
|
|
321
|
+
scopePrefix: body.scopePrefix,
|
|
322
|
+
manifest: body.manifest,
|
|
323
|
+
stepContext: bodySchema
|
|
324
|
+
? buildStepContextSchema(body.manifest, bodySchema, allManifests, defs, aliases, scopes)
|
|
325
|
+
: undefined,
|
|
326
|
+
errorScopes: collectErrorContextScopes(bodySchema),
|
|
327
|
+
};
|
|
328
|
+
});
|
|
300
329
|
}
|
|
301
330
|
/**
|
|
302
331
|
* What the expression at `site` is typed against.
|
|
@@ -333,16 +362,36 @@ export class CelScopeResolver {
|
|
|
333
362
|
const { path } = site;
|
|
334
363
|
const m = site.source;
|
|
335
364
|
let matched = site.contextSchema ?? this.invocationContext;
|
|
336
|
-
|
|
365
|
+
// Inside a template body the step and error regions are the NESTED kind's.
|
|
366
|
+
// Its `steps` accumulator and its `catch:` branches are declared there, and
|
|
367
|
+
// the enclosing definition's (there are none) would say nothing about them.
|
|
368
|
+
const inBody = this.bodyScopes.length > 0 ? bodyForPath(this.bodyScopes, path) : undefined;
|
|
369
|
+
const stepContext = inBody ? inBody.stepContext : this.stepContext;
|
|
370
|
+
const errorScopes = inBody ? inBody.errorScopes : this.errorScopes;
|
|
371
|
+
if (stepContext) {
|
|
337
372
|
const base = matched ?? { type: "object", properties: {}, additionalProperties: true };
|
|
338
373
|
matched = {
|
|
339
374
|
...base,
|
|
340
|
-
properties: {
|
|
375
|
+
properties: {
|
|
376
|
+
// `inputs` is in scope beside `steps` wherever a step body runs — the
|
|
377
|
+
// step engine evaluates a step against the enclosing kind's own
|
|
378
|
+
// arguments, which is what `steps[0].inputs.x` reads. Modelled here
|
|
379
|
+
// because it was modelled NOWHERE: the step context is open, so a
|
|
380
|
+
// chain through it was never checked and the name's absence went
|
|
381
|
+
// unnoticed until something asked whether the root existed.
|
|
382
|
+
//
|
|
383
|
+
// OPEN, not typed from the kind's `inputType`: closing it would newly
|
|
384
|
+
// reject every read of an argument the contract does not spell out,
|
|
385
|
+
// which is a separate decision from knowing the name is legal.
|
|
386
|
+
inputs: { type: "object", additionalProperties: true },
|
|
387
|
+
...(base.properties ?? {}),
|
|
388
|
+
steps: stepContext,
|
|
389
|
+
},
|
|
341
390
|
};
|
|
342
391
|
}
|
|
343
392
|
// `error` is only in scope inside an error-bearing branch (e.g. a
|
|
344
393
|
// `catch:` / `finally:`), so it's merged per-path, not resource-wide.
|
|
345
|
-
const errorSchema =
|
|
394
|
+
const errorSchema = errorScopes.size > 0 ? errorContextForPath(path, errorScopes) : undefined;
|
|
346
395
|
if (errorSchema) {
|
|
347
396
|
const base = matched ?? { type: "object", properties: {}, additionalProperties: true };
|
|
348
397
|
matched = {
|
|
@@ -358,17 +407,46 @@ export class CelScopeResolver {
|
|
|
358
407
|
return this.inputs.observedStateContext;
|
|
359
408
|
}
|
|
360
409
|
const { defs, aliases, scopes, allManifests, kernelGlobals } = this.inputs;
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
410
|
+
// A template body's context annotations are the NESTED kind's, and every one
|
|
411
|
+
// that anchors at a root — `x-telo-context-element-from`,
|
|
412
|
+
// `-collection-from`, `-from-root`, `x-telo-bindings-from` — means the root
|
|
413
|
+
// of the DECLARATION they were written for. Resolving them against the
|
|
414
|
+
// enclosing `Telo.Definition` looked `collection:` up on a document that has
|
|
415
|
+
// no such field, so `item` typed open and a typo below it went unreported —
|
|
416
|
+
// the one place a nested declaration did not answer as the same declaration
|
|
417
|
+
// written at the top level.
|
|
418
|
+
//
|
|
419
|
+
// So the path and the scope are rebased into the body and the body becomes
|
|
420
|
+
// the resolution root. `self` is the single binding that genuinely belongs to
|
|
421
|
+
// the enclosing definition, and it is substituted below rather than left as
|
|
422
|
+
// an annotation the rebased root would misread.
|
|
423
|
+
const body = inBody;
|
|
424
|
+
const localPath = body ? path.slice(body.prefix.length + 1) : path;
|
|
425
|
+
const localScope = body && site.matchedScope?.startsWith(`${body.scopePrefix}.`)
|
|
426
|
+
? `$.${site.matchedScope.slice(body.scopePrefix.length + 1)}`
|
|
427
|
+
: body
|
|
428
|
+
? undefined
|
|
429
|
+
: site.matchedScope;
|
|
430
|
+
const rootManifest = body ? body.manifest : m;
|
|
431
|
+
if (body && this.selfSchema && matched.properties?.self) {
|
|
432
|
+
matched = {
|
|
433
|
+
...matched,
|
|
434
|
+
properties: { ...matched.properties, self: this.selfSchema },
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
const manifestItem = localScope
|
|
438
|
+
? getManifestItem(localPath, localScope, rootManifest)
|
|
439
|
+
: rootManifest;
|
|
440
|
+
const rootForResolver = body
|
|
441
|
+
? rootManifest
|
|
442
|
+
: manifestRootForResolver(m, defs, aliases, allManifests, scopes);
|
|
365
443
|
const resolved = resolveContextAnnotations(matched, manifestItem, {
|
|
366
444
|
manifestRoot: rootForResolver,
|
|
367
445
|
defs,
|
|
368
446
|
aliases,
|
|
369
447
|
allManifests: allManifests,
|
|
370
448
|
});
|
|
371
|
-
return mergeKernelGlobalsIntoContext(withBindingNames(resolved,
|
|
449
|
+
return mergeKernelGlobalsIntoContext(withBindingNames(resolved, rootManifest),
|
|
372
450
|
// Typed in the module that DECLARED this resource — for a manifest
|
|
373
451
|
// forwarded from an imported library, that is its `moduleGlobals` stamp,
|
|
374
452
|
// not the consuming application's block.
|
|
@@ -18,6 +18,9 @@ export declare class DefinitionRegistry {
|
|
|
18
18
|
private readonly fieldMaps;
|
|
19
19
|
/** Reverse inheritance index: parent kind → direct child kinds. */
|
|
20
20
|
private readonly extendedBy;
|
|
21
|
+
/** Memoized inheritance-resolved schemas. `null` records "resolved to
|
|
22
|
+
* nothing", so a kind with no schema is not re-walked on every resource. */
|
|
23
|
+
private readonly effectiveSchemas;
|
|
21
24
|
/** DEPRECATED module identity table: identity string → canonical module name
|
|
22
25
|
* ("std/pipeline" → "pipeline"). Serves only the legacy
|
|
23
26
|
* `<namespace>/<module>#<Kind>` form of `x-telo-ref`, kept resolvable for
|
|
@@ -131,6 +134,27 @@ export declare class DefinitionRegistry {
|
|
|
131
134
|
* never registered. */
|
|
132
135
|
resolveRef(xTeloRef: string): string | undefined;
|
|
133
136
|
resolve(kind: string): ResourceDefinition | undefined;
|
|
137
|
+
/**
|
|
138
|
+
* The kind's AUTHOR-FACING schema — its own merged with everything it
|
|
139
|
+
* inherits along `extends` — memoized per kind.
|
|
140
|
+
*
|
|
141
|
+
* This is what every consumer reading a kind's schema to interpret an
|
|
142
|
+
* INSTANCE must use, because that is the schema the instance was authored
|
|
143
|
+
* against and the one the kernel stamps at definition registration. Reading
|
|
144
|
+
* `resolve(kind).schema` instead sees only what the leaf declared, so an
|
|
145
|
+
* annotation on a parent — a CEL context region, a step body, an error
|
|
146
|
+
* context — is invisible on every child, silently and per consumer. Sharing
|
|
147
|
+
* one memo is what keeps those consumers from drifting apart.
|
|
148
|
+
*
|
|
149
|
+
* Lazy for the same reason `getFieldMap` is: a child registered before its
|
|
150
|
+
* parent still sees the inherited half once both are present.
|
|
151
|
+
*/
|
|
152
|
+
effectiveSchema(kind: string): Record<string, unknown> | undefined;
|
|
153
|
+
/** The effective schema for a definition, by the kind it is registered under.
|
|
154
|
+
* Falls back to the definition's own schema when it is not in this registry
|
|
155
|
+
* (a synthesized or not-yet-registered kind), which is the pre-inheritance
|
|
156
|
+
* behaviour and the safe direction: it types less, never wrongly. */
|
|
157
|
+
effectiveSchemaOf(definition: ResourceDefinition | undefined): Record<string, unknown> | undefined;
|
|
134
158
|
/** Returns the reference field map for the given kind, computed lazily from the
|
|
135
159
|
* kind's AUTHOR-FACING (inheritance-resolved) schema and memoized. Lazy so a
|
|
136
160
|
* child registered before its parent still sees the parent's inherited ref
|
|
@@ -166,6 +190,23 @@ export declare class DefinitionRegistry {
|
|
|
166
190
|
*/
|
|
167
191
|
resolveSchemaFromNode(schemaFrom: string, ownerScope: AliasResolver): Record<string, any> | undefined;
|
|
168
192
|
private resolveSchemaFromSubMap;
|
|
193
|
+
/** The kinds a definition descends from DIRECTLY — the same two edges
|
|
194
|
+
* `register` feeds into `extendedBy`, read the other way.
|
|
195
|
+
*
|
|
196
|
+
* Both spellings, because either can carry the edge that satisfies a slot:
|
|
197
|
+
* `capability:` is the legacy implements-this form, `extends:` the canonical
|
|
198
|
+
* one. A consumer walking these by hand would be a second reading of what an
|
|
199
|
+
* inheritance edge IS, and the downward index and the upward walk have to
|
|
200
|
+
* agree about that forever. */
|
|
201
|
+
parentsOf(kind: string): string[];
|
|
202
|
+
/** Whether every kind this one descends from is registered.
|
|
203
|
+
*
|
|
204
|
+
* When it is, what the kind implements is FULLY KNOWN: a target it does not
|
|
205
|
+
* reach is one it genuinely does not implement, so a mismatch is a verdict.
|
|
206
|
+
* When a hop is missing — an unimported abstract, an alias the declaring file
|
|
207
|
+
* could not resolve — a mismatch cannot be told from a missing dependency,
|
|
208
|
+
* which is the partial context every reference check stays lenient in. */
|
|
209
|
+
ancestryResolved(kind: string): boolean;
|
|
169
210
|
/** Returns all definitions that transitively extend the given abstract kind.
|
|
170
211
|
* Follows the capability chain to any depth (equivalent to instanceof in OOP).
|
|
171
212
|
* Definitions are included regardless of registration order. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,0BAA0B,CAAC;AAOlC,qBAAa,kBAAkB;;IAK7B;;sFAEkF;IAClF,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IACnC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAyD;IAC5F;;mFAE+E;IAC/E,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IAEzD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAyC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwC;IAClE,mEAAmE;IACnE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA+B;IAC1D;;;;wEAIoE;IACpE,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,QAAQ,CAAC,UAAU,EAAE,kBAAkB,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"definition-registry.d.ts","sourceRoot":"","sources":["../src/definition-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -23,6 +23,9 @@ export class DefinitionRegistry {
|
|
|
23
23
|
fieldMaps = new Map();
|
|
24
24
|
/** Reverse inheritance index: parent kind → direct child kinds. */
|
|
25
25
|
extendedBy = new Map();
|
|
26
|
+
/** Memoized inheritance-resolved schemas. `null` records "resolved to
|
|
27
|
+
* nothing", so a kind with no schema is not re-walked on every resource. */
|
|
28
|
+
effectiveSchemas = new Map();
|
|
26
29
|
/** DEPRECATED module identity table: identity string → canonical module name
|
|
27
30
|
* ("std/pipeline" → "pipeline"). Serves only the legacy
|
|
28
31
|
* `<namespace>/<module>#<Kind>` form of `x-telo-ref`, kept resolvable for
|
|
@@ -38,6 +41,7 @@ export class DefinitionRegistry {
|
|
|
38
41
|
// so any already-computed map recomputes against the now-larger def set; the
|
|
39
42
|
// maps rebuild lazily on first `getFieldMap` (after all defs are registered).
|
|
40
43
|
this.fieldMaps.clear();
|
|
44
|
+
this.effectiveSchemas.clear();
|
|
41
45
|
// `capability` populates extendedBy for backward-compat with the legacy pattern where
|
|
42
46
|
// a concrete definition overloaded `capability: <AbstractKind>` to mean "implements
|
|
43
47
|
// this abstract." The canonical pattern is `extends` (below). Both populate the index,
|
|
@@ -263,6 +267,43 @@ export class DefinitionRegistry {
|
|
|
263
267
|
resolve(kind) {
|
|
264
268
|
return this.defs.get(kind);
|
|
265
269
|
}
|
|
270
|
+
/**
|
|
271
|
+
* The kind's AUTHOR-FACING schema — its own merged with everything it
|
|
272
|
+
* inherits along `extends` — memoized per kind.
|
|
273
|
+
*
|
|
274
|
+
* This is what every consumer reading a kind's schema to interpret an
|
|
275
|
+
* INSTANCE must use, because that is the schema the instance was authored
|
|
276
|
+
* against and the one the kernel stamps at definition registration. Reading
|
|
277
|
+
* `resolve(kind).schema` instead sees only what the leaf declared, so an
|
|
278
|
+
* annotation on a parent — a CEL context region, a step body, an error
|
|
279
|
+
* context — is invisible on every child, silently and per consumer. Sharing
|
|
280
|
+
* one memo is what keeps those consumers from drifting apart.
|
|
281
|
+
*
|
|
282
|
+
* Lazy for the same reason `getFieldMap` is: a child registered before its
|
|
283
|
+
* parent still sees the inherited half once both are present.
|
|
284
|
+
*/
|
|
285
|
+
effectiveSchema(kind) {
|
|
286
|
+
const cached = this.effectiveSchemas.get(kind);
|
|
287
|
+
if (cached !== undefined)
|
|
288
|
+
return cached ?? undefined;
|
|
289
|
+
const def = this.defs.get(kind);
|
|
290
|
+
if (!def)
|
|
291
|
+
return undefined;
|
|
292
|
+
const schema = effectiveAuthorSchema(def, (k) => this.resolve(k));
|
|
293
|
+
this.effectiveSchemas.set(kind, schema ?? null);
|
|
294
|
+
return schema;
|
|
295
|
+
}
|
|
296
|
+
/** The effective schema for a definition, by the kind it is registered under.
|
|
297
|
+
* Falls back to the definition's own schema when it is not in this registry
|
|
298
|
+
* (a synthesized or not-yet-registered kind), which is the pre-inheritance
|
|
299
|
+
* behaviour and the safe direction: it types less, never wrongly. */
|
|
300
|
+
effectiveSchemaOf(definition) {
|
|
301
|
+
if (!definition)
|
|
302
|
+
return undefined;
|
|
303
|
+
const { name, module: mod } = definition.metadata;
|
|
304
|
+
const resolved = this.effectiveSchema(mod ? `${mod}.${name}` : name);
|
|
305
|
+
return resolved ?? definition.schema;
|
|
306
|
+
}
|
|
266
307
|
/** Returns the reference field map for the given kind, computed lazily from the
|
|
267
308
|
* kind's AUTHOR-FACING (inheritance-resolved) schema and memoized. Lazy so a
|
|
268
309
|
* child registered before its parent still sees the parent's inherited ref
|
|
@@ -368,6 +409,41 @@ export class DefinitionRegistry {
|
|
|
368
409
|
return null;
|
|
369
410
|
return buildFieldMapAtPath(subSchema, fieldPath);
|
|
370
411
|
}
|
|
412
|
+
/** The kinds a definition descends from DIRECTLY — the same two edges
|
|
413
|
+
* `register` feeds into `extendedBy`, read the other way.
|
|
414
|
+
*
|
|
415
|
+
* Both spellings, because either can carry the edge that satisfies a slot:
|
|
416
|
+
* `capability:` is the legacy implements-this form, `extends:` the canonical
|
|
417
|
+
* one. A consumer walking these by hand would be a second reading of what an
|
|
418
|
+
* inheritance edge IS, and the downward index and the upward walk have to
|
|
419
|
+
* agree about that forever. */
|
|
420
|
+
parentsOf(kind) {
|
|
421
|
+
const def = this.defs.get(kind);
|
|
422
|
+
if (!def)
|
|
423
|
+
return [];
|
|
424
|
+
return [def.capability, def.extends].filter((parent) => typeof parent === "string" && parent.length > 0);
|
|
425
|
+
}
|
|
426
|
+
/** Whether every kind this one descends from is registered.
|
|
427
|
+
*
|
|
428
|
+
* When it is, what the kind implements is FULLY KNOWN: a target it does not
|
|
429
|
+
* reach is one it genuinely does not implement, so a mismatch is a verdict.
|
|
430
|
+
* When a hop is missing — an unimported abstract, an alias the declaring file
|
|
431
|
+
* could not resolve — a mismatch cannot be told from a missing dependency,
|
|
432
|
+
* which is the partial context every reference check stays lenient in. */
|
|
433
|
+
ancestryResolved(kind) {
|
|
434
|
+
const queue = [kind];
|
|
435
|
+
const seen = new Set();
|
|
436
|
+
while (queue.length > 0) {
|
|
437
|
+
const current = queue.shift();
|
|
438
|
+
if (seen.has(current))
|
|
439
|
+
continue;
|
|
440
|
+
seen.add(current);
|
|
441
|
+
if (!this.defs.has(current))
|
|
442
|
+
return false;
|
|
443
|
+
queue.push(...this.parentsOf(current));
|
|
444
|
+
}
|
|
445
|
+
return true;
|
|
446
|
+
}
|
|
371
447
|
/** Returns all definitions that transitively extend the given abstract kind.
|
|
372
448
|
* Follows the capability chain to any depth (equivalent to instanceof in OOP).
|
|
373
449
|
* Definitions are included regardless of registration order. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"dependency-graph.d.ts","sourceRoot":"","sources":["../src/dependency-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B;kDAC8C;IAC9C,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;IACpC;oFACgF;IAChF,KAAK,CAAC,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CACrC;AAID;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,CAAC,EAAE,aAAa,EACvB,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,GAC3C,eAAe,CA2DjB;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,MAAM,CAOtE"}
|
package/dist/dependency-graph.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildCallGraph, projectToPairs } from "./call-graph.js";
|
|
2
|
+
import { readSuppliedResources } from "./resource-input.js";
|
|
2
3
|
const nodeKey = (kind, name) => `${kind}\0${name}`;
|
|
3
4
|
/**
|
|
4
5
|
* Builds a directed acyclic graph (DAG) of boot-time resource dependencies and
|
|
@@ -45,6 +46,7 @@ export function buildDependencyGraph(resources, registry, aliases, aliasesByModu
|
|
|
45
46
|
for (const key of nodes.keys())
|
|
46
47
|
if (!deps.has(key))
|
|
47
48
|
deps.set(key, new Set());
|
|
49
|
+
addResourceInputEdges(resources, nodes, deps);
|
|
48
50
|
// --- Kahn's topological sort ---
|
|
49
51
|
// in-degree[X] = number of X's dependencies (size of deps[X])
|
|
50
52
|
// reverse[dep] = set of nodes that depend on dep (for degree decrement)
|
|
@@ -130,3 +132,66 @@ function findCycle(nodes, deps) {
|
|
|
130
132
|
}
|
|
131
133
|
return [];
|
|
132
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Boot-order edges for the one thing a `Telo.Import` does hold: the instances it
|
|
137
|
+
* hands DOWN to its target library's declared `resources:` inputs.
|
|
138
|
+
*
|
|
139
|
+
* An import is otherwise module wiring rather than a runtime node — it is in
|
|
140
|
+
* `DEPENDENCY_GRAPH_SKIP_KINDS` and the call graph gives it no node at all — but
|
|
141
|
+
* a borrowed instance must exist before the import initializes, and a cycle
|
|
142
|
+
* through one is a cycle like any other. The edges are added here rather than
|
|
143
|
+
* read off the reference field map because the accepted KIND at this slot is
|
|
144
|
+
* declared by the TARGET library, not by the `Telo.Import` schema, so there is
|
|
145
|
+
* no `x-telo-ref` for the map to read; the constraint itself is checked by
|
|
146
|
+
* `validate-resource-inputs`.
|
|
147
|
+
*/
|
|
148
|
+
function addResourceInputEdges(resources, nodes, deps) {
|
|
149
|
+
// Every import that supplies inputs becomes a node FIRST, so a cross-module
|
|
150
|
+
// reference below can resolve to the import that exports its target.
|
|
151
|
+
const imports = [];
|
|
152
|
+
for (const m of resources) {
|
|
153
|
+
if (m.kind !== "Telo.Import")
|
|
154
|
+
continue;
|
|
155
|
+
const alias = m.metadata?.name;
|
|
156
|
+
const supplied = readSuppliedResources(m);
|
|
157
|
+
if (!alias || Object.keys(supplied).length === 0)
|
|
158
|
+
continue;
|
|
159
|
+
const key = nodeKey(m.kind, alias);
|
|
160
|
+
nodes.set(key, { kind: m.kind, name: alias });
|
|
161
|
+
if (!deps.has(key))
|
|
162
|
+
deps.set(key, new Set());
|
|
163
|
+
imports.push({ key, supplied });
|
|
164
|
+
}
|
|
165
|
+
if (imports.length === 0)
|
|
166
|
+
return;
|
|
167
|
+
const byName = new Map();
|
|
168
|
+
for (const [key, node] of nodes)
|
|
169
|
+
byName.set(node.name, key);
|
|
170
|
+
// An import is keyed by its alias, and that is also how a cross-module
|
|
171
|
+
// reference names it — index those so one resolves.
|
|
172
|
+
for (const m of resources) {
|
|
173
|
+
if (m.kind !== "Telo.Import")
|
|
174
|
+
continue;
|
|
175
|
+
const alias = m.metadata?.name;
|
|
176
|
+
const key = alias ? nodeKey(m.kind, alias) : undefined;
|
|
177
|
+
if (alias && key && nodes.has(key))
|
|
178
|
+
byName.set(alias, key);
|
|
179
|
+
}
|
|
180
|
+
for (const { key, supplied } of imports) {
|
|
181
|
+
const set = deps.get(key);
|
|
182
|
+
for (const value of Object.values(supplied)) {
|
|
183
|
+
const ref = value;
|
|
184
|
+
// A CROSS-MODULE reference (`!ref Other.db`) names an instance exported by
|
|
185
|
+
// another import, never a local resource. Looking it up in the local name
|
|
186
|
+
// map would find an unrelated resource of the same name — a wrong edge,
|
|
187
|
+
// and possibly a phantom cycle — or nothing at all. What it depends on is
|
|
188
|
+
// the IMPORT that exports it, which is the projection
|
|
189
|
+
// `localDependencyNames` already makes at runtime.
|
|
190
|
+
const alias = typeof ref?.alias === "string" ? ref.alias : undefined;
|
|
191
|
+
const targetName = alias && alias !== "Self" ? alias : typeof ref?.name === "string" ? ref.name : undefined;
|
|
192
|
+
const target = targetName ? byName.get(targetName) : undefined;
|
|
193
|
+
if (target && target !== key)
|
|
194
|
+
set.add(target);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|