veryfront 0.1.1152 → 0.1.1153

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 (38) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/platform/adapters/base.d.ts +10 -0
  3. package/esm/src/platform/adapters/base.d.ts.map +1 -1
  4. package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts +15 -0
  5. package/esm/src/platform/adapters/fs/veryfront/adapter.d.ts.map +1 -1
  6. package/esm/src/platform/adapters/fs/veryfront/adapter.js +181 -17
  7. package/esm/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.d.ts.map +1 -1
  8. package/esm/src/platform/adapters/fs/veryfront/default-invalidation-callbacks.js +12 -7
  9. package/esm/src/platform/adapters/fs/veryfront/multi-project-adapter.d.ts +2 -0
  10. package/esm/src/platform/adapters/fs/veryfront/multi-project-adapter.d.ts.map +1 -1
  11. package/esm/src/platform/adapters/fs/veryfront/multi-project-adapter.js +28 -0
  12. package/esm/src/platform/adapters/fs/veryfront/types.d.ts +10 -7
  13. package/esm/src/platform/adapters/fs/veryfront/types.d.ts.map +1 -1
  14. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +3 -10
  15. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts.map +1 -1
  16. package/esm/src/platform/adapters/fs/veryfront/websocket-manager.js +39 -17
  17. package/esm/src/platform/adapters/fs/wrapper.d.ts +2 -0
  18. package/esm/src/platform/adapters/fs/wrapper.d.ts.map +1 -1
  19. package/esm/src/platform/adapters/fs/wrapper.js +8 -0
  20. package/esm/src/rendering/app-route-resolver.js +17 -19
  21. package/esm/src/rendering/router-detection.js +41 -10
  22. package/esm/src/server/handlers/request/agent-stream.handler.d.ts.map +1 -1
  23. package/esm/src/server/handlers/request/agent-stream.handler.js +1 -0
  24. package/esm/src/server/handlers/request/api/api-handler-wrapper.d.ts +1 -0
  25. package/esm/src/server/handlers/request/api/api-handler-wrapper.d.ts.map +1 -1
  26. package/esm/src/server/handlers/request/api/api-handler-wrapper.js +37 -3
  27. package/esm/src/server/handlers/request/api/pages-api-handler.d.ts +7 -1
  28. package/esm/src/server/handlers/request/api/pages-api-handler.d.ts.map +1 -1
  29. package/esm/src/server/handlers/request/api/pages-api-handler.js +26 -10
  30. package/esm/src/server/handlers/request/api/project-discovery.d.ts +6 -0
  31. package/esm/src/server/handlers/request/api/project-discovery.d.ts.map +1 -1
  32. package/esm/src/server/handlers/request/api/project-discovery.js +43 -11
  33. package/esm/src/server/runtime-handler/adapter-factory.d.ts.map +1 -1
  34. package/esm/src/server/runtime-handler/adapter-factory.js +10 -8
  35. package/esm/src/types/entities/getEntityInfo.js +0 -15
  36. package/esm/src/utils/version-constant.d.ts +1 -1
  37. package/esm/src/utils/version-constant.js +1 -1
  38. package/package.json +5 -5
@@ -506,32 +506,45 @@ export class WebSocketManager {
506
506
  parentDirs: Array.from(parentDirs),
507
507
  prefixes: ["file:", "stat:", "dir:"],
508
508
  });
509
- this.deps.invalidationCallbacks.invalidateModulePaths?.(changedPaths);
510
509
  const projectId = this.deps.client.getProjectId();
510
+ const invalidations = [
511
+ this.deps.invalidationCallbacks.invalidateModulePaths?.(changedPaths),
512
+ ];
511
513
  logger.debug("Clearing SSR module cache for HMR", {
512
514
  changedPaths,
513
515
  projectId,
514
516
  usePerProject: !!this.deps.invalidationCallbacks.clearSSRModuleCacheForProject,
515
517
  });
516
518
  if (this.deps.invalidationCallbacks.clearSSRModuleCacheForProject && projectId) {
517
- this.deps.invalidationCallbacks.clearSSRModuleCacheForProject(projectId);
519
+ invalidations.push(this.deps.invalidationCallbacks.clearSSRModuleCacheForProject(projectId));
518
520
  }
519
521
  else {
520
- this.deps.invalidationCallbacks.clearSSRModuleCache?.();
522
+ invalidations.push(this.deps.invalidationCallbacks.clearSSRModuleCache?.());
523
+ }
524
+ if (projectId) {
525
+ if (this.deps.invalidationCallbacks.clearRouterDetectionCacheForProject) {
526
+ invalidations.push(this.deps.invalidationCallbacks.clearRouterDetectionCacheForProject(projectId));
527
+ }
528
+ if (this.deps.invalidationCallbacks.clearProjectDiscoveryCacheForProject) {
529
+ invalidations.push(this.deps.invalidationCallbacks.clearProjectDiscoveryCacheForProject(projectId));
530
+ }
521
531
  }
522
532
  if (this.deps.invalidationCallbacks.clearRendererCacheForProject && projectId) {
523
- await this.deps.invalidationCallbacks.clearRendererCacheForProject(projectId);
533
+ invalidations.push(this.deps.invalidationCallbacks.clearRendererCacheForProject(projectId));
524
534
  }
525
535
  if (this.deps.invalidationCallbacks.clearProjectCSSCache && this.deps.projectSlug) {
526
- this.deps.invalidationCallbacks.clearProjectCSSCache(this.deps.projectSlug);
536
+ invalidations.push(this.deps.invalidationCallbacks.clearProjectCSSCache(this.deps.projectSlug));
537
+ }
538
+ const pendingInvalidations = invalidations.filter((invalidation) => invalidation !== undefined);
539
+ if (pendingInvalidations.length > 0) {
540
+ await Promise.all(pendingInvalidations);
527
541
  }
528
542
  if (contentContext?.sourceType === "branch") {
529
543
  await this.deps.cache.deleteByPrefixAsync("files:branch:");
530
544
  try {
531
545
  const files = await this.deps.client.listAllFiles();
532
546
  const cacheKey = buildFileListCacheKey(contentContext);
533
- await this.deps.setFileListCache(cacheKey, files);
534
- this.deps.clearFileListIndex();
547
+ await this.deps.replaceSourceSnapshot(cacheKey, files);
535
548
  preparedStyleArtifact = await this.deps.pregenerateStyles?.(files);
536
549
  logger.debug("Fresh files cached (memory + Redis)", {
537
550
  cacheKey,
@@ -597,24 +610,34 @@ export class WebSocketManager {
597
610
  this.deps.clearMemoryCaches();
598
611
  this.deps.invalidationCallbacks.clearDomainCache?.();
599
612
  const projectId = this.deps.client.getProjectId();
613
+ const invalidations = [];
600
614
  if (this.deps.invalidationCallbacks.clearSSRModuleCacheForProject && projectId) {
601
- this.deps.invalidationCallbacks.clearSSRModuleCacheForProject(projectId);
615
+ invalidations.push(this.deps.invalidationCallbacks.clearSSRModuleCacheForProject(projectId));
602
616
  }
603
617
  else {
604
- this.deps.invalidationCallbacks.clearSSRModuleCache?.();
618
+ invalidations.push(this.deps.invalidationCallbacks.clearSSRModuleCache?.());
605
619
  }
606
- if (this.deps.invalidationCallbacks.clearRouterDetectionCacheForProject && projectId) {
607
- this.deps.invalidationCallbacks.clearRouterDetectionCacheForProject(projectId);
620
+ if (projectId) {
621
+ if (this.deps.invalidationCallbacks.clearRouterDetectionCacheForProject) {
622
+ invalidations.push(this.deps.invalidationCallbacks.clearRouterDetectionCacheForProject(projectId));
623
+ }
624
+ if (this.deps.invalidationCallbacks.clearProjectDiscoveryCacheForProject) {
625
+ invalidations.push(this.deps.invalidationCallbacks.clearProjectDiscoveryCacheForProject(projectId));
626
+ }
608
627
  }
609
- this.deps.invalidationCallbacks.clearModulePathCache?.();
628
+ invalidations.push(this.deps.invalidationCallbacks.clearModulePathCache?.());
610
629
  if (this.deps.invalidationCallbacks.clearSnippetCacheForProject && this.deps.projectSlug) {
611
- this.deps.invalidationCallbacks.clearSnippetCacheForProject(this.deps.projectSlug);
630
+ invalidations.push(this.deps.invalidationCallbacks.clearSnippetCacheForProject(this.deps.projectSlug));
612
631
  }
613
632
  if (this.deps.invalidationCallbacks.clearRendererCacheForProject && projectId) {
614
- await this.deps.invalidationCallbacks.clearRendererCacheForProject(projectId);
633
+ invalidations.push(this.deps.invalidationCallbacks.clearRendererCacheForProject(projectId));
615
634
  }
616
635
  if (this.deps.invalidationCallbacks.clearProjectCSSCache && this.deps.projectSlug) {
617
- this.deps.invalidationCallbacks.clearProjectCSSCache(this.deps.projectSlug);
636
+ invalidations.push(this.deps.invalidationCallbacks.clearProjectCSSCache(this.deps.projectSlug));
637
+ }
638
+ const pendingInvalidations = invalidations.filter((invalidation) => invalidation !== undefined);
639
+ if (pendingInvalidations.length > 0) {
640
+ await Promise.all(pendingInvalidations);
618
641
  }
619
642
  const totalFileCount = fileBranchCount + fileReleaseCount + fileEnvCount;
620
643
  const totalStatCount = statBranchCount + statReleaseCount + statEnvCount;
@@ -630,8 +653,7 @@ export class WebSocketManager {
630
653
  try {
631
654
  const files = await this.deps.client.listAllFiles();
632
655
  const cacheKey = buildFileListCacheKey(contentContext);
633
- await this.deps.setFileListCache(cacheKey, files);
634
- this.deps.clearFileListIndex();
656
+ await this.deps.replaceSourceSnapshot(cacheKey, files);
635
657
  preparedStyleArtifact = await this.deps.pregenerateStyles?.(files);
636
658
  logger.debug("FRESH FILES FETCHED", {
637
659
  cacheKey,
@@ -35,6 +35,8 @@ export declare class NotSupportedError extends Error {
35
35
  export declare class FSAdapterWrapper implements ExtendedFileSystemAdapter {
36
36
  private readonly _fsAdapter;
37
37
  readonly refreshSourceSnapshot?: (reason?: string) => Promise<void>;
38
+ readonly ensureSourceSnapshotFresh?: (reason?: string) => Promise<void>;
39
+ readonly getSourceSnapshotVersion?: () => number | undefined | Promise<number | undefined>;
38
40
  constructor(fsAdapter: FSAdapter);
39
41
  getUnderlyingAdapter(): FSAdapter;
40
42
  getAdapterType(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/adapters/fs/wrapper.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"wrapper.d.ts","sourceRoot":"","sources":["../../../../../src/src/platform/adapters/fs/wrapper.ts"],"names":[],"mappings":"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"}
@@ -37,11 +37,19 @@ function isContextualAdapter(adapter) {
37
37
  export class FSAdapterWrapper {
38
38
  _fsAdapter;
39
39
  refreshSourceSnapshot;
40
+ ensureSourceSnapshotFresh;
41
+ getSourceSnapshotVersion;
40
42
  constructor(fsAdapter) {
41
43
  this._fsAdapter = fsAdapter;
42
44
  if (typeof fsAdapter.refreshSourceSnapshot === "function") {
43
45
  this.refreshSourceSnapshot = (reason) => fsAdapter.refreshSourceSnapshot.call(fsAdapter, reason);
44
46
  }
47
+ if (typeof fsAdapter.ensureSourceSnapshotFresh === "function") {
48
+ this.ensureSourceSnapshotFresh = (reason) => fsAdapter.ensureSourceSnapshotFresh.call(fsAdapter, reason);
49
+ }
50
+ if (typeof fsAdapter.getSourceSnapshotVersion === "function") {
51
+ this.getSourceSnapshotVersion = () => fsAdapter.getSourceSnapshotVersion.call(fsAdapter);
52
+ }
45
53
  }
46
54
  getUnderlyingAdapter() {
47
55
  return this._fsAdapter;
@@ -6,7 +6,7 @@
6
6
  * - Dynamic segment matching ([id], [...slug], etc.)
7
7
  * - Page file loading with frontmatter extraction
8
8
  */
9
- import { isDynamicSegment } from "../utils/route-path-utils.js";
9
+ import { isCatchAllSegment, isDynamicSegment } from "../utils/route-path-utils.js";
10
10
  import { join } from "../platform/compat/path/index.js";
11
11
  import { extract } from "../platform/compat/std/front-matter-yaml.js";
12
12
  export async function getAppRouteEntity(projectDir, slug, adapter, appDirName = "app") {
@@ -53,22 +53,11 @@ async function tryDynamicMatch(projectDir, slug, adapter, appDirName) {
53
53
  const segments = slug ? slug.split("/").filter(Boolean) : [];
54
54
  let currentDir = join(projectDir, appDirName);
55
55
  for (const segment of segments) {
56
- const exactPath = join(currentDir, segment);
57
- try {
58
- const stat = await adapter.fs.stat(exactPath);
59
- if (stat.isDirectory) {
60
- currentDir = exactPath;
61
- continue;
62
- }
63
- }
64
- catch (_) {
65
- /* expected: exact path may not exist, try dynamic segments */
66
- }
67
- const dynamic = await findDynamicDir(currentDir, adapter);
68
- if (!dynamic)
56
+ const routeDirectory = await findRouteDirectory(currentDir, segment, adapter);
57
+ if (!routeDirectory)
69
58
  return null;
70
- currentDir = join(currentDir, dynamic.name);
71
- if (dynamic.isCatchAll)
59
+ currentDir = join(currentDir, routeDirectory.name);
60
+ if (routeDirectory.isCatchAll)
72
61
  break;
73
62
  }
74
63
  for (const ext of [".mdx", ".md", ".tsx", ".jsx", ".ts", ".js"]) {
@@ -79,14 +68,23 @@ async function tryDynamicMatch(projectDir, slug, adapter, appDirName) {
79
68
  }
80
69
  return null;
81
70
  }
82
- async function findDynamicDir(dir, adapter) {
71
+ async function findRouteDirectory(dir, segment, adapter) {
83
72
  try {
84
73
  const entries = await adapter.fs.readDir(dir);
74
+ let dynamic = null;
85
75
  for await (const entry of entries) {
86
- if (!entry.isDirectory || !isDynamicSegment(entry.name))
76
+ if (!entry.isDirectory && !entry.isSymlink)
87
77
  continue;
88
- return { name: entry.name, isCatchAll: entry.name.startsWith("[...") };
78
+ if (entry.name === segment)
79
+ return { name: entry.name, isCatchAll: false };
80
+ if (!dynamic && isDynamicSegment(entry.name)) {
81
+ dynamic = {
82
+ name: entry.name,
83
+ isCatchAll: isCatchAllSegment(entry.name),
84
+ };
85
+ }
89
86
  }
87
+ return dynamic;
90
88
  }
91
89
  catch (_) {
92
90
  /* expected: adapter.fs.readDir may fail for npm compatibility */
@@ -6,7 +6,7 @@
6
6
  * - Directory structure analysis
7
7
  * - Route file presence detection
8
8
  **************************/
9
- import { isAbsolute, join, normalize, relative } from "../platform/compat/path/index.js";
9
+ import { basename, dirname, isAbsolute, join, normalize, relative } from "../platform/compat/path/index.js";
10
10
  import { createFileSystem } from "../platform/compat/fs.js";
11
11
  import { rendererLogger } from "../utils/index.js";
12
12
  import { LRUCache } from "../utils/lru-wrapper.js";
@@ -113,14 +113,12 @@ export async function detectAppRouter(projectDir, config, adapter, options) {
113
113
  async function detectAppRouterImpl(projectDir, config, adapter) {
114
114
  const appDirName = config?.directories?.app ?? "app";
115
115
  const pagesDirName = config?.directories?.pages ?? "pages";
116
- const appDir = join(projectDir, appDirName);
117
- const pagesDir = join(projectDir, pagesDirName);
118
- const [appStat, pagesStat] = await Promise.all([
119
- statWithFallback(appDir, adapter),
120
- statWithFallback(pagesDir, adapter),
116
+ const appDir = normalize(join(projectDir, appDirName));
117
+ const pagesDir = normalize(join(projectDir, pagesDirName));
118
+ const [hasAppDir, hasPagesDir] = await Promise.all([
119
+ directoryExists(appDir, projectDir, adapter),
120
+ directoryExists(pagesDir, projectDir, adapter),
121
121
  ]);
122
- const hasAppDir = Boolean(appStat?.isDirectory);
123
- const hasPagesDir = Boolean(pagesStat?.isDirectory);
124
122
  if (hasAppDir && (await hasRouteFiles(appDir, adapter)))
125
123
  return true;
126
124
  if (hasPagesDir && (await hasRouteFiles(pagesDir, adapter)))
@@ -131,7 +129,23 @@ async function detectAppRouterImpl(projectDir, config, adapter) {
131
129
  }
132
130
  const ROUTE_EXTENSIONS = new Set([".mdx", ".md", ".tsx", ".jsx", ".ts", ".js"]);
133
131
  const ROUTE_PATTERNS = ["page", "layout", "error", "loading", "not-found", "index"];
134
- async function hasRouteFiles(dir, adapter) {
132
+ async function resolveTraversalKey(dir, adapter, requireCanonicalPath) {
133
+ if (!adapter.fs.realPath) {
134
+ return requireCanonicalPath ? null : normalize(dir);
135
+ }
136
+ try {
137
+ return normalize(await adapter.fs.realPath(dir));
138
+ }
139
+ catch (_) {
140
+ /* expected: virtual adapters may not resolve physical paths */
141
+ return requireCanonicalPath ? null : normalize(dir);
142
+ }
143
+ }
144
+ async function hasRouteFiles(dir, adapter, visited = new Set(), requireCanonicalPath = false) {
145
+ const traversalKey = await resolveTraversalKey(dir, adapter, requireCanonicalPath);
146
+ if (traversalKey === null || visited.has(traversalKey))
147
+ return false;
148
+ visited.add(traversalKey);
135
149
  const entries = await readDirWithFallback(dir, adapter);
136
150
  for (const entry of entries) {
137
151
  if (entry.isFile) {
@@ -143,7 +157,12 @@ async function hasRouteFiles(dir, adapter) {
143
157
  }
144
158
  continue;
145
159
  }
146
- if (entry.isDirectory && (await hasRouteFiles(join(dir, entry.name), adapter))) {
160
+ if (entry.isDirectory &&
161
+ (await hasRouteFiles(join(dir, entry.name), adapter, visited))) {
162
+ return true;
163
+ }
164
+ if (entry.isSymlink &&
165
+ (await hasRouteFiles(join(dir, entry.name), adapter, visited, true))) {
147
166
  return true;
148
167
  }
149
168
  }
@@ -164,6 +183,18 @@ async function withAdapterFallback(adapterFn, fallbackFn, defaultValue) {
164
183
  }
165
184
  }
166
185
  }
186
+ async function directoryExists(path, projectDir, adapter) {
187
+ if (normalize(path) === normalize(projectDir))
188
+ return true;
189
+ const parentEntries = await readDirWithFallback(dirname(path), adapter);
190
+ const directoryName = basename(path);
191
+ const listed = parentEntries.some((entry) => entry.name === directoryName && (entry.isDirectory || entry.isSymlink));
192
+ if (listed || adapter.id !== "cloudflare")
193
+ return listed;
194
+ // Cloudflare KV represents directories only as key prefixes, so its parent
195
+ // listing can omit a directory that stat can still resolve.
196
+ return Boolean((await statWithFallback(path, adapter))?.isDirectory);
197
+ }
167
198
  async function statWithFallback(path, adapter) {
168
199
  const fs = createFileSystem();
169
200
  return await withAdapterFallback(async () => (await adapter.fs.stat(path)), async () => {
@@ -1 +1 @@
1
- {"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAGpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;AAchD,OAAO,EACL,4BAA4B,EAE7B,MAAM,2CAA2C,CAAC;AA0BnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBnG,MAAM,WAAW,sBACf,SAAQ,yBAAyB,EAAE,+BAA+B;IAClE,4BAA4B,CAAC,EAAE,OAAO,4BAA4B,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,+BAA+B,CAAC,YAAY,CAAC,CAAC;CACpF;AAugBD,qBAAa,kBAAmB,SAAQ,WAAW;IASrC,OAAO,CAAC,QAAQ,CAAC,IAAI;IARjC,QAAQ,EAAE,eAAe,CAMvB;gBAE2B,IAAI,GAAE,sBAAoC;IAIvE,OAAO,CAAC,sBAAsB;IAsBxB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA6NxE"}
1
+ {"version":3,"file":"agent-stream.handler.d.ts","sourceRoot":"","sources":["../../../../../src/src/server/handlers/request/agent-stream.handler.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,yBAAyB,EAAE,MAAM,oCAAoC,CAAC;AAGpF,OAAO,EAEL,KAAK,+BAA+B,EACrC,MAAM,wCAAwC,CAAC;AAchD,OAAO,EACL,4BAA4B,EAE7B,MAAM,2CAA2C,CAAC;AA0BnD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAmB,aAAa,EAAE,MAAM,aAAa,CAAC;AAkBnG,MAAM,WAAW,sBACf,SAAQ,yBAAyB,EAAE,+BAA+B;IAClE,4BAA4B,CAAC,EAAE,OAAO,4BAA4B,CAAC;IACnE,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,+BAA+B,CAAC,YAAY,CAAC,CAAC;CACpF;AAwgBD,qBAAa,kBAAmB,SAAQ,WAAW;IASrC,OAAO,CAAC,QAAQ,CAAC,IAAI;IARjC,QAAQ,EAAE,eAAe,CAMvB;gBAE2B,IAAI,GAAE,sBAAoC;IAIvE,OAAO,CAAC,sBAAsB;IAsBxB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;CA6NxE"}
@@ -232,6 +232,7 @@ async function resolveAgentSourceConfig(ctx, sourceContext) {
232
232
  if (!cacheKey) {
233
233
  throw new Error("Explicit agent source requires a project identity");
234
234
  }
235
+ await ctx.adapter.fs.ensureSourceSnapshotFresh?.("agent-source-config");
235
236
  return await getConfig(ctx.projectDir, ctx.adapter, {
236
237
  cacheKey,
237
238
  sourceContext: buildAgentSourceRunOptions(sourceContext),
@@ -9,5 +9,6 @@ export declare class ApiHandlerWrapper extends BaseHandler {
9
9
  initialize(): Promise<void>;
10
10
  handle(req: Request, ctx: HandlerContext): Promise<HandlerResult>;
11
11
  private handleWithContext;
12
+ private isPageRequest;
12
13
  }
13
14
  //# sourceMappingURL=api-handler-wrapper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-handler-wrapper.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/api/api-handler-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,gBAAgB,CAAC;AAsBxB,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAiE;IAChF,OAAO,CAAC,WAAW,CAA8B;IAEjD,QAAQ,EAAE,eAAe,CAGvB;gBAGA,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,uCAAuC,EAAE,cAAc;IAOnE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAmDvE,OAAO,CAAC,iBAAiB;CA2D1B"}
1
+ {"version":3,"file":"api-handler-wrapper.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/api/api-handler-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AACrD,OAAO,KAAK,EACV,cAAc,EACd,eAAe,EAEf,aAAa,EACd,MAAM,gBAAgB,CAAC;AA2BxB,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAiE;IAChF,OAAO,CAAC,WAAW,CAA8B;IAEjD,QAAQ,EAAE,eAAe,CAGvB;gBAGA,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,uCAAuC,EAAE,cAAc;IAOnE,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAW3B,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;YAmDzD,iBAAiB;YAkFjB,aAAa;CAuB5B"}
@@ -1,8 +1,9 @@
1
1
  import { BaseHandler } from "../../response/base.js";
2
- import { getApiHandler, withApiHandler } from "./pages-api-handler.js";
2
+ import { ensurePreviewSourceSnapshotFresh, getApiHandler, withApiHandler, } from "./pages-api-handler.js";
3
3
  import { PRIORITY_MEDIUM_API } from "../../../../utils/constants/index.js";
4
4
  import { withSpan } from "../../../../observability/tracing/otlp-setup.js";
5
5
  import { ensureProjectDiscovery } from "./project-discovery.js";
6
+ import { PageResolver } from "../../../../rendering/page-resolution/page-resolver.js";
6
7
  export class ApiHandlerWrapper extends BaseHandler {
7
8
  projectDir;
8
9
  adapter;
@@ -54,13 +55,27 @@ export class ApiHandlerWrapper extends BaseHandler {
54
55
  environmentName: ctx.environmentName,
55
56
  });
56
57
  }
57
- handleWithContext(req, ctx, pathname) {
58
+ async handleWithContext(req, ctx, pathname) {
58
59
  return withSpan("api.handleWithContext", async () => {
59
60
  try {
61
+ // WebSocket pokes update mutable previews immediately. This bounded,
62
+ // coalesced check is the fallback for missed pokes and establishes
63
+ // one source snapshot for route and primitive discovery.
64
+ await ensurePreviewSourceSnapshotFresh(ctx);
65
+ const canResolveAsPage = pathname !== "/api" &&
66
+ !pathname.startsWith("/api/") &&
67
+ (req.method === "GET" || req.method === "HEAD");
68
+ let isPageRequest = false;
69
+ if (canResolveAsPage) {
70
+ isPageRequest = await this.isPageRequest(pathname, ctx);
71
+ }
72
+ if (isPageRequest) {
73
+ return this.continue();
74
+ }
60
75
  // Lazy per-project primitive discovery (agents, tools) on first access.
61
76
  // Must run within runWithContext so VFS and registry scope are correct.
62
77
  await ensureProjectDiscovery(ctx);
63
- const apiRes = await withApiHandler(ctx, (api) => api.handle(req, ctx));
78
+ const apiRes = await withApiHandler(ctx, (api) => api.handle(req, ctx), { sourceSnapshotReady: true });
64
79
  if (!apiRes) {
65
80
  this.logDebug("[API-Wrapper] API handler returned null, continuing to next handler", { pathname }, ctx);
66
81
  return this.continue();
@@ -88,4 +103,23 @@ export class ApiHandlerWrapper extends BaseHandler {
88
103
  "api.projectSlug": ctx.projectSlug ?? "unknown",
89
104
  });
90
105
  }
106
+ async isPageRequest(pathname, ctx) {
107
+ const slug = pathname === "/" ? "" : pathname.replace(/^\/+|\/+$/g, "");
108
+ const pageResolver = new PageResolver({
109
+ projectDir: ctx.projectDir,
110
+ projectId: ctx.projectId,
111
+ config: ctx.config ?? {},
112
+ adapter: ctx.adapter,
113
+ });
114
+ try {
115
+ return await pageResolver.pageExists(slug);
116
+ }
117
+ catch (error) {
118
+ this.logDebug("[API-Wrapper] Page ownership is indeterminate; preserving API discovery", {
119
+ pathname,
120
+ error: this.getErrorMessage(error),
121
+ }, ctx);
122
+ return false;
123
+ }
124
+ }
91
125
  }
@@ -31,13 +31,18 @@ export declare class LRUHandlerCache<T> implements HandlerCache<T> {
31
31
  values(): IterableIterator<T>;
32
32
  }
33
33
  export declare function __injectCacheForTests(cache: HandlerCache<Promise<APIRouteHandler>> | null): void;
34
+ export declare function ensurePreviewSourceSnapshotFresh(ctx: HandlerContext): Promise<void>;
35
+ export declare function refreshPreviewSourceSnapshot(ctx: HandlerContext): Promise<void>;
36
+ interface ApiHandlerOptions {
37
+ sourceSnapshotReady?: boolean;
38
+ }
34
39
  export declare function getApiHandler(ctx: HandlerContext): Promise<APIRouteHandler>;
35
40
  /**
36
41
  * Use an API handler while holding a lease that defers cache-eviction cleanup.
37
42
  * The lease begins before the handler promise is awaited, closing the gap
38
43
  * between a cache lookup and APIRouteHandler.handle() registering the request.
39
44
  */
40
- export declare function withApiHandler<T>(ctx: HandlerContext, use: (handler: APIRouteHandler) => T | Promise<T>): Promise<T>;
45
+ export declare function withApiHandler<T>(ctx: HandlerContext, use: (handler: APIRouteHandler) => T | Promise<T>, options?: ApiHandlerOptions): Promise<T>;
41
46
  export declare function resetApiHandler(projectDir?: string): Promise<void>;
42
47
  /**
43
48
  * Reset cached API handlers for a specific project slug.
@@ -47,4 +52,5 @@ export declare function resetApiHandler(projectDir?: string): Promise<void>;
47
52
  * all entries and destroy those scoped to the project slug.
48
53
  */
49
54
  export declare function resetApiHandlerForProject(projectSlug: string): Promise<void>;
55
+ export {};
50
56
  //# sourceMappingURL=pages-api-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pages-api-handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/api/pages-api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAO/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIrD,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACjC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAAC;CAC/B;AAID,qBAAa,eAAe,CAAC,CAAC,CAAE,YAAW,YAAY,CAAC,CAAC,CAAC;IACxD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,uBAAuB,CAAS;gBAGtC,OAAO,GAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;KACzB;IAUR,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAG/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAGhC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAU5B,KAAK,IAAI,IAAI;IAQb,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAGvC,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;CAG/B;AAqBD,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,GAAG,IAAI,GACnD,IAAI,CAEN;AAiHD,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAEjF;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,CAAC,EACpC,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAChD,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED,wBAAsB,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAaxE;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAcf"}
1
+ {"version":3,"file":"pages-api-handler.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/api/pages-api-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAO/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAIrD,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAC;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,CAAC;IACjC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B,KAAK,IAAI,IAAI,CAAC;IACd,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IACzC,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC,CAAC;CAC/B;AAID,qBAAa,eAAe,CAAC,CAAC,CAAE,YAAW,YAAY,CAAC,CAAC,CAAC;IACxD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,uBAAuB,CAAS;gBAGtC,OAAO,GAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;KACzB;IAUR,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAG/B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAGhC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAU5B,KAAK,IAAI,IAAI;IAQb,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAGvC,MAAM,IAAI,gBAAgB,CAAC,CAAC,CAAC;CAG/B;AAqBD,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,GAAG,IAAI,GACnD,IAAI,CAEN;AA8BD,wBAAsB,gCAAgC,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAUzF;AAED,wBAAsB,4BAA4B,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrF;AAED,UAAU,iBAAiB;IACzB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AAyFD,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAEjF;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,CAAC,EACpC,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EACjD,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,CAAC,CAAC,CAUZ;AAED,wBAAsB,eAAe,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAaxE;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -91,17 +91,33 @@ function shouldCacheApiHandler(ctx) {
91
91
  // Cannot confirm a production context → do not cache.
92
92
  return getApiHandlerCacheContext(ctx)?.mode === "production";
93
93
  }
94
- async function refreshPreviewSourceSnapshot(ctx) {
94
+ function hasMutablePreviewSource(ctx) {
95
95
  if (!ctx.projectSlug)
96
- return;
96
+ return false;
97
97
  const cacheContext = getApiHandlerCacheContext(ctx);
98
98
  // Skip when production, or when the context is indeterminate.
99
- if (!cacheContext || cacheContext.mode === "production")
99
+ return !!cacheContext && cacheContext.mode !== "production";
100
+ }
101
+ export async function ensurePreviewSourceSnapshotFresh(ctx) {
102
+ if (!hasMutablePreviewSource(ctx))
103
+ return;
104
+ if (ctx.adapter.fs.ensureSourceSnapshotFresh) {
105
+ await ctx.adapter.fs.ensureSourceSnapshotFresh("preview-request-routing");
106
+ return;
107
+ }
108
+ // Backward compatibility for custom remote adapters that only implement the
109
+ // original unconditional refresh contract.
110
+ await refreshPreviewSourceSnapshot(ctx);
111
+ }
112
+ export async function refreshPreviewSourceSnapshot(ctx) {
113
+ if (!hasMutablePreviewSource(ctx))
100
114
  return;
101
115
  await ctx.adapter.fs.refreshSourceSnapshot?.("preview-api-route-discovery");
102
116
  }
103
- async function createApiHandler(ctx) {
104
- await refreshPreviewSourceSnapshot(ctx);
117
+ async function createApiHandler(ctx, options = {}) {
118
+ if (!options.sourceSnapshotReady) {
119
+ await ensurePreviewSourceSnapshotFresh(ctx);
120
+ }
105
121
  const handler = new APIRouteHandler(ctx.projectDir, ctx.adapter);
106
122
  await handler.initialize();
107
123
  return handler;
@@ -156,15 +172,15 @@ function retainHandler(promise) {
156
172
  }
157
173
  };
158
174
  }
159
- function getApiHandlerPromise(ctx) {
175
+ function getApiHandlerPromise(ctx, options = {}) {
160
176
  if (!shouldCacheApiHandler(ctx)) {
161
- return { promise: createApiHandler(ctx), cached: false };
177
+ return { promise: createApiHandler(ctx, options), cached: false };
162
178
  }
163
179
  const cache = getCache();
164
180
  const key = getCacheKey(ctx);
165
181
  let promise = cache.get(key);
166
182
  if (!promise) {
167
- promise = createApiHandler(ctx);
183
+ promise = createApiHandler(ctx, options);
168
184
  cache.set(key, promise);
169
185
  }
170
186
  return { promise, cached: true };
@@ -177,8 +193,8 @@ export async function getApiHandler(ctx) {
177
193
  * The lease begins before the handler promise is awaited, closing the gap
178
194
  * between a cache lookup and APIRouteHandler.handle() registering the request.
179
195
  */
180
- export async function withApiHandler(ctx, use) {
181
- const { promise, cached } = getApiHandlerPromise(ctx);
196
+ export async function withApiHandler(ctx, use, options = {}) {
197
+ const { promise, cached } = getApiHandlerPromise(ctx, options);
182
198
  const release = retainHandler(promise);
183
199
  try {
184
200
  return await use(await promise);
@@ -7,4 +7,10 @@ import type { HandlerContext } from "../../types.js";
7
7
  * correct project scope.
8
8
  */
9
9
  export declare function ensureProjectDiscovery(ctx: HandlerContext): Promise<DiscoveryResult>;
10
+ /**
11
+ * Drop completed and in-flight discovery records for every source scope owned
12
+ * by a project. The next scoped request rebuilds registries transactionally
13
+ * from its current source snapshot.
14
+ */
15
+ export declare function clearProjectDiscoveryCacheForProject(projectId: string): void;
10
16
  //# sourceMappingURL=project-discovery.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"project-discovery.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/api/project-discovery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAOtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAyHrD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAoF1F"}
1
+ {"version":3,"file":"project-discovery.d.ts","sourceRoot":"","sources":["../../../../../../src/src/server/handlers/request/api/project-discovery.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAOtE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AA4HrD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC,CAyG1F;AAED;;;;GAIG;AACH,wBAAgB,oCAAoC,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAM5E"}
@@ -68,15 +68,17 @@ function discoveryKey(ctx) {
68
68
  if (registryScope) {
69
69
  return registryScope.scopeId;
70
70
  }
71
- const slug = ctx.projectSlug ?? ctx.projectDir;
71
+ const projectIdentity = ctx.projectId ?? ctx.projectSlug ?? ctx.projectDir;
72
72
  const environment = ctx.enriched?.environment ?? ctx.resolvedEnvironment ??
73
73
  (ctx.releaseId ? "production" : "preview");
74
74
  if (environment === "production") {
75
- return ctx.releaseId ? `${slug}:release:${ctx.releaseId}` : `${slug}:production:unreleased`;
75
+ return ctx.releaseId
76
+ ? `${projectIdentity}:release:${ctx.releaseId}`
77
+ : `${projectIdentity}:production:unreleased`;
76
78
  }
77
79
  const branch = ctx.requestContext?.branch ?? ctx.enriched?.branch ?? ctx.parsedDomain?.branch ??
78
80
  "main";
79
- return `${slug}:preview:${branch}`;
81
+ return `${projectIdentity}:preview:${branch}`;
80
82
  }
81
83
  function shouldCacheCompletedDiscovery(ctx) {
82
84
  const registryScope = tryGetRegistryScopeContext();
@@ -94,17 +96,24 @@ function shouldCacheCompletedDiscovery(ctx) {
94
96
  * correct project scope.
95
97
  */
96
98
  export async function ensureProjectDiscovery(ctx) {
99
+ await ctx.adapter.fs.ensureSourceSnapshotFresh?.("primitive-discovery");
97
100
  const key = discoveryKey(ctx);
98
- const cacheCompletedDiscovery = shouldCacheCompletedDiscovery(ctx);
101
+ const sourceSnapshotVersion = await ctx.adapter.fs.getSourceSnapshotVersion?.();
102
+ const cacheCompletedDiscovery = shouldCacheCompletedDiscovery(ctx) ||
103
+ sourceSnapshotVersion !== undefined;
99
104
  const existing = discoveredProjects.get(key);
100
- if (existing)
105
+ if (existing &&
106
+ (sourceSnapshotVersion === undefined ||
107
+ existing.sourceSnapshotVersion === sourceSnapshotVersion)) {
101
108
  return existing.promise;
109
+ }
102
110
  const discovery = {
111
+ sourceSnapshotVersion,
103
112
  promise: (async () => {
104
- const { clearTranspileCache, discoverAll } = await import("../../../../discovery/index.js");
105
- const { agentRegistry } = await import("../../../../agent/composition/composition.js");
106
- const { toolRegistry } = await import("../../../../tool/registry.js");
107
113
  return await runWithRegistryTransaction(async () => {
114
+ const { clearTranspileCache, discoverAll } = await import("../../../../discovery/index.js");
115
+ const { agentRegistry } = await import("../../../../agent/composition/composition.js");
116
+ const { toolRegistry } = await import("../../../../tool/registry.js");
108
117
  // Clear stale entries in a transaction-local copy. Concurrent runs keep
109
118
  // using the prior live registry until discovery succeeds and the staged
110
119
  // replacement is committed atomically.
@@ -146,11 +155,22 @@ export async function ensureProjectDiscovery(ctx) {
146
155
  };
147
156
  discoveredProjects.set(key, discovery);
148
157
  try {
149
- return await discovery.promise;
158
+ const result = await discovery.promise;
159
+ if (result.errors.length > 0) {
160
+ const current = discoveredProjects.get(key);
161
+ if (current === discovery) {
162
+ discoveredProjects.delete(key);
163
+ }
164
+ }
165
+ return result;
150
166
  }
151
167
  catch (error) {
152
- // Allow retry on next request
153
- discoveredProjects.delete(key);
168
+ // A superseded generation may already own this key. Delete only our own
169
+ // record so the newer discovery remains reusable after it completes.
170
+ const current = discoveredProjects.get(key);
171
+ if (current === discovery) {
172
+ discoveredProjects.delete(key);
173
+ }
154
174
  logger.warn("Primitive discovery failed (will retry)", {
155
175
  projectSlug: ctx.projectSlug,
156
176
  error: error instanceof Error ? error.message : String(error),
@@ -170,3 +190,15 @@ export async function ensureProjectDiscovery(ctx) {
170
190
  }
171
191
  }
172
192
  }
193
+ /**
194
+ * Drop completed and in-flight discovery records for every source scope owned
195
+ * by a project. The next scoped request rebuilds registries transactionally
196
+ * from its current source snapshot.
197
+ */
198
+ export function clearProjectDiscoveryCacheForProject(projectId) {
199
+ for (const key of discoveredProjects.keys()) {
200
+ if (key === projectId || key.startsWith(`${projectId}:`)) {
201
+ discoveredProjects.delete(key);
202
+ }
203
+ }
204
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"adapter-factory.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/adapter-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAQ9D,UAAU,uBAAuB;IAC/B,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,OAAO,EAAE,cAAc,CAAC;IACxB,kCAAkC;IAClC,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;IACpC,yDAAyD;IACzD,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,UAAU,wBAAwB;IAChC;;;OAGG;IACH,GAAG,EAAE,OAAO,CAAC;IACb,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,6BAA6B;IAC7B,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;IACpC,mBAAmB;IACnB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iBAAiB;IACjB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,kBAAkB;IAClB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,iBAAiB;IACjB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IAC/C,kBAAkB;IAClB,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,yCAAyC;IACzC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,yBAAyB;IACzB,YAAY,EAAE,YAAY,CAAC;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,sEAAsE;IACtE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qEAAqE;IACrE,KAAK,CAAC,EAAE,qBAAqB,CAAC;CAC/B;AASD;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,uBAAuB,CAAC,CA8IlC"}
1
+ {"version":3,"file":"adapter-factory.d.ts","sourceRoot":"","sources":["../../../../src/src/server/runtime-handler/adapter-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAG7D,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAQ9D,UAAU,uBAAuB;IAC/B,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,OAAO,EAAE,cAAc,CAAC;IACxB,kCAAkC;IAClC,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;IACpC,yDAAyD;IACzD,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,UAAU,wBAAwB;IAChC;;;OAGG;IACH,GAAG,EAAE,OAAO,CAAC;IACb,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,6BAA6B;IAC7B,MAAM,EAAE,eAAe,GAAG,SAAS,CAAC;IACpC,mBAAmB;IACnB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,iBAAiB;IACjB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,kBAAkB;IAClB,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,iBAAiB;IACjB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,EAAE,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;IAC/C,kBAAkB;IAClB,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,yCAAyC;IACzC,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,yBAAyB;IACzB,YAAY,EAAE,YAAY,CAAC;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oCAAoC;IACpC,WAAW,EAAE,OAAO,CAAC;IACrB,sEAAsE;IACtE,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,qEAAqE;IACrE,KAAK,CAAC,EAAE,qBAAqB,CAAC;CAC/B;AASD;;;;;GAKG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,uBAAuB,CAAC,CAiJlC"}