@telorun/analyzer 0.21.0 → 0.23.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 +9 -17
- package/dist/alias-resolver.d.ts +7 -0
- package/dist/alias-resolver.d.ts.map +1 -1
- package/dist/alias-resolver.js +14 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +59 -15
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +10 -9
- package/dist/cel-environment.d.ts +8 -0
- package/dist/cel-environment.d.ts.map +1 -1
- package/dist/cel-environment.js +48 -0
- package/dist/flatten-for-analyzer.d.ts +52 -0
- package/dist/flatten-for-analyzer.d.ts.map +1 -1
- package/dist/flatten-for-analyzer.js +192 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/resolve-ref-sentinels.d.ts +22 -7
- package/dist/resolve-ref-sentinels.d.ts.map +1 -1
- package/dist/resolve-ref-sentinels.js +46 -136
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +28 -8
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +5 -0
- package/dist/validate-reference-forms.d.ts +28 -0
- package/dist/validate-reference-forms.d.ts.map +1 -0
- package/dist/validate-reference-forms.js +91 -0
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +4 -37
- package/package.json +2 -2
- package/src/alias-resolver.ts +14 -0
- package/src/analyzer.ts +69 -19
- package/src/builtins.ts +10 -9
- package/src/cel-environment.ts +57 -0
- package/src/flatten-for-analyzer.ts +217 -4
- package/src/index.ts +7 -0
- package/src/resolve-ref-sentinels.ts +39 -133
- package/src/schema-compat.ts +27 -8
- package/src/validate-cel-context.ts +5 -0
- package/src/validate-reference-forms.ts +110 -0
- package/src/validate-references.ts +4 -39
package/README.md
CHANGED
|
@@ -55,11 +55,11 @@ metadata:
|
|
|
55
55
|
A complete feedback collection REST API — no code, pure YAML.
|
|
56
56
|
Persists entries to SQLite and serves them over HTTP.
|
|
57
57
|
imports:
|
|
58
|
-
Http: std/http-server@0.
|
|
59
|
-
Sql: std/sql@0.
|
|
58
|
+
Http: std/http-server@0.11.0
|
|
59
|
+
Sql: std/sql@0.9.0
|
|
60
60
|
targets:
|
|
61
|
-
- Migrations
|
|
62
|
-
- Server
|
|
61
|
+
- !ref Migrations
|
|
62
|
+
- !ref Server
|
|
63
63
|
---
|
|
64
64
|
# SQLite database — swap driver/host/database for PostgreSQL with zero YAML changes
|
|
65
65
|
kind: Sql.Connection
|
|
@@ -72,9 +72,7 @@ file: ./tmp/feedback.db
|
|
|
72
72
|
kind: Sql.Migrations
|
|
73
73
|
metadata:
|
|
74
74
|
name: Migrations
|
|
75
|
-
connection:
|
|
76
|
-
kind: Sql.Connection
|
|
77
|
-
name: Db
|
|
75
|
+
connection: !ref Db
|
|
78
76
|
---
|
|
79
77
|
kind: Sql.Migration
|
|
80
78
|
metadata:
|
|
@@ -101,7 +99,7 @@ openapi:
|
|
|
101
99
|
version: 1.0.0
|
|
102
100
|
mounts:
|
|
103
101
|
- path: /v1
|
|
104
|
-
|
|
102
|
+
mount: !ref FeedbackRoutes
|
|
105
103
|
---
|
|
106
104
|
kind: Http.Api
|
|
107
105
|
metadata:
|
|
@@ -123,9 +121,7 @@ routes:
|
|
|
123
121
|
required: [ text ]
|
|
124
122
|
handler:
|
|
125
123
|
kind: Sql.Exec
|
|
126
|
-
connection:
|
|
127
|
-
kind: Sql.Connection
|
|
128
|
-
name: Db
|
|
124
|
+
connection: !ref Db
|
|
129
125
|
inputs:
|
|
130
126
|
sql: "INSERT INTO feedback (text, source, score) VALUES (?, ?, ?)"
|
|
131
127
|
bindings:
|
|
@@ -146,9 +142,7 @@ routes:
|
|
|
146
142
|
method: GET
|
|
147
143
|
handler:
|
|
148
144
|
kind: Sql.Select
|
|
149
|
-
connection:
|
|
150
|
-
kind: Sql.Connection
|
|
151
|
-
name: Db
|
|
145
|
+
connection: !ref Db
|
|
152
146
|
from: feedback
|
|
153
147
|
columns: [ id, text, source, score, created_at ]
|
|
154
148
|
orderBy:
|
|
@@ -172,9 +166,7 @@ routes:
|
|
|
172
166
|
required: [ id ]
|
|
173
167
|
handler:
|
|
174
168
|
kind: Sql.Select
|
|
175
|
-
connection:
|
|
176
|
-
kind: Sql.Connection
|
|
177
|
-
name: Db
|
|
169
|
+
connection: !ref Db
|
|
178
170
|
from: feedback
|
|
179
171
|
columns: [ id, text, source, score, created_at ]
|
|
180
172
|
where:
|
package/dist/alias-resolver.d.ts
CHANGED
|
@@ -3,7 +3,14 @@
|
|
|
3
3
|
export declare class AliasResolver {
|
|
4
4
|
private readonly importAliases;
|
|
5
5
|
private readonly importedKinds;
|
|
6
|
+
/** `${alias}.${suffix}` → canonical `<owningModule>.<Kind>` for kinds an import
|
|
7
|
+
* transitively RE-EXPORTS (`exports.kinds: [Alias.Kind]`), which don't live in the
|
|
8
|
+
* import's own module. Resolved before the normal `<module>.<suffix>` construction. */
|
|
9
|
+
private readonly reExportedKinds;
|
|
6
10
|
registerImport(alias: string, targetModule: string, exportedKinds: string[]): void;
|
|
11
|
+
/** Register that `<alias>.<suffix>` re-exports the kind canonically named `canonicalKind`
|
|
12
|
+
* (owned by a module the alias's target imports, possibly several hops away). */
|
|
13
|
+
registerKindReExport(alias: string, suffix: string, canonicalKind: string): void;
|
|
7
14
|
/** Real module name an alias points at (e.g. "Console" → "console"), or undefined.
|
|
8
15
|
* Used to resolve an alias-qualified instance reference "Console.writeLine" to the
|
|
9
16
|
* forwarded resource declared in that module. The `exports.resources` gate is enforced
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alias-resolver.d.ts","sourceRoot":"","sources":["../src/alias-resolver.ts"],"names":[],"mappings":"AAAA;gFACgF;AAChF,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;
|
|
1
|
+
{"version":3,"file":"alias-resolver.d.ts","sourceRoot":"","sources":["../src/alias-resolver.ts"],"names":[],"mappings":"AAAA;gFACgF;AAChF,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA6B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAChE;;4FAEwF;IACxF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAA6B;IAE7D,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,IAAI;IAOlF;sFACkF;IAClF,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI;IAIhF;;;;qDAIiD;IACjD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAIjD,sFAAsF;IACtF,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAmB7C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;IAIhC,YAAY,IAAI,MAAM,EAAE;IAIxB;;qEAEiE;IACjE,UAAU,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,EAAE;CAO3C"}
|
package/dist/alias-resolver.js
CHANGED
|
@@ -3,12 +3,21 @@
|
|
|
3
3
|
export class AliasResolver {
|
|
4
4
|
importAliases = new Map();
|
|
5
5
|
importedKinds = new Map();
|
|
6
|
+
/** `${alias}.${suffix}` → canonical `<owningModule>.<Kind>` for kinds an import
|
|
7
|
+
* transitively RE-EXPORTS (`exports.kinds: [Alias.Kind]`), which don't live in the
|
|
8
|
+
* import's own module. Resolved before the normal `<module>.<suffix>` construction. */
|
|
9
|
+
reExportedKinds = new Map();
|
|
6
10
|
registerImport(alias, targetModule, exportedKinds) {
|
|
7
11
|
this.importAliases.set(alias, targetModule);
|
|
8
12
|
if (exportedKinds.length > 0) {
|
|
9
13
|
this.importedKinds.set(alias, new Set(exportedKinds));
|
|
10
14
|
}
|
|
11
15
|
}
|
|
16
|
+
/** Register that `<alias>.<suffix>` re-exports the kind canonically named `canonicalKind`
|
|
17
|
+
* (owned by a module the alias's target imports, possibly several hops away). */
|
|
18
|
+
registerKindReExport(alias, suffix, canonicalKind) {
|
|
19
|
+
this.reExportedKinds.set(`${alias}.${suffix}`, canonicalKind);
|
|
20
|
+
}
|
|
12
21
|
/** Real module name an alias points at (e.g. "Console" → "console"), or undefined.
|
|
13
22
|
* Used to resolve an alias-qualified instance reference "Console.writeLine" to the
|
|
14
23
|
* forwarded resource declared in that module. The `exports.resources` gate is enforced
|
|
@@ -27,6 +36,11 @@ export class AliasResolver {
|
|
|
27
36
|
return undefined;
|
|
28
37
|
const prefix = kind.slice(0, dot);
|
|
29
38
|
const suffix = kind.slice(dot + 1);
|
|
39
|
+
// Re-export takes precedence: a re-exported kind resolves to its true owning module,
|
|
40
|
+
// not `${prefix-target}.${suffix}` (and bypasses the gate — it's explicitly re-exported).
|
|
41
|
+
const reExported = this.reExportedKinds.get(`${prefix}.${suffix}`);
|
|
42
|
+
if (reExported)
|
|
43
|
+
return reExported;
|
|
30
44
|
const realModule = this.importAliases.get(prefix);
|
|
31
45
|
if (!realModule)
|
|
32
46
|
return undefined;
|
package/dist/analyzer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAiB9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA4hB/F,MAAM,WAAW,qBAAqB;IACpC,WAAW,CAAC,EAAE,WAAW,CAAC;CAC3B;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,OAAO,GAAE,qBAA0B;IAI/C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IA+sBvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CACP,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,EAI1B,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,GACtC,gBAAgB,EAAE;IAerB,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,gBAAgB,GACzB;QAAE,WAAW,EAAE,kBAAkB,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAsB5F"}
|
package/dist/analyzer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { defaultRegistry, isTaggedSentinel } from "@telorun/templating";
|
|
2
2
|
import { AliasResolver } from "./alias-resolver.js";
|
|
3
|
-
import { buildCelEnvironment, buildTypedCelEnvironment, } from "./cel-environment.js";
|
|
3
|
+
import { buildCelEnvironment, buildImportInputCelEnvironment, buildTypedCelEnvironment, } from "./cel-environment.js";
|
|
4
4
|
import { DefinitionRegistry } from "./definition-registry.js";
|
|
5
5
|
import { buildDependencyGraph, formatCycle } from "./dependency-graph.js";
|
|
6
6
|
import { buildKernelGlobalsSchema, mergeKernelGlobalsIntoContext } from "./kernel-globals.js";
|
|
@@ -18,6 +18,7 @@ import { validateExtends } from "./validate-extends.js";
|
|
|
18
18
|
import { validateNestedInlineResources } from "./validate-nested-inline.js";
|
|
19
19
|
import { validateProviderCoherence } from "./validate-provider-coherence.js";
|
|
20
20
|
import { validateReferences } from "./validate-references.js";
|
|
21
|
+
import { validateReferenceForms } from "./validate-reference-forms.js";
|
|
21
22
|
import { validateUnusedDeclarations } from "./validate-unused-declarations.js";
|
|
22
23
|
import { validateThrowsCoverage } from "./validate-throws-coverage.js";
|
|
23
24
|
const SELF_PREFIX = "Self.";
|
|
@@ -578,17 +579,18 @@ export class StaticAnalyzer {
|
|
|
578
579
|
if (resolvedModuleName) {
|
|
579
580
|
defs.registerModuleIdentity(resolvedNamespace ?? null, resolvedModuleName);
|
|
580
581
|
}
|
|
582
|
+
// `metadata.reExportedKinds` (stamped by flattenForAnalyzer / the editor projection)
|
|
583
|
+
// maps an exported suffix to the true owning module's canonical kind for kinds this
|
|
584
|
+
// import transitively re-exports (`exports.kinds: [Alias.Kind]`).
|
|
585
|
+
const reExportedKinds = (m.metadata?.reExportedKinds ?? {});
|
|
581
586
|
// Alias registration is scoped: consumer imports vs. imported-library imports.
|
|
582
|
-
|
|
583
|
-
aliases
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
aliasesByModule.set(ownModule, libResolver);
|
|
590
|
-
}
|
|
591
|
-
libResolver.registerImport(alias, targetModule, exportedKinds);
|
|
587
|
+
const resolver = !ownModule || rootModules.has(ownModule)
|
|
588
|
+
? aliases
|
|
589
|
+
: (aliasesByModule.get(ownModule) ??
|
|
590
|
+
aliasesByModule.set(ownModule, new AliasResolver()).get(ownModule));
|
|
591
|
+
resolver.registerImport(alias, targetModule, exportedKinds);
|
|
592
|
+
for (const [suffix, canonical] of Object.entries(reExportedKinds)) {
|
|
593
|
+
resolver.registerKindReExport(alias, suffix, canonical);
|
|
592
594
|
}
|
|
593
595
|
}
|
|
594
596
|
}
|
|
@@ -646,13 +648,21 @@ export class StaticAnalyzer {
|
|
|
646
648
|
: def;
|
|
647
649
|
defs.register(normalized);
|
|
648
650
|
}
|
|
651
|
+
// Reference-form validation — enforce `!ref` as the only reference shape.
|
|
652
|
+
// Runs on the RAW manifests, BEFORE inline extraction and sentinel
|
|
653
|
+
// resolution, while an author-written `{kind, name}` is still
|
|
654
|
+
// distinguishable from the resolver's own substitution (after Phase 2/2.5
|
|
655
|
+
// they are the same object).
|
|
656
|
+
if (!options?.skipValidation) {
|
|
657
|
+
diagnostics.push(...validateReferenceForms(manifests, defs, aliases, aliasesByModule));
|
|
658
|
+
}
|
|
649
659
|
// Phase 2: extract inline resources from x-telo-ref slots into first-class manifests
|
|
650
660
|
const allManifests = normalizeInlineResources(manifests, defs, aliases, aliasesByModule);
|
|
651
661
|
// Phase 2.5: resolve `!ref <name>` sentinels at every ref slot to canonical
|
|
652
662
|
// {kind, name} objects so downstream phases (validation, dependency graph,
|
|
653
663
|
// kernel controllers) see a uniform shape. Runs after normalize so both
|
|
654
664
|
// original and inline-extracted manifests have their sentinels resolved.
|
|
655
|
-
resolveRefSentinels(allManifests,
|
|
665
|
+
resolveRefSentinels(allManifests, aliases, aliasesByModule);
|
|
656
666
|
// Trusted-input fast path: when the caller has already attested that
|
|
657
667
|
// this exact manifest set passes analysis (e.g. via the kernel's
|
|
658
668
|
// hash-stamped `.validated.json` cache), skip the validation walk.
|
|
@@ -726,6 +736,26 @@ export class StaticAnalyzer {
|
|
|
726
736
|
}
|
|
727
737
|
}
|
|
728
738
|
}
|
|
739
|
+
// `exports.resources` entries are plain names: `Db` (local) or `Alias.Name` (re-export),
|
|
740
|
+
// mirroring `exports.kinds`. The `!ref` tag is not accepted here — a `!ref` parses to a
|
|
741
|
+
// sentinel object that the schema's CEL/ref exemption would silently pass, so reject any
|
|
742
|
+
// non-string entry with an actionable message instead.
|
|
743
|
+
const exportsResources = m.exports?.resources;
|
|
744
|
+
if (Array.isArray(exportsResources)) {
|
|
745
|
+
for (let i = 0; i < exportsResources.length; i++) {
|
|
746
|
+
if (typeof exportsResources[i] === "string")
|
|
747
|
+
continue;
|
|
748
|
+
diagnostics.push({
|
|
749
|
+
severity: DiagnosticSeverity.Error,
|
|
750
|
+
code: "INVALID_EXPORT",
|
|
751
|
+
source: SOURCE,
|
|
752
|
+
message: `Telo.Library exports.resources[${i}]: write the exported name as a plain string — ` +
|
|
753
|
+
`'Name' to export a local instance, or 'Alias.Name' to re-export an imported one. ` +
|
|
754
|
+
`The '!ref' tag is not allowed in exports.resources.`,
|
|
755
|
+
data: { resource, filePath, path: `exports.resources.${i}` },
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
}
|
|
729
759
|
}
|
|
730
760
|
// Build typed kernel globals schema so x-telo-context chain validation
|
|
731
761
|
// recognises variables, secrets, resources, env automatically
|
|
@@ -795,8 +825,22 @@ export class StaticAnalyzer {
|
|
|
795
825
|
},
|
|
796
826
|
}
|
|
797
827
|
: definition.schema;
|
|
798
|
-
// Phase 1: CEL type checking — walk data+schema together, check env.check() return types
|
|
799
|
-
|
|
828
|
+
// Phase 1: CEL type checking — walk data+schema together, check env.check() return types.
|
|
829
|
+
// A Telo.Import's variables/secrets are a config-only contract evaluated against the
|
|
830
|
+
// IMPORTING module's scope, so type them from the owning module doc (matched by
|
|
831
|
+
// `metadata.module`) and drop `resources`/`env` so referencing them is an error. A
|
|
832
|
+
// library's own internal import is validated against that library in the library's
|
|
833
|
+
// standalone analysis; in this flattened app pass the library doc is absent, so the
|
|
834
|
+
// importer is undefined here and variables/secrets fall back to a permissive `map`
|
|
835
|
+
// (no false positives) while resources/env stay rejected.
|
|
836
|
+
const importerModule = m.kind === "Telo.Import"
|
|
837
|
+
? allManifests.find((mm) => (mm.kind === "Telo.Application" || mm.kind === "Telo.Library") &&
|
|
838
|
+
mm.metadata?.name ===
|
|
839
|
+
m.metadata?.module)
|
|
840
|
+
: undefined;
|
|
841
|
+
const baseTypedEnv = m.kind === "Telo.Import"
|
|
842
|
+
? buildImportInputCelEnvironment(this.celEnv, importerModule)
|
|
843
|
+
: buildTypedCelEnvironment(this.celEnv, m, undefined, moduleManifest);
|
|
800
844
|
const celIssues = collectCelTypeIssues(m, schema, "", definition, m, baseTypedEnv, this.celEnv, moduleManifest);
|
|
801
845
|
// Phase 2+3: AJV on substituted data — CEL fields replaced with typed placeholders
|
|
802
846
|
const ajvIssues = validateAgainstSchema(substituteCelFields(m, schema), schema);
|
|
@@ -1058,7 +1102,7 @@ export class StaticAnalyzer {
|
|
|
1058
1102
|
// Resolve !ref sentinels after normalize so both the original and
|
|
1059
1103
|
// inline-extracted manifests get their refs canonicalized to
|
|
1060
1104
|
// {kind, name} for the kernel that consumes this output.
|
|
1061
|
-
resolveRefSentinels(normalized, ctx.
|
|
1105
|
+
resolveRefSentinels(normalized, ctx.aliases, ctx.aliasesByModule, crossModuleTargets ?? []);
|
|
1062
1106
|
return normalized;
|
|
1063
1107
|
}
|
|
1064
1108
|
prepare(manifests, registry) {
|
package/dist/builtins.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"builtins.d.ts","sourceRoot":"","sources":["../src/builtins.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,eAAO,MAAM,eAAe,EAAE,kBAAkB,EAud/C,CAAC"}
|
package/dist/builtins.js
CHANGED
|
@@ -234,7 +234,7 @@ export const KERNEL_BUILTINS = [
|
|
|
234
234
|
},
|
|
235
235
|
// Gated reference: run() a Runnable/Service only when the
|
|
236
236
|
// `when` CEL guard holds. Discriminated by the `ref` key. `ref`
|
|
237
|
-
// is a
|
|
237
|
+
// is a `!ref` that resolves to the `{ kind, name }` shape below.
|
|
238
238
|
{
|
|
239
239
|
type: "object",
|
|
240
240
|
required: ["ref"],
|
|
@@ -262,12 +262,11 @@ export const KERNEL_BUILTINS = [
|
|
|
262
262
|
// with an optional `name` (for steps.<name>.result plumbing),
|
|
263
263
|
// `when` guard, and `inputs`. Discriminated by the `invoke` key.
|
|
264
264
|
// Control flow (if/while/switch/try) is not available here —
|
|
265
|
-
// reach for Run.Sequence. `invoke` is ref-only
|
|
266
|
-
// to
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
// Run.Sequence invoke steps.
|
|
265
|
+
// reach for Run.Sequence. `invoke` is ref-only: a `!ref` that
|
|
266
|
+
// resolves to the `{ kind, name }` shape below. Requiring `name`
|
|
267
|
+
// rejects an inline `{ kind }` definition (no name) at analysis
|
|
268
|
+
// instead of failing at boot with an undefined resource name. The
|
|
269
|
+
// Invocable/Runnable kind set mirrors Run.Sequence invoke steps.
|
|
271
270
|
{
|
|
272
271
|
type: "object",
|
|
273
272
|
required: ["invoke"],
|
|
@@ -454,8 +453,10 @@ export const KERNEL_BUILTINS = [
|
|
|
454
453
|
type: "object",
|
|
455
454
|
properties: {
|
|
456
455
|
kinds: { type: "array", items: { type: "string" } },
|
|
457
|
-
//
|
|
458
|
-
//
|
|
456
|
+
// An entry is a bare name (`Db`, a locally-owned export) or a dotted `Alias.Name`
|
|
457
|
+
// (re-export of the instance reached via this library's import aliased `Alias`,
|
|
458
|
+
// under the name `Name`) — mirroring `exports.kinds`. `variables` / `secrets` are
|
|
459
|
+
// reserved on the resources.<Alias> value-flow surface, so they may not be exported.
|
|
459
460
|
resources: {
|
|
460
461
|
type: "array",
|
|
461
462
|
items: { type: "string", not: { enum: ["variables", "secrets"] } },
|
|
@@ -13,4 +13,12 @@ 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
|
+
/** CEL environment for the `variables:`/`secrets:` expressions on a `Telo.Import`.
|
|
17
|
+
*
|
|
18
|
+
* Import inputs are a config-only contract: their expressions are evaluated
|
|
19
|
+
* against the IMPORTING module's `variables`/`secrets`, never the import's own
|
|
20
|
+
* values map (the bug) nor the imported child's. `resources`, `env`, and `ports`
|
|
21
|
+
* are registered as empty typed objects, so referencing them is a "No such key"
|
|
22
|
+
* error that steers authors to a typed `variables` entry. */
|
|
23
|
+
export declare function buildImportInputCelEnvironment(baseEnv: Environment, moduleManifest: ResourceManifest | undefined): Environment;
|
|
16
24
|
//# sourceMappingURL=cel-environment.d.ts.map
|
|
@@ -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;AAUrD,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,CAuEb"}
|
|
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;AAUrD,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,CAuEb;AAuBD;;;;;;8DAM8D;AAC9D,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,WAAW,EACpB,cAAc,EAAE,gBAAgB,GAAG,SAAS,GAC3C,WAAW,CAwBb"}
|
package/dist/cel-environment.js
CHANGED
|
@@ -85,3 +85,51 @@ rootModuleManifest) {
|
|
|
85
85
|
return baseEnv.clone();
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
+
/** Register a `variables`/`secrets` namespace typed from a module doc's schema map
|
|
89
|
+
* (`{ name: <schema>, … }`), falling back to dyn `map` when absent or untyped. */
|
|
90
|
+
function registerConfigNamespace(env, block, name) {
|
|
91
|
+
if (block !== null && typeof block === "object" && !Array.isArray(block)) {
|
|
92
|
+
const entries = Object.entries(block).filter(([, v]) => v !== null && typeof v === "object" && !Array.isArray(v));
|
|
93
|
+
if (entries.length > 0) {
|
|
94
|
+
const schema = {};
|
|
95
|
+
for (const [k, v] of entries)
|
|
96
|
+
schema[k] = jsonSchemaToCelType(v);
|
|
97
|
+
env.registerVariable({ name, schema });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
env.registerVariable(name, "map");
|
|
102
|
+
}
|
|
103
|
+
/** CEL environment for the `variables:`/`secrets:` expressions on a `Telo.Import`.
|
|
104
|
+
*
|
|
105
|
+
* Import inputs are a config-only contract: their expressions are evaluated
|
|
106
|
+
* against the IMPORTING module's `variables`/`secrets`, never the import's own
|
|
107
|
+
* values map (the bug) nor the imported child's. `resources`, `env`, and `ports`
|
|
108
|
+
* are registered as empty typed objects, so referencing them is a "No such key"
|
|
109
|
+
* error that steers authors to a typed `variables` entry. */
|
|
110
|
+
export function buildImportInputCelEnvironment(baseEnv, moduleManifest) {
|
|
111
|
+
const env = baseEnv.clone();
|
|
112
|
+
for (const brand of Object.keys(VALUE_BRAND_BASE)) {
|
|
113
|
+
env.registerType(brand, { fields: {} });
|
|
114
|
+
}
|
|
115
|
+
const mod = moduleManifest;
|
|
116
|
+
// Typing variables/secrets from the importer's schema can fail on a malformed
|
|
117
|
+
// schema; degrade those to permissive `map` if so — but never lose the
|
|
118
|
+
// resources/env/ports rejection registered below (the catch is scoped so a
|
|
119
|
+
// typing failure can't silently re-open the config-only contract).
|
|
120
|
+
try {
|
|
121
|
+
registerConfigNamespace(env, mod?.variables, "variables");
|
|
122
|
+
registerConfigNamespace(env, mod?.secrets, "secrets");
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
env.registerVariable("variables", "map");
|
|
126
|
+
env.registerVariable("secrets", "map");
|
|
127
|
+
}
|
|
128
|
+
// Override the base env's dyn `resources`/`env`/`ports` with empty typed objects
|
|
129
|
+
// so any access (`resources.X`, `env.X`) is a "No such key" error — these
|
|
130
|
+
// surfaces are not part of the config-only import contract.
|
|
131
|
+
for (const name of ["resources", "env", "ports"]) {
|
|
132
|
+
env.registerVariable({ name, schema: {} });
|
|
133
|
+
}
|
|
134
|
+
return env;
|
|
135
|
+
}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
2
|
import type { LoadedGraph, LoadedModule } from "./loaded-types.js";
|
|
3
|
+
/** One parsed `exports.resources` / `exports.kinds` entry. `name` is the exported
|
|
4
|
+
* instance name or kind suffix (the part after the dot, or the whole entry); `alias`
|
|
5
|
+
* (when set) is this library's own import the entry RE-EXPORTS from. */
|
|
6
|
+
export interface ParsedExportEntry {
|
|
7
|
+
name: string;
|
|
8
|
+
alias?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Parse a single dotted export entry: `Alias.Name` → `{name: "Name", alias: "Alias"}`,
|
|
11
|
+
* bare `Name` → `{name: "Name"}`. The single grammar for `exports.resources` and
|
|
12
|
+
* `exports.kinds`, shared by the kernel's import controller and the analyzer/editor so
|
|
13
|
+
* the dotted-name split can't drift. A leading dot (`.Name`) has no alias by design —
|
|
14
|
+
* the empty prefix isn't a valid alias. */
|
|
15
|
+
export declare function parseExportEntry(entry: string): ParsedExportEntry;
|
|
3
16
|
/** The import-boundary forwarding rule, shared by `flattenForAnalyzer` (the
|
|
4
17
|
* CLI / kernel loader path) and the telo-editor's workspace projection so the
|
|
5
18
|
* two cannot drift. Given one module's stamped manifests and whether that
|
|
@@ -42,6 +55,45 @@ export declare function selectModuleManifestsForAnalysis(moduleManifests: Resour
|
|
|
42
55
|
* Position metadata (`positionIndex`) is NOT stamped on manifests —
|
|
43
56
|
* callers look it up via `findPositions(graph, ...)` on the LoadedGraph. */
|
|
44
57
|
export declare function flattenForAnalyzer(graph: LoadedGraph): ResourceManifest[];
|
|
58
|
+
/** A re-export declared in a library's `exports.resources` as a dotted `Alias.Name`:
|
|
59
|
+
* module `module` re-exports the instance `name` reached through its own import
|
|
60
|
+
* aliased `alias`. */
|
|
61
|
+
export interface ReExportSpec {
|
|
62
|
+
module: string;
|
|
63
|
+
alias: string;
|
|
64
|
+
name: string;
|
|
65
|
+
}
|
|
66
|
+
/** Extract re-export specs from a library doc's `exports.resources` — the dotted `Alias.Name`
|
|
67
|
+
* entries (bare-name locals are forwarded by the BFS instead). Shared by the CLI graph path
|
|
68
|
+
* and the editor's workspace projection so the two cannot drift. */
|
|
69
|
+
export declare function reExportSpecsFromExports(moduleName: string, exportsResources: readonly unknown[] | undefined): ReExportSpec[];
|
|
70
|
+
/** Forward re-exported instances (`exports.resources: [!ref Alias.name]`) transitively so a
|
|
71
|
+
* consumer's `!ref Consumer.name` resolves in `resolveRefSentinels` (keyed by the RE-EXPORTING
|
|
72
|
+
* module). The owning instance is already forwarded under its own module; here we emit an
|
|
73
|
+
* additional copy stamped under each re-exporting module, with an already-canonical kind. A
|
|
74
|
+
* fixpoint loop forwards chains of arbitrary depth (`app → api → domain → …`): each pass can
|
|
75
|
+
* resolve a re-export whose source was emitted in a prior pass. Graph-agnostic: `aliasToModule`
|
|
76
|
+
* maps `(module, alias)` to the imported module's name. Mutates `result` in place. */
|
|
77
|
+
export declare function forwardReExportManifests(result: ResourceManifest[], specs: readonly ReExportSpec[], aliasToModule: (module: string, alias: string) => string | undefined): void;
|
|
78
|
+
/** Resolve every library's `exports.kinds` to a per-module map `suffix → canonical
|
|
79
|
+
* <owningModule>.<Kind>`, following re-exports (`Alias.Kind`) transitively via a fixpoint.
|
|
80
|
+
* `modules` lists each library's name + its raw `exports.kinds`; `aliasToModule(module, alias)`
|
|
81
|
+
* maps one of that module's import aliases to the imported module's name. Graph-agnostic —
|
|
82
|
+
* shared by the CLI graph path and the editor's workspace projection. */
|
|
83
|
+
export declare function resolveExportedKinds(modules: ReadonlyArray<{
|
|
84
|
+
module: string;
|
|
85
|
+
exportsKinds: readonly string[];
|
|
86
|
+
}>, aliasToModule: (module: string, alias: string) => string | undefined): Map<string, Map<string, string>>;
|
|
87
|
+
/** Stamp `metadata.reExportedKinds` (suffix → canonical kind) onto every `Telo.Import` whose
|
|
88
|
+
* target re-exports kinds, so the analyzer can register the re-export mappings. Only entries
|
|
89
|
+
* that point at a module OTHER than the import's own target are stamped (genuine re-exports;
|
|
90
|
+
* a locally-defined kind resolves through the normal alias path). Stamped on `metadata` (which
|
|
91
|
+
* permits additional properties, like `resolvedModuleName`) since the `Telo.Import` schema
|
|
92
|
+
* forbids extra top-level fields. Shared by both paths. */
|
|
93
|
+
export declare function stampReExportedKinds(imports: ReadonlyArray<{
|
|
94
|
+
manifest: ResourceManifest;
|
|
95
|
+
targetModule: string;
|
|
96
|
+
}>, exportedKinds: Map<string, Map<string, string>>): void;
|
|
45
97
|
/** Project a LoadedModule (owner + partials) to a flat ResourceManifest[]
|
|
46
98
|
* with `metadata.module` stamped on non-module docs. The kernel's runtime
|
|
47
99
|
* entry load uses this to convert a `Loader.loadModule` result into the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flatten-for-analyzer.d.ts","sourceRoot":"","sources":["../src/flatten-for-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"flatten-for-analyzer.d.ts","sourceRoot":"","sources":["../src/flatten-for-analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAInE;;yEAEyE;AACzE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;4CAI4C;AAC5C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAGjE;AAED;;;;;;;;;;;;;;;;;;;2CAmB2C;AAC3C,wBAAgB,gCAAgC,CAC9C,eAAe,EAAE,gBAAgB,EAAE,EACnC,MAAM,EAAE,OAAO,GACd,gBAAgB,EAAE,CA6BpB;AAED;;;;;;;;;;;;;;;;;;;6EAmB6E;AAC7E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,gBAAgB,EAAE,CAiDzE;AAED;;uBAEuB;AACvB,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;qEAEqE;AACrE,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,GAC/C,YAAY,EAAE,CAShB;AAED;;;;;;uFAMuF;AACvF,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,EAAE,EAC1B,KAAK,EAAE,SAAS,YAAY,EAAE,EAC9B,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GACnE,IAAI,CAgDN;AAED;;;;0EAI0E;AAC1E,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC,EAC3E,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,GACnE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CA8BlC;AAED;;;;;4DAK4D;AAC5D,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,CAAC;IAAE,QAAQ,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5E,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAC9C,IAAI,CAWN;AAwCD;;;;;6BAK6B;AAC7B,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,YAAY,GAAG,gBAAgB,EAAE,CAEzE"}
|