vinext 0.2.1 → 1.0.0-beta.0

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 (79) hide show
  1. package/README.md +50 -29
  2. package/dist/build/client-build-config.d.ts +13 -92
  3. package/dist/build/client-build-config.js +17 -95
  4. package/dist/build/prerender-paths.d.ts +23 -0
  5. package/dist/build/prerender-paths.js +296 -0
  6. package/dist/build/prerender.d.ts +12 -1
  7. package/dist/build/prerender.js +1 -1
  8. package/dist/cache/cache-adapters-virtual.d.ts +7 -1
  9. package/dist/cache/cache-adapters-virtual.js +26 -2
  10. package/dist/check.js +5 -0
  11. package/dist/cli.js +33 -53
  12. package/dist/config/next-config.js +1 -2
  13. package/dist/config/prerender.d.ts +11 -1
  14. package/dist/config/prerender.js +19 -1
  15. package/dist/config/tsconfig-paths.js +5 -1
  16. package/dist/image/image-adapters-virtual.js +1 -0
  17. package/dist/index.d.ts +1 -1
  18. package/dist/index.js +248 -146
  19. package/dist/init-cloudflare.js +12 -3
  20. package/dist/init-platform.d.ts +10 -1
  21. package/dist/init-platform.js +78 -12
  22. package/dist/init.d.ts +11 -3
  23. package/dist/init.js +129 -31
  24. package/dist/server/app-browser-entry.js +4 -6
  25. package/dist/server/app-browser-navigation-controller.js +6 -3
  26. package/dist/server/app-browser-state.js +1 -1
  27. package/dist/server/app-browser-visible-commit.js +1 -1
  28. package/dist/server/app-layout-param-observation.d.ts +1 -1
  29. package/dist/server/app-page-cache-render.d.ts +1 -1
  30. package/dist/server/app-page-dispatch.d.ts +1 -1
  31. package/dist/server/app-page-dispatch.js +3 -3
  32. package/dist/server/app-page-render.d.ts +1 -1
  33. package/dist/server/app-page-route-wiring.js +3 -3
  34. package/dist/server/app-route-handler-execution.d.ts +1 -1
  35. package/dist/server/app-route-handler-execution.js +1 -1
  36. package/dist/server/app-rsc-cache-busting.js +0 -1
  37. package/dist/server/app-rsc-handler.js +1 -1
  38. package/dist/server/app-rsc-render-mode.d.ts +2 -5
  39. package/dist/server/app-rsc-render-mode.js +2 -12
  40. package/dist/server/app-server-action-execution.js +4 -4
  41. package/dist/server/app-ssr-entry.js +2 -2
  42. package/dist/server/dev-module-runner.js +1 -1
  43. package/dist/server/dev-origin-check.d.ts +2 -2
  44. package/dist/server/dev-origin-check.js +2 -2
  45. package/dist/server/dev-server.js +4 -4
  46. package/dist/server/image-optimization.d.ts +12 -1
  47. package/dist/server/image-optimization.js +13 -1
  48. package/dist/server/isr-cache.d.ts +12 -4
  49. package/dist/server/isr-cache.js +2 -2
  50. package/dist/server/pages-page-data.js +1 -1
  51. package/dist/server/pages-page-response.js +2 -2
  52. package/dist/server/pages-request-pipeline.js +1 -1
  53. package/dist/server/prerender-manifest.d.ts +15 -1
  54. package/dist/server/prerender-manifest.js +29 -1
  55. package/dist/server/prod-server.d.ts +6 -5
  56. package/dist/server/prod-server.js +6 -7
  57. package/dist/server/static-file-cache.js +1 -1
  58. package/dist/shims/cache.d.ts +1 -1
  59. package/dist/shims/cdn-cache.d.ts +2 -7
  60. package/dist/shims/cdn-cache.js +2 -14
  61. package/dist/shims/form.d.ts +1 -1
  62. package/dist/shims/navigation.react-server.js +1 -1
  63. package/dist/shims/request-state-types.d.ts +2 -2
  64. package/dist/shims/unified-request-context.d.ts +1 -1
  65. package/dist/utils/project.d.ts +1 -1
  66. package/dist/utils/vite-version.d.ts +3 -13
  67. package/dist/utils/vite-version.js +38 -30
  68. package/package.json +31 -13
  69. package/dist/cloudflare/index.d.ts +0 -3
  70. package/dist/cloudflare/index.js +0 -3
  71. package/dist/packages/cloudflare/src/cache/cdn-adapter.runtime.js +0 -102
  72. package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +0 -126
  73. package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +0 -442
  74. package/dist/packages/cloudflare/src/deploy-config.js +0 -150
  75. package/dist/packages/cloudflare/src/deploy-help.js +0 -55
  76. package/dist/packages/cloudflare/src/deploy.js +0 -272
  77. package/dist/packages/cloudflare/src/tpr.d.ts +0 -45
  78. package/dist/packages/cloudflare/src/tpr.js +0 -582
  79. package/dist/packages/cloudflare/src/utils/cache-control-metadata.js +0 -20
@@ -1,6 +1,4 @@
1
- import { getRequestExecutionContext } from "./request-context.js";
2
1
  import { getDataCacheHandler } from "./cache-handler.js";
3
- import { CloudflareCdnCacheAdapter } from "../packages/cloudflare/src/cache/cdn-adapter.runtime.js";
4
2
  //#region src/shims/cdn-cache.ts
5
3
  /**
6
4
  * CDN cache adapter — owns the *page-level ISR serving strategy*.
@@ -78,22 +76,12 @@ function setCdnCacheAdapter(adapter) {
78
76
  _gCdn[_CDN_KEY] = adapter;
79
77
  }
80
78
  /**
81
- * Get the active CDN cache adapter. See the precedence note above:
82
- * explicit (or an already-resolved auto-detected edge adapter) → origin-managed
83
- * {@link DefaultCdnCacheAdapter}.
84
- *
85
- * Auto-detection of the Cloudflare Workers Cache (`ctx.cache`) only runs when
86
- * `VINEXT_CDN_CACHE_AUTO_DETECT === "1"`; otherwise the default adapter is used
87
- * unless an adapter was set explicitly.
79
+ * Get the active CDN cache adapter. An explicitly configured adapter wins;
80
+ * otherwise the origin-managed {@link DefaultCdnCacheAdapter} is used.
88
81
  */
89
82
  function getCdnCacheAdapter() {
90
83
  const active = _gCdn[_CDN_KEY];
91
84
  if (active) return active;
92
- if (process.env.VINEXT_CDN_CACHE_AUTO_DETECT === "1" && getRequestExecutionContext()?.cache) {
93
- const edge = new CloudflareCdnCacheAdapter();
94
- _gCdn[_CDN_KEY] = edge;
95
- return edge;
96
- }
97
85
  return _defaultAdapter ??= new DefaultCdnCacheAdapter();
98
86
  }
99
87
  //#endregion
@@ -16,6 +16,6 @@ declare const Form: import("react").ForwardRefExoticComponent<{
16
16
  * In pages dir, prefetch is not supported and the prop has no effect.
17
17
  */
18
18
  prefetch?: false | null;
19
- } & Omit<FormHTMLAttributes<HTMLFormElement>, "target" | "method" | "encType"> & import("react").RefAttributes<HTMLFormElement>>;
19
+ } & Omit<FormHTMLAttributes<HTMLFormElement>, "method" | "encType" | "target"> & import("react").RefAttributes<HTMLFormElement>>;
20
20
  //#endregion
21
21
  export { createFormSubmitDestinationUrl, Form as default, useActionState };
@@ -1,8 +1,8 @@
1
1
  import { GLOBAL_ACCESSORS_KEY, ServerInsertedHTMLContext, _registerStateAccessors, clearServerInsertedHTML, flushServerInsertedHTML, getLayoutSegmentContext, getNavigationContext, renderServerInsertedHTML, setNavigationContext } from "./navigation-context-state.js";
2
2
  import { BailoutToCSRError, DynamicServerError, HTTP_ERROR_FALLBACK_ERROR_CODE, RedirectType, forbidden, getAccessFallbackHTTPStatus, isBailoutToCSRError, isDynamicServerError, isHTTPAccessFallbackError, isNextRouterError, isRedirectError, notFound, permanentRedirect, redirect, unauthorized, unstable_rethrow } from "./navigation-errors.js";
3
3
  import "./navigation-server.js";
4
- import { throwClientHookError } from "./client-hook-error.js";
5
4
  import { ReadonlyURLSearchParams } from "./readonly-url-search-params.js";
5
+ import { throwClientHookError } from "./client-hook-error.js";
6
6
  //#region src/shims/navigation.react-server.ts
7
7
  function usePathname() {
8
8
  throwClientHookError("usePathname()");
@@ -1,7 +1,7 @@
1
- import { CacheLifeConfig, CacheState } from "./cache-request-state.js";
2
- import { ExecutionContextLike } from "./request-context.js";
3
1
  import { NavigationContext } from "./navigation-context-state.js";
4
2
  import { RootParamsState } from "./root-params.js";
3
+ import { CacheLifeConfig, CacheState } from "./cache-request-state.js";
4
+ import { ExecutionContextLike } from "./request-context.js";
5
5
  import { FetchCacheState } from "./fetch-cache.js";
6
6
  import { HeadersAccessPhase, HeadersContext, VinextHeadersShimState } from "./headers.js";
7
7
  import { RouterState, SSRContext } from "./router-state.js";
@@ -1,6 +1,6 @@
1
+ import { RootParamsState } from "./root-params.js";
1
2
  import { CacheState } from "./cache-request-state.js";
2
3
  import { ExecutionContextLike } from "./request-context.js";
3
- import { RootParamsState } from "./root-params.js";
4
4
  import { FetchCacheState } from "./fetch-cache.js";
5
5
  import { VinextHeadersShimState } from "./headers.js";
6
6
  import { RouterState } from "./router-state.js";
@@ -123,4 +123,4 @@ declare function findDir(root: string, ...candidates: string[]): string | null;
123
123
  */
124
124
  declare function hasAppDir(root: string): boolean;
125
125
  //#endregion
126
- export { ProjectInfo, detectPackageManager, detectPackageManagerName, detectProject, ensureESModule, ensureViteConfigCompatibility, findDir, findInNodeModules, findViteConfigPath, formatMissingCloudflarePluginError, getMissingDeps, hasAppDir, hasViteConfig, hasWranglerConfig, isPackageResolvable, renameCJSConfigs };
126
+ export { PackageManagerName, ProjectInfo, detectPackageManager, detectPackageManagerName, detectProject, ensureESModule, ensureViteConfigCompatibility, findDir, findInNodeModules, findViteConfigPath, formatMissingCloudflarePluginError, getMissingDeps, hasAppDir, hasViteConfig, hasWranglerConfig, isPackageResolvable, renameCJSConfigs };
@@ -1,23 +1,13 @@
1
1
  //#region src/utils/vite-version.d.ts
2
2
  declare function serializeViteDefine(value: unknown): string;
3
- declare function getDepOptimizeNodeEnvOptions(viteMajorVersion: number, nodeEnvDefine: string): {
3
+ declare function getDepOptimizeNodeEnvOptions(nodeEnvDefine: string): {
4
4
  rolldownOptions?: {
5
5
  transform: {
6
6
  define: Record<string, string>;
7
7
  };
8
8
  moduleTypes?: Record<string, "jsx">;
9
9
  };
10
- esbuildOptions?: {
11
- define: Record<string, string>;
12
- loader?: Record<string, "jsx">;
13
- };
14
10
  };
15
- /**
16
- * Detect Vite major version at runtime by resolving from cwd.
17
- * The plugin may be installed in a workspace root with Vite 7 but used
18
- * by a project that has Vite 8 — so we resolve from cwd, not from
19
- * the plugin's own location.
20
- */
21
- declare function getViteMajorVersion(): number;
11
+ declare function assertSupportedViteVersion(): number;
22
12
  //#endregion
23
- export { getDepOptimizeNodeEnvOptions, getViteMajorVersion, serializeViteDefine };
13
+ export { assertSupportedViteVersion, getDepOptimizeNodeEnvOptions, serializeViteDefine };
@@ -4,49 +4,57 @@ import path from "node:path";
4
4
  /**
5
5
  * Vite major-version detection.
6
6
  *
7
- * Several vinext behaviors depend on whether the host project is running Vite 8
8
- * (Rolldown-based, with native `resolve.tsconfigPaths`, `oxc` transforms, and
9
- * `rolldownOptions`) or Vite 7 (Rollup/esbuild). This helper centralizes the
10
- * detection so the Vite-major gate is computed the same way everywhere.
7
+ * vinext requires Vite 8 or newer so it can rely on Rolldown-based build
8
+ * options, native `resolve.tsconfigPaths`, and OXC transforms.
11
9
  */
12
10
  function serializeViteDefine(value) {
13
11
  if (typeof value === "string") return value;
14
12
  return JSON.stringify(value) ?? "undefined";
15
13
  }
16
- function getDepOptimizeNodeEnvOptions(viteMajorVersion, nodeEnvDefine) {
17
- const define = { "process.env.NODE_ENV": nodeEnvDefine };
18
- const jsxModuleTypes = {
19
- ".js": "jsx",
20
- ".mjs": "jsx"
21
- };
22
- return viteMajorVersion >= 8 ? { rolldownOptions: {
23
- transform: { define },
24
- moduleTypes: jsxModuleTypes
25
- } } : { esbuildOptions: {
26
- define,
27
- loader: jsxModuleTypes
14
+ function getDepOptimizeNodeEnvOptions(nodeEnvDefine) {
15
+ return { rolldownOptions: {
16
+ transform: { define: { "process.env.NODE_ENV": nodeEnvDefine } },
17
+ moduleTypes: {
18
+ ".js": "jsx",
19
+ ".mjs": "jsx"
20
+ }
28
21
  } };
29
22
  }
30
23
  /**
31
- * Detect Vite major version at runtime by resolving from cwd.
32
- * The plugin may be installed in a workspace root with Vite 7 but used
33
- * by a project that has Vite 8 — so we resolve from cwd, not from
34
- * the plugin's own location.
24
+ * Detect Vite major version at runtime. Prefer the project cwd, then fall back
25
+ * to vinext's own dependency graph for tests and linked source checkouts.
35
26
  */
36
27
  function getViteMajorVersion() {
37
28
  try {
38
- const vitePkg = createRequire(path.join(process.cwd(), "package.json"))("vite/package.json");
39
- const viteMajor = parseInt(vitePkg?.version, 10);
40
- if (vitePkg?.name === "vite" && Number.isFinite(viteMajor)) return viteMajor;
41
- const bundledViteMajor = parseInt(vitePkg?.bundledVersions?.vite, 10);
42
- if (Number.isFinite(bundledViteMajor)) return bundledViteMajor;
43
- console.warn(`[vinext] Could not determine Vite major version from ${vitePkg?.name ?? "vite/package.json"}; assuming Vite 7`);
44
- return 7;
29
+ return getViteMajorVersionFromRequire(createRequire(path.join(process.cwd(), "package.json")));
30
+ } catch (error) {
31
+ if (!isModuleNotFoundError(error)) {
32
+ const message = error instanceof Error ? error.message : String(error);
33
+ throw new Error(`[vinext] Vite 8 or newer is required, but ${message}`);
34
+ }
35
+ }
36
+ try {
37
+ return getViteMajorVersionFromRequire(createRequire(import.meta.url));
45
38
  } catch (error) {
46
39
  const message = error instanceof Error ? error.message : String(error);
47
- console.warn(`[vinext] Failed to resolve vite/package.json (${message}); assuming Vite 7`);
48
- return 7;
40
+ throw new Error(`[vinext] Vite 8 or newer is required, but vinext could not resolve vite/package.json (${message})`);
49
41
  }
50
42
  }
43
+ function getViteMajorVersionFromRequire(require) {
44
+ const vitePkg = require("vite/package.json");
45
+ const viteMajor = parseInt(vitePkg?.version, 10);
46
+ if (vitePkg?.name === "vite" && Number.isFinite(viteMajor)) return viteMajor;
47
+ const bundledViteMajor = parseInt(vitePkg?.bundledVersions?.vite, 10);
48
+ if (Number.isFinite(bundledViteMajor)) return bundledViteMajor;
49
+ throw new Error(`could not determine Vite version from ${vitePkg?.name ?? "vite/package.json"}`);
50
+ }
51
+ function isModuleNotFoundError(error) {
52
+ return !!error && typeof error === "object" && "code" in error && error.code === "MODULE_NOT_FOUND";
53
+ }
54
+ function assertSupportedViteVersion() {
55
+ const viteMajorVersion = getViteMajorVersion();
56
+ if (viteMajorVersion < 8) throw new Error(`[vinext] Vite 8 or newer is required. Detected Vite ${viteMajorVersion}.`);
57
+ return viteMajorVersion;
58
+ }
51
59
  //#endregion
52
- export { getDepOptimizeNodeEnvOptions, getViteMajorVersion, serializeViteDefine };
60
+ export { assertSupportedViteVersion, getDepOptimizeNodeEnvOptions, serializeViteDefine };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinext",
3
- "version": "0.2.1",
3
+ "version": "1.0.0-beta.0",
4
4
  "description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,10 +37,6 @@
37
37
  "types": "./dist/server/pages-i18n.d.ts",
38
38
  "import": "./dist/server/pages-i18n.js"
39
39
  },
40
- "./cloudflare": {
41
- "types": "./dist/cloudflare/index.d.ts",
42
- "import": "./dist/cloudflare/index.js"
43
- },
44
40
  "./server/image-optimization": {
45
41
  "types": "./dist/server/image-optimization.d.ts",
46
42
  "import": "./dist/server/image-optimization.js"
@@ -81,10 +77,34 @@
81
77
  "types": "./dist/server/app-page-cache.d.ts",
82
78
  "import": "./dist/server/app-page-cache.js"
83
79
  },
80
+ "./internal/server/prerender-manifest": {
81
+ "types": "./dist/server/prerender-manifest.d.ts",
82
+ "import": "./dist/server/prerender-manifest.js"
83
+ },
84
+ "./internal/server/pregenerated-concrete-paths": {
85
+ "types": "./dist/server/pregenerated-concrete-paths.d.ts",
86
+ "import": "./dist/server/pregenerated-concrete-paths.js"
87
+ },
84
88
  "./internal/build/run-prerender": {
85
89
  "types": "./dist/build/run-prerender.d.ts",
86
90
  "import": "./dist/build/run-prerender.js"
87
91
  },
92
+ "./internal/build/prerender-paths": {
93
+ "types": "./dist/build/prerender-paths.d.ts",
94
+ "import": "./dist/build/prerender-paths.js"
95
+ },
96
+ "./internal/cache-adapters": {
97
+ "types": "./dist/cache/cache-adapters-virtual.d.ts",
98
+ "import": "./dist/cache/cache-adapters-virtual.js"
99
+ },
100
+ "./internal/init": {
101
+ "types": "./dist/init.d.ts",
102
+ "import": "./dist/init.js"
103
+ },
104
+ "./internal/init-platform": {
105
+ "types": "./dist/init-platform.d.ts",
106
+ "import": "./dist/init-platform.js"
107
+ },
88
108
  "./internal/config/dotenv": {
89
109
  "types": "./dist/config/dotenv.d.ts",
90
110
  "import": "./dist/config/dotenv.js"
@@ -128,6 +148,10 @@
128
148
  "./internal/utils/project": {
129
149
  "types": "./dist/utils/project.d.ts",
130
150
  "import": "./dist/utils/project.js"
151
+ },
152
+ "./internal/utils/prerender-output-paths": {
153
+ "types": "./dist/utils/prerender-output-paths.d.ts",
154
+ "import": "./dist/utils/prerender-output-paths.js"
131
155
  }
132
156
  },
133
157
  "dependencies": {
@@ -148,9 +172,7 @@
148
172
  "am-i-vibing": "^0.5.0",
149
173
  "react-server-dom-webpack": "^19.2.7",
150
174
  "vite": "npm:@voidzero-dev/vite-plus-core@0.2.1",
151
- "vite-plus": "0.2.1",
152
- "vite-tsconfig-paths": "^6.1.1",
153
- "@vinext/cloudflare": "0.2.1"
175
+ "vite-plus": "0.2.1"
154
176
  },
155
177
  "peerDependencies": {
156
178
  "@mdx-js/rollup": "^3.0.0",
@@ -159,8 +181,7 @@
159
181
  "react": "^19.2.6",
160
182
  "react-dom": "^19.2.6",
161
183
  "react-server-dom-webpack": "^19.2.6",
162
- "vite": "^7.0.0 || ^8.0.0",
163
- "vite-tsconfig-paths": "^6.1.1"
184
+ "vite": "^8.0.0"
164
185
  },
165
186
  "peerDependenciesMeta": {
166
187
  "@mdx-js/rollup": {
@@ -171,9 +192,6 @@
171
192
  },
172
193
  "react-server-dom-webpack": {
173
194
  "optional": true
174
- },
175
- "vite-tsconfig-paths": {
176
- "optional": true
177
195
  }
178
196
  },
179
197
  "engines": {
@@ -1,3 +0,0 @@
1
- import { KVCacheHandler } from "../packages/cloudflare/src/cache/kv-data-adapter.runtime.js";
2
- import { TPROptions, TPRResult, runTPR } from "../packages/cloudflare/src/tpr.js";
3
- export { KVCacheHandler, type TPROptions, type TPRResult, runTPR };
@@ -1,3 +0,0 @@
1
- import { KVCacheHandler } from "../packages/cloudflare/src/cache/kv-data-adapter.runtime.js";
2
- import { runTPR } from "../packages/cloudflare/src/tpr.js";
3
- export { KVCacheHandler, runTPR };
@@ -1,102 +0,0 @@
1
- import { getRequestExecutionContext } from "../../../../shims/request-context.js";
2
- //#region ../cloudflare/src/cache/cdn-adapter.runtime.ts
3
- function getWorkersCache() {
4
- const cache = getRequestExecutionContext()?.cache;
5
- if (cache && typeof cache.purge === "function") return cache;
6
- return null;
7
- }
8
- /** Non-cacheable responses: nobody (edge or browser) stores them. */
9
- const NO_STORE = "no-store";
10
- /**
11
- * Browser-facing policy for cacheable responses. `public` allows shared caches
12
- * to participate, but `max-age=0, must-revalidate` forces every reuse to
13
- * revalidate (against the edge) rather than serving a stored copy — so the user
14
- * always sees edge-fresh content while still permitting conditional 304s.
15
- */
16
- const BROWSER_REVALIDATE = "public, max-age=0, must-revalidate";
17
- /**
18
- * A concrete stale window (1 year) substituted for a value-less
19
- * `stale-while-revalidate`. The framework emits a bare `stale-while-revalidate`
20
- * (no `=seconds`) to mean an unbounded stale window — a Vercel CDN extension.
21
- * Cloudflare follows RFC 5861, which requires `stale-while-revalidate=<seconds>`,
22
- * and treats the value-less form as a zero-width window (no stale serving →
23
- * hard expiry at `max-age`, so the next request is a MISS instead of UPDATING).
24
- * Stamping a large finite value preserves the "serve stale ~indefinitely while
25
- * revalidating" intent in a form the edge honors.
26
- */
27
- const UNBOUNDED_SWR_SECONDS = 31536e3;
28
- /**
29
- * Convert the framework's shared-cache policy into a CDN-scoped one:
30
- * `s-maxage=…` → `max-age=…` (the edge honors `max-age` inside
31
- * `CDN-Cache-Control`), give a value-less `stale-while-revalidate` an explicit
32
- * seconds value (Cloudflare ignores the bare directive), and ensure a leading
33
- * `public`.
34
- */
35
- function toEdgeCacheControl(cacheControl) {
36
- const withMaxAge = cacheControl.replace(/\bs-maxage=/g, "max-age=").replace(/\bstale-while-revalidate\b(?!=)/g, `stale-while-revalidate=${UNBOUNDED_SWR_SECONDS}`);
37
- return /\bpublic\b/.test(withMaxAge) ? withMaxAge : `public, ${withMaxAge}`;
38
- }
39
- /**
40
- * Cloudflare's `Cache-Tag` header budget is 16 KB total with each tag capped at
41
- * 1024 bytes. Keep a conservative ceiling so a page with a large tag set never
42
- * produces an oversized (silently-dropped) header.
43
- */
44
- const MAX_CACHE_TAG_BYTES = 8 * 1024;
45
- const MAX_SINGLE_TAG_BYTES = 1024;
46
- /**
47
- * Build a `Cache-Tag` header value from canonicalised tags. Tags containing a
48
- * comma (the header separator) or exceeding the per-tag size are skipped, and
49
- * the whole value is bounded to stay within Cloudflare's limit.
50
- */
51
- function formatCacheTag(tags) {
52
- const parts = [];
53
- let total = 0;
54
- for (const tag of tags) {
55
- if (!tag || tag.includes(",") || tag.length > MAX_SINGLE_TAG_BYTES) continue;
56
- const next = total + tag.length + (parts.length > 0 ? 1 : 0);
57
- if (next > MAX_CACHE_TAG_BYTES) break;
58
- parts.push(tag);
59
- total = next;
60
- }
61
- return parts.length > 0 ? parts.join(",") : null;
62
- }
63
- var CloudflareCdnCacheAdapter = class {
64
- ownsBackgroundRevalidation = false;
65
- /**
66
- * The origin keeps no page store — return null so the request renders fresh.
67
- * The edge serves cached HIT/STALE responses without reaching the origin.
68
- */
69
- async get() {
70
- return null;
71
- }
72
- /** No-op: the platform caches the response via its headers, not an origin store. */
73
- async set(_key, _data, _ctx) {}
74
- buildResponseHeaders(input) {
75
- if (!input.cacheControl) return { "Cache-Control": NO_STORE };
76
- if (/\b(?:no-store|no-cache|private)\b/.test(input.cacheControl)) return {
77
- "Cache-Control": input.cacheControl,
78
- "CDN-Cache-Control": null,
79
- "Cloudflare-CDN-Cache-Control": null,
80
- "Cache-Tag": null
81
- };
82
- const headers = {
83
- "Cache-Control": BROWSER_REVALIDATE,
84
- "CDN-Cache-Control": toEdgeCacheControl(input.cacheControl)
85
- };
86
- if (input.tags && input.tags.length > 0) {
87
- const cacheTag = formatCacheTag(input.tags);
88
- if (cacheTag) headers["Cache-Tag"] = cacheTag;
89
- }
90
- return headers;
91
- }
92
- /** Purge edge-cached responses by tag via the request context's `cache.purge`. */
93
- async revalidateTag(tags, _durations) {
94
- const cache = getWorkersCache();
95
- if (!cache) return;
96
- const tagList = (Array.isArray(tags) ? tags : [tags]).filter((t) => typeof t === "string" && t.length > 0);
97
- if (tagList.length === 0) return;
98
- await cache.purge({ tags: tagList });
99
- }
100
- };
101
- //#endregion
102
- export { CloudflareCdnCacheAdapter };
@@ -1,126 +0,0 @@
1
- import { CacheHandler, CacheHandlerValue, IncrementalCacheValue } from "../../../../shims/cache-handler.js";
2
- import { ExecutionContextLike } from "../../../../shims/request-context.js";
3
-
4
- //#region ../cloudflare/src/cache/kv-data-adapter.runtime.d.ts
5
- type KVNamespace = {
6
- get(key: string, options?: {
7
- type?: string;
8
- }): Promise<string | null>;
9
- get(key: string, options: {
10
- type: "arrayBuffer";
11
- }): Promise<ArrayBuffer | null>;
12
- put(key: string, value: string | ArrayBuffer | ReadableStream, options?: {
13
- expirationTtl?: number;
14
- metadata?: Record<string, unknown>;
15
- }): Promise<void>;
16
- delete(key: string): Promise<void>;
17
- list(options?: {
18
- prefix?: string;
19
- limit?: number;
20
- cursor?: string;
21
- }): Promise<{
22
- keys: Array<{
23
- name: string;
24
- metadata?: Record<string, unknown>;
25
- }>;
26
- list_complete: boolean;
27
- cursor?: string;
28
- }>;
29
- };
30
- /** Key prefix for cache entries. */
31
- /**
32
- * Cloudflare KV data cache handler.
33
- *
34
- * @deprecated Consumers should not instantiate or register this handler by
35
- * hand. Configure it declaratively via the `cache.data` option on the
36
- * `vinext()` plugin, using the {@link kvDataAdapter} builder:
37
- *
38
- * ```ts
39
- * import { vinext } from "vinext";
40
- * import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";
41
- *
42
- * export default defineConfig({
43
- * plugins: [vinext({ cache: { data: kvDataAdapter({ binding: "VINEXT_KV_CACHE" }) } })],
44
- * });
45
- * ```
46
- *
47
- * `kvDataAdapter()` is safe to call from `vite.config.ts` — it never touches
48
- * the Workers runtime — and the plugin defers instantiation of this handler to
49
- * the first request. This class stays exported for the runtime factory and for
50
- * backwards compatibility, but is not the recommended consumer API.
51
- */
52
- declare class KVCacheHandler implements CacheHandler {
53
- private kv;
54
- private prefix;
55
- private ctx;
56
- private ttlSeconds;
57
- /** Local in-memory cache for tag invalidation timestamps. Avoids redundant KV reads. */
58
- private _tagCache;
59
- /** TTL (ms) for local tag cache entries. After this, re-fetch from KV. */
60
- private _tagCacheTtl;
61
- constructor(kvNamespace: KVNamespace, options?: {
62
- appPrefix?: string;
63
- ctx?: ExecutionContextLike;
64
- ttlSeconds?: number; /** TTL in milliseconds for the local tag cache. Defaults to 5000ms. */
65
- tagCacheTtlMs?: number;
66
- });
67
- get(key: string, _ctx?: Record<string, unknown>): Promise<CacheHandlerValue | null>;
68
- /**
69
- * Check tag invalidation markers for stored tags or read-time soft tags.
70
- * Uses a local in-memory cache to avoid redundant KV reads for recently-seen tags.
71
- */
72
- private _hasRevalidatedTag;
73
- set(key: string, data: IncrementalCacheValue | null, ctx?: Record<string, unknown>): Promise<void>;
74
- revalidateTag(tags: string | string[], _durations?: {
75
- expire?: number;
76
- }): Promise<void>;
77
- /**
78
- * Invalidate all cache entries whose path tags fall under `pathPrefix`.
79
- *
80
- * Uses KV list metadata to discover tags without fetching entry values —
81
- * entries written by `set()` store their tags in KV metadata, so
82
- * `kv.list()` returns them inline with each key. This makes prefix
83
- * invalidation O(list_pages) instead of O(entries × get).
84
- *
85
- * Entries written before metadata was added (no metadata.tags) are
86
- * gracefully skipped — they'll be picked up on next `set()` which
87
- * writes metadata.
88
- *
89
- * When present, this method fully replaces the `revalidateTag` call
90
- * path in `revalidatePath()` — implementors own all path-based tag
91
- * handling.
92
- */
93
- revalidateByPathPrefix(pathPrefix: string): Promise<void>;
94
- /**
95
- * Clear the in-memory tag cache for this KVCacheHandler instance.
96
- *
97
- * Note: KVCacheHandler instances are typically reused across multiple
98
- * requests in a Cloudflare Worker. The `_tagCache` is intentionally
99
- * cross-request — it reduces redundant KV reads for recently-seen tags
100
- * across all requests hitting the same isolate, bounded by `tagCacheTtlMs`
101
- * (default 5s). vinext does NOT call this method per request.
102
- *
103
- * This is an opt-in escape hatch for callers that need stricter isolation
104
- * (e.g., tests, or environments with custom lifecycle management).
105
- * Callers that require per-request isolation should either construct a
106
- * fresh KVCacheHandler per request or invoke this method explicitly.
107
- */
108
- resetRequestCache(): void;
109
- /**
110
- * Fire a KV delete in the background.
111
- * Prefers the per-request ExecutionContext from ALS (set by
112
- * runWithExecutionContext in the worker entry) so that background KV
113
- * operations are registered with the correct request's waitUntil().
114
- * Falls back to the constructor-provided ctx for callers that set it
115
- * explicitly, and to fire-and-forget when neither is available (Node.js dev).
116
- */
117
- private _deleteInBackground;
118
- /**
119
- * Execute a KV put and return the promise so callers can await completion.
120
- * Also registers with ctx.waitUntil() so the Workers runtime keeps the
121
- * isolate alive even if the caller does not await the returned promise.
122
- */
123
- private _put;
124
- }
125
- //#endregion
126
- export { KVCacheHandler };