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.
- package/esm/deno.js +1 -1
- package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts +4 -0
- package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/adapter.js +76 -0
- package/esm/src/platform/adapters/fs/veryfront/content-metrics.d.ts +1 -1
- package/esm/src/platform/adapters/fs/veryfront/content-metrics.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/content-metrics.js +7 -1
- package/esm/src/platform/adapters/fs/veryfront/file-list-index.d.ts +10 -0
- package/esm/src/platform/adapters/fs/veryfront/file-list-index.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/file-list-index.js +83 -19
- package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts +5 -0
- package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/read-operations-helpers.js +6 -0
- package/esm/src/platform/adapters/fs/veryfront/read-operations.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/read-operations.js +53 -34
- package/esm/src/rendering/page-resolution/page-resolver.d.ts.map +1 -1
- package/esm/src/rendering/page-resolution/page-resolver.js +10 -4
- package/esm/src/utils/version.d.ts +1 -1
- package/esm/src/utils/version.js +1 -1
- package/package.json +1 -1
- package/src/deno.js +1 -1
- package/src/src/platform/adapters/fs/veryfront/adapter.ts +87 -0
- package/src/src/platform/adapters/fs/veryfront/content-metrics.ts +13 -2
- package/src/src/platform/adapters/fs/veryfront/file-list-index.ts +101 -18
- package/src/src/platform/adapters/fs/veryfront/read-operations-helpers.ts +10 -0
- package/src/src/platform/adapters/fs/veryfront/read-operations.ts +87 -30
- package/src/src/rendering/page-resolution/page-resolver.ts +17 -6
- package/src/src/utils/version.ts +1 -1
package/esm/deno.js
CHANGED
|
@@ -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":"AAEA,OAAO,KAAK,EACV,UAAU,EAEV,cAAc,EACd,SAAS,EACT,eAAe,EAEf,sBAAsB,EACvB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qCAAqC,CAAC;AA2BnE,qBAAa,kBAAmB,YAAW,SAAS;IAClD,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,WAAW,CAAS;IAE5B,kFAAkF;IAClF,OAAO,CAAC,oBAAoB,CAA6B;
|
|
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,
|
|
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: {
|
|
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;
|
|
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
|
|
48
|
-
if (!
|
|
55
|
+
const snapshot = await this.getOrBuild();
|
|
56
|
+
if (!snapshot) {
|
|
49
57
|
logger.debug("No file list cache available");
|
|
50
|
-
return
|
|
58
|
+
return { status: "unavailable", fresh: false };
|
|
51
59
|
}
|
|
52
|
-
|
|
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:
|
|
63
|
+
indexSize: snapshot.content.size,
|
|
64
|
+
fresh: snapshot.fresh,
|
|
57
65
|
});
|
|
58
|
-
return
|
|
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
|
|
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.
|
|
70
|
-
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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;
|
|
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 (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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,
|
|
134
|
+
this.cache.set(cacheKey, match.content);
|
|
130
135
|
}
|
|
131
|
-
setRequestScopedFile(cacheKey,
|
|
132
|
-
return fileListContent;
|
|
136
|
+
setRequestScopedFile(cacheKey, match.content);
|
|
133
137
|
}
|
|
134
|
-
|
|
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
|
|
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 =
|
|
252
|
-
const resolved = await this.fileListIndex.
|
|
253
|
-
if (!resolved)
|
|
254
|
-
return
|
|
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
|
|
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
|
|
313
|
-
if (
|
|
314
|
-
return
|
|
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
|
-
|
|
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;
|
|
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"}
|