veryfront 0.0.81 → 0.0.83
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/README.md +15 -1
- package/esm/deno.js +1 -1
- package/esm/proxy/cache/index.d.ts +41 -0
- package/esm/proxy/cache/index.d.ts.map +1 -0
- package/esm/proxy/cache/index.js +75 -0
- package/esm/proxy/cache/memory-cache.d.ts +18 -0
- package/esm/proxy/cache/memory-cache.d.ts.map +1 -0
- package/esm/proxy/cache/memory-cache.js +100 -0
- package/esm/proxy/cache/redis-cache.d.ts +27 -0
- package/esm/proxy/cache/redis-cache.d.ts.map +1 -0
- package/esm/proxy/cache/redis-cache.js +183 -0
- package/esm/proxy/cache/resilient-cache.d.ts +44 -0
- package/esm/proxy/cache/resilient-cache.d.ts.map +1 -0
- package/esm/proxy/cache/resilient-cache.js +178 -0
- package/esm/proxy/cache/types.d.ts +65 -0
- package/esm/proxy/cache/types.d.ts.map +1 -0
- package/esm/proxy/cache/types.js +7 -0
- package/esm/proxy/handler.d.ts +81 -0
- package/esm/proxy/handler.d.ts.map +1 -0
- package/esm/proxy/handler.js +417 -0
- package/esm/proxy/logger.d.ts +29 -0
- package/esm/proxy/logger.d.ts.map +1 -0
- package/esm/proxy/logger.js +258 -0
- package/esm/proxy/oauth-client.d.ts +15 -0
- package/esm/proxy/oauth-client.d.ts.map +1 -0
- package/esm/proxy/oauth-client.js +52 -0
- package/esm/proxy/token-manager.d.ts +59 -0
- package/esm/proxy/token-manager.d.ts.map +1 -0
- package/esm/proxy/token-manager.js +125 -0
- package/esm/proxy/tracing.d.ts +39 -0
- package/esm/proxy/tracing.d.ts.map +1 -0
- package/esm/proxy/tracing.js +194 -0
- package/esm/src/cache/backend.d.ts +22 -0
- package/esm/src/cache/backend.d.ts.map +1 -1
- package/esm/src/cache/backend.js +59 -0
- package/esm/src/cache/cache-key-builder.d.ts +0 -4
- package/esm/src/cache/cache-key-builder.d.ts.map +1 -1
- package/esm/src/cache/cache-key-builder.js +0 -6
- package/esm/src/cache/hash.d.ts +107 -0
- package/esm/src/cache/hash.d.ts.map +1 -0
- package/esm/src/cache/hash.js +166 -0
- package/esm/src/cache/index.d.ts +3 -0
- package/esm/src/cache/index.d.ts.map +1 -1
- package/esm/src/cache/index.js +3 -0
- package/esm/src/cache/module-cache.d.ts +82 -0
- package/esm/src/cache/module-cache.d.ts.map +1 -0
- package/esm/src/cache/module-cache.js +214 -0
- package/esm/src/cache/multi-tier.d.ts +148 -0
- package/esm/src/cache/multi-tier.d.ts.map +1 -0
- package/esm/src/cache/multi-tier.js +326 -0
- package/esm/src/cli/app/actions.d.ts +26 -0
- package/esm/src/cli/app/actions.d.ts.map +1 -0
- package/esm/src/cli/app/actions.js +152 -0
- package/esm/src/cli/app/components/inline-input.d.ts +35 -0
- package/esm/src/cli/app/components/inline-input.d.ts.map +1 -0
- package/esm/src/cli/app/components/inline-input.js +220 -0
- package/esm/src/cli/app/components/list-select.d.ts +69 -0
- package/esm/src/cli/app/components/list-select.d.ts.map +1 -0
- package/esm/src/cli/app/components/list-select.js +137 -0
- package/esm/src/cli/app/index.d.ts +45 -0
- package/esm/src/cli/app/index.d.ts.map +1 -0
- package/esm/src/cli/app/index.js +1252 -0
- package/esm/src/cli/app/state.d.ts +122 -0
- package/esm/src/cli/app/state.d.ts.map +1 -0
- package/esm/src/cli/app/state.js +232 -0
- package/esm/src/cli/app/views/dashboard.d.ts +19 -0
- package/esm/src/cli/app/views/dashboard.d.ts.map +1 -0
- package/esm/src/cli/app/views/dashboard.js +178 -0
- package/esm/src/cli/index/command-router.d.ts.map +1 -1
- package/esm/src/cli/index/command-router.js +9 -39
- package/esm/src/cli/index/start-handler.d.ts +3 -0
- package/esm/src/cli/index/start-handler.d.ts.map +1 -0
- package/esm/src/cli/index/start-handler.js +145 -0
- package/esm/src/cli/mcp/index.d.ts +11 -0
- package/esm/src/cli/mcp/index.d.ts.map +1 -0
- package/esm/src/cli/mcp/index.js +10 -0
- package/esm/src/cli/templates/integration-loader.d.ts.map +1 -1
- package/esm/src/cli/templates/integration-loader.js +2 -4
- package/esm/src/middleware/builtin/security/redis-rate-limit.d.ts +2 -0
- package/esm/src/middleware/builtin/security/redis-rate-limit.d.ts.map +1 -1
- package/esm/src/middleware/builtin/security/redis-rate-limit.js +23 -9
- package/esm/src/modules/react-loader/ssr-module-loader/cache/redis.d.ts +10 -0
- package/esm/src/modules/react-loader/ssr-module-loader/cache/redis.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/cache/redis.js +30 -42
- package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/loader.js +148 -20
- package/esm/src/observability/tracing/span-names.d.ts +2 -0
- package/esm/src/observability/tracing/span-names.d.ts.map +1 -1
- package/esm/src/observability/tracing/span-names.js +2 -0
- package/esm/src/platform/adapters/fs/cache/file-cache.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/cache/file-cache.js +9 -3
- package/esm/src/rendering/orchestrator/module-loader/cache.d.ts +10 -2
- package/esm/src/rendering/orchestrator/module-loader/cache.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/module-loader/cache.js +11 -6
- package/esm/src/rendering/orchestrator/module-loader/index.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/module-loader/index.js +72 -77
- package/esm/src/server/context/cache-invalidation.d.ts.map +1 -1
- package/esm/src/server/context/cache-invalidation.js +4 -0
- package/esm/src/server/handlers/dev/dashboard/api.js +4 -0
- package/esm/src/server/handlers/dev/projects/ui-handler.d.ts.map +1 -1
- package/esm/src/server/handlers/dev/projects/ui-handler.js +6 -0
- package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
- package/esm/src/transforms/esm/http-cache.js +145 -93
- package/esm/src/transforms/esm/transform-cache.d.ts +25 -0
- package/esm/src/transforms/esm/transform-cache.d.ts.map +1 -1
- package/esm/src/transforms/esm/transform-cache.js +45 -0
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.d.ts.map +1 -1
- package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/index.js +2 -36
- package/esm/src/utils/constants/cache.d.ts +4 -0
- package/esm/src/utils/constants/cache.d.ts.map +1 -1
- package/esm/src/utils/constants/cache.js +14 -1
- package/esm/src/utils/index.d.ts +1 -1
- package/esm/src/utils/index.d.ts.map +1 -1
- package/esm/src/utils/index.js +1 -1
- package/package.json +2 -1
- package/src/deno.js +1 -1
- package/src/proxy/cache/index.ts +93 -0
- package/src/proxy/cache/memory-cache.ts +120 -0
- package/src/proxy/cache/redis-cache.ts +203 -0
- package/src/proxy/cache/resilient-cache.ts +205 -0
- package/src/proxy/cache/types.ts +72 -0
- package/src/proxy/handler.ts +593 -0
- package/src/proxy/logger.ts +329 -0
- package/src/proxy/oauth-client.ts +91 -0
- package/src/proxy/token-manager.ts +174 -0
- package/src/proxy/tracing.ts +237 -0
- package/src/src/cache/backend.ts +65 -0
- package/src/src/cache/cache-key-builder.ts +0 -9
- package/src/src/cache/hash.ts +205 -0
- package/src/src/cache/index.ts +3 -0
- package/src/src/cache/module-cache.ts +252 -0
- package/src/src/cache/multi-tier.ts +462 -0
- package/src/src/cli/app/actions.ts +190 -0
- package/src/src/cli/app/components/inline-input.ts +255 -0
- package/src/src/cli/app/components/list-select.ts +215 -0
- package/src/src/cli/app/index.ts +1471 -0
- package/src/src/cli/app/state.ts +385 -0
- package/src/src/cli/app/views/dashboard.ts +212 -0
- package/src/src/cli/index/command-router.ts +9 -40
- package/src/src/cli/index/start-handler.ts +195 -0
- package/src/src/cli/mcp/index.ts +11 -0
- package/src/src/cli/templates/integration-loader.ts +2 -8
- package/src/src/middleware/builtin/security/redis-rate-limit.ts +24 -11
- package/src/src/modules/react-loader/ssr-module-loader/cache/redis.ts +36 -50
- package/src/src/modules/react-loader/ssr-module-loader/loader.ts +168 -25
- package/src/src/observability/tracing/span-names.ts +2 -0
- package/src/src/platform/adapters/fs/cache/file-cache.ts +9 -3
- package/src/src/rendering/orchestrator/module-loader/cache.ts +14 -8
- package/src/src/rendering/orchestrator/module-loader/index.ts +94 -89
- package/src/src/server/context/cache-invalidation.ts +4 -0
- package/src/src/server/handlers/dev/dashboard/api.ts +2 -0
- package/src/src/server/handlers/dev/projects/ui-handler.ts +6 -0
- package/src/src/transforms/esm/http-cache.ts +160 -105
- package/src/src/transforms/esm/transform-cache.ts +53 -0
- package/src/src/transforms/mdx/esm-module-loader/module-fetcher/index.ts +2 -40
- package/src/src/utils/constants/cache.ts +21 -1
- package/src/src/utils/index.ts +0 -1
|
@@ -12,7 +12,6 @@ import { isAbsolute, join } from "../../platform/compat/path/index.js";
|
|
|
12
12
|
import { cwd } from "../../platform/compat/process.js";
|
|
13
13
|
import { rendererLogger as logger } from "../../utils/index.js";
|
|
14
14
|
import { simpleHash } from "../../utils/hash-utils.js";
|
|
15
|
-
import { Singleflight } from "../../utils/singleflight.js";
|
|
16
15
|
import { LRUCache } from "../../utils/lru-wrapper.js";
|
|
17
16
|
import { withSpan } from "../../observability/tracing/otlp-setup.js";
|
|
18
17
|
import { SpanNames } from "../../observability/tracing/span-names.js";
|
|
@@ -20,35 +19,13 @@ import { resolveImport } from "../../modules/import-map/resolver.js";
|
|
|
20
19
|
import { isDeno } from "../../platform/compat/runtime.js";
|
|
21
20
|
import { getDenoNpmReactMap, getReactImportMap, REACT_VERSION } from "./package-registry.js";
|
|
22
21
|
import { parseImports, replaceSpecifiers } from "./lexer.js";
|
|
22
|
+
import { CacheBackends, createDistributedCacheAccessor } from "../../cache/backend.js";
|
|
23
|
+
import { HTTP_MODULE_CACHE_MAX_ENTRIES, HTTP_MODULE_DISTRIBUTED_TTL_SEC, } from "../../utils/constants/cache.js";
|
|
23
24
|
/** Lazy-loaded distributed cache backend for cross-pod sharing */
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return Promise.resolve(distributedCache);
|
|
29
|
-
return distributedCacheInit.do("init", async () => {
|
|
30
|
-
try {
|
|
31
|
-
const { CacheBackends } = await import("../../cache/backend.js");
|
|
32
|
-
const backend = await CacheBackends.httpModule();
|
|
33
|
-
if (backend.type === "memory") {
|
|
34
|
-
distributedCache = null;
|
|
35
|
-
logger.debug("[HTTP-CACHE] No distributed cache available (memory only)");
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
distributedCache = backend;
|
|
39
|
-
logger.debug("[HTTP-CACHE] Distributed cache initialized", { type: backend.type });
|
|
40
|
-
return backend;
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
logger.debug("[HTTP-CACHE] Failed to initialize distributed cache", { error });
|
|
44
|
-
distributedCache = null;
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
/** TTL for cached modules in distributed cache (24 hours) */
|
|
50
|
-
const DISTRIBUTED_CACHE_TTL_SECONDS = 86400;
|
|
51
|
-
const cachedPaths = new LRUCache({ maxEntries: 2000 });
|
|
25
|
+
const getDistributedCache = createDistributedCacheAccessor(() => CacheBackends.httpModule(), "HTTP-CACHE");
|
|
26
|
+
/** TTL for cached modules in distributed cache (uses centralized config) */
|
|
27
|
+
const DISTRIBUTED_CACHE_TTL_SECONDS = HTTP_MODULE_DISTRIBUTED_TTL_SEC;
|
|
28
|
+
const cachedPaths = new LRUCache({ maxEntries: HTTP_MODULE_CACHE_MAX_ENTRIES });
|
|
52
29
|
const processingStack = new Set();
|
|
53
30
|
function ensureAbsoluteDir(path) {
|
|
54
31
|
return isAbsolute(path) ? path : join(cwd(), path);
|
|
@@ -247,16 +224,22 @@ async function cacheHttpModule(url, options) {
|
|
|
247
224
|
await fs.mkdir(cacheDir, { recursive: true });
|
|
248
225
|
await fs.writeTextFile(cachePath, code);
|
|
249
226
|
if (distributed) {
|
|
250
|
-
// Store code by URL, by hash (for direct recovery), and URL mapping (for debugging)
|
|
251
|
-
// Storing code by hash enables recovery without needing URL lookup
|
|
227
|
+
// Store code by URL, by hash (for direct recovery), and URL mapping (for debugging).
|
|
228
|
+
// Storing code by hash enables recovery without needing URL lookup.
|
|
229
|
+
// IMPORTANT: await the writes so other pods can recover this bundle immediately.
|
|
230
|
+
// Without await, a transform referencing this bundle could reach Redis before
|
|
231
|
+
// the bundle code does, causing ensureHttpBundlesExist on another pod to miss.
|
|
252
232
|
const hash = simpleHash(normalizedUrl);
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
233
|
+
try {
|
|
234
|
+
await Promise.all([
|
|
235
|
+
distributed.set(normalizedUrl, code, DISTRIBUTED_CACHE_TTL_SECONDS),
|
|
236
|
+
distributed.set(`code:${hash}`, code, DISTRIBUTED_CACHE_TTL_SECONDS),
|
|
237
|
+
distributed.set(`hash:${hash}`, normalizedUrl, DISTRIBUTED_CACHE_TTL_SECONDS),
|
|
238
|
+
]);
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
258
241
|
logger.debug("[HTTP-CACHE] Distributed cache set failed", { url: normalizedUrl, error });
|
|
259
|
-
}
|
|
242
|
+
}
|
|
260
243
|
}
|
|
261
244
|
cachedPaths.set(cacheKey, cachePath);
|
|
262
245
|
return cachePath;
|
|
@@ -365,6 +348,26 @@ export async function recoverHttpBundleByHash(hash, cacheDir) {
|
|
|
365
348
|
await fs.mkdir(absoluteCacheDir, { recursive: true });
|
|
366
349
|
await fs.writeTextFile(cachePath, cachedCode);
|
|
367
350
|
logger.info("[HTTP-CACHE] Bundle recovery successful (direct)", { hash, path: cachePath });
|
|
351
|
+
// Proactively recover transitive deps so the import retry doesn't
|
|
352
|
+
// fail again with a different missing bundle.
|
|
353
|
+
const BUNDLE_RE = /file:\/\/([^"'\s]+veryfront-http-bundle\/http-([a-f0-9]+)\.mjs)/gi;
|
|
354
|
+
const transitiveDeps = [];
|
|
355
|
+
let m;
|
|
356
|
+
while ((m = BUNDLE_RE.exec(cachedCode)) !== null) {
|
|
357
|
+
const tHash = m[2];
|
|
358
|
+
if (tHash !== hash) {
|
|
359
|
+
transitiveDeps.push({
|
|
360
|
+
path: join(absoluteCacheDir, `http-${tHash}.mjs`),
|
|
361
|
+
hash: tHash,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
if (transitiveDeps.length > 0) {
|
|
366
|
+
logger.info("[HTTP-CACHE] Recovering transitive deps from last-resort recovery", {
|
|
367
|
+
count: transitiveDeps.length,
|
|
368
|
+
});
|
|
369
|
+
await ensureHttpBundlesExist(transitiveDeps, cacheDir);
|
|
370
|
+
}
|
|
368
371
|
return true;
|
|
369
372
|
}
|
|
370
373
|
// Strategy 2: URL lookup then re-fetch (fallback for bundles cached before code:{hash} was added)
|
|
@@ -403,69 +406,118 @@ export async function ensureHttpBundlesExist(bundlePaths, cacheDir) {
|
|
|
403
406
|
if (bundlePaths.length === 0)
|
|
404
407
|
return [];
|
|
405
408
|
const fs = createFileSystem();
|
|
406
|
-
const
|
|
407
|
-
//
|
|
408
|
-
const
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
missing: missing.length,
|
|
420
|
-
total: bundlePaths.length,
|
|
421
|
-
});
|
|
422
|
-
const distributed = await getDistributedCache();
|
|
423
|
-
if (!distributed) {
|
|
424
|
-
logger.error("[HTTP-CACHE] No distributed cache available for bundle recovery");
|
|
425
|
-
return missing.map((m) => m.hash);
|
|
426
|
-
}
|
|
427
|
-
// Batch fetch from distributed cache
|
|
428
|
-
const codeKeys = missing.map((m) => `code:${m.hash}`);
|
|
429
|
-
let codes;
|
|
430
|
-
try {
|
|
431
|
-
if (distributed.getBatch) {
|
|
432
|
-
codes = await distributed.getBatch(codeKeys);
|
|
409
|
+
const absoluteCacheDir = ensureAbsoluteDir(cacheDir);
|
|
410
|
+
// Use [a-f0-9]+ to match both hex and decimal hashes consistently
|
|
411
|
+
const BUNDLE_RE = /file:\/\/([^"'\s]+veryfront-http-bundle\/http-([a-f0-9]+)\.mjs)/gi;
|
|
412
|
+
const extractBundleRefs = (code) => {
|
|
413
|
+
const refs = [];
|
|
414
|
+
const dedup = new Set();
|
|
415
|
+
let match;
|
|
416
|
+
while ((match = BUNDLE_RE.exec(code)) !== null) {
|
|
417
|
+
const hash = match[2];
|
|
418
|
+
if (!dedup.has(hash)) {
|
|
419
|
+
dedup.add(hash);
|
|
420
|
+
refs.push({ hash });
|
|
421
|
+
}
|
|
433
422
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
423
|
+
BUNDLE_RE.lastIndex = 0;
|
|
424
|
+
return refs;
|
|
425
|
+
};
|
|
426
|
+
const pending = bundlePaths.map((b) => ({ hash: b.hash }));
|
|
427
|
+
const seen = new Set();
|
|
428
|
+
const failed = new Set();
|
|
429
|
+
while (pending.length > 0) {
|
|
430
|
+
const batch = pending.splice(0, pending.length).filter((b) => !seen.has(b.hash));
|
|
431
|
+
if (batch.length === 0)
|
|
432
|
+
break;
|
|
433
|
+
for (const item of batch) {
|
|
434
|
+
seen.add(item.hash);
|
|
438
435
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
const
|
|
448
|
-
if (
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
436
|
+
// Check which bundles exist locally using canonical paths derived from
|
|
437
|
+
// cacheDir + hash. Don't trust caller-provided paths — they may reference
|
|
438
|
+
// a different pod's absolute directory.
|
|
439
|
+
const existenceChecks = await Promise.all(batch.map(async ({ hash }) => ({
|
|
440
|
+
hash,
|
|
441
|
+
canonicalPath: join(absoluteCacheDir, `http-${hash}.mjs`),
|
|
442
|
+
exists: await exists(join(absoluteCacheDir, `http-${hash}.mjs`)),
|
|
443
|
+
})));
|
|
444
|
+
const missing = existenceChecks.filter((b) => !b.exists);
|
|
445
|
+
if (missing.length === 0)
|
|
446
|
+
continue;
|
|
447
|
+
logger.info("[HTTP-CACHE] Fetching missing bundles from distributed cache", {
|
|
448
|
+
missing: missing.length,
|
|
449
|
+
total: batch.length,
|
|
450
|
+
});
|
|
451
|
+
const distributed = await getDistributedCache();
|
|
452
|
+
if (!distributed) {
|
|
453
|
+
logger.error("[HTTP-CACHE] No distributed cache available for bundle recovery");
|
|
454
|
+
for (const m of missing)
|
|
455
|
+
failed.add(m.hash);
|
|
456
|
+
continue;
|
|
452
457
|
}
|
|
458
|
+
// Batch fetch from distributed cache
|
|
459
|
+
const codeKeys = missing.map((m) => `code:${m.hash}`);
|
|
460
|
+
let codes;
|
|
453
461
|
try {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
462
|
+
if (distributed.getBatch) {
|
|
463
|
+
codes = await distributed.getBatch(codeKeys);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
const results = await Promise.all(codeKeys.map(async (key) => [key, await distributed.get(key)]));
|
|
467
|
+
codes = new Map(results);
|
|
468
|
+
}
|
|
458
469
|
}
|
|
459
470
|
catch (error) {
|
|
460
|
-
logger.error("[HTTP-CACHE]
|
|
461
|
-
|
|
471
|
+
logger.error("[HTTP-CACHE] Batch fetch from distributed cache failed", { error });
|
|
472
|
+
for (const m of missing)
|
|
473
|
+
failed.add(m.hash);
|
|
474
|
+
continue;
|
|
462
475
|
}
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
476
|
+
// Write fetched bundles to disk using canonical paths and scan for transitive deps
|
|
477
|
+
await Promise.all(missing.map(async ({ hash, canonicalPath }) => {
|
|
478
|
+
const code = codes.get(`code:${hash}`);
|
|
479
|
+
if (!code) {
|
|
480
|
+
// Try single-bundle recovery as last resort
|
|
481
|
+
const recovered = await recoverHttpBundleByHash(hash, absoluteCacheDir);
|
|
482
|
+
if (!recovered) {
|
|
483
|
+
failed.add(hash);
|
|
484
|
+
}
|
|
485
|
+
else {
|
|
486
|
+
// Read the recovered bundle to scan for transitive deps
|
|
487
|
+
try {
|
|
488
|
+
const recoveredCode = await fs.readTextFile(canonicalPath);
|
|
489
|
+
for (const ref of extractBundleRefs(recoveredCode)) {
|
|
490
|
+
if (!seen.has(ref.hash))
|
|
491
|
+
pending.push(ref);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
catch { /* ignore read errors for dep scanning */ }
|
|
495
|
+
}
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
try {
|
|
499
|
+
await fs.mkdir(absoluteCacheDir, { recursive: true });
|
|
500
|
+
await fs.writeTextFile(canonicalPath, code);
|
|
501
|
+
logger.debug("[HTTP-CACHE] Wrote bundle to disk", { hash, path: canonicalPath });
|
|
502
|
+
// Scan recovered code for transitive HTTP bundle dependencies.
|
|
503
|
+
// HTTP bundles import other bundles (e.g., esm.sh packages depending
|
|
504
|
+
// on other packages). Without this, Pod B recovers only the top-level
|
|
505
|
+
// bundle and gets "Module not found" for transitive deps at import time.
|
|
506
|
+
for (const ref of extractBundleRefs(code)) {
|
|
507
|
+
if (!seen.has(ref.hash))
|
|
508
|
+
pending.push(ref);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
logger.error("[HTTP-CACHE] Failed to write bundle to disk", { hash, error });
|
|
513
|
+
failed.add(hash);
|
|
514
|
+
}
|
|
515
|
+
}));
|
|
466
516
|
}
|
|
467
|
-
|
|
468
|
-
logger.
|
|
517
|
+
if (failed.size > 0) {
|
|
518
|
+
logger.warn("[HTTP-CACHE] Some bundles could not be recovered", {
|
|
519
|
+
failed: Array.from(failed),
|
|
520
|
+
});
|
|
469
521
|
}
|
|
470
|
-
return failed;
|
|
522
|
+
return Array.from(failed);
|
|
471
523
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CacheBackend } from "../../cache/backend.js";
|
|
1
2
|
export interface TransformCacheEntry {
|
|
2
3
|
code: string;
|
|
3
4
|
hash: string;
|
|
@@ -15,6 +16,30 @@ export declare function getCachedTransform(key: string): TransformCacheEntry | u
|
|
|
15
16
|
export declare function setCachedTransformAsync(key: string, code: string, hash: string, ttlSeconds?: number): Promise<void>;
|
|
16
17
|
export declare function setCachedTransform(key: string, code: string, hash: string, ttlSeconds?: number): void;
|
|
17
18
|
export declare function destroyTransformCache(): void;
|
|
19
|
+
/**
|
|
20
|
+
* Get the underlying distributed cache backend.
|
|
21
|
+
*
|
|
22
|
+
* This is exposed for callers that need direct access to the distributed
|
|
23
|
+
* cache (e.g., MDX module-fetcher that stores raw code strings instead of
|
|
24
|
+
* TransformCacheEntry JSON). Ensures initialization happens only once.
|
|
25
|
+
*
|
|
26
|
+
* Returns null if distributed cache is not available (memory-only mode).
|
|
27
|
+
*/
|
|
28
|
+
export declare function getDistributedTransformBackend(): Promise<CacheBackend | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Get a cached transform or compute it if not found.
|
|
31
|
+
*
|
|
32
|
+
* This is the preferred way to use the transform cache - it handles:
|
|
33
|
+
* - Cache lookup (distributed first, then local fallback)
|
|
34
|
+
* - Compute on miss
|
|
35
|
+
* - Cache storage on compute
|
|
36
|
+
*
|
|
37
|
+
* @param key - Cache key (use generateCacheKey to build it)
|
|
38
|
+
* @param computeFn - Function to compute the transform if not cached
|
|
39
|
+
* @param ttlSeconds - TTL for the cached entry
|
|
40
|
+
* @returns The cached or computed code
|
|
41
|
+
*/
|
|
42
|
+
export declare function getOrComputeTransform(key: string, computeFn: () => Promise<string>, ttlSeconds?: number): Promise<string>;
|
|
18
43
|
export declare function getTransformCacheStats(): {
|
|
19
44
|
fallbackEntries: number;
|
|
20
45
|
maxFallbackEntries: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transform-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/transform-cache.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"transform-cache.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/transform-cache.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,YAAY,EAAqC,MAAM,wBAAwB,CAAC;AAK9F,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAeD,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,OAAO,CAAC,CAuBjE;AAED,wBAAgB,yBAAyB,IAAI,OAAO,CAEnD;AAED,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,iCAA2B,CAAC;AAE7D,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,kCAA4B,CAAC;AAE7D,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,GAAG,GAAE,OAAe,EACpB,WAAW,GAAE,OAAe,GAC3B,MAAM,CAER;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC,CAW1C;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAG/E;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAA4B,GACvC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,MAA4B,GACvC,IAAI,CAeN;AAuBD,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAED;;;;;;;;GAQG;AACH,wBAAsB,8BAA8B,IAAI,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAInF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAChC,UAAU,GAAE,MAA4B,GACvC,OAAO,CAAC,MAAM,CAAC,CAmBjB;AAED,wBAAgB,sBAAsB,IAAI;IACxC,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB,CAMA"}
|
|
@@ -110,6 +110,51 @@ function pruneLocalFallback() {
|
|
|
110
110
|
export function destroyTransformCache() {
|
|
111
111
|
localFallback.clear();
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Get the underlying distributed cache backend.
|
|
115
|
+
*
|
|
116
|
+
* This is exposed for callers that need direct access to the distributed
|
|
117
|
+
* cache (e.g., MDX module-fetcher that stores raw code strings instead of
|
|
118
|
+
* TransformCacheEntry JSON). Ensures initialization happens only once.
|
|
119
|
+
*
|
|
120
|
+
* Returns null if distributed cache is not available (memory-only mode).
|
|
121
|
+
*/
|
|
122
|
+
export async function getDistributedTransformBackend() {
|
|
123
|
+
await initializeTransformCache();
|
|
124
|
+
if (!cacheBackend || cacheBackend.type === "memory")
|
|
125
|
+
return null;
|
|
126
|
+
return cacheBackend;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Get a cached transform or compute it if not found.
|
|
130
|
+
*
|
|
131
|
+
* This is the preferred way to use the transform cache - it handles:
|
|
132
|
+
* - Cache lookup (distributed first, then local fallback)
|
|
133
|
+
* - Compute on miss
|
|
134
|
+
* - Cache storage on compute
|
|
135
|
+
*
|
|
136
|
+
* @param key - Cache key (use generateCacheKey to build it)
|
|
137
|
+
* @param computeFn - Function to compute the transform if not cached
|
|
138
|
+
* @param ttlSeconds - TTL for the cached entry
|
|
139
|
+
* @returns The cached or computed code
|
|
140
|
+
*/
|
|
141
|
+
export async function getOrComputeTransform(key, computeFn, ttlSeconds = DEFAULT_TTL_SECONDS) {
|
|
142
|
+
// Try to get from cache first
|
|
143
|
+
const cached = await getCachedTransformAsync(key);
|
|
144
|
+
if (cached) {
|
|
145
|
+
logger.debug("[TransformCache] Cache hit", { key });
|
|
146
|
+
return cached.code;
|
|
147
|
+
}
|
|
148
|
+
// Compute on miss
|
|
149
|
+
logger.debug("[TransformCache] Cache miss, computing", { key });
|
|
150
|
+
const code = await computeFn();
|
|
151
|
+
// Store in cache (fire-and-forget for performance)
|
|
152
|
+
const hash = String(Date.now()); // Simple hash for now
|
|
153
|
+
setCachedTransformAsync(key, code, hash, ttlSeconds).catch((error) => {
|
|
154
|
+
logger.debug("[TransformCache] Failed to cache computed transform", { key, error });
|
|
155
|
+
});
|
|
156
|
+
return code;
|
|
157
|
+
}
|
|
113
158
|
export function getTransformCacheStats() {
|
|
114
159
|
return {
|
|
115
160
|
fallbackEntries: localFallback.size,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/mdx/esm-module-loader/module-fetcher/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../../src/src/transforms/mdx/esm-module-loader/module-fetcher/index.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAW5E,OAAO,KAAK,EAAE,oBAAoB,EAAsB,MAAM,aAAa,CAAC;AA4F5E;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,MAAM,EACjB,WAAW,CAAC,EAAE,MAAM,EACpB,KAAK,CAAC,EAAE,MAAM,GACb,IAAI,CAON;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAiCxD;AAsND;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,oBAAoB,EAC7B,gBAAgB,CAAC,EAAE,MAAM,GACxB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6CxB;AA4OD;;GAEG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,EACvB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,GACvD,oBAAoB,CAUtB"}
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
import * as dntShim from "../../../../../_dnt.shims.js";
|
|
15
15
|
import { join, posix } from "../../../../../deps/deno.land/std@0.220.0/path/mod.js";
|
|
16
16
|
import { rendererLogger as logger } from "../../../../utils/index.js";
|
|
17
|
-
import { Singleflight } from "../../../../utils/singleflight.js";
|
|
18
17
|
import { withSpan } from "../../../../observability/tracing/otlp-setup.js";
|
|
19
18
|
import { SpanNames } from "../../../../observability/tracing/span-names.js";
|
|
20
19
|
import { transformToESM } from "../../../esm-transform.js";
|
|
@@ -27,43 +26,10 @@ import { hashString } from "../utils/hash.js";
|
|
|
27
26
|
import { createStubModule } from "../utils/stub-module.js";
|
|
28
27
|
import { resolveModuleFile } from "../resolution/file-finder.js";
|
|
29
28
|
import { recordSSRModules } from "../../../../modules/manifest/route-module-manifest.js";
|
|
29
|
+
import { getDistributedTransformBackend } from "../../../esm/transform-cache.js";
|
|
30
30
|
import { TRANSFORM_DISTRIBUTED_TTL_SEC } from "../../../../utils/constants/cache.js";
|
|
31
|
-
/**
|
|
32
|
-
* Distributed transform cache for cross-pod sharing.
|
|
33
|
-
* Caches transformed module code in Redis/API so other pods don't need to re-transform.
|
|
34
|
-
*/
|
|
35
|
-
let distributedTransformCache;
|
|
36
|
-
const distributedCacheInit = new Singleflight();
|
|
37
31
|
/** TTL for cached transforms (uses centralized config) */
|
|
38
32
|
const TRANSFORM_CACHE_TTL_SECONDS = TRANSFORM_DISTRIBUTED_TTL_SEC;
|
|
39
|
-
function getDistributedTransformCache() {
|
|
40
|
-
if (distributedTransformCache !== undefined)
|
|
41
|
-
return Promise.resolve(distributedTransformCache);
|
|
42
|
-
return distributedCacheInit.do("init", async () => {
|
|
43
|
-
try {
|
|
44
|
-
const { CacheBackends } = await import("../../../../cache/backend.js");
|
|
45
|
-
const backend = await CacheBackends.transform();
|
|
46
|
-
// Only use distributed cache if API or Redis (not memory - that's per-process)
|
|
47
|
-
if (backend.type === "memory") {
|
|
48
|
-
distributedTransformCache = null;
|
|
49
|
-
logger.debug(`${LOG_PREFIX_MDX_LOADER} No distributed transform cache (memory only)`);
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
distributedTransformCache = backend;
|
|
53
|
-
logger.debug(`${LOG_PREFIX_MDX_LOADER} Distributed transform cache initialized`, {
|
|
54
|
-
type: backend.type,
|
|
55
|
-
});
|
|
56
|
-
return backend;
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
logger.debug(`${LOG_PREFIX_MDX_LOADER} Failed to init distributed transform cache`, {
|
|
60
|
-
error,
|
|
61
|
-
});
|
|
62
|
-
distributedTransformCache = null;
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
33
|
/**
|
|
68
34
|
* Build cache key for transformed module.
|
|
69
35
|
* Includes content hash so cache invalidates when source changes.
|
|
@@ -415,7 +381,7 @@ async function doFetchAndCacheModule(normalizedPath, context, fetchAndCacheModul
|
|
|
415
381
|
const contentHash = hashString(sourceCode);
|
|
416
382
|
const transformCacheKey = getTransformCacheKey(projectId, normalizedPath, contentHash);
|
|
417
383
|
let moduleCode = null;
|
|
418
|
-
const distributedCache = await
|
|
384
|
+
const distributedCache = await getDistributedTransformBackend();
|
|
419
385
|
if (distributedCache) {
|
|
420
386
|
try {
|
|
421
387
|
const cached = await distributedCache.get(transformCacheKey);
|
|
@@ -51,4 +51,8 @@ export declare const REVALIDATION_TIMEOUT_MS: number;
|
|
|
51
51
|
export declare const HTTP_MODULE_CACHE_MAX_ENTRIES: number;
|
|
52
52
|
export declare const HTTP_MODULE_DISTRIBUTED_TTL_SEC: number;
|
|
53
53
|
export declare const TRANSFORM_DISTRIBUTED_TTL_SEC: number;
|
|
54
|
+
export declare const MODULE_CACHE_MAX_ENTRIES: number;
|
|
55
|
+
export declare const MODULE_CACHE_TTL_MS: number;
|
|
56
|
+
export declare const ESM_CACHE_MAX_ENTRIES: number;
|
|
57
|
+
export declare const ESM_CACHE_TTL_MS: number;
|
|
54
58
|
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/src/utils/constants/cache.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC,eAAO,MAAM,aAAa,QAAqC,CAAC;AAChE,eAAO,MAAM,WAAW,QAAmC,CAAC;AAC5D,eAAO,MAAM,UAAU,QAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../../src/src/utils/constants/cache.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,gBAAgB,KAAK,CAAC;AACnC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC,eAAO,MAAM,aAAa,QAAqC,CAAC;AAChE,eAAO,MAAM,WAAW,QAAmC,CAAC;AAC5D,eAAO,MAAM,UAAU,QAA8B,CAAC;AA8BtD,eAAO,MAAM,uBAAuB,QAA+C,CAAC;AAEpF,eAAO,MAAM,4BAA4B,QAAoD,CAAC;AAC9F,eAAO,MAAM,uBAAuB,QAAqB,CAAC;AAE1D,eAAO,MAAM,wBAAwB,QAAgD,CAAC;AACtF,eAAO,MAAM,mBAAmB,QAAqB,CAAC;AAEtD,eAAO,MAAM,yBAAyB,QAAiD,CAAC;AACxF,eAAO,MAAM,oBAAoB,QAAoB,CAAC;AAEtD,eAAO,MAAM,sBAAsB,QAA8C,CAAC;AAClF,eAAO,MAAM,iBAAiB,QAAqB,CAAC;AAEpD,eAAO,MAAM,yBAAyB,QAAiD,CAAC;AACxF,eAAO,MAAM,oBAAoB,QAAqB,CAAC;AAEvD,eAAO,MAAM,2BAA2B,QAAa,CAAC;AACtD,eAAO,MAAM,4BAA4B,QAAoB,CAAC;AAE9D,eAAO,MAAM,8BAA8B,QAAa,CAAC;AACzD,eAAO,MAAM,+BAA+B,QAAoB,CAAC;AAEjE,eAAO,MAAM,2BAA2B,QAAiB,CAAC;AAC1D,eAAO,MAAM,0BAA0B,QAAc,CAAC;AAEtD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,eAAO,MAAM,6BAA6B,QAAwC,CAAC;AAMnF,eAAO,MAAM,yCAAyC,QAGrD,CAAC;AACF,eAAO,MAAM,sCAAsC,QAGlD,CAAC;AAEF,eAAO,MAAM,wCAAwC,QAGpD,CAAC;AACF,eAAO,MAAM,qCAAqC,QAGjD,CAAC;AAEF,eAAO,MAAM,mCAAmC,QAG/C,CAAC;AACF,eAAO,MAAM,gCAAgC,QAG5C,CAAC;AAEF,eAAO,MAAM,kCAAkC,QAG9C,CAAC;AACF,eAAO,MAAM,+BAA+B,QAG3C,CAAC;AAEF,6DAA6D;AAC7D,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,YAAY,GAAG,WAAW,GAAG,MAAM,GAAG,KAAK,EACtD,YAAY,GAAE,OAA4B,GACzC,MAAM,CAeR;AAGD,eAAO,MAAM,6BAA6B,QAAS,CAAC;AACpD,eAAO,MAAM,0BAA0B,QAAwC,CAAC;AAChF,eAAO,MAAM,0BAA0B,QAAqD,CAAC;AAC7F,eAAO,MAAM,wBAAwB,QAAiD,CAAC;AACvF,eAAO,MAAM,sBAAsB,QAA+C,CAAC;AACnF,eAAO,MAAM,sBAAsB,QAA8C,CAAC;AAGlF,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAC/C,eAAO,MAAM,6BAA6B,OAAO,CAAC;AAClD,eAAO,MAAM,2BAA2B,WAAW,CAAC;AAGpD,eAAO,MAAM,yBAAyB,QAAQ,CAAC;AAC/C,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAG7C,eAAO,MAAM,4BAA4B,QAAmD,CAAC;AAC7F,eAAO,MAAM,2BAA2B,QAAkD,CAAC;AAC3F,eAAO,MAAM,uBAAuB,QAAiD,CAAC;AAItF,eAAO,MAAM,6BAA6B,QAAsD,CAAC;AACjG,eAAO,MAAM,+BAA+B,QAG3C,CAAC;AAIF,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAIF,eAAO,MAAM,wBAAwB,QAAkD,CAAC;AACxF,eAAO,MAAM,mBAAmB,QAG/B,CAAC;AAGF,eAAO,MAAM,qBAAqB,QAA8C,CAAC;AACjF,eAAO,MAAM,gBAAgB,QAG5B,CAAC"}
|
|
@@ -8,7 +8,13 @@ export const MS_PER_HOUR = MINUTES_PER_HOUR * MS_PER_MINUTE;
|
|
|
8
8
|
export const ONE_DAY_MS = HOURS_PER_DAY * MS_PER_HOUR;
|
|
9
9
|
function getEnvString(key) {
|
|
10
10
|
const g = dntShim.dntGlobalThis;
|
|
11
|
-
|
|
11
|
+
try {
|
|
12
|
+
return g.Deno?.env?.get?.(key) ?? g.process?.env?.[key];
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
// Gracefully handle missing --allow-env permission in Deno
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
12
18
|
}
|
|
13
19
|
function getEnvNumber(key, fallback) {
|
|
14
20
|
const value = getEnvString(key);
|
|
@@ -95,3 +101,10 @@ export const HTTP_MODULE_DISTRIBUTED_TTL_SEC = getEnvNumber("HTTP_MODULE_DISTRIB
|
|
|
95
101
|
// Transform cache for module compilation
|
|
96
102
|
// Same TTL as HTTP module cache since transforms are tied to content hashes
|
|
97
103
|
export const TRANSFORM_DISTRIBUTED_TTL_SEC = getEnvNumber("TRANSFORM_DISTRIBUTED_TTL_SEC", HOURS_PER_DAY * MINUTES_PER_HOUR * SECONDS_PER_MINUTE);
|
|
104
|
+
// Pod-level module cache (shared across all RenderPipeline instances)
|
|
105
|
+
// These caches map module paths to transformed temp file paths
|
|
106
|
+
export const MODULE_CACHE_MAX_ENTRIES = getEnvNumber("MODULE_CACHE_MAX_ENTRIES", 10000);
|
|
107
|
+
export const MODULE_CACHE_TTL_MS = getEnvNumber("MODULE_CACHE_TTL_MS", 5 * MS_PER_MINUTE);
|
|
108
|
+
// ESM cache for external module mappings
|
|
109
|
+
export const ESM_CACHE_MAX_ENTRIES = getEnvNumber("ESM_CACHE_MAX_ENTRIES", 5000);
|
|
110
|
+
export const ESM_CACHE_TTL_MS = getEnvNumber("ESM_CACHE_TTL_MS", 10 * MS_PER_MINUTE);
|
package/esm/src/utils/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export * from "./logger/index.js";
|
|
|
3
3
|
export * from "./constants/index.js";
|
|
4
4
|
export { VERSION } from "./version.js";
|
|
5
5
|
export * from "./paths.js";
|
|
6
|
-
export { type BundleCode as HashBundleCode, computeCodeHash, computeContentHash, computeHash, getContentHash, shortHash, simpleHash,
|
|
6
|
+
export { type BundleCode as HashBundleCode, computeCodeHash, computeContentHash, computeHash, getContentHash, shortHash, simpleHash, } from "./hash-utils.js";
|
|
7
7
|
export { MemoCache, memoize, memoizeAsync, simpleHash as memoizeHash } from "./memoize.js";
|
|
8
8
|
export * from "./path-utils.js";
|
|
9
9
|
export * from "./format-utils.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,cAAc,YAAY,CAAC;AAE3B,OAAO,EACL,KAAK,UAAU,IAAI,cAAc,EACjC,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,SAAS,EACT,UAAU,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,cAAc,YAAY,CAAC;AAE3B,OAAO,EACL,KAAK,UAAU,IAAI,cAAc,EACjC,eAAe,EACf,kBAAkB,EAClB,WAAW,EACX,cAAc,EACd,SAAS,EACT,UAAU,GACX,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,EAAE,UAAU,IAAI,WAAW,EAAE,MAAM,cAAc,CAAC;AAE3F,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,cAAc,sBAAsB,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC"}
|
package/esm/src/utils/index.js
CHANGED
|
@@ -3,7 +3,7 @@ export * from "./logger/index.js";
|
|
|
3
3
|
export * from "./constants/index.js";
|
|
4
4
|
export { VERSION } from "./version.js";
|
|
5
5
|
export * from "./paths.js";
|
|
6
|
-
export { computeCodeHash, computeContentHash, computeHash, getContentHash, shortHash, simpleHash,
|
|
6
|
+
export { computeCodeHash, computeContentHash, computeHash, getContentHash, shortHash, simpleHash, } from "./hash-utils.js";
|
|
7
7
|
export { MemoCache, memoize, memoizeAsync, simpleHash as memoizeHash } from "./memoize.js";
|
|
8
8
|
export * from "./path-utils.js";
|
|
9
9
|
export * from "./format-utils.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "veryfront",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.83",
|
|
4
4
|
"description": "Zero-config React meta-framework for building agentic AI applications",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -195,6 +195,7 @@
|
|
|
195
195
|
"picocolors": "^1.1.0",
|
|
196
196
|
"react": "19.1.1",
|
|
197
197
|
"react-dom": "19.1.1",
|
|
198
|
+
"redis": "*",
|
|
198
199
|
"rehype-highlight": "7.0.2",
|
|
199
200
|
"rehype-slug": "6.0.0",
|
|
200
201
|
"rehype-starry-night": "2.2.0",
|
package/src/deno.js
CHANGED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Cache Module
|
|
3
|
+
*
|
|
4
|
+
* Provides configurable caching for OAuth tokens.
|
|
5
|
+
* Supports in-memory (single instance) and Redis (distributed) backends.
|
|
6
|
+
* Redis cache includes automatic fallback to memory when Redis is unavailable.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type {
|
|
10
|
+
CacheOptions,
|
|
11
|
+
CacheStats,
|
|
12
|
+
MemoryCacheOptions,
|
|
13
|
+
RedisCacheOptions,
|
|
14
|
+
TokenCache,
|
|
15
|
+
TokenCacheEntry,
|
|
16
|
+
} from "./types.js";
|
|
17
|
+
export { MemoryCache } from "./memory-cache.js";
|
|
18
|
+
export { RedisCache } from "./redis-cache.js";
|
|
19
|
+
export { ResilientCache } from "./resilient-cache.js";
|
|
20
|
+
|
|
21
|
+
import type { CacheOptions, TokenCache } from "./types.js";
|
|
22
|
+
import { MemoryCache } from "./memory-cache.js";
|
|
23
|
+
import { RedisCache } from "./redis-cache.js";
|
|
24
|
+
import { ResilientCache } from "./resilient-cache.js";
|
|
25
|
+
import { getEnv } from "../../src/platform/compat/process.js";
|
|
26
|
+
import { proxyLogger } from "../logger.js";
|
|
27
|
+
import { withSpan } from "../tracing.js";
|
|
28
|
+
|
|
29
|
+
const logger = proxyLogger.child({ module: "cache" });
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a token cache based on configuration.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* // In-memory cache (default)
|
|
37
|
+
* const cache = createCache({ type: "memory" });
|
|
38
|
+
*
|
|
39
|
+
* // Redis cache for distributed deployments
|
|
40
|
+
* const cache = createCache({
|
|
41
|
+
* type: "redis",
|
|
42
|
+
* options: { url: "redis://localhost:6379" }
|
|
43
|
+
* });
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export async function createCache(options: CacheOptions): Promise<TokenCache> {
|
|
47
|
+
return withSpan("cache.create", async () => {
|
|
48
|
+
switch (options.type) {
|
|
49
|
+
case "redis":
|
|
50
|
+
return new RedisCache(options.options);
|
|
51
|
+
case "memory":
|
|
52
|
+
default:
|
|
53
|
+
return new MemoryCache(options.options);
|
|
54
|
+
}
|
|
55
|
+
}, { "cache.type": options.type });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create cache from environment variables.
|
|
60
|
+
*
|
|
61
|
+
* Environment variables:
|
|
62
|
+
* - CACHE_TYPE: "memory" or "redis" (default: "memory")
|
|
63
|
+
* - REDIS_URL: Redis connection URL (required if CACHE_TYPE=redis)
|
|
64
|
+
* - REDIS_PREFIX: Key prefix (default: "vf:token:")
|
|
65
|
+
*
|
|
66
|
+
* When CACHE_TYPE=redis, automatically wraps with ResilientCache for
|
|
67
|
+
* graceful fallback to memory when Redis is unavailable.
|
|
68
|
+
*/
|
|
69
|
+
export async function createCacheFromEnv(): Promise<TokenCache> {
|
|
70
|
+
return withSpan("cache.createFromEnv", async () => {
|
|
71
|
+
const cacheType = getEnv("CACHE_TYPE") || "memory";
|
|
72
|
+
|
|
73
|
+
if (cacheType === "redis") {
|
|
74
|
+
const url = getEnv("REDIS_URL");
|
|
75
|
+
if (!url) {
|
|
76
|
+
logger.warn("[Cache] CACHE_TYPE=redis but REDIS_URL not set, falling back to memory");
|
|
77
|
+
return new MemoryCache();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const redisCache = new RedisCache({
|
|
81
|
+
url,
|
|
82
|
+
prefix: getEnv("REDIS_PREFIX") || "vf:token:",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Wrap Redis with resilient fallback to memory cache
|
|
86
|
+
// This ensures the proxy continues to function when Redis is unavailable
|
|
87
|
+
logger.info("[Cache] Using Redis with memory fallback (ResilientCache)");
|
|
88
|
+
return new ResilientCache(redisCache, new MemoryCache());
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return new MemoryCache();
|
|
92
|
+
}, { "cache.type": getEnv("CACHE_TYPE") || "memory" });
|
|
93
|
+
}
|