veryfront 0.1.748 → 0.1.749

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 (83) hide show
  1. package/esm/cli/templates/manifest.js +2 -2
  2. package/esm/deno.js +1 -1
  3. package/esm/src/build/production-build/static-generation.d.ts.map +1 -1
  4. package/esm/src/build/production-build/static-generation.js +2 -0
  5. package/esm/src/cache/backends/disk.d.ts +1 -1
  6. package/esm/src/cache/backends/disk.d.ts.map +1 -1
  7. package/esm/src/cache/backends/disk.js +41 -13
  8. package/esm/src/cache/backends/glob.d.ts +6 -0
  9. package/esm/src/cache/backends/glob.d.ts.map +1 -0
  10. package/esm/src/cache/backends/glob.js +41 -0
  11. package/esm/src/cache/backends/memory.d.ts +1 -1
  12. package/esm/src/cache/backends/memory.d.ts.map +1 -1
  13. package/esm/src/cache/backends/memory.js +14 -12
  14. package/esm/src/cache/backends/redis.d.ts.map +1 -1
  15. package/esm/src/cache/backends/redis.js +33 -7
  16. package/esm/src/cache/dependency-graph.d.ts +1 -0
  17. package/esm/src/cache/dependency-graph.d.ts.map +1 -1
  18. package/esm/src/cache/dependency-graph.js +15 -1
  19. package/esm/src/cache/paths.d.ts.map +1 -1
  20. package/esm/src/cache/paths.js +5 -0
  21. package/esm/src/embedding/index.d.ts +4 -1
  22. package/esm/src/embedding/index.d.ts.map +1 -1
  23. package/esm/src/embedding/index.js +3 -1
  24. package/esm/src/embedding/rag-store.d.ts.map +1 -1
  25. package/esm/src/embedding/rag-store.js +157 -8
  26. package/esm/src/embedding/react/use-uploads.d.ts +1 -0
  27. package/esm/src/embedding/react/use-uploads.d.ts.map +1 -1
  28. package/esm/src/embedding/react/use-uploads.js +4 -1
  29. package/esm/src/embedding/upload-handler.d.ts +29 -8
  30. package/esm/src/embedding/upload-handler.d.ts.map +1 -1
  31. package/esm/src/embedding/upload-handler.js +66 -6
  32. package/esm/src/html/html-shell-generator.js +5 -5
  33. package/esm/src/html/nonce-injection.d.ts.map +1 -1
  34. package/esm/src/html/nonce-injection.js +11 -4
  35. package/esm/src/html/utils.d.ts +5 -0
  36. package/esm/src/html/utils.d.ts.map +1 -1
  37. package/esm/src/html/utils.js +19 -9
  38. package/esm/src/modules/react-loader/ssr-module-loader/import-rewriter.d.ts.map +1 -1
  39. package/esm/src/modules/react-loader/ssr-module-loader/import-rewriter.js +12 -5
  40. package/esm/src/modules/server/module-batch-handler.d.ts +1 -0
  41. package/esm/src/modules/server/module-batch-handler.d.ts.map +1 -1
  42. package/esm/src/modules/server/module-batch-handler.js +52 -26
  43. package/esm/src/modules/server/module-server.d.ts.map +1 -1
  44. package/esm/src/modules/server/module-server.js +87 -69
  45. package/esm/src/modules/server/module-source-resolution-cache.d.ts +16 -0
  46. package/esm/src/modules/server/module-source-resolution-cache.d.ts.map +1 -0
  47. package/esm/src/modules/server/module-source-resolution-cache.js +43 -0
  48. package/esm/src/oauth/providers/base.d.ts +0 -2
  49. package/esm/src/oauth/providers/base.d.ts.map +1 -1
  50. package/esm/src/oauth/providers/base.js +35 -10
  51. package/esm/src/oauth/token-store/memory.d.ts +7 -0
  52. package/esm/src/oauth/token-store/memory.d.ts.map +1 -1
  53. package/esm/src/oauth/token-store/memory.js +14 -1
  54. package/esm/src/observability/file-log-subscriber.d.ts +1 -0
  55. package/esm/src/observability/file-log-subscriber.d.ts.map +1 -1
  56. package/esm/src/observability/file-log-subscriber.js +2 -1
  57. package/esm/src/observability/tracing/api-shim.d.ts +1 -0
  58. package/esm/src/observability/tracing/api-shim.d.ts.map +1 -1
  59. package/esm/src/observability/tracing/api-shim.js +7 -0
  60. package/esm/src/observability/tracing/otlp-setup.d.ts.map +1 -1
  61. package/esm/src/observability/tracing/otlp-setup.js +17 -7
  62. package/esm/src/proxy/handler.d.ts +5 -2
  63. package/esm/src/proxy/handler.d.ts.map +1 -1
  64. package/esm/src/proxy/handler.js +26 -25
  65. package/esm/src/proxy/main.js +8 -11
  66. package/esm/src/rendering/renderer.d.ts.map +1 -1
  67. package/esm/src/rendering/renderer.js +32 -12
  68. package/esm/src/server/context/cache-invalidation.d.ts.map +1 -1
  69. package/esm/src/server/context/cache-invalidation.js +2 -0
  70. package/esm/src/server/handlers/dev/scripts/error-overlay.d.ts.map +1 -1
  71. package/esm/src/server/handlers/dev/scripts/error-overlay.js +32 -18
  72. package/esm/src/server/handlers/monitoring/client-log.handler.d.ts +1 -0
  73. package/esm/src/server/handlers/monitoring/client-log.handler.d.ts.map +1 -1
  74. package/esm/src/server/handlers/monitoring/client-log.handler.js +39 -5
  75. package/esm/src/server/handlers/request/module/batch-module-handler.d.ts.map +1 -1
  76. package/esm/src/server/handlers/request/module/batch-module-handler.js +2 -1
  77. package/esm/src/utils/logger/redact.d.ts.map +1 -1
  78. package/esm/src/utils/logger/redact.js +13 -1
  79. package/esm/src/utils/redis-client.d.ts +1 -0
  80. package/esm/src/utils/redis-client.d.ts.map +1 -1
  81. package/esm/src/utils/version-constant.d.ts +1 -1
  82. package/esm/src/utils/version-constant.js +1 -1
  83. package/package.json +1 -1
@@ -22,6 +22,7 @@ export interface BatchHandlerOptions {
22
22
  projectSlug?: string;
23
23
  projectId?: string;
24
24
  branch?: string | null;
25
+ releaseId?: string | null;
25
26
  dev?: boolean;
26
27
  /**
27
28
  * Restrict module imports to specific directories (opt-in security).
@@ -1 +1 @@
1
- {"version":3,"file":"module-batch-handler.d.ts","sourceRoot":"","sources":["../../../../src/src/modules/server/module-batch-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AA2DtE,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA2K/F;AAkPD;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAY1D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAKrE"}
1
+ {"version":3,"file":"module-batch-handler.d.ts","sourceRoot":"","sources":["../../../../src/src/modules/server/module-batch-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AASH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAiGtE,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,cAAc,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,GAAG,CAAC,EAAE,OAAO,CAAC;IACd;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6K/F;AA0PD;;GAEG;AACH,wBAAgB,eAAe,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAc1D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAKrE"}
@@ -27,6 +27,7 @@ import { getFrameworkRootFromMeta } from "../../platform/compat/vfs-paths.js";
27
27
  import { LRUCache } from "../../utils/lru-wrapper.js";
28
28
  import { registerLRUCache } from "../../cache/index.js";
29
29
  import { sha256Short } from "../../cache/hash.js";
30
+ import { buildSourceMissCacheKey, clearSourceMissCache, hasSourceMiss, rememberSourceMiss, } from "./module-source-resolution-cache.js";
30
31
  const logger = serverLogger.component("module-batch");
31
32
  /** Slow request threshold in milliseconds */
32
33
  const SLOW_REQUEST_THRESHOLD_MS = 500;
@@ -57,6 +58,30 @@ const FRAMEWORK_EXTENSIONS = [
57
58
  ".jsx",
58
59
  ".js", // Regular sources for dev mode
59
60
  ];
61
+ async function findFirstSecureFile(secureFs, paths) {
62
+ const results = await Promise.all(paths.map(async (path) => {
63
+ try {
64
+ const stat = await secureFs.stat(path);
65
+ return stat.isFile ? path : null;
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }));
71
+ return results.find((path) => path !== null) ?? null;
72
+ }
73
+ async function findFirstPlatformFile(platformFs, paths) {
74
+ const results = await Promise.all(paths.map(async (path) => {
75
+ try {
76
+ const stat = await platformFs.stat(path);
77
+ return stat.isFile ? path : null;
78
+ }
79
+ catch {
80
+ return null;
81
+ }
82
+ }));
83
+ return results.find((path) => path !== null) ?? null;
84
+ }
60
85
  /**
61
86
  * Handle a batch module request
62
87
  */
@@ -87,7 +112,7 @@ export function handleModuleBatch(req, options) {
87
112
  headers: { "Content-Type": "text/plain" },
88
113
  });
89
114
  }
90
- const { projectDir, adapter, projectSlug, projectId, branch, dev = false, allowedImportDirs, reactVersion, } = options;
115
+ const { projectDir, adapter, projectSlug, projectId, branch, releaseId, dev = false, allowedImportDirs, reactVersion, } = options;
91
116
  const projectKey = projectId || projectSlug || "default";
92
117
  const userAgent = req.headers.get("user-agent") ?? "";
93
118
  const isSSR = url.searchParams.get("ssr") === "true" || userAgent.startsWith("Deno/");
@@ -124,6 +149,7 @@ export function handleModuleBatch(req, options) {
124
149
  projectSlug,
125
150
  branch,
126
151
  projectId,
152
+ releaseId,
127
153
  reactVersion,
128
154
  });
129
155
  const transformDurationMs = performance.now() - moduleStart;
@@ -196,21 +222,27 @@ export function handleModuleBatch(req, options) {
196
222
  */
197
223
  async function loadAndTransformModule(modulePath, projectDir, adapter, secureFs, options) {
198
224
  const basePath = modulePath.replace(/\.js$/, "");
199
- for (const ext of EXTENSIONS) {
200
- const fullPath = join(projectDir, basePath + ext);
201
- try {
202
- const stat = await secureFs.stat(fullPath);
203
- if (!stat.isFile)
204
- continue;
205
- const source = await secureFs.readFile(fullPath);
206
- return transformModule(source, fullPath, modulePath, projectDir, adapter, secureFs, options);
207
- }
208
- catch (_) {
209
- /* expected: file may not exist at this extension */
210
- }
225
+ const missCacheKey = buildSourceMissCacheKey({
226
+ resolver: "module-batch",
227
+ projectDir,
228
+ projectId: options.projectId,
229
+ projectSlug: options.projectSlug,
230
+ branch: options.branch,
231
+ releaseId: options.releaseId,
232
+ basePath,
233
+ reactVersion: options.reactVersion,
234
+ });
235
+ if (hasSourceMiss(missCacheKey))
236
+ return null;
237
+ const sourcePath = await findFirstSecureFile(secureFs, EXTENSIONS.map((ext) => join(projectDir, basePath + ext)));
238
+ if (sourcePath) {
239
+ const source = await secureFs.readFile(sourcePath);
240
+ return transformModule(source, sourcePath, modulePath, projectDir, adapter, secureFs, options);
211
241
  }
212
- if (!basePath.startsWith("lib/"))
242
+ if (!basePath.startsWith("lib/")) {
243
+ rememberSourceMiss(missCacheKey);
213
244
  return null;
245
+ }
214
246
  // Framework lookup directories in priority order
215
247
  const frameworkLookupDirs = [
216
248
  EMBEDDED_SRC_DIR, // Embedded sources for compiled binaries (.src files)
@@ -218,20 +250,13 @@ async function loadAndTransformModule(modulePath, projectDir, adapter, secureFs,
218
250
  ];
219
251
  const platformFs = createFileSystem();
220
252
  for (const lookupDir of frameworkLookupDirs) {
221
- for (const ext of FRAMEWORK_EXTENSIONS) {
222
- const frameworkPath = join(lookupDir, basePath + ext);
223
- try {
224
- const stat = await platformFs.stat(frameworkPath);
225
- if (!stat.isFile)
226
- continue;
227
- const source = await platformFs.readTextFile(frameworkPath);
228
- return transformModule(source, frameworkPath, modulePath, projectDir, adapter, secureFs, options);
229
- }
230
- catch (_) {
231
- /* expected: framework file may not exist at this extension */
232
- }
253
+ const frameworkPath = await findFirstPlatformFile(platformFs, FRAMEWORK_EXTENSIONS.map((ext) => join(lookupDir, basePath + ext)));
254
+ if (frameworkPath) {
255
+ const source = await platformFs.readTextFile(frameworkPath);
256
+ return transformModule(source, frameworkPath, modulePath, projectDir, adapter, secureFs, options);
233
257
  }
234
258
  }
259
+ rememberSourceMiss(missCacheKey);
235
260
  return null;
236
261
  }
237
262
  async function transformModule(source, sourceFile, modulePath, projectDir, adapter, secureFs, options) {
@@ -369,6 +394,7 @@ function transformExportsForBundle(code) {
369
394
  * Clear the transform cache (on deployment or memory pressure)
370
395
  */
371
396
  export function clearBatchCache(projectSlug) {
397
+ clearSourceMissCache("module-batch");
372
398
  if (!projectSlug) {
373
399
  transformCache.clear();
374
400
  logger.debug("Cleared all cache");
@@ -1 +1 @@
1
- {"version":3,"file":"module-server.d.ts","sourceRoot":"","sources":["../../../../src/src/modules/server/module-server.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AA6BtE;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAsDrD,CAAC;AAQF,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,OAAO,EAAE,cAAc,CAAC;IACxB,uBAAuB;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkVzF;AAgTD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGrD"}
1
+ {"version":3,"file":"module-server.d.ts","sourceRoot":"","sources":["../../../../src/src/modules/server/module-server.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAG5E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAkCtE;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAsDrD,CAAC;AAiBF,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB;IACtB,OAAO,EAAE,cAAc,CAAC;IACxB,uBAAuB;IACvB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkWzF;AAiVD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGrD"}
@@ -17,6 +17,7 @@ import { FRAMEWORK_ROOT, resolveFrameworkSourcePath, } from "../../platform/comp
17
17
  import { getReactUrls, REACT_DEFAULT_VERSION } from "../../utils/constants/cdn.js";
18
18
  import { readLimitedCrossProjectSource } from "./cross-project-source-limit.js";
19
19
  import { sha256Short } from "../../cache/hash.js";
20
+ import { buildSourceMissCacheKey, hasSourceMiss, rememberSourceMiss, } from "./module-source-resolution-cache.js";
20
21
  const logger = serverLogger.component("module-server");
21
22
  /**
22
23
  * Embedded polyfills for compiled Deno binaries.
@@ -161,6 +162,10 @@ export function serveModule(req, options) {
161
162
  secureFs,
162
163
  projectDir,
163
164
  currentModulePath: `_snippets/${hash}.js`,
165
+ projectId: effectiveProjectId,
166
+ projectSlug: snippetProjectSlug,
167
+ branch: snippetBranch,
168
+ releaseId: options.releaseId,
164
169
  reactVersion,
165
170
  }),
166
171
  });
@@ -229,6 +234,8 @@ export function serveModule(req, options) {
229
234
  projectDir,
230
235
  currentModulePath: crossPath,
231
236
  crossProjectRef: projectRef,
237
+ projectId: effectiveProjectId,
238
+ releaseId: options.releaseId,
232
239
  reactVersion,
233
240
  }),
234
241
  });
@@ -262,7 +269,13 @@ export function serveModule(req, options) {
262
269
  branch ??= parsedHost.branch;
263
270
  }
264
271
  try {
265
- const findResult = await findSourceFile(secureFs, projectDir, filePathWithoutExt, reactVersion);
272
+ const findResult = await findSourceFile(secureFs, projectDir, filePathWithoutExt, {
273
+ projectId: effectiveProjectId,
274
+ projectSlug,
275
+ branch,
276
+ releaseId: options.releaseId,
277
+ reactVersion,
278
+ });
266
279
  if (!findResult) {
267
280
  logger.warn("Module not found", {
268
281
  modulePath,
@@ -326,6 +339,10 @@ export function serveModule(req, options) {
326
339
  secureFs,
327
340
  projectDir,
328
341
  currentModulePath: modulePath,
342
+ projectId: effectiveProjectId,
343
+ projectSlug,
344
+ branch,
345
+ releaseId: options.releaseId,
329
346
  reactVersion,
330
347
  }),
331
348
  });
@@ -375,7 +392,13 @@ function createSSRTargetCacheBusterResolver(options) {
375
392
  const source = await fetchCrossProjectSource(options.crossProjectRef, targetPath);
376
393
  return source === null ? undefined : await sha256Short(`${targetPath}\0${source}`);
377
394
  }
378
- const findResult = await findSourceFile(options.secureFs, options.projectDir, stripSSRModuleJsExtension(targetPath), options.reactVersion);
395
+ const findResult = await findSourceFile(options.secureFs, options.projectDir, stripSSRModuleJsExtension(targetPath), {
396
+ projectId: options.projectId,
397
+ projectSlug: options.projectSlug,
398
+ branch: options.branch,
399
+ releaseId: options.releaseId,
400
+ reactVersion: options.reactVersion,
401
+ });
379
402
  if (!findResult)
380
403
  return undefined;
381
404
  const source = await readSourceFileForVersion(options.secureFs, findResult);
@@ -415,20 +438,34 @@ function createBrowserReactPackageShim(basePathWithoutExt, reactVersion = REACT_
415
438
  async function findFrameworkPackageAssetFile(fs, basePathWithoutExt, extensions) {
416
439
  if (hasUnsafePackageAssetPath(basePathWithoutExt))
417
440
  return null;
418
- for (const ext of extensions) {
419
- const candidatePath = join(FRAMEWORK_ROOT, basePathWithoutExt + ext);
441
+ return await findFirstPlatformFile(fs, extensions.map((ext) => join(FRAMEWORK_ROOT, basePathWithoutExt + ext)));
442
+ }
443
+ async function findFirstPlatformFile(fs, paths) {
444
+ const results = await Promise.all(paths.map(async (path) => {
420
445
  try {
421
- const stat = await fs.stat(candidatePath);
422
- if (stat.isFile)
423
- return candidatePath;
446
+ const stat = await fs.stat(path);
447
+ return stat.isFile ? path : null;
424
448
  }
425
449
  catch {
426
- /* expected: package asset may not exist in source checkouts */
450
+ return null;
427
451
  }
428
- }
429
- return null;
452
+ }));
453
+ return results.find((path) => path !== null) ?? null;
430
454
  }
431
- async function findSourceFile(secureFs, projectDir, basePath, reactVersion) {
455
+ async function findFirstSecureFile(secureFs, paths) {
456
+ const results = await Promise.all(paths.map(async (path) => {
457
+ try {
458
+ const stat = await secureFs.stat(path);
459
+ return stat.isFile ? path : null;
460
+ }
461
+ catch {
462
+ return null;
463
+ }
464
+ }));
465
+ return results.find((path) => path !== null) ?? null;
466
+ }
467
+ async function findSourceFile(secureFs, projectDir, basePath, context) {
468
+ const { reactVersion } = context;
432
469
  // Extensions including .src for compiled binary embedded sources
433
470
  const extensions = [
434
471
  ".json",
@@ -457,6 +494,16 @@ async function findSourceFile(secureFs, projectDir, basePath, reactVersion) {
457
494
  const isFrameworkPath = basePathWithoutExt.startsWith("_veryfront/");
458
495
  const isFrameworkPackageAssetPath = basePathWithoutExt.startsWith("react/") ||
459
496
  basePathWithoutExt.startsWith("deps/");
497
+ const missCacheKey = buildSourceMissCacheKey({
498
+ resolver: "module-server",
499
+ projectDir,
500
+ projectId: context.projectId,
501
+ projectSlug: context.projectSlug,
502
+ branch: context.branch,
503
+ releaseId: context.releaseId,
504
+ basePath: basePathWithoutExt,
505
+ reactVersion,
506
+ });
460
507
  // Check embedded polyfills first (no filesystem access needed).
461
508
  // These cover both compiled-binary polyfills (node:async_hooks etc.)
462
509
  // and dnt build artifacts (_dnt.shims, _dnt.polyfills) that don't
@@ -475,6 +522,8 @@ async function findSourceFile(secureFs, projectDir, basePath, reactVersion) {
475
522
  embeddedContent,
476
523
  };
477
524
  }
525
+ if (hasSourceMiss(missCacheKey))
526
+ return null;
478
527
  if (isFrameworkPackageAssetPath) {
479
528
  const browserReactShim = createBrowserReactPackageShim(basePathWithoutExt, reactVersion);
480
529
  if (browserReactShim) {
@@ -525,78 +574,47 @@ async function findSourceFile(secureFs, projectDir, basePath, reactVersion) {
525
574
  }
526
575
  }
527
576
  // Project file lookups (using secureFs which may go through FSAdapter in proxy mode)
528
- for (const ext of extensions) {
529
- const fullPath = join(projectDir, basePathWithoutExt + ext);
530
- try {
531
- const stat = await secureFs.stat(fullPath);
532
- if (stat.isFile) {
533
- logger.debug("Found file", { basePath, resolvedPath: fullPath });
534
- return { path: fullPath, isFrameworkFile: false };
535
- }
536
- }
537
- catch (_) {
538
- /* expected: file may not exist at this extension */
539
- }
577
+ const projectFilePath = await findFirstSecureFile(secureFs, extensions.map((ext) => join(projectDir, basePathWithoutExt + ext)));
578
+ if (projectFilePath) {
579
+ logger.debug("Found file", { basePath, resolvedPath: projectFilePath });
580
+ return { path: projectFilePath, isFrameworkFile: false };
540
581
  }
541
582
  const prefixesToStrip = ["components/", "pages/", "lib/", "app/", "src/"];
542
583
  for (const prefix of prefixesToStrip) {
543
584
  if (!basePathWithoutExt.startsWith(prefix))
544
585
  continue;
545
586
  const strippedPath = basePathWithoutExt.slice(prefix.length);
546
- for (const ext of extensions) {
547
- const fullPath = join(projectDir, strippedPath + ext);
548
- try {
549
- const stat = await secureFs.stat(fullPath);
550
- if (stat.isFile) {
551
- logger.debug("Found file after stripping prefix", {
552
- originalPath: basePathWithoutExt,
553
- strippedPath,
554
- resolvedPath: fullPath,
555
- });
556
- return { path: fullPath, isFrameworkFile: false };
557
- }
558
- }
559
- catch (_) {
560
- /* expected: file may not exist after stripping prefix */
561
- }
587
+ const strippedFilePath = await findFirstSecureFile(secureFs, extensions.map((ext) => join(projectDir, strippedPath + ext)));
588
+ if (strippedFilePath) {
589
+ logger.debug("Found file after stripping prefix", {
590
+ originalPath: basePathWithoutExt,
591
+ strippedPath,
592
+ resolvedPath: strippedFilePath,
593
+ });
594
+ return { path: strippedFilePath, isFrameworkFile: false };
562
595
  }
563
596
  }
564
- for (const ext of extensions) {
565
- const fullPath = join(projectDir, basePathWithoutExt, `index${ext}`);
566
- try {
567
- const stat = await secureFs.stat(fullPath);
568
- if (stat.isFile) {
569
- logger.debug("Found index file", {
570
- basePath: basePathWithoutExt,
571
- resolvedPath: fullPath,
572
- });
573
- return { path: fullPath, isFrameworkFile: false };
574
- }
575
- }
576
- catch (_) {
577
- /* expected: index file may not exist at this extension */
578
- }
597
+ const indexFilePath = await findFirstSecureFile(secureFs, extensions.map((ext) => join(projectDir, basePathWithoutExt, `index${ext}`)));
598
+ if (indexFilePath) {
599
+ logger.debug("Found index file", {
600
+ basePath: basePathWithoutExt,
601
+ resolvedPath: indexFilePath,
602
+ });
603
+ return { path: indexFilePath, isFrameworkFile: false };
579
604
  }
580
605
  // Try looking in common project directories
581
606
  const commonDirs = ["components", "app", "pages", "lib", "src"];
582
607
  for (const dir of commonDirs) {
583
- for (const ext of extensions) {
584
- const fullPath = join(projectDir, dir, basePathWithoutExt + ext);
585
- try {
586
- const stat = await secureFs.stat(fullPath);
587
- if (stat.isFile) {
588
- logger.debug("Found file in common directory", {
589
- basePath,
590
- resolvedPath: fullPath,
591
- });
592
- return { path: fullPath, isFrameworkFile: false };
593
- }
594
- }
595
- catch (_) {
596
- /* expected: file may not exist in common directory */
597
- }
608
+ const commonDirFilePath = await findFirstSecureFile(secureFs, extensions.map((ext) => join(projectDir, dir, basePathWithoutExt + ext)));
609
+ if (commonDirFilePath) {
610
+ logger.debug("Found file in common directory", {
611
+ basePath,
612
+ resolvedPath: commonDirFilePath,
613
+ });
614
+ return { path: commonDirFilePath, isFrameworkFile: false };
598
615
  }
599
616
  }
617
+ rememberSourceMiss(missCacheKey);
600
618
  return null;
601
619
  }
602
620
  /**
@@ -0,0 +1,16 @@
1
+ type ModuleSourceResolver = "module-server" | "module-batch";
2
+ export declare function buildSourceMissCacheKey(options: {
3
+ resolver: ModuleSourceResolver;
4
+ projectDir: string;
5
+ projectId?: string;
6
+ projectSlug?: string | null;
7
+ branch?: string | null;
8
+ releaseId?: string | null;
9
+ basePath: string;
10
+ reactVersion?: string;
11
+ }): string;
12
+ export declare function hasSourceMiss(cacheKey: string): boolean;
13
+ export declare function rememberSourceMiss(cacheKey: string): void;
14
+ export declare function clearSourceMissCache(resolver?: ModuleSourceResolver): void;
15
+ export {};
16
+ //# sourceMappingURL=module-source-resolution-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-source-resolution-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/modules/server/module-source-resolution-cache.ts"],"names":[],"mappings":"AAGA,KAAK,oBAAoB,GAAG,eAAe,GAAG,cAAc,CAAC;AAgB7D,wBAAgB,uBAAuB,CAAC,OAAO,EAAE;IAC/C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,MAAM,CAWT;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEvD;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEzD;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAO1E"}
@@ -0,0 +1,43 @@
1
+ import { registerLRUCache } from "../../cache/index.js";
2
+ import { LRUCache } from "../../utils/lru-wrapper.js";
3
+ const SOURCE_MISS_CACHE_MAX_ENTRIES = 5_000;
4
+ const sourceMissCaches = {
5
+ "module-server": new LRUCache({
6
+ maxEntries: SOURCE_MISS_CACHE_MAX_ENTRIES,
7
+ }),
8
+ "module-batch": new LRUCache({
9
+ maxEntries: SOURCE_MISS_CACHE_MAX_ENTRIES,
10
+ }),
11
+ };
12
+ registerLRUCache("module-server-source-miss-cache", sourceMissCaches["module-server"]);
13
+ registerLRUCache("module-batch-source-miss-cache", sourceMissCaches["module-batch"]);
14
+ export function buildSourceMissCacheKey(options) {
15
+ return [
16
+ options.resolver,
17
+ options.projectDir,
18
+ options.projectId ?? "",
19
+ options.projectSlug ?? "",
20
+ options.branch ?? "",
21
+ options.releaseId ?? "",
22
+ options.reactVersion ?? "",
23
+ options.basePath,
24
+ ].join("\0");
25
+ }
26
+ export function hasSourceMiss(cacheKey) {
27
+ return sourceMissCaches[getResolverFromCacheKey(cacheKey)].has(cacheKey);
28
+ }
29
+ export function rememberSourceMiss(cacheKey) {
30
+ sourceMissCaches[getResolverFromCacheKey(cacheKey)].set(cacheKey, true);
31
+ }
32
+ export function clearSourceMissCache(resolver) {
33
+ if (resolver) {
34
+ sourceMissCaches[resolver].clear();
35
+ return;
36
+ }
37
+ for (const cache of Object.values(sourceMissCaches))
38
+ cache.clear();
39
+ }
40
+ function getResolverFromCacheKey(cacheKey) {
41
+ const resolver = cacheKey.split("\0", 1)[0];
42
+ return resolver === "module-batch" ? "module-batch" : "module-server";
43
+ }
@@ -36,11 +36,9 @@ export declare class OAuthProvider {
36
36
  export declare class OAuthService extends OAuthProvider {
37
37
  protected serviceConfig: OAuthServiceConfig;
38
38
  protected tokenStore?: TokenStore;
39
- private refreshPromises;
40
39
  constructor(config: OAuthServiceConfig, tokenStore?: TokenStore, envReader?: EnvReader);
41
40
  get serviceId(): string;
42
41
  get apiBaseUrl(): string;
43
- private refreshKey;
44
42
  createAuthorizationUrl(options?: AuthorizationUrlOptions): Promise<{
45
43
  url: string;
46
44
  state: OAuthState;
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/src/oauth/providers/base.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACX,MAAM,aAAa,CAAC;AAarB,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAuB5D,gCAAgC;AAChC,qBAAa,aAAa;IACxB,SAAS,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACtC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC;gBAEnB,MAAM,EAAE,mBAAmB,EAAE,SAAS,GAAE,SAAkB;IAKtE,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,YAAY,IAAI,OAAO;IAIjB,sBAAsB,CAC1B,OAAO,GAAE,uBAAuB,GAAG;QAAE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GACnE,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;IA0C9C,OAAO,CAAC,iBAAiB;IAazB;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;YA0CZ,gBAAgB;IAe9B,OAAO,CAAC,4BAA4B;YAQtB,aAAa;IAyCrB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAgCzE,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAwBjE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAiBnD;AAED,+BAA+B;AAC/B,qBAAa,YAAa,SAAQ,aAAa;IAC7C,SAAS,CAAC,aAAa,EAAE,kBAAkB,CAAC;IAC5C,SAAS,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IAClC,OAAO,CAAC,eAAe,CAA6C;gBAExD,MAAM,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,SAAS;IAMtF,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,OAAO,CAAC,UAAU;IAIT,sBAAsB,CAC7B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;IAO9C;;;;;;OAMG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YAyB9C,0BAA0B;IAcxC;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAuBpB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;CA+BxF"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../../../src/src/oauth/providers/base.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,uBAAuB,EACvB,mBAAmB,EACnB,kBAAkB,EAClB,UAAU,EAEV,oBAAoB,EACpB,mBAAmB,EACnB,UAAU,EACX,MAAM,aAAa,CAAC;AAarB,MAAM,MAAM,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAuB5D,gCAAgC;AAChC,qBAAa,aAAa;IACxB,SAAS,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACtC,SAAS,CAAC,SAAS,EAAE,SAAS,CAAC;gBAEnB,MAAM,EAAE,mBAAmB,EAAE,SAAS,GAAE,SAAkB;IAKtE,WAAW,IAAI,MAAM,GAAG,IAAI;IAI5B,eAAe,IAAI,MAAM,GAAG,IAAI;IAIhC,YAAY,IAAI,OAAO;IAIjB,sBAAsB,CAC1B,OAAO,GAAE,uBAAuB,GAAG;QAAE,aAAa,CAAC,EAAE,MAAM,EAAE,CAAA;KAAO,GACnE,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;IA0C9C,OAAO,CAAC,iBAAiB;IAazB;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;YA0CZ,gBAAgB;IAe9B,OAAO,CAAC,4BAA4B;YAQtB,aAAa;IAyCrB,YAAY,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAgCzE,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAwBjE,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAiBnD;AA2CD,+BAA+B;AAC/B,qBAAa,YAAa,SAAQ,aAAa;IAC7C,SAAS,CAAC,aAAa,EAAE,kBAAkB,CAAC;IAC5C,SAAS,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;gBAEtB,MAAM,EAAE,kBAAkB,EAAE,UAAU,CAAC,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,SAAS;IAMtF,IAAI,SAAS,IAAI,MAAM,CAEtB;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAEQ,sBAAsB,CAC7B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE,CAAC;IAO9C;;;;;;OAMG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;YA4B9C,0BAA0B;IAcxC;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAuBpB,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,CAAC,CAAC;CA+BxF"}
@@ -229,11 +229,37 @@ export class OAuthProvider {
229
229
  }
230
230
  }
231
231
  }
232
+ /**
233
+ * Module-level singleflight for token refreshes, scoped by TokenStore instance
234
+ * and then `(serviceId, userId)`. This dedupes separate OAuthService instances
235
+ * that share the same scoped store without making different project stores
236
+ * share refresh promises.
237
+ */
238
+ const refreshInFlightByStore = new WeakMap();
239
+ function getRefreshInFlight(tokenStore, key) {
240
+ return refreshInFlightByStore.get(tokenStore)?.get(key);
241
+ }
242
+ function setRefreshInFlight(tokenStore, key, promise) {
243
+ let storeInflight = refreshInFlightByStore.get(tokenStore);
244
+ if (!storeInflight) {
245
+ storeInflight = new Map();
246
+ refreshInFlightByStore.set(tokenStore, storeInflight);
247
+ }
248
+ storeInflight.set(key, promise);
249
+ }
250
+ function clearRefreshInFlight(tokenStore, key, promise) {
251
+ const storeInflight = refreshInFlightByStore.get(tokenStore);
252
+ if (!storeInflight || storeInflight.get(key) !== promise)
253
+ return;
254
+ storeInflight.delete(key);
255
+ if (storeInflight.size === 0) {
256
+ refreshInFlightByStore.delete(tokenStore);
257
+ }
258
+ }
232
259
  /** Implement oauth service. */
233
260
  export class OAuthService extends OAuthProvider {
234
261
  serviceConfig;
235
262
  tokenStore;
236
- refreshPromises = new Map();
237
263
  constructor(config, tokenStore, envReader) {
238
264
  super(config, envReader);
239
265
  this.serviceConfig = config;
@@ -245,9 +271,6 @@ export class OAuthService extends OAuthProvider {
245
271
  get apiBaseUrl() {
246
272
  return this.serviceConfig.apiBaseUrl;
247
273
  }
248
- refreshKey(userId) {
249
- return JSON.stringify([this.serviceId, userId]);
250
- }
251
274
  createAuthorizationUrl(options = {}) {
252
275
  return super.createAuthorizationUrl({
253
276
  ...options,
@@ -270,19 +293,21 @@ export class OAuthService extends OAuthProvider {
270
293
  return tokens.accessToken;
271
294
  if (!tokens.refreshToken)
272
295
  return null;
273
- const key = this.refreshKey(userId);
274
- const existingRefresh = this.refreshPromises.get(key);
296
+ if (!this.tokenStore) {
297
+ throw new Error("TokenStore not configured");
298
+ }
299
+ const key = JSON.stringify([this.serviceId, userId]);
300
+ const existingRefresh = getRefreshInFlight(this.tokenStore, key);
275
301
  if (existingRefresh)
276
302
  return existingRefresh;
303
+ const tokenStore = this.tokenStore;
277
304
  const refreshPromise = this.refreshAndStoreAccessToken(userId, tokens.refreshToken);
278
- this.refreshPromises.set(key, refreshPromise);
305
+ setRefreshInFlight(tokenStore, key, refreshPromise);
279
306
  try {
280
307
  return await refreshPromise;
281
308
  }
282
309
  finally {
283
- if (this.refreshPromises.get(key) === refreshPromise) {
284
- this.refreshPromises.delete(key);
285
- }
310
+ clearRefreshInFlight(tokenStore, key, refreshPromise);
286
311
  }
287
312
  }
288
313
  async refreshAndStoreAccessToken(userId, refreshToken) {
@@ -7,6 +7,11 @@ export interface MemoryTokenStoreOptions {
7
7
  * {@link DEFAULT_MAX_TOKEN_ENTRIES}.
8
8
  */
9
9
  maxEntries?: number;
10
+ /**
11
+ * Maximum number of in-flight OAuth state entries to retain before oldest
12
+ * entries are evicted. Defaults to {@link DEFAULT_MAX_STATE_ENTRIES}.
13
+ */
14
+ maxStateEntries?: number;
10
15
  }
11
16
  /**
12
17
  * In-memory TokenStore keyed by `(serviceId, userId)`.
@@ -24,6 +29,7 @@ export interface MemoryTokenStoreOptions {
24
29
  export declare class MemoryTokenStore implements TokenStore {
25
30
  private tokens;
26
31
  private states;
32
+ private maxStateEntries;
27
33
  private projectId;
28
34
  private warnedProductionUse;
29
35
  constructor(projectId?: string, options?: MemoryTokenStoreOptions);
@@ -44,6 +50,7 @@ export declare class MemoryTokenStore implements TokenStore {
44
50
  */
45
51
  consumeState(state: string): Promise<StoredOAuthState | null>;
46
52
  private cleanupExpiredStates;
53
+ private evictOldestStates;
47
54
  /** List connected slots as `${serviceId}:${userId}` strings (test/debug aid). */
48
55
  getConnectedServices(): string[];
49
56
  /** Whether a given user has usable tokens for a service. */
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../../src/src/oauth/token-store/memory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAgB7E,4CAA4C;AAC5C,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,gBAAiB,YAAW,UAAU;IACjD,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,mBAAmB,CAAS;gBAExB,SAAS,SAAY,EAAE,OAAO,GAAE,uBAA4B;IAOxE,OAAO,CAAC,SAAS;IAIjB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAW3B,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAMzE,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhF,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7D,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAM9D;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAU7D,OAAO,CAAC,oBAAoB;IAS5B,iFAAiF;IACjF,oBAAoB,IAAI,MAAM,EAAE;IAOhC,4DAA4D;IAC5D,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAQvD,QAAQ,IAAI,IAAI;CAIjB;AAED,eAAO,MAAM,gBAAgB,EAAE,UAAmC,CAAC"}
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../../src/src/oauth/token-store/memory.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAmB7E,4CAA4C;AAC5C,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,gBAAiB,YAAW,UAAU;IACjD,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,mBAAmB,CAAS;gBAExB,SAAS,SAAY,EAAE,OAAO,GAAE,uBAA4B;IAQxE,OAAO,CAAC,SAAS;IAIjB;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IAW3B,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAMzE,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhF,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK7D,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9D;;;OAGG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAU7D,OAAO,CAAC,oBAAoB;IAS5B,OAAO,CAAC,iBAAiB;IAQzB,iFAAiF;IACjF,oBAAoB,IAAI,MAAM,EAAE;IAOhC,4DAA4D;IAC5D,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAQvD,QAAQ,IAAI,IAAI;CAIjB;AAED,eAAO,MAAM,gBAAgB,EAAE,UAAmC,CAAC"}
@@ -12,6 +12,8 @@ const STATE_EXPIRY_MS = 10 * 60 * 1_000;
12
12
  * eviction is by capacity/recency only.
13
13
  */
14
14
  const DEFAULT_MAX_TOKEN_ENTRIES = 10_000;
15
+ /** Default cap on in-flight OAuth state values. */
16
+ const DEFAULT_MAX_STATE_ENTRIES = 10_000;
15
17
  /**
16
18
  * In-memory TokenStore keyed by `(serviceId, userId)`.
17
19
  *
@@ -28,10 +30,12 @@ const DEFAULT_MAX_TOKEN_ENTRIES = 10_000;
28
30
  export class MemoryTokenStore {
29
31
  tokens;
30
32
  states = new Map();
33
+ maxStateEntries;
31
34
  projectId;
32
35
  warnedProductionUse = false;
33
36
  constructor(projectId = "default", options = {}) {
34
37
  this.projectId = projectId;
38
+ this.maxStateEntries = Math.max(1, options.maxStateEntries ?? DEFAULT_MAX_STATE_ENTRIES);
35
39
  this.tokens = new LRUCacheAdapter({
36
40
  maxEntries: options.maxEntries ?? DEFAULT_MAX_TOKEN_ENTRIES,
37
41
  });
@@ -66,8 +70,9 @@ export class MemoryTokenStore {
66
70
  return Promise.resolve();
67
71
  }
68
72
  setState(state, meta) {
69
- this.states.set(state, meta);
70
73
  this.cleanupExpiredStates();
74
+ this.states.set(state, meta);
75
+ this.evictOldestStates();
71
76
  return Promise.resolve();
72
77
  }
73
78
  /**
@@ -92,6 +97,14 @@ export class MemoryTokenStore {
92
97
  }
93
98
  }
94
99
  }
100
+ evictOldestStates() {
101
+ while (this.states.size > this.maxStateEntries) {
102
+ const oldestState = this.states.keys().next().value;
103
+ if (oldestState === undefined)
104
+ return;
105
+ this.states.delete(oldestState);
106
+ }
107
+ }
95
108
  /** List connected slots as `${serviceId}:${userId}` strings (test/debug aid). */
96
109
  getConnectedServices() {
97
110
  const prefix = `${this.projectId}:`;
@@ -21,6 +21,7 @@ export declare class FileLogSubscriber {
21
21
  private closed;
22
22
  private permissionFailed;
23
23
  private config;
24
+ private readonly encoder;
24
25
  constructor(config: FileLogConfig);
25
26
  getSubscriber(): LogSubscriber;
26
27
  private enqueue;