@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # SUSTAINABLE USE LICENSE (Fair-code)
2
2
 
3
- Copyright (c) 2026 DiglyAI
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 DiglyAI.
17
+ For commercial licensing, managed hosting exemptions, or enterprise inquiries, please contact <contact@codenet.pl>.
@@ -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;IA6cvB,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"}
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
- private resolveImportUrl;
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;IAOrD;;;+BAG2B;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC;IAcvE;;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,OAAO,CAAC,gBAAgB;IAOxB,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"}
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"}
@@ -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
- const { source } = await this.pick(url).read(url);
49
- return source;
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
- const cacheKey = `${options?.compile ? "compiled" : "raw"}:${source}`;
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
@@ -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.1.0",
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) {
@@ -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
- const { source } = await this.pick(url).read(url);
71
- return source;
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
- const cacheKey = `${options?.compile ? "compiled" : "raw"}:${source}`;
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
- private resolveImportUrl(fromSource: string, importSource: string): string {
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