commit 7eb7070c8701b397a42b155c462b1e68740ca305
parent c61b300b7b8469a8e9df6bd931d37a41996218b0
Author: Minerva_juppiter <94231606+minerva-jupiter@users.noreply.github.com>
Date: Tue, 28 Jul 2026 22:14:29 +0900
feat(deps): update dependencies and add build script for Cloudflare P… (#89)
* feat(deps): update dependencies and add build script for Cloudflare Pages
Update core dependencies including Next.js, React, Sentry, TypeScript, and Biome, add the @cloudflare/next-on-pages package along with the build:pages script, and update trusted dependencies.
* feat(config): migrate from next-on-pages to opennextjs-cloudflare
* feat(config): add Wrangler configuration file for OpenNext deployment
* chore(deps): update typescript dependency
Diffstat:
5 files changed, 464 insertions(+), 9 deletions(-)
diff --git a/.open-next/.build/open-next.config.edge.mjs b/.open-next/.build/open-next.config.edge.mjs
@@ -0,0 +1,215 @@
+// node_modules/@opennextjs/cloudflare/dist/api/cloudflare-context.js
+var cloudflareContextSymbol = Symbol.for("__cloudflare-context__");
+function getCloudflareContext(options = { async: false }) {
+ return options.async ? getCloudflareContextAsync() : getCloudflareContextSync();
+}
+function getCloudflareContextFromGlobalScope() {
+ const global = globalThis;
+ return global[cloudflareContextSymbol];
+}
+function inSSG() {
+ const global = globalThis;
+ return global.__NEXT_DATA__?.nextExport === true;
+}
+function getCloudflareContextSync() {
+ const cloudflareContext = getCloudflareContextFromGlobalScope();
+ if (cloudflareContext) {
+ return cloudflareContext;
+ }
+ if (inSSG()) {
+ throw new Error(`
+
+ERROR: \`getCloudflareContext\` has been called in sync mode in either a static route or at the top level of a non-static one, both cases are not allowed but can be solved by either:
+ - make sure that the call is not at the top level and that the route is not static
+ - call \`getCloudflareContext({async: true})\` to use the \`async\` mode
+ - avoid calling \`getCloudflareContext\` in the route
+`);
+ }
+ throw new Error(initOpenNextCloudflareForDevErrorMsg);
+}
+async function getCloudflareContextAsync() {
+ const cloudflareContext = getCloudflareContextFromGlobalScope();
+ if (cloudflareContext) {
+ return cloudflareContext;
+ }
+ const inNodejsRuntime = process.env.NEXT_RUNTIME === "nodejs";
+ if (inNodejsRuntime || inSSG()) {
+ const cloudflareContext2 = await getCloudflareContextFromWrangler();
+ addCloudflareContextToNodejsGlobal(cloudflareContext2);
+ return cloudflareContext2;
+ }
+ throw new Error(initOpenNextCloudflareForDevErrorMsg);
+}
+function addCloudflareContextToNodejsGlobal(cloudflareContext) {
+ const global = globalThis;
+ global[cloudflareContextSymbol] = cloudflareContext;
+}
+async function getCloudflareContextFromWrangler(options) {
+ const { getPlatformProxy } = await import(
+ /* webpackIgnore: true */
+ `${"__wrangler".replaceAll("_", "")}`
+ );
+ const environment = options?.environment ?? process.env.NEXT_DEV_WRANGLER_ENV;
+ const { env, cf, ctx } = await getPlatformProxy({
+ ...options,
+ // The `env` passed to the fetch handler does not contain variables from `.env*` files.
+ // because we invoke wrangler with `CLOUDFLARE_LOAD_DEV_VARS_FROM_DOT_ENV`=`"false"`.
+ // Initializing `envFiles` with an empty list is the equivalent for this API call.
+ envFiles: [],
+ environment
+ });
+ return {
+ env,
+ cf,
+ ctx
+ };
+}
+var initOpenNextCloudflareForDevErrorMsg = `
+
+ERROR: \`getCloudflareContext\` has been called without having called \`initOpenNextCloudflareForDev\` from the Next.js config file.
+You should update your Next.js config file as shown below:
+
+ \`\`\`
+ // next.config.mjs
+
+ import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
+
+ initOpenNextCloudflareForDev();
+
+ const nextConfig = { ... };
+ export default nextConfig;
+ \`\`\`
+
+`;
+
+// node_modules/@opennextjs/cloudflare/dist/api/overrides/asset-resolver/index.js
+var resolver = {
+ name: "cloudflare-asset-resolver",
+ async maybeGetAssetResult(event) {
+ const { ASSETS } = getCloudflareContext().env;
+ if (!ASSETS || !isUserWorkerFirst(globalThis.__ASSETS_RUN_WORKER_FIRST__, event.rawPath)) {
+ return void 0;
+ }
+ const { method, headers } = event;
+ if (method !== "GET" && method != "HEAD") {
+ return void 0;
+ }
+ const url = new URL(event.rawPath, "https://assets.local");
+ const response = await ASSETS.fetch(url, {
+ headers,
+ method
+ });
+ if (response.status === 404) {
+ await response.body?.cancel();
+ return void 0;
+ }
+ return {
+ type: "core",
+ statusCode: response.status,
+ headers: Object.fromEntries(response.headers.entries()),
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ body: getResponseBody(method, response),
+ isBase64Encoded: false
+ };
+ }
+};
+function getResponseBody(method, response) {
+ if (method === "HEAD") {
+ return null;
+ }
+ return response.body || new ReadableStream();
+}
+function isUserWorkerFirst(runWorkerFirst, pathname) {
+ if (!Array.isArray(runWorkerFirst)) {
+ return runWorkerFirst ?? false;
+ }
+ let hasPositiveMatch = false;
+ for (let rule of runWorkerFirst) {
+ let isPositiveRule = true;
+ if (rule.startsWith("!")) {
+ rule = rule.slice(1);
+ isPositiveRule = false;
+ } else if (hasPositiveMatch) {
+ continue;
+ }
+ const match = new RegExp(`^${rule.replace(/([[\]().*+?^$|{}\\])/g, "\\$1").replace("\\*", ".*")}$`).test(pathname);
+ if (match) {
+ if (isPositiveRule) {
+ hasPositiveMatch = true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return hasPositiveMatch;
+}
+var asset_resolver_default = resolver;
+
+// node_modules/@opennextjs/cloudflare/dist/api/config.js
+function defineCloudflareConfig(config = {}) {
+ const { incrementalCache, tagCache, queue, cachePurge, enableCacheInterception = false, routePreloadingBehavior = "none" } = config;
+ return {
+ default: {
+ override: {
+ wrapper: "cloudflare-node",
+ converter: "edge",
+ proxyExternalRequest: "fetch",
+ incrementalCache: resolveIncrementalCache(incrementalCache),
+ tagCache: resolveTagCache(tagCache),
+ queue: resolveQueue(queue),
+ cdnInvalidation: resolveCdnInvalidation(cachePurge)
+ },
+ routePreloadingBehavior
+ },
+ // node:crypto is used to compute cache keys
+ edgeExternals: ["node:crypto"],
+ cloudflare: {
+ useWorkerdCondition: true
+ },
+ dangerous: {
+ enableCacheInterception
+ },
+ middleware: {
+ external: true,
+ override: {
+ wrapper: "cloudflare-edge",
+ converter: "edge",
+ proxyExternalRequest: "fetch",
+ incrementalCache: resolveIncrementalCache(incrementalCache),
+ tagCache: resolveTagCache(tagCache),
+ queue: resolveQueue(queue)
+ },
+ assetResolver: () => asset_resolver_default
+ }
+ };
+}
+function resolveIncrementalCache(value = "dummy") {
+ if (typeof value === "string") {
+ return value;
+ }
+ return typeof value === "function" ? value : () => value;
+}
+function resolveTagCache(value = "dummy") {
+ if (typeof value === "string") {
+ return value;
+ }
+ return typeof value === "function" ? value : () => value;
+}
+function resolveQueue(value = "dummy") {
+ if (typeof value === "string") {
+ return value;
+ }
+ return typeof value === "function" ? value : () => value;
+}
+function resolveCdnInvalidation(value = "dummy") {
+ if (typeof value === "string") {
+ return value;
+ }
+ return typeof value === "function" ? value : () => value;
+}
+
+// open-next.config.ts
+var open_next_config_default = defineCloudflareConfig();
+export {
+ open_next_config_default as default
+};
diff --git a/.open-next/.build/open-next.config.mjs b/.open-next/.build/open-next.config.mjs
@@ -0,0 +1,217 @@
+import { createRequire as topLevelCreateRequire } from 'module';const require = topLevelCreateRequire(import.meta.url);import bannerUrl from 'url';const __dirname = bannerUrl.fileURLToPath(new URL('.', import.meta.url));
+
+// node_modules/@opennextjs/cloudflare/dist/api/cloudflare-context.js
+var cloudflareContextSymbol = Symbol.for("__cloudflare-context__");
+function getCloudflareContext(options = { async: false }) {
+ return options.async ? getCloudflareContextAsync() : getCloudflareContextSync();
+}
+function getCloudflareContextFromGlobalScope() {
+ const global = globalThis;
+ return global[cloudflareContextSymbol];
+}
+function inSSG() {
+ const global = globalThis;
+ return global.__NEXT_DATA__?.nextExport === true;
+}
+function getCloudflareContextSync() {
+ const cloudflareContext = getCloudflareContextFromGlobalScope();
+ if (cloudflareContext) {
+ return cloudflareContext;
+ }
+ if (inSSG()) {
+ throw new Error(`
+
+ERROR: \`getCloudflareContext\` has been called in sync mode in either a static route or at the top level of a non-static one, both cases are not allowed but can be solved by either:
+ - make sure that the call is not at the top level and that the route is not static
+ - call \`getCloudflareContext({async: true})\` to use the \`async\` mode
+ - avoid calling \`getCloudflareContext\` in the route
+`);
+ }
+ throw new Error(initOpenNextCloudflareForDevErrorMsg);
+}
+async function getCloudflareContextAsync() {
+ const cloudflareContext = getCloudflareContextFromGlobalScope();
+ if (cloudflareContext) {
+ return cloudflareContext;
+ }
+ const inNodejsRuntime = process.env.NEXT_RUNTIME === "nodejs";
+ if (inNodejsRuntime || inSSG()) {
+ const cloudflareContext2 = await getCloudflareContextFromWrangler();
+ addCloudflareContextToNodejsGlobal(cloudflareContext2);
+ return cloudflareContext2;
+ }
+ throw new Error(initOpenNextCloudflareForDevErrorMsg);
+}
+function addCloudflareContextToNodejsGlobal(cloudflareContext) {
+ const global = globalThis;
+ global[cloudflareContextSymbol] = cloudflareContext;
+}
+async function getCloudflareContextFromWrangler(options) {
+ const { getPlatformProxy } = await import(
+ /* webpackIgnore: true */
+ `${"__wrangler".replaceAll("_", "")}`
+ );
+ const environment = options?.environment ?? process.env.NEXT_DEV_WRANGLER_ENV;
+ const { env, cf, ctx } = await getPlatformProxy({
+ ...options,
+ // The `env` passed to the fetch handler does not contain variables from `.env*` files.
+ // because we invoke wrangler with `CLOUDFLARE_LOAD_DEV_VARS_FROM_DOT_ENV`=`"false"`.
+ // Initializing `envFiles` with an empty list is the equivalent for this API call.
+ envFiles: [],
+ environment
+ });
+ return {
+ env,
+ cf,
+ ctx
+ };
+}
+var initOpenNextCloudflareForDevErrorMsg = `
+
+ERROR: \`getCloudflareContext\` has been called without having called \`initOpenNextCloudflareForDev\` from the Next.js config file.
+You should update your Next.js config file as shown below:
+
+ \`\`\`
+ // next.config.mjs
+
+ import { initOpenNextCloudflareForDev } from "@opennextjs/cloudflare";
+
+ initOpenNextCloudflareForDev();
+
+ const nextConfig = { ... };
+ export default nextConfig;
+ \`\`\`
+
+`;
+
+// node_modules/@opennextjs/cloudflare/dist/api/overrides/asset-resolver/index.js
+var resolver = {
+ name: "cloudflare-asset-resolver",
+ async maybeGetAssetResult(event) {
+ const { ASSETS } = getCloudflareContext().env;
+ if (!ASSETS || !isUserWorkerFirst(globalThis.__ASSETS_RUN_WORKER_FIRST__, event.rawPath)) {
+ return void 0;
+ }
+ const { method, headers } = event;
+ if (method !== "GET" && method != "HEAD") {
+ return void 0;
+ }
+ const url = new URL(event.rawPath, "https://assets.local");
+ const response = await ASSETS.fetch(url, {
+ headers,
+ method
+ });
+ if (response.status === 404) {
+ await response.body?.cancel();
+ return void 0;
+ }
+ return {
+ type: "core",
+ statusCode: response.status,
+ headers: Object.fromEntries(response.headers.entries()),
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ body: getResponseBody(method, response),
+ isBase64Encoded: false
+ };
+ }
+};
+function getResponseBody(method, response) {
+ if (method === "HEAD") {
+ return null;
+ }
+ return response.body || new ReadableStream();
+}
+function isUserWorkerFirst(runWorkerFirst, pathname) {
+ if (!Array.isArray(runWorkerFirst)) {
+ return runWorkerFirst ?? false;
+ }
+ let hasPositiveMatch = false;
+ for (let rule of runWorkerFirst) {
+ let isPositiveRule = true;
+ if (rule.startsWith("!")) {
+ rule = rule.slice(1);
+ isPositiveRule = false;
+ } else if (hasPositiveMatch) {
+ continue;
+ }
+ const match = new RegExp(`^${rule.replace(/([[\]().*+?^$|{}\\])/g, "\\$1").replace("\\*", ".*")}$`).test(pathname);
+ if (match) {
+ if (isPositiveRule) {
+ hasPositiveMatch = true;
+ } else {
+ return false;
+ }
+ }
+ }
+ return hasPositiveMatch;
+}
+var asset_resolver_default = resolver;
+
+// node_modules/@opennextjs/cloudflare/dist/api/config.js
+function defineCloudflareConfig(config = {}) {
+ const { incrementalCache, tagCache, queue, cachePurge, enableCacheInterception = false, routePreloadingBehavior = "none" } = config;
+ return {
+ default: {
+ override: {
+ wrapper: "cloudflare-node",
+ converter: "edge",
+ proxyExternalRequest: "fetch",
+ incrementalCache: resolveIncrementalCache(incrementalCache),
+ tagCache: resolveTagCache(tagCache),
+ queue: resolveQueue(queue),
+ cdnInvalidation: resolveCdnInvalidation(cachePurge)
+ },
+ routePreloadingBehavior
+ },
+ // node:crypto is used to compute cache keys
+ edgeExternals: ["node:crypto"],
+ cloudflare: {
+ useWorkerdCondition: true
+ },
+ dangerous: {
+ enableCacheInterception
+ },
+ middleware: {
+ external: true,
+ override: {
+ wrapper: "cloudflare-edge",
+ converter: "edge",
+ proxyExternalRequest: "fetch",
+ incrementalCache: resolveIncrementalCache(incrementalCache),
+ tagCache: resolveTagCache(tagCache),
+ queue: resolveQueue(queue)
+ },
+ assetResolver: () => asset_resolver_default
+ }
+ };
+}
+function resolveIncrementalCache(value = "dummy") {
+ if (typeof value === "string") {
+ return value;
+ }
+ return typeof value === "function" ? value : () => value;
+}
+function resolveTagCache(value = "dummy") {
+ if (typeof value === "string") {
+ return value;
+ }
+ return typeof value === "function" ? value : () => value;
+}
+function resolveQueue(value = "dummy") {
+ if (typeof value === "string") {
+ return value;
+ }
+ return typeof value === "function" ? value : () => value;
+}
+function resolveCdnInvalidation(value = "dummy") {
+ if (typeof value === "string") {
+ return value;
+ }
+ return typeof value === "function" ? value : () => value;
+}
+
+// open-next.config.ts
+var open_next_config_default = defineCloudflareConfig();
+export {
+ open_next_config_default as default
+};
diff --git a/open-next.config.ts b/open-next.config.ts
@@ -0,0 +1,3 @@
+import { defineCloudflareConfig } from "@opennextjs/cloudflare";
+
+export default defineCloudflareConfig();
diff --git a/package.json b/package.json
@@ -5,26 +5,33 @@
"scripts": {
"dev": "next dev --turbopack",
"build": "next build --turbopack",
+ "build:pages": "opennextjs-cloudflare build",
"testbuild": "cd ./wasm-webtools && wasm-pack build --release --target web && cd .. && next build --turbopack",
"start": "next start",
"lint": "biome check",
- "format": "biome format --write"
+ "format": "biome format --write",
+ "preview": "opennextjs-cloudflare build && opennextjs-cloudflare preview",
+ "deploy": "opennextjs-cloudflare build && opennextjs-cloudflare deploy"
},
"dependencies": {
- "@sentry/nextjs": "^10.52.0",
- "next": "16.2.10",
- "react": "19.2.7",
- "react-dom": "19.2.7"
+ "@sentry/nextjs": "^10.68.0",
+ "next": "16.2.12",
+ "react": "19.2.8",
+ "react-dom": "19.2.8",
+ "@opennextjs/cloudflare": "^1.20.2"
},
"devDependencies": {
"typescript": "^6.0.3",
- "@types/node": "^26.0.0",
- "@types/react": "^19.2.14",
+ "@types/node": "^26.1.2",
+ "@types/react": "^19.2.17",
"@types/react-dom": "^19.2.3",
- "@biomejs/biome": "2.5.2"
+ "@biomejs/biome": "2.5.6",
+ "wrangler": "^4.114.0"
},
"trustedDependencies": [
"@sentry/cli",
- "sharp"
+ "esbuild",
+ "sharp",
+ "workerd"
]
}
diff --git a/wrangler.jsonc b/wrangler.jsonc
@@ -0,0 +1,13 @@
+{
+ "$schema": "./node_modules/wrangler/config-schema.json",
+ "main": ".open-next/worker.js",
+ "name": "webtools",
+ "compatibility_date": "2026-07-28",
+ "compatibility_flags": [
+ "nodejs_compat"
+ ],
+ "assets": {
+ "directory": ".open-next/assets",
+ "binding": "ASSETS"
+ }
+}