@telorun/analyzer 1.1.0 → 1.2.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/LICENSE +2 -2
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +9 -0
- 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/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/analyzer.ts +10 -0
- package/src/manifest-loader.ts +69 -4
- package/src/types.ts +12 -0
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# SUSTAINABLE USE LICENSE (Fair-code)
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026
|
|
3
|
+
Copyright (c) 2026 CodeNet Sp. z o.o.
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify, and distribute the Software for any purpose—including commercial purposes—subject to the following conditions:
|
|
6
6
|
|
|
@@ -14,4 +14,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
|
14
14
|
|
|
15
15
|
5. DISCLAIMER: The Software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the Software or the use or other dealings in the Software.
|
|
16
16
|
|
|
17
|
-
For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact
|
|
17
|
+
For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact <contact@codenet.pl>.
|
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,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAc9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA+c/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,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;
|
|
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,EAGL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAc9B,OAAO,EAAsB,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AA+c/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,OAAO,CACL,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAudvB,aAAa,CACX,SAAS,EAAE,gBAAgB,EAAE,EAC7B,OAAO,CAAC,EAAE,eAAe,EACzB,QAAQ,CAAC,EAAE,gBAAgB,GAC1B,kBAAkB,EAAE;IAMvB,SAAS,CAAC,SAAS,EAAE,gBAAgB,EAAE,EAAE,QAAQ,EAAE,gBAAgB,GAAG,gBAAgB,EAAE;IAUxF,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
|
@@ -492,6 +492,15 @@ export class StaticAnalyzer {
|
|
|
492
492
|
}
|
|
493
493
|
// Phase 2: extract inline resources from x-telo-ref slots into first-class manifests
|
|
494
494
|
const allManifests = normalizeInlineResources(manifests, defs, aliases, aliasesByModule);
|
|
495
|
+
// Trusted-input fast path: when the caller has already attested that
|
|
496
|
+
// this exact manifest set passes analysis (e.g. via the kernel's
|
|
497
|
+
// hash-stamped `.validated.json` cache), skip the validation walk.
|
|
498
|
+
// Registration of identities / aliases / definitions and inline-resource
|
|
499
|
+
// normalisation have already run above; that's all downstream
|
|
500
|
+
// consumers (prepare, init loop) require.
|
|
501
|
+
if (options?.skipValidation) {
|
|
502
|
+
return diagnostics;
|
|
503
|
+
}
|
|
495
504
|
// Build a name→manifest map for looking up referenced resources
|
|
496
505
|
const byName = new Map();
|
|
497
506
|
for (const m of allManifests) {
|
|
@@ -6,12 +6,25 @@ export declare class Loader {
|
|
|
6
6
|
* caller (kernel) and a raw-mode caller (analyzer/editor) on the same file
|
|
7
7
|
* get distinct entries, so neither sees the wrong manifest tree. */
|
|
8
8
|
private readonly fileCache;
|
|
9
|
+
/** requestUrl → canonical `source`. Lets `loadFile` skip the source read
|
|
10
|
+
* when a URL it has already canonicalised is requested again — kernel
|
|
11
|
+
* load → boot and the import-controller each ask the loader for the same
|
|
12
|
+
* modules. Without this fast path every duplicate request re-runs the
|
|
13
|
+
* source's `read()` (a `fetch` for `RegistrySource`, a disk read for
|
|
14
|
+
* `LocalFileSource`). */
|
|
15
|
+
private readonly urlToSource;
|
|
9
16
|
protected sources: ManifestSource[];
|
|
10
17
|
private readonly celEnv;
|
|
11
18
|
constructor(extraSourcesOrOptions?: ManifestSource[] | LoaderInitOptions);
|
|
12
19
|
register(source: ManifestSource): this;
|
|
13
20
|
private pick;
|
|
14
21
|
resolveEntryPoint(url: string): Promise<string>;
|
|
22
|
+
/** Returns the canonical source URL the loader has already mapped `url`
|
|
23
|
+
* to during a prior `loadFile`/`loadModule`/`loadGraph` call, or
|
|
24
|
+
* `undefined` when the URL has not been seen. Callers use it to test
|
|
25
|
+
* set-membership against a previous graph walk's modules without
|
|
26
|
+
* triggering an extra source read. */
|
|
27
|
+
canonicalize(url: string): string | undefined;
|
|
15
28
|
/** Read one file via the source chain and parse it into a LoadedFile.
|
|
16
29
|
* The result is shared with `Loader.fileCache`. Callers that want a
|
|
17
30
|
* private mutable copy must call `parseLoadedFile` directly with the
|
|
@@ -26,7 +39,16 @@ export declare class Loader {
|
|
|
26
39
|
* `importEdges` mapping each importing file's PascalCase aliases to their
|
|
27
40
|
* target's canonical source. */
|
|
28
41
|
loadGraph(entryUrl: string, options?: LoadOptions): Promise<LoadedGraph>;
|
|
29
|
-
|
|
42
|
+
/** Resolve an `import` URL against the file it appears in. Relative /
|
|
43
|
+
* absolute-path forms run through the owning `ManifestSource`'s
|
|
44
|
+
* `resolveRelative`; registry refs and full URLs pass through
|
|
45
|
+
* unchanged. Exposed so the import-controller (and any other
|
|
46
|
+
* caller-side resolver) lands on the *exact same* canonical URL the
|
|
47
|
+
* loader used when walking the entry graph — divergent resolution
|
|
48
|
+
* would silently break optimizations like `canonicalize()`-keyed
|
|
49
|
+
* cache hits whenever a non-trivial `ManifestSource.resolveRelative`
|
|
50
|
+
* is in play. */
|
|
51
|
+
resolveImportUrl(fromSource: string, importSource: string): string;
|
|
30
52
|
private assertSingleModuleDeclaration;
|
|
31
53
|
private assertNoSystemKindsInPartialContext;
|
|
32
54
|
private assertImportTargetIsLibrary;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AASpB,qBAAa,MAAM;IACjB;;;yEAGqE;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,qBAAqB,GAAE,cAAc,EAAE,GAAG,iBAAsB;IAmB5E,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKtC,OAAO,CAAC,IAAI;IAMN,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"manifest-loader.d.ts","sourceRoot":"","sources":["../src/manifest-loader.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,YAAY,EACb,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AASpB,qBAAa,MAAM;IACjB;;;yEAGqE;IACrE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D;;;;;8BAK0B;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA6B;IAEzD,SAAS,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;gBAEzB,qBAAqB,GAAE,cAAc,EAAE,GAAG,iBAAsB;IAmB5E,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKtC,OAAO,CAAC,IAAI;IAMN,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUrD;;;;2CAIuC;IACvC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAM7C;;;+BAG2B;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAgDvE;;wEAEoE;IAC9D,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAsB3E;;;qCAGiC;IAC3B,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IAwG9E;;;;;;;;sBAQkB;IAClB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM;IAOlE,OAAO,CAAC,6BAA6B;IAarC,OAAO,CAAC,mCAAmC;IAc3C,OAAO,CAAC,2BAA2B;YAkCrB,eAAe;IAmB7B;;;0CAGsC;IAChC,gBAAgB,CACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CA0B5D"}
|
package/dist/manifest-loader.js
CHANGED
|
@@ -16,6 +16,13 @@ export class Loader {
|
|
|
16
16
|
* caller (kernel) and a raw-mode caller (analyzer/editor) on the same file
|
|
17
17
|
* get distinct entries, so neither sees the wrong manifest tree. */
|
|
18
18
|
fileCache = new Map();
|
|
19
|
+
/** requestUrl → canonical `source`. Lets `loadFile` skip the source read
|
|
20
|
+
* when a URL it has already canonicalised is requested again — kernel
|
|
21
|
+
* load → boot and the import-controller each ask the loader for the same
|
|
22
|
+
* modules. Without this fast path every duplicate request re-runs the
|
|
23
|
+
* source's `read()` (a `fetch` for `RegistrySource`, a disk read for
|
|
24
|
+
* `LocalFileSource`). */
|
|
25
|
+
urlToSource = new Map();
|
|
19
26
|
sources;
|
|
20
27
|
celEnv;
|
|
21
28
|
constructor(extraSourcesOrOptions = []) {
|
|
@@ -45,8 +52,21 @@ export class Loader {
|
|
|
45
52
|
return s;
|
|
46
53
|
}
|
|
47
54
|
async resolveEntryPoint(url) {
|
|
48
|
-
|
|
49
|
-
|
|
55
|
+
// Route through `loadFile` so the resolved source URL and parsed
|
|
56
|
+
// entry are populated in `urlToSource` + `fileCache` in one read.
|
|
57
|
+
// Callers (kernel.load) immediately call `loadGraph(entryUrl)`
|
|
58
|
+
// afterwards — without this priming, the entry file would be read
|
|
59
|
+
// twice (twice over the network for `RegistrySource`).
|
|
60
|
+
const file = await this.loadFile(url);
|
|
61
|
+
return file.source;
|
|
62
|
+
}
|
|
63
|
+
/** Returns the canonical source URL the loader has already mapped `url`
|
|
64
|
+
* to during a prior `loadFile`/`loadModule`/`loadGraph` call, or
|
|
65
|
+
* `undefined` when the URL has not been seen. Callers use it to test
|
|
66
|
+
* set-membership against a previous graph walk's modules without
|
|
67
|
+
* triggering an extra source read. */
|
|
68
|
+
canonicalize(url) {
|
|
69
|
+
return this.urlToSource.get(url);
|
|
50
70
|
}
|
|
51
71
|
// --- New API: returns LoadedFile / LoadedModule / LoadedGraph ----------
|
|
52
72
|
/** Read one file via the source chain and parse it into a LoadedFile.
|
|
@@ -54,8 +74,42 @@ export class Loader {
|
|
|
54
74
|
* private mutable copy must call `parseLoadedFile` directly with the
|
|
55
75
|
* LoadedFile's `text`. */
|
|
56
76
|
async loadFile(url, options) {
|
|
77
|
+
const compileKey = options?.compile ? "compiled" : "raw";
|
|
78
|
+
const knownSource = this.urlToSource.get(url);
|
|
79
|
+
if (knownSource) {
|
|
80
|
+
const cached = this.fileCache.get(`${compileKey}:${knownSource}`);
|
|
81
|
+
if (cached)
|
|
82
|
+
return cached;
|
|
83
|
+
// The other compile-mode entry is cached — reparse from its text
|
|
84
|
+
// instead of re-reading the source.
|
|
85
|
+
//
|
|
86
|
+
// NOTE for watch-mode reactivation (cli/nodejs/src/commands/run.ts
|
|
87
|
+
// currently has `setupWatchMode` commented out): this branch
|
|
88
|
+
// assumes file contents don't change underneath a single Loader.
|
|
89
|
+
// Reviving watch mode will need a public `invalidate(url)` (or
|
|
90
|
+
// similar) that drops both `urlToSource[url]` and the cached
|
|
91
|
+
// entries for its canonical source before the loader serves the
|
|
92
|
+
// file again.
|
|
93
|
+
const altKey = `${compileKey === "compiled" ? "raw" : "compiled"}:${knownSource}`;
|
|
94
|
+
const alt = this.fileCache.get(altKey);
|
|
95
|
+
if (alt) {
|
|
96
|
+
const reparsed = parseLoadedFile(knownSource, url, alt.text, {
|
|
97
|
+
compile: options?.compile,
|
|
98
|
+
celEnv: this.celEnv,
|
|
99
|
+
});
|
|
100
|
+
this.fileCache.set(`${compileKey}:${knownSource}`, reparsed);
|
|
101
|
+
return reparsed;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
57
104
|
const { text, source } = await this.pick(url).read(url);
|
|
58
|
-
|
|
105
|
+
this.urlToSource.set(url, source);
|
|
106
|
+
// Also map the canonical source to itself so subsequent `loadFile`
|
|
107
|
+
// calls that already received a canonical URL — `kernel.load` passes
|
|
108
|
+
// the result of `resolveEntryPoint` to `loadGraph`, which then asks
|
|
109
|
+
// for that exact URL — hit the urlToSource fast path instead of
|
|
110
|
+
// falling through to a redundant `pick(url).read(url)`.
|
|
111
|
+
this.urlToSource.set(source, source);
|
|
112
|
+
const cacheKey = `${compileKey}:${source}`;
|
|
59
113
|
const cached = this.fileCache.get(cacheKey);
|
|
60
114
|
if (cached && cached.text === text)
|
|
61
115
|
return cached;
|
|
@@ -190,6 +244,15 @@ export class Loader {
|
|
|
190
244
|
}
|
|
191
245
|
return { rootSource, entry, modules, importEdges, errors };
|
|
192
246
|
}
|
|
247
|
+
/** Resolve an `import` URL against the file it appears in. Relative /
|
|
248
|
+
* absolute-path forms run through the owning `ManifestSource`'s
|
|
249
|
+
* `resolveRelative`; registry refs and full URLs pass through
|
|
250
|
+
* unchanged. Exposed so the import-controller (and any other
|
|
251
|
+
* caller-side resolver) lands on the *exact same* canonical URL the
|
|
252
|
+
* loader used when walking the entry graph — divergent resolution
|
|
253
|
+
* would silently break optimizations like `canonicalize()`-keyed
|
|
254
|
+
* cache hits whenever a non-trivial `ManifestSource.resolveRelative`
|
|
255
|
+
* is in play. */
|
|
193
256
|
resolveImportUrl(fromSource, importSource) {
|
|
194
257
|
if (importSource.startsWith(".") || importSource.startsWith("/")) {
|
|
195
258
|
return this.pick(fromSource).resolveRelative(fromSource, importSource);
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/analyzer",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Telo Analyzer - Static manifest validator for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"bun": "./src/index.ts",
|
|
30
30
|
"import": "./dist/index.js",
|
|
31
31
|
"default": "./dist/index.js"
|
|
32
|
-
}
|
|
32
|
+
},
|
|
33
|
+
"./package.json": "./package.json"
|
|
33
34
|
},
|
|
34
35
|
"files": [
|
|
35
36
|
"dist/**",
|
package/src/analyzer.ts
CHANGED
|
@@ -623,6 +623,16 @@ export class StaticAnalyzer {
|
|
|
623
623
|
// Phase 2: extract inline resources from x-telo-ref slots into first-class manifests
|
|
624
624
|
const allManifests = normalizeInlineResources(manifests, defs, aliases, aliasesByModule);
|
|
625
625
|
|
|
626
|
+
// Trusted-input fast path: when the caller has already attested that
|
|
627
|
+
// this exact manifest set passes analysis (e.g. via the kernel's
|
|
628
|
+
// hash-stamped `.validated.json` cache), skip the validation walk.
|
|
629
|
+
// Registration of identities / aliases / definitions and inline-resource
|
|
630
|
+
// normalisation have already run above; that's all downstream
|
|
631
|
+
// consumers (prepare, init loop) require.
|
|
632
|
+
if (options?.skipValidation) {
|
|
633
|
+
return diagnostics;
|
|
634
|
+
}
|
|
635
|
+
|
|
626
636
|
// Build a name→manifest map for looking up referenced resources
|
|
627
637
|
const byName = new Map<string, ResourceManifest>();
|
|
628
638
|
for (const m of allManifests) {
|
package/src/manifest-loader.ts
CHANGED
|
@@ -33,6 +33,14 @@ export class Loader {
|
|
|
33
33
|
* get distinct entries, so neither sees the wrong manifest tree. */
|
|
34
34
|
private readonly fileCache = new Map<string, LoadedFile>();
|
|
35
35
|
|
|
36
|
+
/** requestUrl → canonical `source`. Lets `loadFile` skip the source read
|
|
37
|
+
* when a URL it has already canonicalised is requested again — kernel
|
|
38
|
+
* load → boot and the import-controller each ask the loader for the same
|
|
39
|
+
* modules. Without this fast path every duplicate request re-runs the
|
|
40
|
+
* source's `read()` (a `fetch` for `RegistrySource`, a disk read for
|
|
41
|
+
* `LocalFileSource`). */
|
|
42
|
+
private readonly urlToSource = new Map<string, string>();
|
|
43
|
+
|
|
36
44
|
protected sources: ManifestSource[];
|
|
37
45
|
private readonly celEnv: Environment;
|
|
38
46
|
|
|
@@ -67,8 +75,22 @@ export class Loader {
|
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
async resolveEntryPoint(url: string): Promise<string> {
|
|
70
|
-
|
|
71
|
-
|
|
78
|
+
// Route through `loadFile` so the resolved source URL and parsed
|
|
79
|
+
// entry are populated in `urlToSource` + `fileCache` in one read.
|
|
80
|
+
// Callers (kernel.load) immediately call `loadGraph(entryUrl)`
|
|
81
|
+
// afterwards — without this priming, the entry file would be read
|
|
82
|
+
// twice (twice over the network for `RegistrySource`).
|
|
83
|
+
const file = await this.loadFile(url);
|
|
84
|
+
return file.source;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Returns the canonical source URL the loader has already mapped `url`
|
|
88
|
+
* to during a prior `loadFile`/`loadModule`/`loadGraph` call, or
|
|
89
|
+
* `undefined` when the URL has not been seen. Callers use it to test
|
|
90
|
+
* set-membership against a previous graph walk's modules without
|
|
91
|
+
* triggering an extra source read. */
|
|
92
|
+
canonicalize(url: string): string | undefined {
|
|
93
|
+
return this.urlToSource.get(url);
|
|
72
94
|
}
|
|
73
95
|
|
|
74
96
|
// --- New API: returns LoadedFile / LoadedModule / LoadedGraph ----------
|
|
@@ -78,8 +100,42 @@ export class Loader {
|
|
|
78
100
|
* private mutable copy must call `parseLoadedFile` directly with the
|
|
79
101
|
* LoadedFile's `text`. */
|
|
80
102
|
async loadFile(url: string, options?: LoadOptions): Promise<LoadedFile> {
|
|
103
|
+
const compileKey = options?.compile ? "compiled" : "raw";
|
|
104
|
+
const knownSource = this.urlToSource.get(url);
|
|
105
|
+
if (knownSource) {
|
|
106
|
+
const cached = this.fileCache.get(`${compileKey}:${knownSource}`);
|
|
107
|
+
if (cached) return cached;
|
|
108
|
+
// The other compile-mode entry is cached — reparse from its text
|
|
109
|
+
// instead of re-reading the source.
|
|
110
|
+
//
|
|
111
|
+
// NOTE for watch-mode reactivation (cli/nodejs/src/commands/run.ts
|
|
112
|
+
// currently has `setupWatchMode` commented out): this branch
|
|
113
|
+
// assumes file contents don't change underneath a single Loader.
|
|
114
|
+
// Reviving watch mode will need a public `invalidate(url)` (or
|
|
115
|
+
// similar) that drops both `urlToSource[url]` and the cached
|
|
116
|
+
// entries for its canonical source before the loader serves the
|
|
117
|
+
// file again.
|
|
118
|
+
const altKey = `${compileKey === "compiled" ? "raw" : "compiled"}:${knownSource}`;
|
|
119
|
+
const alt = this.fileCache.get(altKey);
|
|
120
|
+
if (alt) {
|
|
121
|
+
const reparsed = parseLoadedFile(knownSource, url, alt.text, {
|
|
122
|
+
compile: options?.compile,
|
|
123
|
+
celEnv: this.celEnv,
|
|
124
|
+
});
|
|
125
|
+
this.fileCache.set(`${compileKey}:${knownSource}`, reparsed);
|
|
126
|
+
return reparsed;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
81
130
|
const { text, source } = await this.pick(url).read(url);
|
|
82
|
-
|
|
131
|
+
this.urlToSource.set(url, source);
|
|
132
|
+
// Also map the canonical source to itself so subsequent `loadFile`
|
|
133
|
+
// calls that already received a canonical URL — `kernel.load` passes
|
|
134
|
+
// the result of `resolveEntryPoint` to `loadGraph`, which then asks
|
|
135
|
+
// for that exact URL — hit the urlToSource fast path instead of
|
|
136
|
+
// falling through to a redundant `pick(url).read(url)`.
|
|
137
|
+
this.urlToSource.set(source, source);
|
|
138
|
+
const cacheKey = `${compileKey}:${source}`;
|
|
83
139
|
const cached = this.fileCache.get(cacheKey);
|
|
84
140
|
if (cached && cached.text === text) return cached;
|
|
85
141
|
|
|
@@ -224,7 +280,16 @@ export class Loader {
|
|
|
224
280
|
return { rootSource, entry, modules, importEdges, errors };
|
|
225
281
|
}
|
|
226
282
|
|
|
227
|
-
|
|
283
|
+
/** Resolve an `import` URL against the file it appears in. Relative /
|
|
284
|
+
* absolute-path forms run through the owning `ManifestSource`'s
|
|
285
|
+
* `resolveRelative`; registry refs and full URLs pass through
|
|
286
|
+
* unchanged. Exposed so the import-controller (and any other
|
|
287
|
+
* caller-side resolver) lands on the *exact same* canonical URL the
|
|
288
|
+
* loader used when walking the entry graph — divergent resolution
|
|
289
|
+
* would silently break optimizations like `canonicalize()`-keyed
|
|
290
|
+
* cache hits whenever a non-trivial `ManifestSource.resolveRelative`
|
|
291
|
+
* is in play. */
|
|
292
|
+
resolveImportUrl(fromSource: string, importSource: string): string {
|
|
228
293
|
if (importSource.startsWith(".") || importSource.startsWith("/")) {
|
|
229
294
|
return this.pick(fromSource).resolveRelative(fromSource, importSource);
|
|
230
295
|
}
|
package/src/types.ts
CHANGED
|
@@ -81,6 +81,18 @@ export interface LoaderInitOptions {
|
|
|
81
81
|
|
|
82
82
|
export interface AnalysisOptions {
|
|
83
83
|
strictContexts?: boolean;
|
|
84
|
+
/** When true, `analyze()` runs the state-mutating setup (module identity /
|
|
85
|
+
* alias / definition registration plus `normalizeInlineResources`) but
|
|
86
|
+
* skips every diagnostic-producing pass — per-resource validation, the
|
|
87
|
+
* Library `env:` check, `validateExtends`, `validateProviderCoherence`,
|
|
88
|
+
* and `validateThrowsCoverage`. Used by the kernel when a previous load
|
|
89
|
+
* has already stamped the manifest set as valid (by content hash), so
|
|
90
|
+
* the registry still gets populated without paying the validation walk
|
|
91
|
+
* on every cold start. The caller takes responsibility for the
|
|
92
|
+
* correctness guarantee — pass this only when something durable
|
|
93
|
+
* (on-disk stamp) attests that the manifests passed a real analyze
|
|
94
|
+
* pass at the same analyzer / kernel version. */
|
|
95
|
+
skipValidation?: boolean;
|
|
84
96
|
}
|
|
85
97
|
|
|
86
98
|
/** Pre-seeded state for incremental analysis. Passed to StaticAnalyzer.analyze() so it does
|