veryfront 0.1.85 → 0.1.87

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.
Files changed (28) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts +4 -0
  3. package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
  4. package/esm/src/platform/adapters/fs/veryfront/adapter.js +76 -0
  5. package/esm/src/platform/adapters/fs/veryfront/content-metrics.d.ts +1 -1
  6. package/esm/src/platform/adapters/fs/veryfront/content-metrics.d.ts.map +1 -1
  7. package/esm/src/platform/adapters/fs/veryfront/content-metrics.js +7 -1
  8. package/esm/src/platform/adapters/fs/veryfront/file-list-index.d.ts +10 -0
  9. package/esm/src/platform/adapters/fs/veryfront/file-list-index.d.ts.map +1 -1
  10. package/esm/src/platform/adapters/fs/veryfront/file-list-index.js +83 -19
  11. package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts +5 -0
  12. package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts.map +1 -1
  13. package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.js +6 -0
  14. package/esm/src/platform/adapters/fs/veryfront/read-operations.d.ts.map +1 -1
  15. package/esm/src/platform/adapters/fs/veryfront/read-operations.js +53 -34
  16. package/esm/src/rendering/page-resolution/page-resolver.d.ts.map +1 -1
  17. package/esm/src/rendering/page-resolution/page-resolver.js +10 -4
  18. package/esm/src/utils/version.d.ts +1 -1
  19. package/esm/src/utils/version.js +1 -1
  20. package/package.json +1 -1
  21. package/src/deno.js +1 -1
  22. package/src/src/platform/adapters/fs/veryfront/adapter.ts +87 -0
  23. package/src/src/platform/adapters/fs/veryfront/content-metrics.ts +13 -2
  24. package/src/src/platform/adapters/fs/veryfront/file-list-index.ts +101 -18
  25. package/src/src/platform/adapters/fs/veryfront/read-operations-helpers.ts +10 -0
  26. package/src/src/platform/adapters/fs/veryfront/read-operations.ts +87 -30
  27. package/src/src/rendering/page-resolution/page-resolver.ts +17 -6
  28. package/src/src/utils/version.ts +1 -1
package/esm/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export default {
2
2
  "name": "veryfront",
3
- "version": "0.1.85",
3
+ "version": "0.1.87",
4
4
  "license": "Apache-2.0",
5
5
  "nodeModulesDir": "auto",
6
6
  "exclude": [
@@ -12,6 +12,9 @@ export declare class VeryfrontFSAdapter implements FSAdapter {
12
12
  private initialized;
13
13
  /** Resolves when file list initialization is complete (for coordinating reads) */
14
14
  private fileListReadyResolve;
15
+ /** Single-flight background rewarm when the file list cache disappears */
16
+ private fileListWarmupPromise;
17
+ private fileListWarmupKey;
15
18
  private projectData?;
16
19
  private apiBaseUrl;
17
20
  private apiToken;
@@ -29,6 +32,7 @@ export declare class VeryfrontFSAdapter implements FSAdapter {
29
32
  constructor(config: FSAdapterConfig);
30
33
  initialize(): Promise<void>;
31
34
  private isPersistentCacheInvalidated;
35
+ private scheduleFileListWarmup;
32
36
  getPokeMetrics(): {
33
37
  received: number;
34
38
  invalidationsTriggered: number;
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/adapter.ts"],"names":[],"mappings":"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"}
@@ -27,6 +27,9 @@ export class VeryfrontFSAdapter {
27
27
  initialized = false;
28
28
  /** Resolves when file list initialization is complete (for coordinating reads) */
29
29
  fileListReadyResolve = null;
30
+ /** Single-flight background rewarm when the file list cache disappears */
31
+ fileListWarmupPromise = null;
32
+ fileListWarmupKey = null;
30
33
  projectData;
31
34
  apiBaseUrl;
32
35
  apiToken;
@@ -94,6 +97,9 @@ export class VeryfrontFSAdapter {
94
97
  hasResult: !!result,
95
98
  resultSize: result?.length ?? 0,
96
99
  });
100
+ if (!result?.length) {
101
+ this.scheduleFileListWarmup("getFileList miss", cacheKey);
102
+ }
97
103
  return result;
98
104
  },
99
105
  hasCachedFileList: async () => {
@@ -109,6 +115,9 @@ export class VeryfrontFSAdapter {
109
115
  hasResult,
110
116
  resultSize: result?.length ?? 0,
111
117
  });
118
+ if (!hasResult) {
119
+ this.scheduleFileListWarmup("hasCachedFileList miss", cacheKey);
120
+ }
112
121
  return hasResult;
113
122
  },
114
123
  isPersistentCacheInvalidated: (prefix) => this.isPersistentCacheInvalidated(prefix),
@@ -132,6 +141,9 @@ export class VeryfrontFSAdapter {
132
141
  resultSize: result?.length ?? 0,
133
142
  hasContent: result?.filter((f) => f.content)?.length ?? 0,
134
143
  });
144
+ if (!result?.length) {
145
+ this.scheduleFileListWarmup("getFileListCache miss", cacheKey);
146
+ }
135
147
  return result;
136
148
  });
137
149
  this.dirOps = new DirectoryOperations(this.client, this.cache, this.normalizer, contentContextGetter);
@@ -280,6 +292,65 @@ export class VeryfrontFSAdapter {
280
292
  isPersistentCacheInvalidated(prefix) {
281
293
  return isPrefixBeingInvalidated(prefix);
282
294
  }
295
+ scheduleFileListWarmup(reason, cacheKey) {
296
+ if (!this.initialized || !this.contentContext)
297
+ return;
298
+ const effectiveCacheKey = cacheKey ?? buildFileListCacheKey(this.contentContext);
299
+ if (this.fileListWarmupPromise && this.fileListWarmupKey === effectiveCacheKey) {
300
+ logger.debug("File list warmup already in progress", {
301
+ reason,
302
+ cacheKey: effectiveCacheKey,
303
+ });
304
+ return;
305
+ }
306
+ const warmupContext = this.contentContext;
307
+ let warmupPromise = null;
308
+ warmupPromise = (async () => {
309
+ try {
310
+ const existing = await this.cache.getAsync(effectiveCacheKey);
311
+ if (existing?.length) {
312
+ logger.debug("Skipping file list warmup because cache is already populated", {
313
+ reason,
314
+ cacheKey: effectiveCacheKey,
315
+ fileCount: existing.length,
316
+ });
317
+ return;
318
+ }
319
+ logger.debug("Starting file list warmup", {
320
+ reason,
321
+ cacheKey: effectiveCacheKey,
322
+ sourceType: warmupContext.sourceType,
323
+ branch: warmupContext.branch,
324
+ environmentName: warmupContext.environmentName,
325
+ releaseId: warmupContext.releaseId,
326
+ });
327
+ const files = await fetchFileListForContext(this.client, warmupContext);
328
+ await this.cache.setAsync(effectiveCacheKey, files);
329
+ logger.debug("File list warmup complete", {
330
+ reason,
331
+ cacheKey: effectiveCacheKey,
332
+ totalFiles: files.length,
333
+ filesWithContent: files.filter((file) => file.content).length,
334
+ });
335
+ }
336
+ catch (error) {
337
+ logger.warn("File list warmup failed", {
338
+ reason,
339
+ cacheKey: effectiveCacheKey,
340
+ error: error instanceof Error ? error.message : String(error),
341
+ });
342
+ }
343
+ finally {
344
+ if (warmupPromise && this.fileListWarmupPromise === warmupPromise) {
345
+ this.fileListWarmupPromise = null;
346
+ this.fileListWarmupKey = null;
347
+ }
348
+ }
349
+ })();
350
+ this.fileListWarmupPromise = warmupPromise;
351
+ this.fileListWarmupKey = effectiveCacheKey;
352
+ this.readOps.setFileListReadyPromise(warmupPromise);
353
+ }
283
354
  getPokeMetrics() {
284
355
  return this.wsManager.getPokeMetrics();
285
356
  }
@@ -317,6 +388,8 @@ export class VeryfrontFSAdapter {
317
388
  this.statOps.clearIndex();
318
389
  this.dirOps.clearTree();
319
390
  this.initialized = false;
391
+ this.fileListWarmupPromise = null;
392
+ this.fileListWarmupKey = null;
320
393
  logger.debug("Disposed");
321
394
  }
322
395
  getCacheStats() {
@@ -342,6 +415,7 @@ export class VeryfrontFSAdapter {
342
415
  hasFiles: !!files,
343
416
  fileCount: files?.length ?? 0,
344
417
  });
418
+ this.scheduleFileListWarmup("getAllSourceFiles miss", cacheKey);
345
419
  return [];
346
420
  }
347
421
  const fileSummary = summarizeFileList(files);
@@ -429,6 +503,8 @@ export class VeryfrontFSAdapter {
429
503
  if (contextChanged) {
430
504
  this.statOps.clearIndex();
431
505
  this.dirOps.clearTree();
506
+ this.fileListWarmupPromise = null;
507
+ this.fileListWarmupKey = null;
432
508
  logger.debug("Cleared index and dirTree due to context change", {
433
509
  oldContext,
434
510
  newContext: context,
@@ -1,4 +1,4 @@
1
- export type MissReason = "cold_start" | "not_in_filelist" | "invalidation" | "no_filelist_cache";
1
+ export type MissReason = "cold_start" | "not_in_filelist" | "invalidation" | "no_filelist_cache" | "indexed_without_content";
2
2
  interface CumulativeMetrics {
3
3
  requestScopedHits: number;
4
4
  persistentCacheHits: number;
@@ -1 +1 @@
1
- {"version":3,"file":"content-metrics.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/content-metrics.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,iBAAiB,GAAG,cAAc,GAAG,mBAAmB,CAAC;AAejG,UAAU,iBAAiB;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAsCD,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,wBAAgB,iBAAiB,CAC/B,cAAc,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACxE,IAAI,CAuCN;AAED,KAAK,kBAAkB,GACnB,oBAAoB,GACpB,sBAAsB,GACtB,eAAe,GACf,eAAe,GACf,wBAAwB,GACxB,YAAY,CAAC;AAEjB,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE;IACP,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,GACA,IAAI,CAyCN;AAED,wBAAgB,yBAAyB,IAAI,iBAAiB,GAAG;IAC/D,sBAAsB,EAAE,MAAM,CAAC;CAChC,CAOA;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C"}
1
+ {"version":3,"file":"content-metrics.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/content-metrics.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,UAAU,GAClB,YAAY,GACZ,iBAAiB,GACjB,cAAc,GACd,mBAAmB,GACnB,yBAAyB,CAAC;AAe9B,UAAU,iBAAiB;IACzB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AA4CD,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,wBAAgB,iBAAiB,CAC/B,cAAc,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACxE,IAAI,CAuCN;AAED,KAAK,kBAAkB,GACnB,oBAAoB,GACpB,sBAAsB,GACtB,eAAe,GACf,eAAe,GACf,wBAAwB,GACxB,YAAY,CAAC;AAEjB,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE;IACP,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,GACA,IAAI,CAyCN;AAED,wBAAgB,yBAAyB,IAAI,iBAAiB,GAAG;IAC/D,sBAAsB,EAAE,MAAM,CAAC;CAChC,CAOA;AAED,wBAAgB,mBAAmB,IAAI,IAAI,CAO1C"}
@@ -20,7 +20,13 @@ function createFreshRequestMetrics() {
20
20
  networkFetches: 0,
21
21
  networkMs: 0,
22
22
  fetchesByType: { page: 0, layout: 0, component: 0, api: 0, data: 0, config: 0, other: 0 },
23
- missReasons: { cold_start: 0, not_in_filelist: 0, invalidation: 0, no_filelist_cache: 0 },
23
+ missReasons: {
24
+ cold_start: 0,
25
+ not_in_filelist: 0,
26
+ invalidation: 0,
27
+ no_filelist_cache: 0,
28
+ indexed_without_content: 0,
29
+ },
24
30
  isPreviewMode: null,
25
31
  filesAccessed: new Set(),
26
32
  };
@@ -2,21 +2,31 @@ interface FileListCacheEntry {
2
2
  path: string;
3
3
  content?: string;
4
4
  }
5
+ export interface FileListMatchResult {
6
+ status: "unavailable" | "missing" | "present_without_content" | "hit";
7
+ fresh: boolean;
8
+ path?: string;
9
+ content?: string;
10
+ }
5
11
  export declare class FileListIndex {
6
12
  private readonly getFileListCache?;
7
13
  private index;
14
+ private pathSet;
8
15
  private indexKey;
9
16
  private indexBuiltAt;
17
+ private indexFresh;
10
18
  private readyPromise;
11
19
  constructor(getFileListCache?: (() => Promise<Array<FileListCacheEntry> | undefined>) | undefined);
12
20
  setReadyPromise(promise: Promise<void>): void;
13
21
  clear(): void;
14
22
  private ensureReady;
15
23
  lookup(normalizedPath: string): Promise<string | undefined>;
24
+ match(normalizedPath: string): Promise<FileListMatchResult>;
16
25
  findFirstWithContent(normalizedPaths: string[]): Promise<{
17
26
  path: string;
18
27
  content: string;
19
28
  } | undefined>;
29
+ findFirstMatch(normalizedPaths: string[]): Promise<FileListMatchResult>;
20
30
  private getOrBuild;
21
31
  }
22
32
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"file-list-index.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/file-list-index.ts"],"names":[],"mappings":"AAIA,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAeD,qBAAa,aAAa;IAOtB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IANpC,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAA8B;gBAG/B,gBAAgB,CAAC,GAAE,MAAM,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC,aAAA;IAG1F,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAI7C,KAAK,IAAI,IAAI;YAUC,WAAW;IAWnB,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IA4B3D,oBAAoB,CACxB,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;YAc3C,UAAU;CA0EzB"}
1
+ {"version":3,"file":"file-list-index.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/file-list-index.ts"],"names":[],"mappings":"AAIA,UAAU,kBAAkB;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,aAAa,GAAG,SAAS,GAAG,yBAAyB,GAAG,KAAK,CAAC;IACtE,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAeD,qBAAa,aAAa;IAStB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IARpC,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAA8B;gBAG/B,gBAAgB,CAAC,GAAE,MAAM,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC,aAAA;IAG1F,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAI7C,KAAK,IAAI,IAAI;YAYC,WAAW;IAWnB,MAAM,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAK3D,KAAK,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA8C3D,oBAAoB,CACxB,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;IAMnD,cAAc,CAClB,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAAC,mBAAmB,CAAC;YA6BjB,UAAU;CAoGzB"}
@@ -13,8 +13,10 @@ const INDEX_STALENESS_LIMIT_MS = 5 * 60 * 1000; // 5 minutes
13
13
  export class FileListIndex {
14
14
  getFileListCache;
15
15
  index = null;
16
+ pathSet = null;
16
17
  indexKey = null;
17
18
  indexBuiltAt = 0;
19
+ indexFresh = false;
18
20
  readyPromise = null;
19
21
  constructor(getFileListCache) {
20
22
  this.getFileListCache = getFileListCache;
@@ -27,8 +29,10 @@ export class FileListIndex {
27
29
  return;
28
30
  const indexedWithContent = this.index.size;
29
31
  this.index = null;
32
+ this.pathSet = null;
30
33
  this.indexKey = null;
31
34
  this.indexBuiltAt = 0;
35
+ this.indexFresh = false;
32
36
  logger.debug("Cleared file list index", { indexedWithContent });
33
37
  }
34
38
  async ensureReady() {
@@ -43,19 +47,35 @@ export class FileListIndex {
43
47
  }
44
48
  }
45
49
  async lookup(normalizedPath) {
50
+ const match = await this.match(normalizedPath);
51
+ return match.status === "hit" ? match.content : undefined;
52
+ }
53
+ async match(normalizedPath) {
46
54
  await this.ensureReady();
47
- const index = await this.getOrBuild();
48
- if (!index) {
55
+ const snapshot = await this.getOrBuild();
56
+ if (!snapshot) {
49
57
  logger.debug("No file list cache available");
50
- return undefined;
58
+ return { status: "unavailable", fresh: false };
51
59
  }
52
- const content = index.get(normalizedPath);
53
- if (!content) {
60
+ if (!snapshot.paths.has(normalizedPath)) {
54
61
  logger.debug("Content not in file list index", {
55
62
  path: normalizedPath,
56
- indexSize: index.size,
63
+ indexSize: snapshot.content.size,
64
+ fresh: snapshot.fresh,
57
65
  });
58
- return undefined;
66
+ return { status: "missing", fresh: snapshot.fresh };
67
+ }
68
+ const content = snapshot.content.get(normalizedPath);
69
+ if (!content) {
70
+ logger.debug("File list index contains path without inline content", {
71
+ path: normalizedPath,
72
+ fresh: snapshot.fresh,
73
+ });
74
+ return {
75
+ status: "present_without_content",
76
+ fresh: snapshot.fresh,
77
+ path: normalizedPath,
78
+ };
59
79
  }
60
80
  logger.debug("FILE_LIST_CACHE_HIT - serving from file list cache", {
61
81
  path: normalizedPath,
@@ -63,19 +83,43 @@ export class FileListIndex {
63
83
  contentHash: hashPreview(content),
64
84
  contentPreview: previewText(content, 200).replace(/\n/g, "\\n"),
65
85
  });
66
- return content;
86
+ return {
87
+ status: "hit",
88
+ fresh: snapshot.fresh,
89
+ path: normalizedPath,
90
+ content,
91
+ };
67
92
  }
68
93
  async findFirstWithContent(normalizedPaths) {
69
- await this.ensureReady();
70
- const index = await this.getOrBuild();
71
- if (!index)
94
+ const match = await this.findFirstMatch(normalizedPaths);
95
+ if (match.status !== "hit" || !match.path || !match.content)
72
96
  return undefined;
97
+ return { path: match.path, content: match.content };
98
+ }
99
+ async findFirstMatch(normalizedPaths) {
100
+ await this.ensureReady();
101
+ const snapshot = await this.getOrBuild();
102
+ if (!snapshot)
103
+ return { status: "unavailable", fresh: false };
73
104
  for (const path of normalizedPaths) {
74
- const content = index.get(path);
75
- if (content)
76
- return { path, content };
105
+ if (!snapshot.paths.has(path))
106
+ continue;
107
+ const content = snapshot.content.get(path);
108
+ if (content) {
109
+ return {
110
+ status: "hit",
111
+ fresh: snapshot.fresh,
112
+ path,
113
+ content,
114
+ };
115
+ }
116
+ return {
117
+ status: "present_without_content",
118
+ fresh: snapshot.fresh,
119
+ path,
120
+ };
77
121
  }
78
- return undefined;
122
+ return { status: "missing", fresh: snapshot.fresh };
79
123
  }
80
124
  async getOrBuild() {
81
125
  if (!this.getFileListCache) {
@@ -95,7 +139,12 @@ export class FileListIndex {
95
139
  indexSize: this.index.size,
96
140
  indexAgeMs: age,
97
141
  });
98
- return this.index;
142
+ this.indexFresh = false;
143
+ return {
144
+ content: this.index,
145
+ paths: this.pathSet ?? new Set(),
146
+ fresh: false,
147
+ };
99
148
  }
100
149
  logger.debug("getOrBuildFileListIndex: in-memory index too stale, discarding", {
101
150
  indexSize: this.index.size,
@@ -103,7 +152,9 @@ export class FileListIndex {
103
152
  staleLimitMs: INDEX_STALENESS_LIMIT_MS,
104
153
  });
105
154
  this.index = null;
155
+ this.pathSet = null;
106
156
  this.indexKey = null;
157
+ this.indexFresh = false;
107
158
  }
108
159
  logger.debug("[ReadOperations] getOrBuildFileListIndex: getFileListCache returned null/undefined");
109
160
  return null;
@@ -117,18 +168,27 @@ export class FileListIndex {
117
168
  sampleContentPreview: cacheCheckSample?.content?.slice(0, 200)?.replace(/\n/g, "\\n"),
118
169
  });
119
170
  const indexKey = `${fileList.length}:${fileList[0]?.path ?? ""}:${fileList[fileList.length - 1]?.path ?? ""}`;
120
- if (this.index && this.indexKey === indexKey) {
171
+ if (this.index && this.pathSet && this.indexKey === indexKey) {
121
172
  this.indexBuiltAt = Date.now();
122
- return this.index;
173
+ this.indexFresh = true;
174
+ return {
175
+ content: this.index,
176
+ paths: this.pathSet,
177
+ fresh: true,
178
+ };
123
179
  }
124
180
  const index = new Map();
181
+ const pathSet = new Set();
125
182
  for (const file of fileList) {
183
+ pathSet.add(file.path);
126
184
  if (file.content)
127
185
  index.set(file.path, file.content);
128
186
  }
129
187
  this.index = index;
188
+ this.pathSet = pathSet;
130
189
  this.indexKey = indexKey;
131
190
  this.indexBuiltAt = Date.now();
191
+ this.indexFresh = true;
132
192
  const sampleFile = fileList.find((f) => /welcome/i.test(f.path));
133
193
  const sampleContent = sampleFile?.content;
134
194
  logger.debug("Built file list index", {
@@ -139,6 +199,10 @@ export class FileListIndex {
139
199
  sampleContentHash: sampleContent ? hashPreview(sampleContent) : undefined,
140
200
  sampleContentPreview: sampleContent?.slice(0, 200)?.replace(/\n/g, "\\n"),
141
201
  });
142
- return index;
202
+ return {
203
+ content: index,
204
+ paths: pathSet,
205
+ fresh: true,
206
+ };
143
207
  }
144
208
  }
@@ -1,6 +1,9 @@
1
1
  import { READ_OPERATION_EXTENSION_PRIORITY } from "./extension-priority.js";
2
2
  import type { ResolvedContentContext } from "./types.js";
3
3
  export { READ_OPERATION_EXTENSION_PRIORITY };
4
+ export type NotFoundLikeError = Error & {
5
+ code?: string;
6
+ };
4
7
  interface ReadContextProviderLike {
5
8
  isProductionMode: () => boolean;
6
9
  isPersistentCacheInvalidated?: (prefix: string) => boolean;
@@ -28,9 +31,11 @@ interface BuildReadFetchStateOptions {
28
31
  export declare function assertProjectSourcePath(normalizedPath: string): void;
29
32
  export declare function buildReadFetchState(options: BuildReadFetchStateOptions): ReadFetchState;
30
33
  export declare function getResolvedCacheKey(cacheKeyPrefix: string, normalizedResolvedPath: string): string;
34
+ export declare function buildExtensionCandidatePaths(basePath: string): string[];
31
35
  export declare function splitKnownFileExtension(apiPath: string): {
32
36
  originalExtension: string;
33
37
  basePath: string;
34
38
  } | null;
35
39
  export declare function isNotFoundLikeError(error: unknown): boolean;
40
+ export declare function createNotFoundLikeError(path: string): NotFoundLikeError;
36
41
  //# sourceMappingURL=read-operations-helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"read-operations-helpers.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/read-operations-helpers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iCAAiC,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,iCAAiC,EAAE,CAAC;AAE7C,UAAU,uBAAuB;IAC/B,gBAAgB,EAAE,MAAM,OAAO,CAAC;IAChC,4BAA4B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3D,yBAAyB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5D;AAED,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACrC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1C,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,0BAA0B;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC9C,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/C;AAED,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAOpE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,cAAc,CA2BvF;AAED,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,sBAAsB,EAAE,MAAM,GAC7B,MAAM,CAER;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,GACd;IAAE,iBAAiB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASxD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG3D"}
1
+ {"version":3,"file":"read-operations-helpers.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/read-operations-helpers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iCAAiC,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,iCAAiC,EAAE,CAAC;AAE7C,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,UAAU,uBAAuB;IAC/B,gBAAgB,EAAE,MAAM,OAAO,CAAC;IAChC,4BAA4B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3D,yBAAyB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5D;AAED,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACrC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,oBAAoB,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1C,oBAAoB,EAAE,OAAO,CAAC;CAC/B;AAED,UAAU,0BAA0B;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC9C,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/C;AAED,wBAAgB,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAOpE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,cAAc,CA2BvF;AAED,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,sBAAsB,EAAE,MAAM,GAC7B,MAAM,CAER;AAED,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAEvE;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,GACd;IAAE,iBAAiB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CASxD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAG3D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAEvE"}
@@ -40,6 +40,9 @@ export function buildReadFetchState(options) {
40
40
  export function getResolvedCacheKey(cacheKeyPrefix, normalizedResolvedPath) {
41
41
  return `${cacheKeyPrefix}:${normalizedResolvedPath}`;
42
42
  }
43
+ export function buildExtensionCandidatePaths(basePath) {
44
+ return READ_OPERATION_EXTENSION_PRIORITY.map((ext) => `${basePath}${ext}`);
45
+ }
43
46
  export function splitKnownFileExtension(apiPath) {
44
47
  const extMatch = apiPath.match(/\.(tsx|ts|jsx|js|mdx|md)$/);
45
48
  if (!extMatch)
@@ -54,3 +57,6 @@ export function isNotFoundLikeError(error) {
54
57
  const errorMessage = error instanceof Error ? error.message : String(error);
55
58
  return errorMessage.includes("404") || errorMessage.includes("Not Found");
56
59
  }
60
+ export function createNotFoundLikeError(path) {
61
+ return Object.assign(new Error(`404 Not Found: ${path}`), { code: "ENOENT" });
62
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"read-operations.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/read-operations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAKnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAStD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,WAAW,sBAAsB;IACrC,gBAAgB,EAAE,MAAM,OAAO,CAAC;IAChC,YAAY,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAClC,iBAAiB,EAAE,MAAM,sBAAsB,GAAG,IAAI,CAAC;IACvD,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,OAAO,CACzB,KAAK,CAAC;QACJ,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,GAAG,SAAS,CACf,CAAC;IACF,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,0EAA0E;IAC1E,4BAA4B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3D,+CAA+C;IAC/C,yBAAyB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5D;AAUD,qBAAa,cAAc;IAWvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAfpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAI9B;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,mGAAmG;IACnG,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA6B;gBAGnD,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,cAAc,EAC1B,eAAe,CAAC,EAAE,sBAAsB,YAAA,EACxC,kBAAkB,CAAC,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,aAAA,EAC7C,gBAAgB,CAAC,GAAE,MAAM,OAAO,CAC/C,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,SAAS,CACtD,aAAA;IAKH,uBAAuB,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAIrD,kBAAkB,IAAI,IAAI;IAK1B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAY3C,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAY3C,OAAO,CAAC,mBAAmB;YAsBb,+BAA+B;YA0C/B,mBAAmB;IAgDjC,OAAO,CAAC,kBAAkB;YAoFZ,2BAA2B;YA8D3B,uCAAuC;IAmCrD,OAAO,CAAC,oBAAoB;YAgBd,YAAY;YAgGZ,qBAAqB;YA8DrB,qBAAqB;YAgDrB,+BAA+B;YA8D/B,iBAAiB;CAyBhC"}
1
+ {"version":3,"file":"read-operations.d.ts","sourceRoot":"","sources":["../../../../../../src/src/platform/adapters/fs/veryfront/read-operations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAKnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAWtD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,WAAW,sBAAsB;IACrC,gBAAgB,EAAE,MAAM,OAAO,CAAC;IAChC,YAAY,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAClC,iBAAiB,EAAE,MAAM,sBAAsB,GAAG,IAAI,CAAC;IACvD,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,OAAO,CACzB,KAAK,CAAC;QACJ,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,GAAG,SAAS,CACf,CAAC;IACF,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,0EAA0E;IAC1E,4BAA4B,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3D,+CAA+C;IAC/C,yBAAyB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5D;AAUD,qBAAa,cAAc;IAWvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IACjC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAfpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAI9B;IACH,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,mGAAmG;IACnG,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAA6B;gBAGnD,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,SAAS,EAChB,UAAU,EAAE,cAAc,EAC1B,eAAe,CAAC,EAAE,sBAAsB,YAAA,EACxC,kBAAkB,CAAC,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,aAAA,EAC7C,gBAAgB,CAAC,GAAE,MAAM,OAAO,CAC/C,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,SAAS,CACtD,aAAA;IAKH,uBAAuB,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI;IAIrD,kBAAkB,IAAI,IAAI;IAK1B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAY3C,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAY3C,OAAO,CAAC,mBAAmB;YAsBb,+BAA+B;YA0C/B,mBAAmB;IA0CjC,OAAO,CAAC,kBAAkB;YAoFZ,2BAA2B;YA8D3B,uCAAuC;IAmCrD,OAAO,CAAC,oBAAoB;YAgBd,YAAY;YA6JZ,qBAAqB;YA8DrB,qBAAqB;YAgDrB,+BAA+B;YA8D/B,iBAAiB;CAyBhC"}
@@ -4,7 +4,7 @@ import { logContentMetric } from "./content-metrics.js";
4
4
  import { FileListIndex } from "./file-list-index.js";
5
5
  import { InFlightRequestDeduper } from "./in-flight-dedupe.js";
6
6
  import { getRequestScopedFile, setRequestScopedFile } from "./multi-project-adapter.js";
7
- import { assertProjectSourcePath, buildReadFetchState, getResolvedCacheKey, isNotFoundLikeError, READ_OPERATION_EXTENSION_PRIORITY as EXTENSION_PRIORITY, splitKnownFileExtension, } from "./read-operations-helpers.js";
7
+ import { assertProjectSourcePath, buildExtensionCandidatePaths, buildReadFetchState, createNotFoundLikeError, getResolvedCacheKey, isNotFoundLikeError, READ_OPERATION_EXTENSION_PRIORITY as EXTENSION_PRIORITY, splitKnownFileExtension, } from "./read-operations-helpers.js";
8
8
  export { endRequestMetrics, getContentMetricsSnapshot, resetContentMetrics, startRequestMetrics, } from "./content-metrics.js";
9
9
  const logger = baseLogger.component("read-operations");
10
10
  const IN_FLIGHT_REQUEST_TIMEOUT_MS = 15_000;
@@ -113,10 +113,15 @@ export class ReadOperations {
113
113
  // - File list is refreshed on every WebSocket poke (websocket-manager.ts:483-500)
114
114
  // - Request-scoped cache ensures consistency within a single render
115
115
  // - Persistent cache is only written for production mode (to avoid staleness risk in preview)
116
- if (!skipPersistentCaches) {
117
- const fileListContent = await this.fileListIndex.lookup(normalizedPath);
118
- if (!fileListContent)
119
- return null;
116
+ if (skipPersistentCaches) {
117
+ logger.debug("Skipping file list cache due to invalidation", {
118
+ path: normalizedPath,
119
+ cacheKeyPrefix,
120
+ });
121
+ return { status: "unavailable", fresh: false };
122
+ }
123
+ const match = await this.fileListIndex.match(normalizedPath);
124
+ if (match.status === "hit" && match.content) {
120
125
  logContentMetric("FILE_LIST_HIT", {
121
126
  path: normalizedPath,
122
127
  mode: ctx?.sourceType ?? "unknown",
@@ -126,25 +131,13 @@ export class ReadOperations {
126
131
  // Only cache to persistent storage for production mode
127
132
  // Preview mode uses file list cache directly without persisting (fresher, WebSocket-driven)
128
133
  if (isProduction) {
129
- this.cache.set(cacheKey, fileListContent);
134
+ this.cache.set(cacheKey, match.content);
130
135
  }
131
- setRequestScopedFile(cacheKey, fileListContent);
132
- return fileListContent;
136
+ setRequestScopedFile(cacheKey, match.content);
133
137
  }
134
- // Skip only happens during cache invalidation (both preview and production)
135
- logContentMetric("CACHE_MISS", {
136
- path: normalizedPath,
137
- mode: ctx?.sourceType ?? "unknown",
138
- missReason: "invalidation",
139
- isPreviewMode,
140
- });
141
- logger.debug("Skipping file list cache due to invalidation", {
142
- path: normalizedPath,
143
- cacheKeyPrefix,
144
- });
145
- return null;
138
+ return match;
146
139
  }
147
- setupInFlightFetch(normalizedPath, apiPath, cacheKey, isPublished, isProduction, isPreviewMode, ctx) {
140
+ setupInFlightFetch(normalizedPath, apiPath, cacheKey, isPublished, isProduction, isPreviewMode, ctx, missReason) {
148
141
  const cleanupResult = this.inFlightRequests.cleanup();
149
142
  if (cleanupResult) {
150
143
  logger.warn("Cleaned up in-flight requests", cleanupResult);
@@ -159,11 +152,10 @@ export class ReadOperations {
159
152
  return existingEntry.promise;
160
153
  }
161
154
  // Track why we're making a network fetch (for optimization analysis)
162
- const hasFileListCache = !!this.getFileListCache;
163
155
  logContentMetric("CACHE_MISS", {
164
156
  path: normalizedPath,
165
157
  mode: ctx?.sourceType ?? "unknown",
166
- missReason: (hasFileListCache ? "not_in_filelist" : "no_filelist_cache"),
158
+ missReason,
167
159
  isPreviewMode,
168
160
  });
169
161
  // THIS IS A NETWORK FETCH - every call here = API round trip
@@ -248,10 +240,10 @@ export class ReadOperations {
248
240
  }
249
241
  }
250
242
  async tryResolveExtensionlessPathFromFileList(normalizedPath, cacheKeyPrefix, cacheKey, isProduction, ctx, isPreviewMode) {
251
- const candidatePaths = EXTENSION_PRIORITY.map((ext) => `${normalizedPath}${ext}`);
252
- const resolved = await this.fileListIndex.findFirstWithContent(candidatePaths);
253
- if (!resolved)
254
- return null;
243
+ const candidatePaths = buildExtensionCandidatePaths(normalizedPath);
244
+ const resolved = await this.fileListIndex.findFirstMatch(candidatePaths);
245
+ if (resolved.status !== "hit" || !resolved.path || !resolved.content)
246
+ return resolved;
255
247
  const resolvedCacheKey = getResolvedCacheKey(cacheKeyPrefix, resolved.path);
256
248
  this.extensionResolutionCache.set(normalizedPath, resolved.path);
257
249
  logContentMetric("FILE_LIST_HIT", {
@@ -268,7 +260,7 @@ export class ReadOperations {
268
260
  resolvedCacheKey: resolvedCacheKey === cacheKey ? undefined : resolvedCacheKey,
269
261
  });
270
262
  this.cacheResolvedContent(cacheKey, resolvedCacheKey, resolved.content, isProduction);
271
- return resolved.content;
263
+ return resolved;
272
264
  }
273
265
  cacheResolvedContent(cacheKey, resolvedCacheKey, content, persistToCache) {
274
266
  if (persistToCache) {
@@ -309,20 +301,47 @@ export class ReadOperations {
309
301
  const persistentCached = await this.getProductionPersistentCacheHit(normalizedPath, cacheKeyPrefix, cacheKey, isProduction, skipPersistentCaches, currentReleaseId, isPrefixInvalidated, ctx);
310
302
  if (persistentCached)
311
303
  return persistentCached;
312
- const fileListCached = await this.getFileListCacheHit(normalizedPath, cacheKeyPrefix, cacheKey, isProduction, skipPersistentCaches, isPreviewMode, ctx);
313
- if (fileListCached)
314
- return fileListCached;
304
+ const fileListMatch = await this.getFileListCacheHit(normalizedPath, cacheKeyPrefix, cacheKey, isProduction, skipPersistentCaches, isPreviewMode, ctx);
305
+ if (fileListMatch.status === "hit" && fileListMatch.content)
306
+ return fileListMatch.content;
307
+ if (fileListMatch.status === "present_without_content") {
308
+ return this.setupInFlightFetch(normalizedPath, apiPath, cacheKey, isPublished, isProduction, isPreviewMode, ctx, "indexed_without_content");
309
+ }
315
310
  if (!hasKnownExt) {
316
311
  if (!skipPersistentCaches) {
317
312
  const resolvedFromFileList = await this.tryResolveExtensionlessPathFromFileList(normalizedPath, cacheKeyPrefix, cacheKey, isProduction, ctx, isPreviewMode);
318
- if (resolvedFromFileList)
319
- return resolvedFromFileList;
313
+ if (resolvedFromFileList.status === "hit" && resolvedFromFileList.content) {
314
+ return resolvedFromFileList.content;
315
+ }
316
+ if (resolvedFromFileList.status === "present_without_content" &&
317
+ resolvedFromFileList.path) {
318
+ const resolvedCacheKey = getResolvedCacheKey(cacheKeyPrefix, resolvedFromFileList.path);
319
+ const resolvedApiPath = this.getOriginalApiPath?.(resolvedFromFileList.path) ??
320
+ resolvedFromFileList.path;
321
+ const fetchedResolved = await this.setupInFlightFetch(resolvedFromFileList.path, resolvedApiPath, resolvedCacheKey, isPublished, isProduction, isPreviewMode, ctx, "indexed_without_content");
322
+ this.extensionResolutionCache.set(normalizedPath, resolvedFromFileList.path);
323
+ this.cacheResolvedContent(cacheKey, resolvedCacheKey, fetchedResolved, isProduction && !skipPersistentCaches);
324
+ return fetchedResolved;
325
+ }
326
+ if (fileListMatch.status === "missing" &&
327
+ fileListMatch.fresh &&
328
+ resolvedFromFileList.status === "missing" &&
329
+ resolvedFromFileList.fresh) {
330
+ throw createNotFoundLikeError(normalizedPath);
331
+ }
320
332
  }
321
333
  const resolved = await this.tryResolveExtensionlessPath(apiPath, cacheKeyPrefix, cacheKey, isProduction, skipPersistentCaches);
322
334
  if (resolved)
323
335
  return resolved;
324
336
  }
325
- return this.setupInFlightFetch(normalizedPath, apiPath, cacheKey, isPublished, isProduction, isPreviewMode, ctx);
337
+ if (fileListMatch.status === "missing" && fileListMatch.fresh) {
338
+ throw createNotFoundLikeError(normalizedPath);
339
+ }
340
+ return this.setupInFlightFetch(normalizedPath, apiPath, cacheKey, isPublished, isProduction, isPreviewMode, ctx, skipPersistentCaches
341
+ ? "invalidation"
342
+ : fileListMatch.status === "missing" && fileListMatch.fresh
343
+ ? "not_in_filelist"
344
+ : "no_filelist_cache");
326
345
  }
327
346
  async fetchPublishedContent(normalizedPath, apiPath, cacheKey, releaseId, environmentName, shouldCache) {
328
347
  logger.debug("Fetching published content", {
@@ -1 +1 @@
1
- {"version":3,"file":"page-resolver.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/page-resolution/page-resolver.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAoCvD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAiB;gBAEpB,OAAO,EAAE,mBAAmB;IAOxC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAsDxC,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YAoCxB,sBAAsB;IAoD9B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAY1C,aAAa,IAAI,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC;CAShD"}
1
+ {"version":3,"file":"page-resolver.d.ts","sourceRoot":"","sources":["../../../../src/src/rendering/page-resolution/page-resolver.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAoCvD,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,cAAc,CAAC;CACzB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,OAAO,CAAiB;gBAEpB,OAAO,EAAE,mBAAmB;IAOxC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAiExC,WAAW,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YAoCxB,sBAAsB;IAoD9B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAY1C,aAAa,IAAI,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC;CAShD"}