@telorun/analyzer 0.11.0 → 0.12.1
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/LICENSE +2 -2
- package/README.md +3 -3
- package/dist/analysis-registry.d.ts +7 -0
- package/dist/analysis-registry.d.ts.map +1 -1
- package/dist/analysis-registry.js +38 -0
- package/dist/analyzer.d.ts +15 -0
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +181 -11
- package/dist/builtins.d.ts.map +1 -1
- package/dist/builtins.js +58 -1
- package/dist/definition-registry.d.ts +12 -1
- package/dist/definition-registry.d.ts.map +1 -1
- package/dist/definition-registry.js +36 -1
- package/dist/dependency-graph.d.ts.map +1 -1
- package/dist/dependency-graph.js +27 -13
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/kernel-globals.d.ts.map +1 -1
- package/dist/kernel-globals.js +9 -11
- package/dist/manifest-loader.d.ts +23 -1
- package/dist/manifest-loader.d.ts.map +1 -1
- package/dist/manifest-loader.js +66 -3
- package/dist/normalize-inline-resources.d.ts.map +1 -1
- package/dist/normalize-inline-resources.js +26 -14
- package/dist/position-metadata.d.ts +11 -2
- package/dist/position-metadata.d.ts.map +1 -1
- package/dist/position-metadata.js +18 -3
- package/dist/precompile.d.ts.map +1 -1
- package/dist/precompile.js +9 -1
- package/dist/reference-field-map.d.ts +22 -4
- package/dist/reference-field-map.d.ts.map +1 -1
- package/dist/reference-field-map.js +94 -26
- package/dist/residual-schema.d.ts +23 -0
- package/dist/residual-schema.d.ts.map +1 -0
- package/dist/residual-schema.js +45 -0
- package/dist/resolve-ref-sentinels.d.ts +27 -0
- package/dist/resolve-ref-sentinels.d.ts.map +1 -0
- package/dist/resolve-ref-sentinels.js +114 -0
- package/dist/rewrite-synthetic-origins.d.ts +10 -0
- package/dist/rewrite-synthetic-origins.d.ts.map +1 -0
- package/dist/rewrite-synthetic-origins.js +55 -0
- package/dist/schema-compat.d.ts +11 -1
- package/dist/schema-compat.d.ts.map +1 -1
- package/dist/schema-compat.js +25 -4
- package/dist/system-kinds.d.ts +25 -0
- package/dist/system-kinds.d.ts.map +1 -0
- package/dist/system-kinds.js +34 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validate-cel-context.d.ts +5 -0
- package/dist/validate-cel-context.d.ts.map +1 -1
- package/dist/validate-cel-context.js +27 -15
- package/dist/validate-nested-inline.d.ts +30 -0
- package/dist/validate-nested-inline.d.ts.map +1 -0
- package/dist/validate-nested-inline.js +129 -0
- package/dist/validate-provider-coherence.d.ts +23 -0
- package/dist/validate-provider-coherence.d.ts.map +1 -0
- package/dist/validate-provider-coherence.js +148 -0
- package/dist/validate-references.d.ts.map +1 -1
- package/dist/validate-references.js +141 -36
- package/dist/with-synthetic-positions.d.ts +28 -0
- package/dist/with-synthetic-positions.d.ts.map +1 -0
- package/dist/with-synthetic-positions.js +45 -0
- package/package.json +7 -4
- package/src/analysis-registry.ts +37 -0
- package/src/analyzer.ts +190 -13
- package/src/builtins.ts +58 -1
- package/src/definition-registry.ts +35 -1
- package/src/dependency-graph.ts +27 -14
- package/src/index.ts +2 -0
- package/src/kernel-globals.ts +9 -11
- package/src/manifest-loader.ts +69 -4
- package/src/normalize-inline-resources.ts +48 -13
- package/src/position-metadata.ts +18 -3
- package/src/precompile.ts +8 -1
- package/src/reference-field-map.ts +130 -25
- package/src/residual-schema.ts +49 -0
- package/src/resolve-ref-sentinels.ts +127 -0
- package/src/rewrite-synthetic-origins.ts +75 -0
- package/src/schema-compat.ts +25 -4
- package/src/system-kinds.ts +37 -0
- package/src/types.ts +12 -0
- package/src/validate-cel-context.ts +28 -15
- package/src/validate-nested-inline.ts +158 -0
- package/src/validate-provider-coherence.ts +166 -0
- package/src/validate-references.ts +138 -35
- package/src/with-synthetic-positions.ts +48 -0
package/dist/types.d.ts
CHANGED
|
@@ -73,6 +73,18 @@ export interface LoaderInitOptions {
|
|
|
73
73
|
}
|
|
74
74
|
export interface AnalysisOptions {
|
|
75
75
|
strictContexts?: boolean;
|
|
76
|
+
/** When true, `analyze()` runs the state-mutating setup (module identity /
|
|
77
|
+
* alias / definition registration plus `normalizeInlineResources`) but
|
|
78
|
+
* skips every diagnostic-producing pass — per-resource validation, the
|
|
79
|
+
* Library `env:` check, `validateExtends`, `validateProviderCoherence`,
|
|
80
|
+
* and `validateThrowsCoverage`. Used by the kernel when a previous load
|
|
81
|
+
* has already stamped the manifest set as valid (by content hash), so
|
|
82
|
+
* the registry still gets populated without paying the validation walk
|
|
83
|
+
* on every cold start. The caller takes responsibility for the
|
|
84
|
+
* correctness guarantee — pass this only when something durable
|
|
85
|
+
* (on-disk stamp) attests that the manifests passed a real analyze
|
|
86
|
+
* pass at the same analyzer / kernel version. */
|
|
87
|
+
skipValidation?: boolean;
|
|
76
88
|
}
|
|
77
89
|
/** Pre-seeded state for incremental analysis. Passed to StaticAnalyzer.analyze() so it does
|
|
78
90
|
* not rebuild from scratch on every call. The provided instances are mutated — new definitions
|
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;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;IAChC,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,yDAAyD;IACzD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;6FACyF;IACzF,WAAW,CAAC,EAAE,OAAO,sBAAsB,EAAE,WAAW,CAAC;CAC1D;AAED,MAAM,WAAW,eAAe;IAC9B,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"}
|
|
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;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;IAChC,qDAAqD;IACrD,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,yDAAyD;IACzD,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;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"}
|
|
@@ -63,6 +63,11 @@ export declare function pathMatchesScope(exprPath: string, scope: string): boole
|
|
|
63
63
|
* `manifestRoot.provide.kind` as a kind name, looks up the kind's Telo.Definition,
|
|
64
64
|
* and returns the `outputType` schema.
|
|
65
65
|
*
|
|
66
|
+
* Accepts either a single string or an array of strings. With an array, paths
|
|
67
|
+
* are tried in order and the first one that resolves to a usable schema wins —
|
|
68
|
+
* used by `result:` to find its dispatch target under whichever entry-point
|
|
69
|
+
* field (`provide:` or `invoke:`) the definition declares.
|
|
70
|
+
*
|
|
66
71
|
* - `x-telo-context-ref-from`: existing form — reads `{kind, name}` object from
|
|
67
72
|
* `manifestItem.<path>`, looks up the named manifest, returns its `<subpath>` field.
|
|
68
73
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEtF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAClC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA6BjC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBzE;AAED
|
|
1
|
+
{"version":3,"file":"validate-cel-context.d.ts","sourceRoot":"","sources":["../src/validate-cel-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEtF,MAAM,WAAW,kBAAkB;IACjC;mEAC+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACnC;;kDAE8C;IAC9C,IAAI,CAAC,EAAE;QACL,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CAAC;KACxD,CAAC;IACF,OAAO,CAAC,EAAE;QACR,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KAC/C,CAAC;IACF,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAClC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,SAAS,CA6BjC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAoBzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjC,IAAI,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,GAChD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAqGrB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAQrB"}
|
|
@@ -94,6 +94,11 @@ export function pathMatchesScope(exprPath, scope) {
|
|
|
94
94
|
* `manifestRoot.provide.kind` as a kind name, looks up the kind's Telo.Definition,
|
|
95
95
|
* and returns the `outputType` schema.
|
|
96
96
|
*
|
|
97
|
+
* Accepts either a single string or an array of strings. With an array, paths
|
|
98
|
+
* are tried in order and the first one that resolves to a usable schema wins —
|
|
99
|
+
* used by `result:` to find its dispatch target under whichever entry-point
|
|
100
|
+
* field (`provide:` or `invoke:`) the definition declares.
|
|
101
|
+
*
|
|
97
102
|
* - `x-telo-context-ref-from`: existing form — reads `{kind, name}` object from
|
|
98
103
|
* `manifestItem.<path>`, looks up the named manifest, returns its `<subpath>` field.
|
|
99
104
|
*
|
|
@@ -122,30 +127,37 @@ export function resolveContextAnnotations(schema, manifestItem, opts) {
|
|
|
122
127
|
};
|
|
123
128
|
}
|
|
124
129
|
const fromRoot = schema["x-telo-context-from-root"];
|
|
125
|
-
const
|
|
126
|
-
|
|
130
|
+
const fromRefKindRaw = schema["x-telo-context-from-ref-kind"];
|
|
131
|
+
const fromRefKinds = fromRefKindRaw == null
|
|
132
|
+
? []
|
|
133
|
+
: Array.isArray(fromRefKindRaw)
|
|
134
|
+
? fromRefKindRaw
|
|
135
|
+
: [fromRefKindRaw];
|
|
136
|
+
if (fromRoot || fromRefKinds.length > 0) {
|
|
127
137
|
if (fromRoot) {
|
|
128
138
|
const resolved = navigatePath(manifestRoot, fromRoot.split("/"));
|
|
129
139
|
if (resolved && typeof resolved === "object" && !Array.isArray(resolved)) {
|
|
130
140
|
return resolved;
|
|
131
141
|
}
|
|
132
142
|
}
|
|
133
|
-
if (
|
|
134
|
-
const
|
|
135
|
-
|
|
143
|
+
if (defs) {
|
|
144
|
+
for (const fromRefKind of fromRefKinds) {
|
|
145
|
+
const hashIdx = fromRefKind.indexOf("#");
|
|
146
|
+
if (hashIdx <= 0)
|
|
147
|
+
continue;
|
|
136
148
|
const refPath = fromRefKind.slice(0, hashIdx);
|
|
137
149
|
const field = fromRefKind.slice(hashIdx + 1);
|
|
138
150
|
const kindValue = navigatePath(manifestRoot, refPath.split("/"));
|
|
139
|
-
if (typeof kindValue
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
151
|
+
if (typeof kindValue !== "string" || kindValue.length === 0)
|
|
152
|
+
continue;
|
|
153
|
+
const canonical = aliases?.resolveKind(kindValue) ?? kindValue;
|
|
154
|
+
const def = defs.resolve(canonical);
|
|
155
|
+
const typeField = def
|
|
156
|
+
? def[field]
|
|
157
|
+
: undefined;
|
|
158
|
+
const resolved = resolveTypeFieldToSchema(typeField, allManifests ?? []);
|
|
159
|
+
if (resolved && typeof resolved === "object") {
|
|
160
|
+
return resolved;
|
|
149
161
|
}
|
|
150
162
|
}
|
|
151
163
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
import { type AnalysisDiagnostic } from "./types.js";
|
|
3
|
+
/** Minimal view of a definition needed to validate an inline resource's config. */
|
|
4
|
+
export interface InlineDefinitionLookup {
|
|
5
|
+
(kind: string): {
|
|
6
|
+
schema?: Record<string, any>;
|
|
7
|
+
} | undefined;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Validates inline resources nested inside a resource body against their kind's
|
|
11
|
+
* config schema. The per-resource walk in `analyze()` validates a resource's
|
|
12
|
+
* own top-level config; inline resources at `x-telo-ref` slots reachable only
|
|
13
|
+
* through a local `$ref` (notably `Run.Sequence`'s `steps[].invoke`, hidden
|
|
14
|
+
* behind `#/$defs/step`) never reach the reference field map, so they would
|
|
15
|
+
* otherwise escape schema validation — e.g. `invoke: { kind: Console.ReadLine,
|
|
16
|
+
* prompt: "…" }`, where `prompt` belongs in the step's `inputs`, not the config.
|
|
17
|
+
*
|
|
18
|
+
* Walks the manifest data together with its definition schema, resolving local
|
|
19
|
+
* `$ref`s (so step trees of arbitrary depth are covered). At each `x-telo-ref`
|
|
20
|
+
* slot holding an inline resource, the inline's config is validated against its
|
|
21
|
+
* own kind's schema, then recursed into so inline resources nested inside
|
|
22
|
+
* inline resources are covered.
|
|
23
|
+
*
|
|
24
|
+
* Non-mutating: reads `manifest` and emits diagnostics anchored to its identity
|
|
25
|
+
* and a concrete dotted path matching the position-index key format;
|
|
26
|
+
* `rewriteSyntheticOrigins` reroutes those on inline-extracted (synthetic)
|
|
27
|
+
* manifests back to the root doc.
|
|
28
|
+
*/
|
|
29
|
+
export declare function validateNestedInlineResources(manifest: ResourceManifest, rootSchema: Record<string, any>, lookupDefinition: InlineDefinitionLookup): AnalysisDiagnostic[];
|
|
30
|
+
//# sourceMappingURL=validate-nested-inline.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-nested-inline.d.ts","sourceRoot":"","sources":["../src/validate-nested-inline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAQrD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIzE,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,GAAG,SAAS,CAAC;CAC9D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,gBAAgB,EAC1B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC/B,gBAAgB,EAAE,sBAAsB,GACvC,kBAAkB,EAAE,CAoHtB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { collectRefs, isInlineResource } from "./reference-field-map.js";
|
|
2
|
+
import { collectProperties, resolveRef, substituteCelFields, validateAgainstSchema, } from "./schema-compat.js";
|
|
3
|
+
import { DiagnosticSeverity } from "./types.js";
|
|
4
|
+
const SOURCE = "telo-analyzer";
|
|
5
|
+
/**
|
|
6
|
+
* Validates inline resources nested inside a resource body against their kind's
|
|
7
|
+
* config schema. The per-resource walk in `analyze()` validates a resource's
|
|
8
|
+
* own top-level config; inline resources at `x-telo-ref` slots reachable only
|
|
9
|
+
* through a local `$ref` (notably `Run.Sequence`'s `steps[].invoke`, hidden
|
|
10
|
+
* behind `#/$defs/step`) never reach the reference field map, so they would
|
|
11
|
+
* otherwise escape schema validation — e.g. `invoke: { kind: Console.ReadLine,
|
|
12
|
+
* prompt: "…" }`, where `prompt` belongs in the step's `inputs`, not the config.
|
|
13
|
+
*
|
|
14
|
+
* Walks the manifest data together with its definition schema, resolving local
|
|
15
|
+
* `$ref`s (so step trees of arbitrary depth are covered). At each `x-telo-ref`
|
|
16
|
+
* slot holding an inline resource, the inline's config is validated against its
|
|
17
|
+
* own kind's schema, then recursed into so inline resources nested inside
|
|
18
|
+
* inline resources are covered.
|
|
19
|
+
*
|
|
20
|
+
* Non-mutating: reads `manifest` and emits diagnostics anchored to its identity
|
|
21
|
+
* and a concrete dotted path matching the position-index key format;
|
|
22
|
+
* `rewriteSyntheticOrigins` reroutes those on inline-extracted (synthetic)
|
|
23
|
+
* manifests back to the root doc.
|
|
24
|
+
*/
|
|
25
|
+
export function validateNestedInlineResources(manifest, rootSchema, lookupDefinition) {
|
|
26
|
+
const diagnostics = [];
|
|
27
|
+
const resource = { kind: manifest.kind, name: manifest.metadata?.name };
|
|
28
|
+
const filePath = manifest.metadata?.source;
|
|
29
|
+
function validateInline(inline, path) {
|
|
30
|
+
const kind = inline.kind;
|
|
31
|
+
const def = lookupDefinition(kind);
|
|
32
|
+
// Unknown kind: these `$ref`-hidden slots are invisible to the field-map
|
|
33
|
+
// driven reference checks too, so nothing else would flag it — report here.
|
|
34
|
+
if (!def) {
|
|
35
|
+
diagnostics.push({
|
|
36
|
+
severity: DiagnosticSeverity.Error,
|
|
37
|
+
code: "UNDEFINED_KIND",
|
|
38
|
+
source: SOURCE,
|
|
39
|
+
message: `${resource.kind}/${resource.name}: inline ${kind} at '${path}': No Telo.Definition found for kind '${kind}'.`,
|
|
40
|
+
data: { resource, filePath, path: `${path}.kind` },
|
|
41
|
+
});
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Kind exists but declares no config schema (e.g. a pure Telo.Type): no
|
|
45
|
+
// config to validate and no schema-declared slots to nest resources in.
|
|
46
|
+
if (!def.schema)
|
|
47
|
+
return;
|
|
48
|
+
const schema = def.schema;
|
|
49
|
+
// `kind` / `metadata` are implicit on every resource; inject them so a
|
|
50
|
+
// strict `additionalProperties: false` config schema doesn't reject them.
|
|
51
|
+
const effectiveSchema = schema.additionalProperties === false
|
|
52
|
+
? {
|
|
53
|
+
...schema,
|
|
54
|
+
properties: {
|
|
55
|
+
kind: { type: "string" },
|
|
56
|
+
metadata: { type: "object" },
|
|
57
|
+
...schema.properties,
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
: schema;
|
|
61
|
+
// Inline resources omit `metadata` — it is synthesized when the kernel
|
|
62
|
+
// registers them (and by `normalizeInlineResources` for extracted slots,
|
|
63
|
+
// which assigns a derived `metadata.name`). Config schemas conventionally
|
|
64
|
+
// declare `required: ["metadata", …]` with `metadata.name` required, so add
|
|
65
|
+
// a placeholder before validating to mirror the post-registration shape.
|
|
66
|
+
const existingMeta = inline.metadata && typeof inline.metadata === "object"
|
|
67
|
+
? inline.metadata
|
|
68
|
+
: {};
|
|
69
|
+
const data = { ...inline, metadata: { name: "__inline__", ...existingMeta } };
|
|
70
|
+
const substituted = substituteCelFields(data, effectiveSchema, effectiveSchema);
|
|
71
|
+
for (const issue of validateAgainstSchema(substituted, effectiveSchema)) {
|
|
72
|
+
diagnostics.push({
|
|
73
|
+
severity: DiagnosticSeverity.Error,
|
|
74
|
+
code: "SCHEMA_VIOLATION",
|
|
75
|
+
source: SOURCE,
|
|
76
|
+
message: `${resource.kind}/${resource.name}: inline ${kind} at '${path}': ${issue.message}`,
|
|
77
|
+
data: { resource, filePath, path: issue.path ? `${path}.${issue.path}` : path },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
// Recurse into the inline body against its own schema so deeper inline
|
|
81
|
+
// resources (e.g. an inline Run.Sequence's own steps) are validated too.
|
|
82
|
+
walk(inline, schema, schema, path);
|
|
83
|
+
}
|
|
84
|
+
function walk(data, schema, schemaRoot, path) {
|
|
85
|
+
if (!schema || typeof schema !== "object")
|
|
86
|
+
return;
|
|
87
|
+
const resolved = resolveRef(schema, schemaRoot);
|
|
88
|
+
// Reference slot: the value is either a named reference (`{kind, name}`,
|
|
89
|
+
// validated as its own manifest) or an inline resource to validate here.
|
|
90
|
+
if (collectRefs(resolved).length > 0) {
|
|
91
|
+
if (data &&
|
|
92
|
+
typeof data === "object" &&
|
|
93
|
+
!Array.isArray(data) &&
|
|
94
|
+
isInlineResource(data)) {
|
|
95
|
+
validateInline(data, path);
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (Array.isArray(data)) {
|
|
100
|
+
const itemSchema = resolved.items;
|
|
101
|
+
if (!itemSchema)
|
|
102
|
+
return;
|
|
103
|
+
for (let i = 0; i < data.length; i++) {
|
|
104
|
+
walk(data[i], itemSchema, schemaRoot, `${path}[${i}]`);
|
|
105
|
+
}
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (data && typeof data === "object") {
|
|
109
|
+
const props = collectProperties(resolved);
|
|
110
|
+
const additional = resolved.additionalProperties &&
|
|
111
|
+
typeof resolved.additionalProperties === "object" &&
|
|
112
|
+
!Array.isArray(resolved.additionalProperties)
|
|
113
|
+
? resolved.additionalProperties
|
|
114
|
+
: undefined;
|
|
115
|
+
// Descend only where the schema declares structure. Freeform fields
|
|
116
|
+
// (`additionalProperties: true`, e.g. step `inputs`) carry caller data
|
|
117
|
+
// that may coincidentally look like `{kind: …}`; not descending there
|
|
118
|
+
// keeps the inline-resource detection anchored to real ref slots.
|
|
119
|
+
for (const [key, value] of Object.entries(data)) {
|
|
120
|
+
const propSchema = props[key] ?? additional;
|
|
121
|
+
if (!propSchema)
|
|
122
|
+
continue;
|
|
123
|
+
walk(value, propSchema, schemaRoot, path ? `${path}.${key}` : key);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
walk(manifest, rootSchema, rootSchema, "");
|
|
128
|
+
return diagnostics;
|
|
129
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ResourceManifest } from "@telorun/sdk";
|
|
2
|
+
import type { AliasResolver } from "./alias-resolver.js";
|
|
3
|
+
import type { DefinitionRegistry } from "./definition-registry.js";
|
|
4
|
+
import { type AnalysisDiagnostic } from "./types.js";
|
|
5
|
+
/**
|
|
6
|
+
* Validates coherence rules for `Telo.Definition` documents that use the `provide:`
|
|
7
|
+
* template target, plus the implementation-presence rule on `Telo.Provider`
|
|
8
|
+
* definitions.
|
|
9
|
+
*
|
|
10
|
+
* Diagnostics:
|
|
11
|
+
* - PROVIDE_ON_NON_PROVIDER: `provide:` declared on a definition whose
|
|
12
|
+
* `capability` is not `Telo.Provider`.
|
|
13
|
+
* - PROVIDE_DISPATCHER_CONFLICT: `provide:` co-exists with `invoke:` or `run:`
|
|
14
|
+
* on the same definition.
|
|
15
|
+
* - PROVIDE_TARGET_UNKNOWN: `provide.name` does not resolve to an entry in
|
|
16
|
+
* `resources:`.
|
|
17
|
+
* - PROVIDE_TARGET_NOT_INVOCABLE: `provide.name` resolves to a resource whose
|
|
18
|
+
* kind is registered but not a `Telo.Invocable`.
|
|
19
|
+
* - PROVIDER_MISSING_IMPLEMENTATION: definition with `capability: Telo.Provider`
|
|
20
|
+
* declares neither `controllers:` (TS-backed) nor `provide:` (template-backed).
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateProviderCoherence(manifests: ResourceManifest[], registry: DefinitionRegistry, aliases: AliasResolver): AnalysisDiagnostic[];
|
|
23
|
+
//# sourceMappingURL=validate-provider-coherence.d.ts.map
|
|
@@ -0,0 +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;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,aAAa,GACrB,kBAAkB,EAAE,CAyItB"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { DiagnosticSeverity } from "./types.js";
|
|
2
|
+
const SOURCE = "telo-analyzer";
|
|
3
|
+
/**
|
|
4
|
+
* Validates coherence rules for `Telo.Definition` documents that use the `provide:`
|
|
5
|
+
* template target, plus the implementation-presence rule on `Telo.Provider`
|
|
6
|
+
* definitions.
|
|
7
|
+
*
|
|
8
|
+
* Diagnostics:
|
|
9
|
+
* - PROVIDE_ON_NON_PROVIDER: `provide:` declared on a definition whose
|
|
10
|
+
* `capability` is not `Telo.Provider`.
|
|
11
|
+
* - PROVIDE_DISPATCHER_CONFLICT: `provide:` co-exists with `invoke:` or `run:`
|
|
12
|
+
* on the same definition.
|
|
13
|
+
* - PROVIDE_TARGET_UNKNOWN: `provide.name` does not resolve to an entry in
|
|
14
|
+
* `resources:`.
|
|
15
|
+
* - PROVIDE_TARGET_NOT_INVOCABLE: `provide.name` resolves to a resource whose
|
|
16
|
+
* kind is registered but not a `Telo.Invocable`.
|
|
17
|
+
* - PROVIDER_MISSING_IMPLEMENTATION: definition with `capability: Telo.Provider`
|
|
18
|
+
* declares neither `controllers:` (TS-backed) nor `provide:` (template-backed).
|
|
19
|
+
*/
|
|
20
|
+
export function validateProviderCoherence(manifests, registry, aliases) {
|
|
21
|
+
const diagnostics = [];
|
|
22
|
+
const importedModules = new Set();
|
|
23
|
+
for (const m of manifests) {
|
|
24
|
+
if (m.kind !== "Telo.Import")
|
|
25
|
+
continue;
|
|
26
|
+
const resolved = m.metadata
|
|
27
|
+
?.resolvedModuleName;
|
|
28
|
+
if (resolved)
|
|
29
|
+
importedModules.add(resolved);
|
|
30
|
+
}
|
|
31
|
+
for (const m of manifests) {
|
|
32
|
+
if (m.kind !== "Telo.Definition")
|
|
33
|
+
continue;
|
|
34
|
+
const name = m.metadata?.name;
|
|
35
|
+
if (!name)
|
|
36
|
+
continue;
|
|
37
|
+
const ownModule = m.metadata?.module;
|
|
38
|
+
if (ownModule && importedModules.has(ownModule))
|
|
39
|
+
continue;
|
|
40
|
+
const filePath = m.metadata?.source;
|
|
41
|
+
const resource = { kind: m.kind, name };
|
|
42
|
+
const label = `${m.kind}/${name}`;
|
|
43
|
+
const md = m;
|
|
44
|
+
const capability = typeof md.capability === "string" ? md.capability : undefined;
|
|
45
|
+
const provide = md.provide;
|
|
46
|
+
const invoke = md.invoke;
|
|
47
|
+
const run = md.run;
|
|
48
|
+
const controllers = md.controllers;
|
|
49
|
+
const resources = md.resources;
|
|
50
|
+
const hasProvide = provide !== undefined && provide !== null;
|
|
51
|
+
const hasInvoke = invoke !== undefined && invoke !== null;
|
|
52
|
+
const hasRun = run !== undefined && run !== null;
|
|
53
|
+
const hasControllers = Array.isArray(controllers) && controllers.length > 0;
|
|
54
|
+
if (hasProvide && capability !== "Telo.Provider") {
|
|
55
|
+
diagnostics.push({
|
|
56
|
+
severity: DiagnosticSeverity.Error,
|
|
57
|
+
code: "PROVIDE_ON_NON_PROVIDER",
|
|
58
|
+
source: SOURCE,
|
|
59
|
+
message: `${label}: 'provide:' is only valid on definitions with 'capability: Telo.Provider' ` +
|
|
60
|
+
`(found '${capability ?? "<unset>"}'). Use 'invoke:' or 'run:' for other capabilities.`,
|
|
61
|
+
data: { resource, filePath, path: "provide" },
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (hasProvide && (hasInvoke || hasRun)) {
|
|
65
|
+
const conflict = hasInvoke ? "invoke" : "run";
|
|
66
|
+
diagnostics.push({
|
|
67
|
+
severity: DiagnosticSeverity.Error,
|
|
68
|
+
code: "PROVIDE_DISPATCHER_CONFLICT",
|
|
69
|
+
source: SOURCE,
|
|
70
|
+
message: `${label}: 'provide:' cannot co-exist with '${conflict}:'. ` +
|
|
71
|
+
`A definition declares exactly one dispatch entry-point.`,
|
|
72
|
+
data: { resource, filePath, path: "provide" },
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
if (hasProvide && typeof provide === "object" && !Array.isArray(provide)) {
|
|
76
|
+
const provideObj = provide;
|
|
77
|
+
const providedName = typeof provideObj.name === "string" ? provideObj.name : undefined;
|
|
78
|
+
const providedKind = typeof provideObj.kind === "string" ? provideObj.kind : undefined;
|
|
79
|
+
if (providedName && Array.isArray(resources)) {
|
|
80
|
+
const match = resources.find((r) => {
|
|
81
|
+
const meta = r?.metadata;
|
|
82
|
+
return typeof meta?.name === "string" && meta.name === providedName;
|
|
83
|
+
});
|
|
84
|
+
if (!match) {
|
|
85
|
+
diagnostics.push({
|
|
86
|
+
severity: DiagnosticSeverity.Error,
|
|
87
|
+
code: "PROVIDE_TARGET_UNKNOWN",
|
|
88
|
+
source: SOURCE,
|
|
89
|
+
message: `${label}: 'provide.name: ${providedName}' does not match any entry's ` +
|
|
90
|
+
`metadata.name in 'resources:'.`,
|
|
91
|
+
data: { resource, filePath, path: "provide.name" },
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
else if (typeof match.kind === "string") {
|
|
95
|
+
// `provide.kind` is the type contract the analyzer uses to type
|
|
96
|
+
// `result` CEL against the target's `outputType`. The runtime
|
|
97
|
+
// dispatches on `provide.name` and ignores `provide.kind`, so a
|
|
98
|
+
// mismatch silently degrades `result` typing to an open schema
|
|
99
|
+
// (and at runtime quietly invokes the actually-matched resource).
|
|
100
|
+
// Flag the divergence so result-typing never lies.
|
|
101
|
+
if (providedKind) {
|
|
102
|
+
const providedCanonical = aliases.resolveKind(providedKind) ?? providedKind;
|
|
103
|
+
const matchCanonical = aliases.resolveKind(match.kind) ?? match.kind;
|
|
104
|
+
if (providedCanonical !== matchCanonical) {
|
|
105
|
+
diagnostics.push({
|
|
106
|
+
severity: DiagnosticSeverity.Error,
|
|
107
|
+
code: "PROVIDE_KIND_MISMATCH",
|
|
108
|
+
source: SOURCE,
|
|
109
|
+
message: `${label}: 'provide.kind: ${providedKind}' disagrees with the matched ` +
|
|
110
|
+
`'resources:' entry's kind '${match.kind}' (matched by metadata.name ` +
|
|
111
|
+
`'${providedName}'). The runtime dispatches by name, so 'provide.kind' ` +
|
|
112
|
+
`is decorative — but the analyzer types 'result:' against it, and a ` +
|
|
113
|
+
`mismatch silently turns off that typing.`,
|
|
114
|
+
data: { resource, filePath, path: "provide.kind" },
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
const resolvedKind = aliases.resolveKind(match.kind) ?? match.kind;
|
|
119
|
+
const targetDef = registry.resolve(resolvedKind) ?? registry.resolve(match.kind);
|
|
120
|
+
if (targetDef && targetDef.kind === "Telo.Definition") {
|
|
121
|
+
const targetCap = targetDef.capability;
|
|
122
|
+
if (typeof targetCap === "string" && targetCap !== "Telo.Invocable") {
|
|
123
|
+
diagnostics.push({
|
|
124
|
+
severity: DiagnosticSeverity.Error,
|
|
125
|
+
code: "PROVIDE_TARGET_NOT_INVOCABLE",
|
|
126
|
+
source: SOURCE,
|
|
127
|
+
message: `${label}: 'provide.name: ${providedName}' resolves to a ${match.kind} ` +
|
|
128
|
+
`(capability '${targetCap}'); 'provide:' requires a Telo.Invocable target.`,
|
|
129
|
+
data: { resource, filePath, path: "provide.name" },
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (capability === "Telo.Provider" && !hasControllers && !hasProvide) {
|
|
137
|
+
diagnostics.push({
|
|
138
|
+
severity: DiagnosticSeverity.Error,
|
|
139
|
+
code: "PROVIDER_MISSING_IMPLEMENTATION",
|
|
140
|
+
source: SOURCE,
|
|
141
|
+
message: `${label}: 'capability: Telo.Provider' requires either 'controllers:' ` +
|
|
142
|
+
`(TS-backed) or 'provide:' (template-backed) to declare an implementation.`,
|
|
143
|
+
data: { resource, filePath, path: "capability" },
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return diagnostics;
|
|
148
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"validate-references.d.ts","sourceRoot":"","sources":["../src/validate-references.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAKrD,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA4C/F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,EAAE,eAAe,GACvB,kBAAkB,EAAE,CAsbtB"}
|