veryfront 0.1.87 → 0.1.89

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 (46) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/cache/distributed-cache-init.d.ts +9 -0
  3. package/esm/src/cache/distributed-cache-init.d.ts.map +1 -1
  4. package/esm/src/cache/distributed-cache-init.js +70 -52
  5. package/esm/src/cache/module-cache.d.ts +6 -2
  6. package/esm/src/cache/module-cache.d.ts.map +1 -1
  7. package/esm/src/cache/module-cache.js +73 -56
  8. package/esm/src/rendering/orchestrator/module-loader/index.js +2 -2
  9. package/esm/src/transforms/esm/http-cache-wrapper.d.ts +3 -0
  10. package/esm/src/transforms/esm/http-cache-wrapper.d.ts.map +1 -1
  11. package/esm/src/transforms/esm/http-cache-wrapper.js +21 -7
  12. package/esm/src/transforms/mdx/esm-module-loader/cache/index.d.ts.map +1 -1
  13. package/esm/src/transforms/mdx/esm-module-loader/cache/index.js +6 -7
  14. package/esm/src/transforms/mdx/esm-module-loader/cache-format.d.ts +10 -0
  15. package/esm/src/transforms/mdx/esm-module-loader/cache-format.d.ts.map +1 -0
  16. package/esm/src/transforms/mdx/esm-module-loader/cache-format.js +61 -0
  17. package/esm/src/transforms/mdx/esm-module-loader/import-transformer.d.ts.map +1 -1
  18. package/esm/src/transforms/mdx/esm-module-loader/import-transformer.js +2 -3
  19. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/cache-keys.js +3 -3
  20. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/framework-validator.d.ts.map +1 -1
  21. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/framework-validator.js +2 -1
  22. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/module-cache.d.ts.map +1 -1
  23. package/esm/src/transforms/mdx/esm-module-loader/module-fetcher/module-cache.js +5 -5
  24. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts +2 -2
  25. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.d.ts.map +1 -1
  26. package/esm/src/transforms/pipeline/stages/ssr-vf-modules/transform.js +4 -4
  27. package/esm/src/utils/cache-namespace.d.ts +11 -0
  28. package/esm/src/utils/cache-namespace.d.ts.map +1 -0
  29. package/esm/src/utils/cache-namespace.js +24 -0
  30. package/esm/src/utils/version.d.ts +1 -1
  31. package/esm/src/utils/version.js +1 -1
  32. package/package.json +1 -1
  33. package/src/deno.js +1 -1
  34. package/src/src/cache/distributed-cache-init.ts +95 -59
  35. package/src/src/cache/module-cache.ts +95 -57
  36. package/src/src/rendering/orchestrator/module-loader/index.ts +2 -2
  37. package/src/src/transforms/esm/http-cache-wrapper.ts +28 -7
  38. package/src/src/transforms/mdx/esm-module-loader/cache/index.ts +6 -8
  39. package/src/src/transforms/mdx/esm-module-loader/cache-format.ts +121 -0
  40. package/src/src/transforms/mdx/esm-module-loader/import-transformer.ts +2 -3
  41. package/src/src/transforms/mdx/esm-module-loader/module-fetcher/cache-keys.ts +3 -3
  42. package/src/src/transforms/mdx/esm-module-loader/module-fetcher/framework-validator.ts +2 -1
  43. package/src/src/transforms/mdx/esm-module-loader/module-fetcher/module-cache.ts +5 -5
  44. package/src/src/transforms/pipeline/stages/ssr-vf-modules/transform.ts +4 -4
  45. package/src/src/utils/cache-namespace.ts +42 -0
  46. package/src/src/utils/version.ts +1 -1
@@ -16,6 +16,7 @@ import { rendererLogger } from "../../utils/index.js";
16
16
  import { VERSION } from "../../utils/version.js";
17
17
  import { getCacheBaseDir } from "../../utils/cache-dir.js";
18
18
  import { CacheBackends, createDistributedCacheAccessor } from "../../cache/backend.js";
19
+ import type { CacheBackend } from "../../cache/types.js";
19
20
  import { HTTP_MODULE_DISTRIBUTED_TTL_SEC } from "../../utils/constants/cache.js";
20
21
  import type {
21
22
  BundleHash,
@@ -44,6 +45,26 @@ const getDistributedCache = createDistributedCacheAccessor(
44
45
  "HTTP-CACHE-WRAPPER",
45
46
  );
46
47
 
48
+ let testDistributedCacheAccessor: (() => Promise<CacheBackend | null>) | null = null;
49
+
50
+ function resolveDistributedCache(): Promise<CacheBackend | null> {
51
+ return testDistributedCacheAccessor ? testDistributedCacheAccessor() : getDistributedCache();
52
+ }
53
+
54
+ export function __setDistributedCacheAccessorForTests(
55
+ accessor: (() => Promise<CacheBackend | null>) | null,
56
+ ): void {
57
+ testDistributedCacheAccessor = accessor;
58
+ }
59
+
60
+ export async function initializeHttpModuleDistributedCache(): Promise<boolean> {
61
+ const distributed = await resolveDistributedCache();
62
+ if (!distributed) return false;
63
+
64
+ logger.info("Initialized distributed cache backend", { backend: distributed.type });
65
+ return true;
66
+ }
67
+
47
68
  /**
48
69
  * Generate versioned cache key for HTTP bundles.
49
70
  * Format: {VERSION}:{prefix}:{hash}
@@ -158,7 +179,7 @@ class HttpBundleCache {
158
179
  * @returns Result containing local code or failure reason
159
180
  */
160
181
  async getCodeByHash(hash: BundleHash | string): Promise<GetCodeResult> {
161
- const distributed = await getDistributedCache();
182
+ const distributed = await resolveDistributedCache();
162
183
  if (!distributed) {
163
184
  return { code: null, wasGzipped: false, failReason: "not_found" };
164
185
  }
@@ -214,7 +235,7 @@ class HttpBundleCache {
214
235
  * @returns Result containing local code or failure reason
215
236
  */
216
237
  async getCodeByUrl(hash: BundleHash | string): Promise<GetCodeResult> {
217
- const distributed = await getDistributedCache();
238
+ const distributed = await resolveDistributedCache();
218
239
  if (!distributed) {
219
240
  return { code: null, wasGzipped: false, failReason: "not_found" };
220
241
  }
@@ -265,7 +286,7 @@ class HttpBundleCache {
265
286
  url: NormalizedUrl | string,
266
287
  ttl: number = HTTP_MODULE_DISTRIBUTED_TTL_SEC,
267
288
  ): Promise<void> {
268
- const distributed = await getDistributedCache();
289
+ const distributed = await resolveDistributedCache();
269
290
  if (!distributed) return;
270
291
 
271
292
  const hashStr = typeof hash === "string" ? hash : (hash as unknown as string);
@@ -305,7 +326,7 @@ class HttpBundleCache {
305
326
  async getBatchCodes(
306
327
  hashes: Array<BundleHash | string>,
307
328
  ): Promise<Map<string, LocalModuleCode>> {
308
- const distributed = await getDistributedCache();
329
+ const distributed = await resolveDistributedCache();
309
330
  if (!distributed) return new Map();
310
331
 
311
332
  const results = new Map<string, LocalModuleCode>();
@@ -357,7 +378,7 @@ class HttpBundleCache {
357
378
  * @returns Original URL or null
358
379
  */
359
380
  async getOriginalUrl(hash: BundleHash | string): Promise<string | null> {
360
- const distributed = await getDistributedCache();
381
+ const distributed = await resolveDistributedCache();
361
382
  if (!distributed) return null;
362
383
 
363
384
  const hashStr = typeof hash === "string" ? hash : (hash as unknown as string);
@@ -378,7 +399,7 @@ class HttpBundleCache {
378
399
  * @returns true if deletion was attempted, false if cache unavailable
379
400
  */
380
401
  async deleteCode(hash: BundleHash | string): Promise<boolean> {
381
- const distributed = await getDistributedCache();
402
+ const distributed = await resolveDistributedCache();
382
403
  if (!distributed) return false;
383
404
 
384
405
  const hashStr = typeof hash === "string" ? hash : (hash as unknown as string);
@@ -403,7 +424,7 @@ class HttpBundleCache {
403
424
  * Check if distributed cache is available.
404
425
  */
405
426
  async isAvailable(): Promise<boolean> {
406
- const distributed = await getDistributedCache();
427
+ const distributed = await resolveDistributedCache();
407
428
  return distributed !== null;
408
429
  }
409
430
  }
@@ -18,9 +18,9 @@ import {
18
18
  type FileSystem,
19
19
  isNotFoundError,
20
20
  } from "../../../../platform/compat/fs.js";
21
- import { VERSION } from "../../../../utils/version.js";
22
21
  import { LOG_PREFIX_MDX_LOADER } from "../constants.js";
23
22
  import { LRUCache } from "../../../../utils/lru-wrapper.js";
23
+ import { buildMdxEsmPathCacheKey, MDX_ESM_ALL_FILE_URL_PATTERN_SOURCE } from "../cache-format.js";
24
24
 
25
25
  export type CacheLookupResult =
26
26
  | { status: "hit"; path: string }
@@ -33,8 +33,6 @@ export const verifiedModuleDeps = new LRUCache<string, true>({
33
33
  maxEntries: MAX_VERIFIED_MODULE_DEPS,
34
34
  });
35
35
 
36
- const FILE_PATH_PATTERN = /file:\/\/([^"'\s]+)/gi;
37
-
38
36
  /**
39
37
  * Check if cached code has file:// paths from a different environment.
40
38
  * Checks both HTTP bundle paths and MDX ESM cache paths.
@@ -43,7 +41,7 @@ function hasIncompatibleCachePaths(code: string): boolean {
43
41
  const localCacheBaseDir = getCacheBaseDir();
44
42
  const localHttpCacheDir = getHttpBundleCacheDir();
45
43
  const localMdxCacheDir = getMdxEsmCacheDir();
46
- const pattern = new RegExp(FILE_PATH_PATTERN.source, "gi");
44
+ const pattern = new RegExp(MDX_ESM_ALL_FILE_URL_PATTERN_SOURCE, "gi");
47
45
 
48
46
  let match: RegExpExecArray | null;
49
47
  while ((match = pattern.exec(code)) !== null) {
@@ -87,7 +85,7 @@ function hasIncompatibleCachePaths(code: string): boolean {
87
85
  */
88
86
  async function findMissingFileDependencies(code: string): Promise<string[]> {
89
87
  const localFs = getLocalFs();
90
- const pattern = new RegExp(FILE_PATH_PATTERN.source, "gi");
88
+ const pattern = new RegExp(MDX_ESM_ALL_FILE_URL_PATTERN_SOURCE, "gi");
91
89
  const missing: string[] = [];
92
90
  let match;
93
91
  while ((match = pattern.exec(code)) !== null) {
@@ -335,7 +333,7 @@ function toMdxEsmCacheKey(filePath: string, projectDir?: string): string {
335
333
  relativePath = relativePath.replace(/^\/+/, "");
336
334
  const jsPath = relativePath.replace(/\.(tsx?|jsx|mdx)$/, ".js");
337
335
 
338
- return `v${VERSION}:_vf_modules/${jsPath}`;
336
+ return buildMdxEsmPathCacheKey(`_vf_modules/${jsPath}`);
339
337
  }
340
338
 
341
339
  export async function lookupMdxEsmCache(
@@ -429,9 +427,9 @@ export async function lookupMdxEsmCache(
429
427
  }
430
428
 
431
429
  // Note: We intentionally skip contentHash validation for MDX-ESM cached files.
432
- // The MDX-ESM cache uses transformed-code hashes in filenames (vfmod-v{VERSION}-{hash}.mjs),
430
+ // The MDX-ESM cache uses transformed-code hashes in namespaced filenames,
433
431
  // while the SSR loader provides source-code hashes. These will never match.
434
- // The cache version in the key (v{VERSION}:) provides sufficient staleness protection,
432
+ // The cache namespace in the key provides sufficient staleness protection,
435
433
  // and the file's existence confirms it's a valid transform for this codebase.
436
434
  // This allows both loaders to share the same module instance, preventing
437
435
  // duplicate React contexts which break hooks like useContext.
@@ -0,0 +1,121 @@
1
+ import { hashCodeHex } from "../../../utils/hash-utils.js";
2
+ import { createCacheNamespace } from "../../../utils/cache-namespace.js";
3
+ import { UNRESOLVED_VF_MODULES_PATTERN } from "./constants.js";
4
+ import { hashString } from "./utils/hash.js";
5
+
6
+ const ALL_FILE_URL_PATTERN_SOURCE = /file:\/\/([^"'\s]+)/.source;
7
+ const MJS_FILE_URL_PATTERN_SOURCE = /file:\/\/([^"'\s]+\.mjs)/.source;
8
+ const CACHE_NAMESPACE_SENTINEL = "__vf_cache_namespace__";
9
+
10
+ function formatMdxEsmTransformCacheKey(
11
+ namespace: string,
12
+ projectId: string,
13
+ normalizedPath: string,
14
+ contentHash: string,
15
+ ): string {
16
+ return `${namespace}:${projectId}:${normalizedPath}:${contentHash}:ssr`;
17
+ }
18
+
19
+ function formatMdxEsmPathCacheKey(namespace: string, normalizedPath: string): string {
20
+ return `${namespace}:${normalizedPath}`;
21
+ }
22
+
23
+ function formatMdxEsmModuleFileName(namespace: string, contentHash: string): string {
24
+ return `vfmod-${namespace}-${contentHash}.mjs`;
25
+ }
26
+
27
+ function formatMdxJsxCacheFileName(namespace: string, filePath: string): string {
28
+ return `jsx-${namespace}-${hashString(filePath)}.mjs`;
29
+ }
30
+
31
+ function formatFrameworkVfModuleCacheFileName(
32
+ namespace: string,
33
+ pathHash: string,
34
+ envKey: string,
35
+ contentHash: string,
36
+ ): string {
37
+ return `vfmod-${namespace}-${pathHash}-${envKey}-${contentHash}.mjs`;
38
+ }
39
+
40
+ function buildMdxEsmCacheSchemaSample() {
41
+ return {
42
+ transformKey: formatMdxEsmTransformCacheKey(
43
+ CACHE_NAMESPACE_SENTINEL,
44
+ "__vf_project__",
45
+ "_vf_modules/pages/index.js",
46
+ "deadbeef",
47
+ ),
48
+ pathKey: formatMdxEsmPathCacheKey(CACHE_NAMESPACE_SENTINEL, "_vf_modules/pages/index.js"),
49
+ moduleFile: formatMdxEsmModuleFileName(CACHE_NAMESPACE_SENTINEL, "deadbeef"),
50
+ jsxFile: formatMdxJsxCacheFileName(CACHE_NAMESPACE_SENTINEL, "/tmp/project/Button.tsx"),
51
+ unresolvedVfModulesPattern: UNRESOLVED_VF_MODULES_PATTERN.source,
52
+ allFileUrlPattern: ALL_FILE_URL_PATTERN_SOURCE,
53
+ mjsFileUrlPattern: MJS_FILE_URL_PATTERN_SOURCE,
54
+ sourceHashing: [
55
+ hashString("_vf_modules/pages/index.jsexport default 1;"),
56
+ hashString("/tmp/project/Button.tsx"),
57
+ ],
58
+ };
59
+ }
60
+
61
+ function buildFrameworkVfModuleCacheSchemaSample() {
62
+ return {
63
+ moduleFile: formatFrameworkVfModuleCacheFileName(
64
+ CACHE_NAMESPACE_SENTINEL,
65
+ hashCodeHex("_vf_modules/_veryfront/react/components/Head.js"),
66
+ hashCodeHex("/app/.cache/veryfront-mdx-esm").slice(0, 8),
67
+ hashCodeHex("export default function Head() {}"),
68
+ ),
69
+ };
70
+ }
71
+
72
+ export const MDX_ESM_ALL_FILE_URL_PATTERN_SOURCE = ALL_FILE_URL_PATTERN_SOURCE;
73
+ export const MDX_ESM_MJS_FILE_URL_PATTERN_SOURCE = MJS_FILE_URL_PATTERN_SOURCE;
74
+
75
+ export const MDX_ESM_CACHE_NAMESPACE = createCacheNamespace(
76
+ "mdx-esm",
77
+ buildMdxEsmCacheSchemaSample(),
78
+ );
79
+
80
+ export const FRAMEWORK_VF_MODULE_CACHE_NAMESPACE = createCacheNamespace(
81
+ "vf-framework",
82
+ buildFrameworkVfModuleCacheSchemaSample(),
83
+ );
84
+
85
+ export function buildMdxEsmTransformCacheKey(
86
+ projectId: string,
87
+ normalizedPath: string,
88
+ contentHash: string,
89
+ ): string {
90
+ return formatMdxEsmTransformCacheKey(
91
+ MDX_ESM_CACHE_NAMESPACE,
92
+ projectId,
93
+ normalizedPath,
94
+ contentHash,
95
+ );
96
+ }
97
+
98
+ export function buildMdxEsmPathCacheKey(normalizedPath: string): string {
99
+ return formatMdxEsmPathCacheKey(MDX_ESM_CACHE_NAMESPACE, normalizedPath);
100
+ }
101
+
102
+ export function buildMdxEsmModuleFileName(contentHash: string): string {
103
+ return formatMdxEsmModuleFileName(MDX_ESM_CACHE_NAMESPACE, contentHash);
104
+ }
105
+
106
+ export function buildMdxJsxCacheFileName(filePath: string): string {
107
+ return formatMdxJsxCacheFileName(MDX_ESM_CACHE_NAMESPACE, filePath);
108
+ }
109
+
110
+ export function buildFrameworkVfModuleCacheFileName(
111
+ pathHash: string,
112
+ envKey: string,
113
+ contentHash: string,
114
+ ): string {
115
+ return formatFrameworkVfModuleCacheFileName(
116
+ FRAMEWORK_VF_MODULE_CACHE_NAMESPACE,
117
+ pathHash,
118
+ envKey,
119
+ contentHash,
120
+ );
121
+ }
@@ -11,7 +11,6 @@ import { join } from "../../../platform/compat/path/index.js";
11
11
  import { rendererLogger as logger } from "../../../utils/index.js";
12
12
  import { transformImportsWithMap } from "../../../modules/import-map/index.js";
13
13
  import type { ImportMapConfig } from "../../../modules/import-map/index.js";
14
- import { VERSION } from "../../../utils/version.js";
15
14
  import { replaceSpecifiers } from "../../esm/lexer.js";
16
15
  import { getLocalReactPaths, isReactSpecifier } from "../../../platform/compat/react-paths.js";
17
16
  import {
@@ -23,7 +22,7 @@ import {
23
22
  REACT_IMPORT_PATTERN,
24
23
  } from "./constants.js";
25
24
  import { getLocalFs } from "./cache/index.js";
26
- import { hashString } from "./utils/hash.js";
25
+ import { buildMdxJsxCacheFileName } from "./cache-format.js";
27
26
  import { rewriteDntImports } from "./module-fetcher/index.js";
28
27
  import { ensureCachedJsxModulePatched } from "./jsx-cache.js";
29
28
  import type { ESMLoaderContext } from "./types.js";
@@ -128,7 +127,7 @@ export async function transformJsxImports(
128
127
  const transformResults = await Promise.all(
129
128
  importsToProcess.map(async ({ fullMatch, importClause, filePath, ext }) => {
130
129
  try {
131
- const transformedFileName = `jsx-v${VERSION}-${hashString(filePath)}.mjs`;
130
+ const transformedFileName = buildMdxJsxCacheFileName(filePath);
132
131
  const transformedPath = join(esmCacheDir, transformedFileName);
133
132
 
134
133
  try {
@@ -4,7 +4,7 @@
4
4
  * @module transforms/mdx/esm-module-loader/module-fetcher/cache-keys
5
5
  */
6
6
 
7
- import { VERSION } from "../../../../utils/version.js";
7
+ import { buildMdxEsmPathCacheKey, buildMdxEsmTransformCacheKey } from "../cache-format.js";
8
8
 
9
9
  /**
10
10
  * Build cache key for transformed module.
@@ -18,9 +18,9 @@ export function getTransformCacheKey(
18
18
  normalizedPath: string,
19
19
  contentHash: string,
20
20
  ): string {
21
- return `v${VERSION}:${projectId}:${normalizedPath}:${contentHash}:ssr`;
21
+ return buildMdxEsmTransformCacheKey(projectId, normalizedPath, contentHash);
22
22
  }
23
23
 
24
24
  export function getVersionedPathCacheKey(normalizedPath: string): string {
25
- return `v${VERSION}:${normalizedPath}`;
25
+ return buildMdxEsmPathCacheKey(normalizedPath);
26
26
  }
@@ -13,6 +13,7 @@ import { FRAMEWORK_ROOT, LOG_PREFIX_MDX_LOADER } from "../constants.js";
13
13
  import { getLocalFs } from "../cache/index.js";
14
14
  import { extractHttpBundlePaths } from "../../../../modules/react-loader/ssr-module-loader/http-bundle-helpers.js";
15
15
  import { ensureHttpBundlesExist } from "../../../esm/http-cache.js";
16
+ import { MDX_ESM_MJS_FILE_URL_PATTERN_SOURCE } from "../cache-format.js";
16
17
 
17
18
  /**
18
19
  * Check if cached code has file:// paths that are incompatible with this environment.
@@ -110,7 +111,7 @@ export async function findMissingFileDependenciesInCode(
110
111
  log: Logger,
111
112
  ): Promise<string[]> {
112
113
  const localFs = getLocalFs();
113
- const pattern = /file:\/\/([^"'\s]+\.mjs)/gi;
114
+ const pattern = new RegExp(MDX_ESM_MJS_FILE_URL_PATTERN_SOURCE, "gi");
114
115
  const missing: string[] = [];
115
116
  const checked = new Set<string>();
116
117
 
@@ -10,11 +10,10 @@
10
10
  import { join } from "../../../../platform/compat/path/index.js";
11
11
  import * as posix from "../../../../../deps/jsr.io/@std/path/1.1.4/posix/mod.js";
12
12
  import type { Logger } from "../../../../utils/logger/logger.js";
13
- import { VERSION } from "../../../../utils/version.js";
14
13
  import { LOG_PREFIX_MDX_LOADER } from "../constants.js";
15
14
  import { getLocalFs, saveModulePathCache } from "../cache/index.js";
16
15
  import { hashString } from "../utils/hash.js";
17
- import { getVersionedPathCacheKey } from "./cache-keys.js";
16
+ import { buildMdxEsmModuleFileName, buildMdxEsmPathCacheKey } from "../cache-format.js";
18
17
  import { hasUnresolvedImports } from "./nested-imports.js";
19
18
  import { recordModuleToSession } from "./render-sessions.js";
20
19
 
@@ -60,13 +59,14 @@ export async function cacheModule(
60
59
  }
61
60
 
62
61
  const contentHash = hashString(normalizedPath + moduleCode);
63
- const cachePath = join(esmCacheDir, `vfmod-v${VERSION}-${contentHash}.mjs`);
62
+ const cachePath = join(esmCacheDir, buildMdxEsmModuleFileName(contentHash));
63
+ const pathCacheKey = buildMdxEsmPathCacheKey(normalizedPath);
64
64
 
65
65
  const localFs = getLocalFs();
66
66
  try {
67
67
  const stat = await localFs.stat(cachePath);
68
68
  if (stat?.isFile) {
69
- pathCache.set(getVersionedPathCacheKey(normalizedPath), cachePath);
69
+ pathCache.set(pathCacheKey, cachePath);
70
70
  log.debug(`${LOG_PREFIX_MDX_LOADER} Content cache hit: ${normalizedPath}`);
71
71
  recordModuleToSession(normalizedPath);
72
72
  return cachePath;
@@ -77,7 +77,7 @@ export async function cacheModule(
77
77
 
78
78
  await localFs.mkdir(esmCacheDir, { recursive: true });
79
79
  await localFs.writeTextFile(cachePath, moduleCode);
80
- pathCache.set(getVersionedPathCacheKey(normalizedPath), cachePath);
80
+ pathCache.set(pathCacheKey, cachePath);
81
81
  await saveModulePathCache(esmCacheDir);
82
82
  log.debug(`${LOG_PREFIX_MDX_LOADER} Cached vf_module: ${normalizedPath} -> ${cachePath}`);
83
83
 
@@ -15,7 +15,6 @@ import { hashCodeHex } from "../../../../utils/hash-utils.js";
15
15
  import { getHttpBundleCacheDir, getMdxEsmCacheDir } from "../../../../utils/cache-dir.js";
16
16
  import { cacheHttpImportsToLocal } from "../../../esm/http-cache.js";
17
17
  import { loadImportMap } from "../../../../modules/import-map/index.js";
18
- import { VERSION } from "../../../../utils/version.js";
19
18
  import { buildReactUrl, getReactImportMap } from "../../../import-rewriter/url-builder.js";
20
19
  import { findRelativeImports } from "./import-finder.js";
21
20
  import { resolveRelativeFrameworkImport, resolveVeryfrontSourcePath } from "./path-resolver.js";
@@ -30,6 +29,7 @@ import {
30
29
  transformingFiles,
31
30
  veryfrontTransformCache,
32
31
  } from "./constants.js";
32
+ import { buildFrameworkVfModuleCacheFileName } from "../../../mdx/esm-module-loader/cache-format.js";
33
33
 
34
34
  const DENO_CONFIG_STUB_CODE = `export default ${JSON.stringify(denoConfig)};`;
35
35
 
@@ -46,10 +46,10 @@ export function isCyclePlaceholder(code: string): boolean {
46
46
  /**
47
47
  * Cache transformed framework code and return the file:// path.
48
48
  *
49
- * Cache key format: vfmod-{VERSION}-{pathHash}-{envKey}-{contentHash}.mjs
49
+ * Cache key format: vfmod-{namespace}-{pathHash}-{envKey}-{contentHash}.mjs
50
50
  *
51
51
  * Cache invalidation is handled by:
52
- * - VERSION prefix: Auto-invalidates on framework releases
52
+ * - namespace prefix: Auto-rolls when the framework vfmod cache shape changes
53
53
  * - envKey (FRAMEWORK_ROOT hash): Prevents cross-environment contamination
54
54
  * (compiled binary vs source have different FRAMEWORK_ROOT values)
55
55
  * - contentHash: Content-based invalidation
@@ -66,7 +66,7 @@ export async function cacheTransformedCode(
66
66
  const envKey = hashCodeHex(FRAMEWORK_ROOT).slice(0, 8);
67
67
  const contentHash = hashCodeHex(transformed);
68
68
  const pathHash = hashCodeHex(vfModulePath);
69
- const fileName = `vfmod-${VERSION}-${pathHash}-${envKey}-${contentHash}.mjs`;
69
+ const fileName = buildFrameworkVfModuleCacheFileName(pathHash, envKey, contentHash);
70
70
  const frameworkCacheDir = join(cacheDir, "framework");
71
71
  const cachePath = join(frameworkCacheDir, fileName);
72
72
 
@@ -0,0 +1,42 @@
1
+ import { fnv1aHash } from "./hash-utils.js";
2
+
3
+ type CacheNamespaceValue =
4
+ | string
5
+ | number
6
+ | boolean
7
+ | null
8
+ | readonly CacheNamespaceValue[]
9
+ | { readonly [key: string]: CacheNamespaceValue };
10
+
11
+ function serializeCacheNamespaceValue(value: CacheNamespaceValue): string {
12
+ if (value === null) return "null";
13
+
14
+ if (Array.isArray(value)) {
15
+ return `[${value.map((entry) => serializeCacheNamespaceValue(entry)).join(",")}]`;
16
+ }
17
+
18
+ if (typeof value === "object") {
19
+ const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right));
20
+ return `{${
21
+ entries
22
+ .map(([key, entry]) => `${JSON.stringify(key)}:${serializeCacheNamespaceValue(entry)}`)
23
+ .join(",")
24
+ }}`;
25
+ }
26
+
27
+ return JSON.stringify(value);
28
+ }
29
+
30
+ /**
31
+ * Build a deterministic cache namespace from a declarative schema description.
32
+ * Keep the schema close to the cache builders so format changes roll the
33
+ * namespace automatically instead of relying on a manually bumped constant.
34
+ */
35
+ export function createCacheNamespace(
36
+ scope: string,
37
+ schema: CacheNamespaceValue,
38
+ length = 10,
39
+ ): string {
40
+ const serialized = serializeCacheNamespaceValue(schema);
41
+ return `${scope}-${fnv1aHash(serialized).slice(0, length)}`;
42
+ }
@@ -1,6 +1,6 @@
1
1
  // Keep in sync with deno.json version.
2
2
  // scripts/release.ts updates this constant during releases.
3
- export const VERSION = "0.1.87";
3
+ export const VERSION = "0.1.89";
4
4
 
5
5
  export const SERVER_START_TIME: number = Date.now();
6
6