@opennextjs/cloudflare 0.0.0-5454280
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/LICENSE +25 -0
- package/README.md +46 -0
- package/dist/index.mjs +7087 -0
- package/dist/templates/shims/empty.ts +1 -0
- package/dist/templates/shims/env.ts +1 -0
- package/dist/templates/shims/node-fs.ts +69 -0
- package/dist/templates/shims/throw.ts +1 -0
- package/dist/templates/worker.ts +128 -0
- package/package.json +38 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function loadEnvConfig() {}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/node-fs-methods.ts
|
|
2
|
+
|
|
3
|
+
export const nodeFs = {
|
|
4
|
+
existsSync,
|
|
5
|
+
readFile,
|
|
6
|
+
readFileSync,
|
|
7
|
+
writeFile,
|
|
8
|
+
mkdir,
|
|
9
|
+
stat,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const FILES = new Map<string, unknown>();
|
|
13
|
+
const MTIME = Date.now();
|
|
14
|
+
|
|
15
|
+
function existsSync(path: string) {
|
|
16
|
+
console.log("existsSync", path, new Error().stack?.split("\n").slice(1).join("\n"));
|
|
17
|
+
return FILES.has(path);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function readFile(path: string, options: unknown): Promise<any> {
|
|
21
|
+
console.log(
|
|
22
|
+
"readFile",
|
|
23
|
+
{ path, options }
|
|
24
|
+
// new Error().stack.split("\n").slice(1).join("\n"),
|
|
25
|
+
);
|
|
26
|
+
if (!FILES.has(path)) {
|
|
27
|
+
throw new Error(path + "does not exist");
|
|
28
|
+
}
|
|
29
|
+
return FILES.get(path);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function readFileSync(path: string, options: unknown) {
|
|
33
|
+
console.log(
|
|
34
|
+
"readFileSync",
|
|
35
|
+
{ path, options }
|
|
36
|
+
// new Error().stack.split("\n").slice(1).join("\n"),
|
|
37
|
+
);
|
|
38
|
+
if (!FILES.has(path)) {
|
|
39
|
+
throw new Error(path + "does not exist");
|
|
40
|
+
}
|
|
41
|
+
return FILES.get(path);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function writeFile(file: string, data: unknown) {
|
|
45
|
+
console.log(
|
|
46
|
+
"writeFile",
|
|
47
|
+
{ file, data }
|
|
48
|
+
// new Error().stack.split("\n").slice(1).join("\n"),
|
|
49
|
+
);
|
|
50
|
+
FILES.set(file, data);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function mkdir(dir: string) {
|
|
55
|
+
console.log(
|
|
56
|
+
"mkdir",
|
|
57
|
+
dir
|
|
58
|
+
//new Error().stack.split("\n").slice(1).join("\n"),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function stat(file: string) {
|
|
63
|
+
console.log(
|
|
64
|
+
"stat",
|
|
65
|
+
file
|
|
66
|
+
// new Error().stack.split("\n").slice(1).join("\n"),
|
|
67
|
+
);
|
|
68
|
+
return { mtime: new Date(MTIME) };
|
|
69
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
throw new Error();
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import Stream from "node:stream";
|
|
2
|
+
import type { NextConfig } from "next";
|
|
3
|
+
import { NodeNextRequest, NodeNextResponse } from "next/dist/server/base-http/node";
|
|
4
|
+
import { MockedResponse } from "next/dist/server/lib/mock-request";
|
|
5
|
+
import NextNodeServer, { NodeRequestHandler } from "next/dist/server/next-server";
|
|
6
|
+
import type { IncomingMessage } from "node:http";
|
|
7
|
+
|
|
8
|
+
const NON_BODY_RESPONSES = new Set([101, 204, 205, 304]);
|
|
9
|
+
|
|
10
|
+
// Injected at build time
|
|
11
|
+
const nextConfig: NextConfig = JSON.parse(process.env.__NEXT_PRIVATE_STANDALONE_CONFIG ?? "{}");
|
|
12
|
+
|
|
13
|
+
let requestHandler: NodeRequestHandler | null = null;
|
|
14
|
+
|
|
15
|
+
export default {
|
|
16
|
+
async fetch(request: Request, env: any, ctx: any) {
|
|
17
|
+
if (requestHandler == null) {
|
|
18
|
+
globalThis.process.env = { ...globalThis.process.env, ...env };
|
|
19
|
+
requestHandler = new NextNodeServer({
|
|
20
|
+
conf: { ...nextConfig, env },
|
|
21
|
+
customServer: false,
|
|
22
|
+
dev: false,
|
|
23
|
+
dir: "",
|
|
24
|
+
minimalMode: false,
|
|
25
|
+
}).getRequestHandler();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const url = new URL(request.url);
|
|
29
|
+
|
|
30
|
+
if (url.pathname === "/_next/image") {
|
|
31
|
+
let imageUrl =
|
|
32
|
+
url.searchParams.get("url") ?? "https://developers.cloudflare.com/_astro/logo.BU9hiExz.svg";
|
|
33
|
+
if (imageUrl.startsWith("/")) {
|
|
34
|
+
return env.ASSETS.fetch(new URL(imageUrl, request.url));
|
|
35
|
+
}
|
|
36
|
+
return fetch(imageUrl, { cf: { cacheEverything: true } } as any);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const { req, res, webResponse } = getWrappedStreams(request, ctx);
|
|
40
|
+
|
|
41
|
+
ctx.waitUntil(requestHandler(new NodeNextRequest(req), new NodeNextResponse(res)));
|
|
42
|
+
|
|
43
|
+
return await webResponse();
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function getWrappedStreams(request: Request, ctx: any) {
|
|
48
|
+
const url = new URL(request.url);
|
|
49
|
+
|
|
50
|
+
const req = (
|
|
51
|
+
request.body ? Stream.Readable.fromWeb(request.body as any) : Stream.Readable.from([])
|
|
52
|
+
) as IncomingMessage;
|
|
53
|
+
req.httpVersion = "1.0";
|
|
54
|
+
req.httpVersionMajor = 1;
|
|
55
|
+
req.httpVersionMinor = 0;
|
|
56
|
+
req.url = url.href.slice(url.origin.length);
|
|
57
|
+
req.headers = Object.fromEntries([...request.headers]);
|
|
58
|
+
req.method = request.method;
|
|
59
|
+
Object.defineProperty(req, "__node_stream__", {
|
|
60
|
+
value: true,
|
|
61
|
+
writable: false,
|
|
62
|
+
});
|
|
63
|
+
Object.defineProperty(req, "headersDistinct", {
|
|
64
|
+
get() {
|
|
65
|
+
const headers: Record<string, string[]> = {};
|
|
66
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
67
|
+
if (!value) continue;
|
|
68
|
+
headers[key] = Array.isArray(value) ? value : [value];
|
|
69
|
+
}
|
|
70
|
+
return headers;
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const { readable, writable } = new IdentityTransformStream();
|
|
75
|
+
const resBodyWriter = writable.getWriter();
|
|
76
|
+
|
|
77
|
+
const res = new MockedResponse({
|
|
78
|
+
resWriter: (chunk) => {
|
|
79
|
+
resBodyWriter.write(typeof chunk === "string" ? Buffer.from(chunk) : chunk).catch((err: any) => {
|
|
80
|
+
if (
|
|
81
|
+
err.message.includes("WritableStream has been closed") ||
|
|
82
|
+
err.message.includes("Network connection lost")
|
|
83
|
+
) {
|
|
84
|
+
// safe to ignore
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
console.error("Error in resBodyWriter.write");
|
|
88
|
+
console.error(err);
|
|
89
|
+
});
|
|
90
|
+
return true;
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// It's implemented as a no-op, but really it should mark the headers as done
|
|
95
|
+
res.flushHeaders = () => (res as any).headPromiseResolve();
|
|
96
|
+
|
|
97
|
+
// Only allow statusCode to be modified if not sent
|
|
98
|
+
let { statusCode } = res;
|
|
99
|
+
Object.defineProperty(res, "statusCode", {
|
|
100
|
+
get: function () {
|
|
101
|
+
return statusCode;
|
|
102
|
+
},
|
|
103
|
+
set: function (val) {
|
|
104
|
+
if (this.finished || this.headersSent) {
|
|
105
|
+
console.error("headers already sent");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
statusCode = val;
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Make sure the writer is eventually closed
|
|
113
|
+
ctx.waitUntil((res as any).hasStreamed.finally(() => resBodyWriter.close().catch(() => {})));
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
res,
|
|
117
|
+
req,
|
|
118
|
+
webResponse: async () => {
|
|
119
|
+
await res.headPromise;
|
|
120
|
+
// TODO: remove this once streaming with compression is working nicely
|
|
121
|
+
res.setHeader("content-encoding", "identity");
|
|
122
|
+
return new Response(NON_BODY_RESPONSES.has(res.statusCode) ? null : readable, {
|
|
123
|
+
status: res.statusCode,
|
|
124
|
+
headers: (res as any).headers,
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@opennextjs/cloudflare",
|
|
3
|
+
"description": "Cloudflare builder for next apps",
|
|
4
|
+
"version": "0.0.0-5454280",
|
|
5
|
+
"bin": "dist/index.mjs",
|
|
6
|
+
"files": [
|
|
7
|
+
"README.md",
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/flarelabs-net/poc-next.git",
|
|
13
|
+
"directory": "builder"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"cloudflare",
|
|
17
|
+
"workers",
|
|
18
|
+
"next.js"
|
|
19
|
+
],
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/flarelabs-net/poc-next/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/flarelabs-net/poc-next",
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@cloudflare/workers-types": "^4.20240909.0",
|
|
27
|
+
"@types/node": "^22.2.0",
|
|
28
|
+
"esbuild": "^0.23.0",
|
|
29
|
+
"glob": "^11.0.0",
|
|
30
|
+
"next": "14.2.5",
|
|
31
|
+
"tsup": "^8.2.4",
|
|
32
|
+
"typescript": "^5.5.4"
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "tsup",
|
|
36
|
+
"build:watch": "tsup --watch src"
|
|
37
|
+
}
|
|
38
|
+
}
|