veryfront 0.0.82 → 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 +2 -0
- package/esm/src/cache/backend.d.ts.map +1 -1
- package/esm/src/cache/backend.js +2 -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/multi-tier.d.ts +0 -29
- package/esm/src/cache/multi-tier.d.ts.map +1 -1
- package/esm/src/cache/multi-tier.js +0 -26
- 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/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 +34 -13
- 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/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 +139 -64
- 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 +3 -0
- package/src/src/cache/cache-key-builder.ts +0 -9
- package/src/src/cache/multi-tier.ts +0 -41
- 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/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 +38 -14
- package/src/src/platform/adapters/fs/cache/file-cache.ts +9 -3
- 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 +148 -73
- package/src/src/utils/index.ts +0 -1
|
@@ -280,16 +280,21 @@ async function cacheHttpModule(url: string, options: CacheOptions): Promise<stri
|
|
|
280
280
|
await fs.writeTextFile(cachePath, code);
|
|
281
281
|
|
|
282
282
|
if (distributed) {
|
|
283
|
-
// Store code by URL, by hash (for direct recovery), and URL mapping (for debugging)
|
|
284
|
-
// Storing code by hash enables recovery without needing URL lookup
|
|
283
|
+
// Store code by URL, by hash (for direct recovery), and URL mapping (for debugging).
|
|
284
|
+
// Storing code by hash enables recovery without needing URL lookup.
|
|
285
|
+
// IMPORTANT: await the writes so other pods can recover this bundle immediately.
|
|
286
|
+
// Without await, a transform referencing this bundle could reach Redis before
|
|
287
|
+
// the bundle code does, causing ensureHttpBundlesExist on another pod to miss.
|
|
285
288
|
const hash = simpleHash(normalizedUrl);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
289
|
+
try {
|
|
290
|
+
await Promise.all([
|
|
291
|
+
distributed.set(normalizedUrl, code, DISTRIBUTED_CACHE_TTL_SECONDS),
|
|
292
|
+
distributed.set(`code:${hash}`, code, DISTRIBUTED_CACHE_TTL_SECONDS),
|
|
293
|
+
distributed.set(`hash:${hash}`, normalizedUrl, DISTRIBUTED_CACHE_TTL_SECONDS),
|
|
294
|
+
]);
|
|
295
|
+
} catch (error) {
|
|
291
296
|
logger.debug("[HTTP-CACHE] Distributed cache set failed", { url: normalizedUrl, error });
|
|
292
|
-
}
|
|
297
|
+
}
|
|
293
298
|
}
|
|
294
299
|
|
|
295
300
|
cachedPaths.set(cacheKey, cachePath);
|
|
@@ -431,6 +436,28 @@ export async function recoverHttpBundleByHash(hash: string, cacheDir: string): P
|
|
|
431
436
|
await fs.mkdir(absoluteCacheDir, { recursive: true });
|
|
432
437
|
await fs.writeTextFile(cachePath, cachedCode);
|
|
433
438
|
logger.info("[HTTP-CACHE] Bundle recovery successful (direct)", { hash, path: cachePath });
|
|
439
|
+
|
|
440
|
+
// Proactively recover transitive deps so the import retry doesn't
|
|
441
|
+
// fail again with a different missing bundle.
|
|
442
|
+
const BUNDLE_RE = /file:\/\/([^"'\s]+veryfront-http-bundle\/http-([a-f0-9]+)\.mjs)/gi;
|
|
443
|
+
const transitiveDeps: Array<{ path: string; hash: string }> = [];
|
|
444
|
+
let m;
|
|
445
|
+
while ((m = BUNDLE_RE.exec(cachedCode)) !== null) {
|
|
446
|
+
const tHash = m[2]!;
|
|
447
|
+
if (tHash !== hash) {
|
|
448
|
+
transitiveDeps.push({
|
|
449
|
+
path: join(absoluteCacheDir, `http-${tHash}.mjs`),
|
|
450
|
+
hash: tHash,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
if (transitiveDeps.length > 0) {
|
|
455
|
+
logger.info("[HTTP-CACHE] Recovering transitive deps from last-resort recovery", {
|
|
456
|
+
count: transitiveDeps.length,
|
|
457
|
+
});
|
|
458
|
+
await ensureHttpBundlesExist(transitiveDeps, cacheDir);
|
|
459
|
+
}
|
|
460
|
+
|
|
434
461
|
return true;
|
|
435
462
|
}
|
|
436
463
|
|
|
@@ -474,81 +501,129 @@ export async function ensureHttpBundlesExist(
|
|
|
474
501
|
if (bundlePaths.length === 0) return [];
|
|
475
502
|
|
|
476
503
|
const fs = createFileSystem();
|
|
477
|
-
const
|
|
478
|
-
|
|
479
|
-
// Check which bundles exist locally
|
|
480
|
-
const existenceChecks = await Promise.all(
|
|
481
|
-
bundlePaths.map(async ({ path, hash }) => ({
|
|
482
|
-
path,
|
|
483
|
-
hash,
|
|
484
|
-
exists: await exists(path),
|
|
485
|
-
})),
|
|
486
|
-
);
|
|
504
|
+
const absoluteCacheDir = ensureAbsoluteDir(cacheDir);
|
|
487
505
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
506
|
+
// Use [a-f0-9]+ to match both hex and decimal hashes consistently
|
|
507
|
+
const BUNDLE_RE = /file:\/\/([^"'\s]+veryfront-http-bundle\/http-([a-f0-9]+)\.mjs)/gi;
|
|
508
|
+
|
|
509
|
+
const extractBundleRefs = (code: string): Array<{ hash: string }> => {
|
|
510
|
+
const refs: Array<{ hash: string }> = [];
|
|
511
|
+
const dedup = new Set<string>();
|
|
512
|
+
let match;
|
|
513
|
+
while ((match = BUNDLE_RE.exec(code)) !== null) {
|
|
514
|
+
const hash = match[2] as string;
|
|
515
|
+
if (!dedup.has(hash)) {
|
|
516
|
+
dedup.add(hash);
|
|
517
|
+
refs.push({ hash });
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
BUNDLE_RE.lastIndex = 0;
|
|
521
|
+
return refs;
|
|
522
|
+
};
|
|
493
523
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
});
|
|
524
|
+
const pending: Array<{ hash: string }> = bundlePaths.map((b) => ({ hash: b.hash }));
|
|
525
|
+
const seen = new Set<string>();
|
|
526
|
+
const failed = new Set<string>();
|
|
498
527
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
return missing.map((m) => m.hash);
|
|
503
|
-
}
|
|
528
|
+
while (pending.length > 0) {
|
|
529
|
+
const batch = pending.splice(0, pending.length).filter((b) => !seen.has(b.hash));
|
|
530
|
+
if (batch.length === 0) break;
|
|
504
531
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
532
|
+
for (const item of batch) {
|
|
533
|
+
seen.add(item.hash);
|
|
534
|
+
}
|
|
508
535
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
536
|
+
// Check which bundles exist locally using canonical paths derived from
|
|
537
|
+
// cacheDir + hash. Don't trust caller-provided paths — they may reference
|
|
538
|
+
// a different pod's absolute directory.
|
|
539
|
+
const existenceChecks = await Promise.all(
|
|
540
|
+
batch.map(async ({ hash }) => ({
|
|
541
|
+
hash,
|
|
542
|
+
canonicalPath: join(absoluteCacheDir, `http-${hash}.mjs`),
|
|
543
|
+
exists: await exists(join(absoluteCacheDir, `http-${hash}.mjs`)),
|
|
544
|
+
})),
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
const missing = existenceChecks.filter((b) => !b.exists);
|
|
548
|
+
if (missing.length === 0) continue;
|
|
549
|
+
|
|
550
|
+
logger.info("[HTTP-CACHE] Fetching missing bundles from distributed cache", {
|
|
551
|
+
missing: missing.length,
|
|
552
|
+
total: batch.length,
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
const distributed = await getDistributedCache();
|
|
556
|
+
if (!distributed) {
|
|
557
|
+
logger.error("[HTTP-CACHE] No distributed cache available for bundle recovery");
|
|
558
|
+
for (const m of missing) failed.add(m.hash);
|
|
559
|
+
continue;
|
|
518
560
|
}
|
|
519
|
-
} catch (error) {
|
|
520
|
-
logger.error("[HTTP-CACHE] Batch fetch from distributed cache failed", { error });
|
|
521
|
-
return missing.map((m) => m.hash);
|
|
522
|
-
}
|
|
523
561
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
missing.map(async ({ path, hash }) => {
|
|
528
|
-
const code = codes.get(`code:${hash}`);
|
|
529
|
-
if (!code) {
|
|
530
|
-
logger.warn("[HTTP-CACHE] Bundle not found in distributed cache", { hash });
|
|
531
|
-
failed.push(hash);
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
562
|
+
// Batch fetch from distributed cache
|
|
563
|
+
const codeKeys = missing.map((m) => `code:${m.hash}`);
|
|
564
|
+
let codes: Map<string, string | null>;
|
|
534
565
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
await
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
566
|
+
try {
|
|
567
|
+
if (distributed.getBatch) {
|
|
568
|
+
codes = await distributed.getBatch(codeKeys);
|
|
569
|
+
} else {
|
|
570
|
+
const results = await Promise.all(
|
|
571
|
+
codeKeys.map(async (key) => [key, await distributed.get(key)] as const),
|
|
572
|
+
);
|
|
573
|
+
codes = new Map(results);
|
|
543
574
|
}
|
|
544
|
-
})
|
|
545
|
-
|
|
575
|
+
} catch (error) {
|
|
576
|
+
logger.error("[HTTP-CACHE] Batch fetch from distributed cache failed", { error });
|
|
577
|
+
for (const m of missing) failed.add(m.hash);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
546
580
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
581
|
+
// Write fetched bundles to disk using canonical paths and scan for transitive deps
|
|
582
|
+
await Promise.all(
|
|
583
|
+
missing.map(async ({ hash, canonicalPath }) => {
|
|
584
|
+
const code = codes.get(`code:${hash}`);
|
|
585
|
+
if (!code) {
|
|
586
|
+
// Try single-bundle recovery as last resort
|
|
587
|
+
const recovered = await recoverHttpBundleByHash(hash, absoluteCacheDir);
|
|
588
|
+
if (!recovered) {
|
|
589
|
+
failed.add(hash);
|
|
590
|
+
} else {
|
|
591
|
+
// Read the recovered bundle to scan for transitive deps
|
|
592
|
+
try {
|
|
593
|
+
const recoveredCode = await fs.readTextFile(canonicalPath);
|
|
594
|
+
for (const ref of extractBundleRefs(recoveredCode)) {
|
|
595
|
+
if (!seen.has(ref.hash)) pending.push(ref);
|
|
596
|
+
}
|
|
597
|
+
} catch { /* ignore read errors for dep scanning */ }
|
|
598
|
+
}
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
try {
|
|
603
|
+
await fs.mkdir(absoluteCacheDir, { recursive: true });
|
|
604
|
+
await fs.writeTextFile(canonicalPath, code);
|
|
605
|
+
logger.debug("[HTTP-CACHE] Wrote bundle to disk", { hash, path: canonicalPath });
|
|
606
|
+
|
|
607
|
+
// Scan recovered code for transitive HTTP bundle dependencies.
|
|
608
|
+
// HTTP bundles import other bundles (e.g., esm.sh packages depending
|
|
609
|
+
// on other packages). Without this, Pod B recovers only the top-level
|
|
610
|
+
// bundle and gets "Module not found" for transitive deps at import time.
|
|
611
|
+
for (const ref of extractBundleRefs(code)) {
|
|
612
|
+
if (!seen.has(ref.hash)) pending.push(ref);
|
|
613
|
+
}
|
|
614
|
+
} catch (error) {
|
|
615
|
+
logger.error("[HTTP-CACHE] Failed to write bundle to disk", { hash, error });
|
|
616
|
+
failed.add(hash);
|
|
617
|
+
}
|
|
618
|
+
}),
|
|
619
|
+
);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (failed.size > 0) {
|
|
623
|
+
logger.warn("[HTTP-CACHE] Some bundles could not be recovered", {
|
|
624
|
+
failed: Array.from(failed),
|
|
625
|
+
});
|
|
551
626
|
}
|
|
552
627
|
|
|
553
|
-
return failed;
|
|
628
|
+
return Array.from(failed);
|
|
554
629
|
}
|