@opennextjs/cloudflare 0.0.0-fd3c2b9 → 0.0.3

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 CHANGED
@@ -1,37 +1,110 @@
1
- # Next.js builder for Cloudflare
1
+ # OpenNext for Cloudflare
2
2
 
3
- ## Configure your app
3
+ Deploy Next.js apps to Cloudflare!
4
4
 
5
- - add the following `devDependency` to the `package.json`:
5
+ [OpenNext for Cloudflare](https://opennext.js.org/cloudflare) is Cloudflare specific adapter that enables deployment of Next.js applications to Cloudflare.
6
6
 
7
- ```bash
8
- pnpm add -D wrangler@latest @opennextjs/cloudflare
9
- ```
7
+ ## Getting started
10
8
 
11
- ## Serve your app
9
+ You can use [`create-next-app`](https://nextjs.org/docs/pages/api-reference/cli/create-next-app) to start a new application or take an existing Next.js application and deploy it to Cloudflare using the following few steps:
12
10
 
13
- - build the app and adapt it for Cloudflare
11
+ ## Configure your app
12
+
13
+ - add the following `devDependencies` to the `package.json`:
14
14
 
15
15
  ```bash
16
- pnpx cloudflare
16
+ npm add -D wrangler@latest @opennextjs/cloudflare
17
+ # or
18
+ pnpm add -D wrangler@latest @opennextjs/cloudflare
19
+ # or
20
+ yarn add -D wrangler@latest @opennextjs/cloudflare
21
+ # or
22
+ bun add -D wrangler@latest @opennextjs/cloudflare
17
23
  ```
18
24
 
19
25
  - add a `wrangler.toml` at the root of your project
20
26
 
21
27
  ```toml
22
28
  #:schema node_modules/wrangler/config-schema.json
23
- name = "<app-name>"
29
+ name = "<your-app-name>"
24
30
  main = ".worker-next/index.mjs"
25
31
 
26
- compatibility_date = "2024-08-29"
27
- compatibility_flags = ["nodejs_compat_v2"]
32
+ compatibility_date = "2024-09-23"
33
+ compatibility_flags = ["nodejs_compat"]
28
34
 
29
35
  # Use the new Workers + Assets to host the static frontend files
30
- experimental_assets = { directory = ".worker-next/assets", binding = "ASSETS" }
36
+ assets = { directory = ".worker-next/assets", binding = "ASSETS" }
37
+ ```
38
+
39
+ You can enable Incremental Static Regeneration ([ISR](https://nextjs.org/docs/app/building-your-application/data-fetching/incremental-static-regeneration)) by adding a KV binding named `NEXT_CACHE_WORKERS_KV` to your `wrangler.toml`:
40
+
41
+ - Create the binding
42
+
43
+ ```bash
44
+ npx wrangler kv namespace create NEXT_CACHE_WORKERS_KV
45
+ # or
46
+ pnpm wrangler kv namespace create NEXT_CACHE_WORKERS_KV
47
+ # or
48
+ yarn wrangler kv namespace create NEXT_CACHE_WORKERS_KV
49
+ # or
50
+ bun wrangler kv namespace create NEXT_CACHE_WORKERS_KV
51
+ ```
52
+
53
+ - Paste the snippet to your `wrangler.toml`:
54
+
55
+ ```bash
56
+ [[kv_namespaces]]
57
+ binding = "NEXT_CACHE_WORKERS_KV"
58
+ id = "..."
59
+ ```
60
+
61
+ > [!WARNING]
62
+ > The current support for ISR is limited.
63
+
64
+ ## Local development
65
+
66
+ - you can use the regular `next` CLI to start the Next.js dev server:
67
+
68
+ ## Local preview
69
+
70
+ Run the following commands to preview the production build of your application locally:
71
+
72
+ - build the app and adapt it for Cloudflare
73
+
74
+ ```bash
75
+ npx cloudflare
76
+ # or
77
+ pnpm cloudflare
78
+ # or
79
+ yarn cloudflare
80
+ # or
81
+ bun cloudflare
31
82
  ```
32
83
 
33
84
  - Preview the app in Wrangler
34
85
 
35
86
  ```bash
87
+ npx wrangler dev
88
+ # or
36
89
  pnpm wrangler dev
90
+ # or
91
+ yarn wrangler dev
92
+ # or
93
+ bun wrangler dev
94
+ ```
95
+
96
+ ## Deploy your app
97
+
98
+ Deploy your application to production with the following:
99
+
100
+ - build the app and adapt it for Cloudflare
101
+
102
+ ```bash
103
+ npx cloudflare && npx wrangler deploy
104
+ # or
105
+ pnpm cloudflare && pnpm wrangler deploy
106
+ # or
107
+ yarn cloudflare && yarn wrangler deploy
108
+ # or
109
+ bun cloudflare && bun wrangler deploy
37
110
  ```
@@ -0,0 +1,32 @@
1
+ // src/api/get-cloudflare-context.ts
2
+ import "server-only";
3
+ var cloudflareContextSymbol = Symbol.for("__cloudflare-context__");
4
+ async function getCloudflareContext() {
5
+ const global = globalThis;
6
+ const cloudflareContext = global[cloudflareContextSymbol];
7
+ if (!cloudflareContext) {
8
+ return getCloudflareContextInNextDev();
9
+ }
10
+ return cloudflareContext;
11
+ }
12
+ var cloudflareContextInNextDevSymbol = Symbol.for("__next-dev/cloudflare-context__");
13
+ async function getCloudflareContextInNextDev() {
14
+ const global = globalThis;
15
+ if (!global[cloudflareContextInNextDevSymbol]) {
16
+ const { getPlatformProxy } = await import(
17
+ /* webpackIgnore: true */
18
+ `${"__wrangler".replaceAll("_", "")}`
19
+ );
20
+ const { env, cf, ctx } = await getPlatformProxy();
21
+ global[cloudflareContextInNextDevSymbol] = {
22
+ env,
23
+ cf,
24
+ ctx
25
+ };
26
+ }
27
+ return global[cloudflareContextInNextDevSymbol];
28
+ }
29
+
30
+ export {
31
+ getCloudflareContext
32
+ };
@@ -0,0 +1,26 @@
1
+ declare global {
2
+ interface CloudflareEnv {
3
+ }
4
+ }
5
+ type CloudflareContext<CfProperties extends Record<string, unknown> = IncomingRequestCfProperties, Context = ExecutionContext> = {
6
+ /**
7
+ * the worker's [bindings](https://developers.cloudflare.com/workers/runtime-apis/bindings/)
8
+ */
9
+ env: CloudflareEnv;
10
+ /**
11
+ * the request's [cf properties](https://developers.cloudflare.com/workers/runtime-apis/request/#the-cf-property-requestinitcfproperties)
12
+ */
13
+ cf: CfProperties | undefined;
14
+ /**
15
+ * the current [execution context](https://developers.cloudflare.com/workers/runtime-apis/context)
16
+ */
17
+ ctx: Context;
18
+ };
19
+ /**
20
+ * Utility to get the current Cloudflare context
21
+ *
22
+ * @returns the cloudflare context
23
+ */
24
+ declare function getCloudflareContext<CfProperties extends Record<string, unknown> = IncomingRequestCfProperties, Context = ExecutionContext>(): Promise<CloudflareContext<CfProperties, Context>>;
25
+
26
+ export { type CloudflareContext, getCloudflareContext };
@@ -0,0 +1,6 @@
1
+ import {
2
+ getCloudflareContext
3
+ } from "./chunk-VTBEIZPQ.mjs";
4
+ export {
5
+ getCloudflareContext
6
+ };
@@ -0,0 +1 @@
1
+ export { CloudflareContext, getCloudflareContext } from './get-cloudflare-context.mjs';
@@ -0,0 +1,6 @@
1
+ import {
2
+ getCloudflareContext
3
+ } from "./chunk-VTBEIZPQ.mjs";
4
+ export {
5
+ getCloudflareContext
6
+ };
@@ -0,0 +1,20 @@
1
+ import {
2
+ NEXT_BODY_SUFFIX,
3
+ NEXT_DATA_SUFFIX,
4
+ NEXT_HTML_SUFFIX,
5
+ NEXT_META_SUFFIX,
6
+ OpenNextCacheHandler,
7
+ RSC_PREFETCH_SUFFIX,
8
+ RSC_SUFFIX,
9
+ SEED_DATA_DIR
10
+ } from "../chunk-F7LECSR5.mjs";
11
+ export {
12
+ NEXT_BODY_SUFFIX,
13
+ NEXT_DATA_SUFFIX,
14
+ NEXT_HTML_SUFFIX,
15
+ NEXT_META_SUFFIX,
16
+ OpenNextCacheHandler,
17
+ RSC_PREFETCH_SUFFIX,
18
+ RSC_SUFFIX,
19
+ SEED_DATA_DIR
20
+ };
@@ -0,0 +1,160 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __commonJS = (cb, mod) => function __require() {
8
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
19
+ // If the importer is in node compatibility mode or this is not an ESM
20
+ // file that has been converted to a CommonJS file using a Babel-
21
+ // compatible transform (i.e. "__esModule" has not been set), then set
22
+ // "default" to the CommonJS "module.exports" for node compatibility.
23
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
24
+ mod
25
+ ));
26
+
27
+ // src/cli/cache-handler/constants.ts
28
+ var RSC_PREFETCH_SUFFIX = ".prefetch.rsc";
29
+ var RSC_SUFFIX = ".rsc";
30
+ var NEXT_DATA_SUFFIX = ".json";
31
+ var NEXT_META_SUFFIX = ".meta";
32
+ var NEXT_BODY_SUFFIX = ".body";
33
+ var NEXT_HTML_SUFFIX = ".html";
34
+ var SEED_DATA_DIR = "cdn-cgi/_cf_seed_data";
35
+
36
+ // src/cli/cache-handler/utils.ts
37
+ async function getAsset(key, cb) {
38
+ const resp = await process.env.ASSETS.fetch(key);
39
+ return resp.status === 200 ? await cb(resp) : void 0;
40
+ }
41
+ function getSeedBodyFile(key, suffix) {
42
+ return getAsset(key + suffix, (resp) => resp.arrayBuffer());
43
+ }
44
+ function getSeedTextFile(key, suffix) {
45
+ return getAsset(key + suffix, (resp) => resp.text());
46
+ }
47
+ function getSeedMetaFile(key) {
48
+ return getAsset(key + NEXT_META_SUFFIX, (resp) => resp.json());
49
+ }
50
+ function parseCtx(ctx = {}) {
51
+ return { ...ctx, kind: ctx?.kindHint?.toUpperCase() };
52
+ }
53
+
54
+ // src/cli/cache-handler/open-next-cache-handler.ts
55
+ var OpenNextCacheHandler = class _OpenNextCacheHandler {
56
+ constructor(ctx) {
57
+ this.ctx = ctx;
58
+ }
59
+ static maybeKVNamespace = void 0;
60
+ debug = !!process.env.NEXT_PRIVATE_DEBUG_CACHE;
61
+ async get(...args) {
62
+ const [key, _ctx] = args;
63
+ const ctx = parseCtx(_ctx);
64
+ if (this.debug) console.log(`cache - get: ${key}, ${ctx?.kind}`);
65
+ if (_OpenNextCacheHandler.maybeKVNamespace !== void 0) {
66
+ try {
67
+ const value = await _OpenNextCacheHandler.maybeKVNamespace.get(key, "json");
68
+ if (value) return value;
69
+ } catch (e) {
70
+ console.error(`Failed to get value for key = ${key}: ${e}`);
71
+ }
72
+ }
73
+ if (ctx?.kind === "FETCH" || ctx?.kind === "IMAGE") return null;
74
+ const seedKey = `http://assets.local/${SEED_DATA_DIR}/${key}`.replace(/\/\//g, "/");
75
+ if (ctx?.kind === "APP" || ctx?.kind === "APP_ROUTE") {
76
+ const fallbackBody = await getSeedBodyFile(seedKey, NEXT_BODY_SUFFIX);
77
+ if (fallbackBody) {
78
+ const meta = await getSeedMetaFile(seedKey);
79
+ return {
80
+ lastModified: meta?.lastModified,
81
+ value: {
82
+ kind: ctx.kind === "APP_ROUTE" ? ctx.kind : "ROUTE",
83
+ body: fallbackBody,
84
+ status: meta?.status ?? 200,
85
+ headers: meta?.headers ?? {}
86
+ }
87
+ };
88
+ }
89
+ if (ctx.kind === "APP_ROUTE") {
90
+ return null;
91
+ }
92
+ }
93
+ const seedHtml = await getSeedTextFile(seedKey, NEXT_HTML_SUFFIX);
94
+ if (!seedHtml) return null;
95
+ if (ctx?.kind === "PAGES" || ctx?.kind === "APP" || ctx?.kind === "APP_PAGE") {
96
+ const metaPromise = getSeedMetaFile(seedKey);
97
+ let pageDataPromise = Promise.resolve(void 0);
98
+ if (!ctx.isFallback) {
99
+ const rscSuffix = ctx.isRoutePPREnabled ? RSC_PREFETCH_SUFFIX : RSC_SUFFIX;
100
+ if (ctx.kind === "APP_PAGE") {
101
+ pageDataPromise = getSeedBodyFile(seedKey, rscSuffix);
102
+ } else {
103
+ pageDataPromise = getSeedTextFile(seedKey, ctx.kind === "APP" ? rscSuffix : NEXT_DATA_SUFFIX);
104
+ }
105
+ }
106
+ const [meta, pageData] = await Promise.all([metaPromise, pageDataPromise]);
107
+ return {
108
+ lastModified: meta?.lastModified,
109
+ value: {
110
+ kind: ctx.kind === "APP_PAGE" ? "APP_PAGE" : "PAGE",
111
+ html: seedHtml,
112
+ pageData: pageData ?? "",
113
+ ...ctx.kind === "APP_PAGE" && { rscData: pageData },
114
+ postponed: meta?.postponed,
115
+ status: meta?.status,
116
+ headers: meta?.headers
117
+ }
118
+ };
119
+ }
120
+ return null;
121
+ }
122
+ async set(...args) {
123
+ const [key, entry, _ctx] = args;
124
+ if (_OpenNextCacheHandler.maybeKVNamespace === void 0) {
125
+ return;
126
+ }
127
+ if (this.debug) console.log(`cache - set: ${key}`);
128
+ const data = {
129
+ lastModified: Date.now(),
130
+ value: entry
131
+ };
132
+ try {
133
+ await _OpenNextCacheHandler.maybeKVNamespace.put(key, JSON.stringify(data));
134
+ } catch (e) {
135
+ console.error(`Failed to set value for key = ${key}: ${e}`);
136
+ }
137
+ }
138
+ async revalidateTag(...args) {
139
+ const [tags] = args;
140
+ if (_OpenNextCacheHandler.maybeKVNamespace === void 0) {
141
+ return;
142
+ }
143
+ if (this.debug) console.log(`cache - revalidateTag: ${JSON.stringify([tags].flat())}`);
144
+ }
145
+ resetRequestCache() {
146
+ }
147
+ };
148
+
149
+ export {
150
+ __commonJS,
151
+ __toESM,
152
+ RSC_PREFETCH_SUFFIX,
153
+ RSC_SUFFIX,
154
+ NEXT_DATA_SUFFIX,
155
+ NEXT_META_SUFFIX,
156
+ NEXT_BODY_SUFFIX,
157
+ NEXT_HTML_SUFFIX,
158
+ SEED_DATA_DIR,
159
+ OpenNextCacheHandler
160
+ };