@solcreek/adapter-creek 0.1.0 → 0.1.2
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 +8 -2
- package/dist/bundler.js +41 -734
- package/dist/worker-entry.js +10 -1
- package/package.json +2 -2
- package/src/shims/net.js +44 -0
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# adapter-creek
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@solcreek/adapter-creek)
|
|
4
|
+
[](https://github.com/solcreek/adapter-creek/actions/workflows/checks.yml)
|
|
5
|
+
[](https://github.com/solcreek/adapter-creek/actions/workflows/test-e2e-deploy.yml)
|
|
6
|
+
[](https://nextjs.org/docs/app/api-reference/adapters)
|
|
7
|
+
[](./LICENSE)
|
|
8
|
+
|
|
3
9
|
Next.js deployment adapter for [Creek](https://creek.dev).
|
|
4
10
|
|
|
5
11
|
## What is Creek?
|
|
@@ -36,7 +42,7 @@ Creek is a **platform** — this adapter is the bridge between Next.js and that
|
|
|
36
42
|
|
|
37
43
|
## Status
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
Full Next.js coverage on Cloudflare Workers — zero adapter-specific skips. The `test/deploy-tests-manifest.adapter-creek.json` file is empty; every test that runs against other adapters also runs (and passes) here.
|
|
40
46
|
|
|
41
47
|
| Feature | Status |
|
|
42
48
|
|---------|:------:|
|
|
@@ -50,7 +56,7 @@ Near-complete Next.js coverage on Cloudflare Workers. Single entry in the skip m
|
|
|
50
56
|
| `@vercel/og` (node + edge) | ✅ WASM-path |
|
|
51
57
|
| `sqlite3` native-addon packages | ✅ transparent sql.js shim |
|
|
52
58
|
| Multi-runtime isolation (opt-in) | ✅ `CREEK_MULTI_WORKER=1` |
|
|
53
|
-
| Next.js e2e deploy suite |
|
|
59
|
+
| Next.js e2e deploy suite | ✅ 100% of in-scope tests (full matrix green) |
|
|
54
60
|
|
|
55
61
|
## Requirements
|
|
56
62
|
|
package/dist/bundler.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
import * as fs from "node:fs/promises";
|
|
11
11
|
import * as path from "node:path";
|
|
12
12
|
import { execSync } from "node:child_process";
|
|
13
|
-
import { builtinModules, createRequire } from "node:module";
|
|
14
13
|
/**
|
|
15
14
|
* Patch Turbopack runtime to inline chunk loading.
|
|
16
15
|
*
|
|
@@ -33,31 +32,18 @@ async function patchTurbopackRuntime(distDir) {
|
|
|
33
32
|
path.join(distDir, "server", "chunks"),
|
|
34
33
|
path.join(distDir, "server", "edge", "chunks"),
|
|
35
34
|
];
|
|
36
|
-
|
|
37
|
-
let entries;
|
|
35
|
+
for (const dir of searchDirs) {
|
|
38
36
|
try {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (entry.isDirectory()) {
|
|
47
|
-
await walkRuntimes(full);
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
if (!entry.name.endsWith(".js"))
|
|
51
|
-
continue;
|
|
52
|
-
if (entry.name.includes("[turbopack]_runtime") ||
|
|
53
|
-
(entry.name.startsWith("turbopack-") && entry.name.includes("edge-wrapper")) ||
|
|
54
|
-
entry.name.includes("edge-wrapper")) {
|
|
55
|
-
runtimePaths.push(full);
|
|
37
|
+
const files = await fs.readdir(dir);
|
|
38
|
+
for (const f of files) {
|
|
39
|
+
if (f.endsWith(".js") && (f.includes("[turbopack]_runtime") ||
|
|
40
|
+
// Edge Turbopack runtimes are in turbopack-..._edge-wrapper files
|
|
41
|
+
(f.startsWith("turbopack-") && f.includes("edge-wrapper")))) {
|
|
42
|
+
runtimePaths.push(path.join(dir, f));
|
|
43
|
+
}
|
|
56
44
|
}
|
|
57
45
|
}
|
|
58
|
-
|
|
59
|
-
for (const dir of searchDirs) {
|
|
60
|
-
await walkRuntimes(dir);
|
|
46
|
+
catch { }
|
|
61
47
|
}
|
|
62
48
|
if (runtimePaths.length === 0)
|
|
63
49
|
return; // Not Turbopack
|
|
@@ -150,381 +136,18 @@ ${cases.join("\n")}
|
|
|
150
136
|
}
|
|
151
137
|
}
|
|
152
138
|
}
|
|
153
|
-
async function patchNodeExternalRequireConditions(distDir) {
|
|
154
|
-
const serverChunksDir = path.join(distDir, "server", "chunks");
|
|
155
|
-
const projectRoot = path.dirname(distDir);
|
|
156
|
-
const projectRequire = createRequire(path.join(projectRoot, "package.json"));
|
|
157
|
-
const builtins = new Set([
|
|
158
|
-
...builtinModules,
|
|
159
|
-
...builtinModules.map((name) => `node:${name}`),
|
|
160
|
-
]);
|
|
161
|
-
// Narrow scope: only `[externals]*.js` files in `server/chunks/`.
|
|
162
|
-
// Those are Turbopack's dedicated external-require-registration bundles,
|
|
163
|
-
// which contain nothing but `.x("pkg", () => require("pkg"))` lines —
|
|
164
|
-
// rewriting require() to an absolute path there has no module-identity
|
|
165
|
-
// risk. We tried extending this to `server/chunks/ssr/*.js` to fix the
|
|
166
|
-
// styled-jsx-subpath case, but those chunks contain real SSR render
|
|
167
|
-
// code whose require() calls participate in Turbopack's module graph
|
|
168
|
-
// (same package resolved two ways = two React instances). The
|
|
169
|
-
// styled-jsx case is now handled via wrangler `alias` entries instead —
|
|
170
|
-
// see collectSsrLazyRequireAliases() + the wranglerConfig assembly.
|
|
171
|
-
let entries;
|
|
172
|
-
try {
|
|
173
|
-
entries = await fs.readdir(serverChunksDir);
|
|
174
|
-
}
|
|
175
|
-
catch {
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
const chunkFiles = [];
|
|
179
|
-
for (const entry of entries) {
|
|
180
|
-
if (!entry.startsWith("[externals]") || !entry.endsWith(".js"))
|
|
181
|
-
continue;
|
|
182
|
-
chunkFiles.push(path.join(serverChunksDir, entry));
|
|
183
|
-
}
|
|
184
|
-
if (chunkFiles.length === 0)
|
|
185
|
-
return;
|
|
186
|
-
const externalRequirePattern = /(\w+\.x\(\s*(["'])([^"']+)\2\s*,\s*\(\)\s*=>\s*)require\(\s*(["'])\3\4\s*\)(\s*\))/g;
|
|
187
|
-
const stripTurbopackPackageAlias = (specifier) => {
|
|
188
|
-
const parts = specifier.split("/");
|
|
189
|
-
const packageIndex = specifier.startsWith("@") ? 1 : 0;
|
|
190
|
-
const packageName = parts[packageIndex];
|
|
191
|
-
if (!packageName)
|
|
192
|
-
return null;
|
|
193
|
-
const match = /^(.*)-[0-9a-f]{16}$/i.exec(packageName);
|
|
194
|
-
if (!match || !match[1])
|
|
195
|
-
return null;
|
|
196
|
-
parts[packageIndex] = match[1];
|
|
197
|
-
return parts.join("/");
|
|
198
|
-
};
|
|
199
|
-
for (const filePath of chunkFiles) {
|
|
200
|
-
let code;
|
|
201
|
-
try {
|
|
202
|
-
code = await fs.readFile(filePath, "utf-8");
|
|
203
|
-
}
|
|
204
|
-
catch {
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (!code.includes(".x(") || !code.includes("require("))
|
|
208
|
-
continue;
|
|
209
|
-
const patched = code.replace(externalRequirePattern, (match, prefix, _quote, specifier, _requireQuote, suffix) => {
|
|
210
|
-
if (specifier.startsWith(".") ||
|
|
211
|
-
specifier.startsWith("/") ||
|
|
212
|
-
builtins.has(specifier) ||
|
|
213
|
-
specifier === "next" ||
|
|
214
|
-
specifier.startsWith("next/") ||
|
|
215
|
-
specifier.startsWith("@next/")) {
|
|
216
|
-
return match;
|
|
217
|
-
}
|
|
218
|
-
let resolved;
|
|
219
|
-
try {
|
|
220
|
-
resolved = projectRequire.resolve(specifier);
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
const unaliased = stripTurbopackPackageAlias(specifier);
|
|
224
|
-
if (!unaliased)
|
|
225
|
-
return match;
|
|
226
|
-
try {
|
|
227
|
-
resolved = projectRequire.resolve(unaliased);
|
|
228
|
-
}
|
|
229
|
-
catch {
|
|
230
|
-
return match;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (resolved.endsWith(".node"))
|
|
234
|
-
return match;
|
|
235
|
-
return `${prefix}require(${JSON.stringify(resolved)})${suffix}`;
|
|
236
|
-
});
|
|
237
|
-
if (patched !== code) {
|
|
238
|
-
await fs.writeFile(filePath, patched);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Collect `.x("pkg", () => require("pkg"))` lazy-require specifiers out of
|
|
244
|
-
* `server/chunks/ssr/*.js` and return a map of specifiers → absolute paths
|
|
245
|
-
* that wrangler's esbuild should alias.
|
|
246
|
-
*
|
|
247
|
-
* Why this exists: Turbopack emits those registrations for SSR chunks when
|
|
248
|
-
* a transitive (indirect) dep like `styled-jsx/style.js` is needed. In
|
|
249
|
-
* pnpm projects the package lives at `.pnpm/styled-jsx@X/node_modules/...`
|
|
250
|
-
* and isn't hoisted to a top-level `node_modules/`. esbuild's walker
|
|
251
|
-
* therefore fails with `Could not resolve "styled-jsx/style.js"`.
|
|
252
|
-
*
|
|
253
|
-
* The previous attempt was to rewrite `require("pkg")` inline to an
|
|
254
|
-
* absolute path, mirroring what we do for `[externals]*.js` chunks — but
|
|
255
|
-
* `ssr/*.js` chunks also contain real SSR render code whose require()
|
|
256
|
-
* calls participate in Turbopack's module graph. Absolute-path rewrites
|
|
257
|
-
* there risk creating two module instances of the same package (once via
|
|
258
|
-
* Turbopack, once via our rewrite), which silently breaks React etc.
|
|
259
|
-
* and surfaced as "uncached or runtime data" prerender failures in
|
|
260
|
-
* `resume-data-cache` on Linux CI only.
|
|
261
|
-
*
|
|
262
|
-
* Returning an alias map instead keeps all source untouched. esbuild's
|
|
263
|
-
* alias is a global resolver override, so every reference to the same
|
|
264
|
-
* specifier goes through the same absolute path — module identity stays
|
|
265
|
-
* intact.
|
|
266
|
-
*/
|
|
267
|
-
async function collectSsrLazyRequireAliases(distDir) {
|
|
268
|
-
const ssrDir = path.join(distDir, "server", "chunks", "ssr");
|
|
269
|
-
const projectRoot = path.dirname(distDir);
|
|
270
|
-
const projectRequire = createRequire(path.join(projectRoot, "package.json"));
|
|
271
|
-
const builtins = new Set([
|
|
272
|
-
...builtinModules,
|
|
273
|
-
...builtinModules.map((name) => `node:${name}`),
|
|
274
|
-
]);
|
|
275
|
-
let entries;
|
|
276
|
-
try {
|
|
277
|
-
entries = await fs.readdir(ssrDir);
|
|
278
|
-
}
|
|
279
|
-
catch {
|
|
280
|
-
return {};
|
|
281
|
-
}
|
|
282
|
-
// Same regex as the [externals] patcher: `.x("X", () => require("X"))`
|
|
283
|
-
// where the two strings are identical.
|
|
284
|
-
const lazyRequirePattern = /\w+\.x\(\s*(["'])([^"']+)\1\s*,\s*\(\)\s*=>\s*require\(\s*(["'])\2\3\s*\)\s*\)/g;
|
|
285
|
-
const seen = new Set();
|
|
286
|
-
const aliasMap = {};
|
|
287
|
-
for (const entry of entries) {
|
|
288
|
-
if (!entry.endsWith(".js"))
|
|
289
|
-
continue;
|
|
290
|
-
const filePath = path.join(ssrDir, entry);
|
|
291
|
-
let code;
|
|
292
|
-
try {
|
|
293
|
-
code = await fs.readFile(filePath, "utf-8");
|
|
294
|
-
}
|
|
295
|
-
catch {
|
|
296
|
-
continue;
|
|
297
|
-
}
|
|
298
|
-
if (!code.includes(".x(") || !code.includes("require("))
|
|
299
|
-
continue;
|
|
300
|
-
let m;
|
|
301
|
-
while ((m = lazyRequirePattern.exec(code)) !== null) {
|
|
302
|
-
const specifier = m[2];
|
|
303
|
-
if (!specifier || seen.has(specifier))
|
|
304
|
-
continue;
|
|
305
|
-
seen.add(specifier);
|
|
306
|
-
if (specifier.startsWith(".") ||
|
|
307
|
-
specifier.startsWith("/") ||
|
|
308
|
-
builtins.has(specifier) ||
|
|
309
|
-
specifier === "next" ||
|
|
310
|
-
specifier.startsWith("next/") ||
|
|
311
|
-
specifier.startsWith("@next/")) {
|
|
312
|
-
continue;
|
|
313
|
-
}
|
|
314
|
-
let resolved = null;
|
|
315
|
-
try {
|
|
316
|
-
resolved = projectRequire.resolve(specifier);
|
|
317
|
-
}
|
|
318
|
-
catch {
|
|
319
|
-
// no-op — specifier will fail esbuild too, nothing we can fix
|
|
320
|
-
continue;
|
|
321
|
-
}
|
|
322
|
-
if (!resolved || resolved.endsWith(".node"))
|
|
323
|
-
continue;
|
|
324
|
-
aliasMap[specifier] = resolved;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
return aliasMap;
|
|
328
|
-
}
|
|
329
|
-
async function patchAppPageManifestSingletons(distDir) {
|
|
330
|
-
const ssrDir = path.join(distDir, "server", "chunks", "ssr");
|
|
331
|
-
let entries = [];
|
|
332
|
-
try {
|
|
333
|
-
entries = await fs.readdir(ssrDir);
|
|
334
|
-
}
|
|
335
|
-
catch {
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
const manifestProxyPattern = /case"moduleLoading":case"entryCSSFiles":case"entryJSFiles":\{if\(!(\w+)\)throw[\s\S]*?let (\w+)=(\w+)\.get\(\1\.route\);if\(!\2\)throw[\s\S]*?return \2\[(\w+)\]\}/g;
|
|
339
|
-
for (const entry of entries) {
|
|
340
|
-
if (!entry.endsWith(".js"))
|
|
341
|
-
continue;
|
|
342
|
-
const filePath = path.join(ssrDir, entry);
|
|
343
|
-
let code;
|
|
344
|
-
try {
|
|
345
|
-
code = await fs.readFile(filePath, "utf-8");
|
|
346
|
-
}
|
|
347
|
-
catch {
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
if (!code.includes('entryCSSFiles') || !code.includes('without a work store')) {
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
const patched = code.replace(manifestProxyPattern, (_match, workStoreVar, manifestVar, manifestsVar, propVar) => `case"moduleLoading":case"entryCSSFiles":case"entryJSFiles":{if(!${workStoreVar}){for(let a of ${manifestsVar}.values()){let b=a[${propVar}];if(void 0!==b)return b}return}let ${manifestVar}=${manifestsVar}.get(${workStoreVar}.route);if(!${manifestVar}){for(let a of ${manifestsVar}.values()){let b=a[${propVar}];if(void 0!==b)return b}return}return ${manifestVar}[${propVar}]}`);
|
|
354
|
-
if (patched !== code) {
|
|
355
|
-
await fs.writeFile(filePath, patched);
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
function patchBundledManifestSingleton(workerCode) {
|
|
360
|
-
const bundledManifestProxyPattern = /case "moduleLoading":\s*case "entryCSSFiles":\s*case "entryJSFiles": \{\s*if \(!(\w+)\) throw[\s\S]*?let (\w+) = (\w+)\.get\(\1\.route\);\s*if \(!\2\) throw[\s\S]*?return \2\[(\w+)\];\s*\}/g;
|
|
361
|
-
const bundledManifestLookupPattern = /if \((\w+)\) \{\s*let (\w+) = (\w+)\.get\(\1\.route\);\s*if \(null == \2 \? void 0 : \2\[(\w+)\]\[(\w+)\]\) return \2\[\4\]\[\5\];\s*\} else for \(let (\w+) of \3\.values\(\)\) \{\s*let (\w+) = \6\[\4\]\[\5\];\s*if \(void 0 !== \7\) return \7;\s*\}/g;
|
|
362
|
-
workerCode = workerCode.replace(bundledManifestProxyPattern, (_match, workStoreVar, manifestVar, manifestsVar, propVar) => `case "moduleLoading":
|
|
363
|
-
case "entryCSSFiles":
|
|
364
|
-
case "entryJSFiles": {
|
|
365
|
-
if (!${workStoreVar}) {
|
|
366
|
-
for (const manifest of ${manifestsVar}.values()) {
|
|
367
|
-
const entry = manifest[${propVar}];
|
|
368
|
-
if (entry !== undefined) {
|
|
369
|
-
return entry;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
return undefined;
|
|
373
|
-
}
|
|
374
|
-
let ${manifestVar} = ${manifestsVar}.get(${workStoreVar}.route);
|
|
375
|
-
if (!${manifestVar}) {
|
|
376
|
-
for (const manifest of ${manifestsVar}.values()) {
|
|
377
|
-
const entry = manifest[${propVar}];
|
|
378
|
-
if (entry !== undefined) {
|
|
379
|
-
return entry;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
return undefined;
|
|
383
|
-
}
|
|
384
|
-
return ${manifestVar}[${propVar}];
|
|
385
|
-
}`);
|
|
386
|
-
workerCode = workerCode.replace(bundledManifestLookupPattern, (_match, workStoreVar, manifestVar, manifestsVar, propVar, idVar, iterManifestVar, iterEntryVar) => `if (${workStoreVar}) {
|
|
387
|
-
let ${manifestVar} = ${manifestsVar}.get(${workStoreVar}.route);
|
|
388
|
-
let ${iterEntryVar} = null == ${manifestVar} ? void 0 : ${manifestVar}[${propVar}][${idVar}];
|
|
389
|
-
if (void 0 === ${iterEntryVar} && ${propVar} === "edgeSSRModuleMapping") ${iterEntryVar} = null == ${manifestVar} ? void 0 : ${manifestVar}.ssrModuleMapping[${idVar}];
|
|
390
|
-
if (void 0 === ${iterEntryVar} && ${propVar} === "edgeRscModuleMapping") ${iterEntryVar} = null == ${manifestVar} ? void 0 : ${manifestVar}.rscModuleMapping[${idVar}];
|
|
391
|
-
if (typeof process !== "undefined" && process.env.CREEK_DEBUG_MANIFESTS === "1" && (${idVar} === "99807" || ${idVar} === 99807 || String(${workStoreVar}.route || "").includes("basic-edge"))) {
|
|
392
|
-
console.error("[creek:bundled-manifest:route]", JSON.stringify({
|
|
393
|
-
route: ${workStoreVar}.route,
|
|
394
|
-
prop: ${propVar},
|
|
395
|
-
id: ${idVar},
|
|
396
|
-
routeHit: !!${manifestVar},
|
|
397
|
-
entryId: ${iterEntryVar} && typeof ${iterEntryVar} === "object" ? ${iterEntryVar}.id ?? (typeof ${iterEntryVar}["*"] === "object" ? ${iterEntryVar}["*"].id : undefined) : undefined,
|
|
398
|
-
}));
|
|
399
|
-
}
|
|
400
|
-
if (void 0 !== ${iterEntryVar}) return ${iterEntryVar};
|
|
401
|
-
}
|
|
402
|
-
let __creekNodeFallback;
|
|
403
|
-
for (let ${iterManifestVar} of ${manifestsVar}.values()) {
|
|
404
|
-
let ${iterEntryVar} = ${iterManifestVar}[${propVar}][${idVar}];
|
|
405
|
-
if (typeof process !== "undefined" && process.env.CREEK_DEBUG_MANIFESTS === "1" && (${idVar} === "99807" || ${idVar} === 99807) && void 0 !== ${iterEntryVar}) {
|
|
406
|
-
console.error("[creek:bundled-manifest:scan-hit]", JSON.stringify({
|
|
407
|
-
prop: ${propVar},
|
|
408
|
-
id: ${idVar},
|
|
409
|
-
entryId: ${iterEntryVar} && typeof ${iterEntryVar} === "object" ? ${iterEntryVar}.id ?? (typeof ${iterEntryVar}["*"] === "object" ? ${iterEntryVar}["*"].id : undefined) : undefined,
|
|
410
|
-
}));
|
|
411
|
-
}
|
|
412
|
-
if (void 0 !== ${iterEntryVar}) return ${iterEntryVar};
|
|
413
|
-
if (void 0 === __creekNodeFallback && ${propVar} === "edgeSSRModuleMapping") __creekNodeFallback = ${iterManifestVar}.ssrModuleMapping[${idVar}];
|
|
414
|
-
if (void 0 === __creekNodeFallback && ${propVar} === "edgeRscModuleMapping") __creekNodeFallback = ${iterManifestVar}.rscModuleMapping[${idVar}];
|
|
415
|
-
}
|
|
416
|
-
if (typeof process !== "undefined" && process.env.CREEK_DEBUG_MANIFESTS === "1" && (${idVar} === "99807" || ${idVar} === 99807) && void 0 !== __creekNodeFallback) {
|
|
417
|
-
console.error("[creek:bundled-manifest:node-fallback]", JSON.stringify({
|
|
418
|
-
prop: ${propVar},
|
|
419
|
-
id: ${idVar},
|
|
420
|
-
entryId: __creekNodeFallback && typeof __creekNodeFallback === "object" ? __creekNodeFallback.id ?? (typeof __creekNodeFallback["*"] === "object" ? __creekNodeFallback["*"].id : undefined) : undefined,
|
|
421
|
-
}));
|
|
422
|
-
}
|
|
423
|
-
if (void 0 !== __creekNodeFallback) return __creekNodeFallback;`);
|
|
424
|
-
// Cloudflare Workers executes the bundled app through the edge runtime path,
|
|
425
|
-
// but many app pages only populate the node/RSC module maps. Keep true edge
|
|
426
|
-
// routes on the edge maps when they exist, otherwise fall back to the node
|
|
427
|
-
// maps so React Server Consumer Manifest lookups can still resolve.
|
|
428
|
-
workerCode = workerCode.replace(/moduleMap: j2, serverModuleMap:/g, "moduleMap: Object.keys(i2 || {}).length ? i2 : j2, serverModuleMap:");
|
|
429
|
-
return workerCode;
|
|
430
|
-
}
|
|
431
|
-
function stripAsyncLocalStorageSnapshotTernaries(workerCode) {
|
|
432
|
-
const snapshotCall = ".snapshot()";
|
|
433
|
-
let out = "";
|
|
434
|
-
let last = 0;
|
|
435
|
-
let searchFrom = 0;
|
|
436
|
-
const isIdent = (ch) => /[A-Za-z0-9_$]/.test(ch);
|
|
437
|
-
const skipWsBack = (i) => {
|
|
438
|
-
while (i > 0 && /\s/.test(workerCode[i - 1]))
|
|
439
|
-
i--;
|
|
440
|
-
return i;
|
|
441
|
-
};
|
|
442
|
-
const skipWsForward = (i) => {
|
|
443
|
-
while (i < workerCode.length && /\s/.test(workerCode[i]))
|
|
444
|
-
i++;
|
|
445
|
-
return i;
|
|
446
|
-
};
|
|
447
|
-
while (true) {
|
|
448
|
-
const snapshotIndex = workerCode.indexOf(snapshotCall, searchFrom);
|
|
449
|
-
if (snapshotIndex === -1)
|
|
450
|
-
break;
|
|
451
|
-
let rightNameStart = snapshotIndex;
|
|
452
|
-
while (rightNameStart > 0 && isIdent(workerCode[rightNameStart - 1])) {
|
|
453
|
-
rightNameStart--;
|
|
454
|
-
}
|
|
455
|
-
const name = workerCode.slice(rightNameStart, snapshotIndex);
|
|
456
|
-
if (!name) {
|
|
457
|
-
searchFrom = snapshotIndex + snapshotCall.length;
|
|
458
|
-
continue;
|
|
459
|
-
}
|
|
460
|
-
let qIndex = skipWsBack(rightNameStart);
|
|
461
|
-
if (workerCode[qIndex - 1] !== "?") {
|
|
462
|
-
searchFrom = snapshotIndex + snapshotCall.length;
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
|
-
qIndex--;
|
|
466
|
-
let leftNameEnd = skipWsBack(qIndex);
|
|
467
|
-
let leftNameStart = leftNameEnd;
|
|
468
|
-
while (leftNameStart > 0 && isIdent(workerCode[leftNameStart - 1])) {
|
|
469
|
-
leftNameStart--;
|
|
470
|
-
}
|
|
471
|
-
if (workerCode.slice(leftNameStart, leftNameEnd) !== name) {
|
|
472
|
-
searchFrom = snapshotIndex + snapshotCall.length;
|
|
473
|
-
continue;
|
|
474
|
-
}
|
|
475
|
-
let colonIndex = skipWsForward(snapshotIndex + snapshotCall.length);
|
|
476
|
-
if (workerCode[colonIndex] !== ":") {
|
|
477
|
-
searchFrom = snapshotIndex + snapshotCall.length;
|
|
478
|
-
continue;
|
|
479
|
-
}
|
|
480
|
-
colonIndex = skipWsForward(colonIndex + 1);
|
|
481
|
-
out += workerCode.slice(last, leftNameStart);
|
|
482
|
-
last = colonIndex;
|
|
483
|
-
searchFrom = colonIndex;
|
|
484
|
-
}
|
|
485
|
-
return out ? out + workerCode.slice(last) : workerCode;
|
|
486
|
-
}
|
|
487
|
-
function patchUseCachePrerenderDanglingPromiseBailout(workerCode) {
|
|
488
|
-
const serializedKeyPattern = /let\s+(\w+)\s*=\s*"string"\s*==\s*typeof\s+(\w+)\s*\?\s*\2\s*:\s*await\s+(\w+)\(\2\),\s*(\w+)\s*=\s*(\w+)\.rootParams/g;
|
|
489
|
-
return workerCode.replace(serializedKeyPattern, (match, serializedKeyVar, encodedKeyVar, encodeFnVar, rootParamsVar, workUnitStoreVar, offset, fullCode) => {
|
|
490
|
-
const followingCode = fullCode.slice(offset, offset + 3000);
|
|
491
|
-
const hangingCallPattern = new RegExp(String.raw `\(0,\s*(\w+)\.makeHangingPromise\)\(\s*${workUnitStoreVar}\.renderSignal\s*,\s*(\w+)\.route`);
|
|
492
|
-
const hangingCallMatch = hangingCallPattern.exec(followingCode);
|
|
493
|
-
if (!hangingCallMatch)
|
|
494
|
-
return match;
|
|
495
|
-
const makeHangingPromiseVar = hangingCallMatch[1];
|
|
496
|
-
const workStoreVar = hangingCallMatch[2];
|
|
497
|
-
return `let ${serializedKeyVar} = "string" == typeof ${encodedKeyVar} ? ${encodedKeyVar} : await ${encodeFnVar}(${encodedKeyVar}); if ("prerender" === ${workUnitStoreVar}.type && "string" == typeof ${serializedKeyVar}) { let __creekDanglingThenableStart = ${serializedKeyVar}.lastIndexOf("$@"); if (__creekDanglingThenableStart !== -1 && ${serializedKeyVar}.indexOf(":", __creekDanglingThenableStart) === -1) return (0, ${makeHangingPromiseVar}.makeHangingPromise)(${workUnitStoreVar}.renderSignal, ${workStoreVar}.route, 'dynamic "use cache"'); } let ${rootParamsVar} = ${workUnitStoreVar}.rootParams`;
|
|
498
|
-
});
|
|
499
|
-
}
|
|
500
|
-
function patchNullFallbackPartialShellBlocking(workerCode) {
|
|
501
|
-
const minifiedPartialFallbackUpgradePattern = /true\s*!==\s*(\w+)\.experimental\.partialFallbacks\s*\|\|\s*\(null\s*==\s*(\w+)\s*\?\s*void\s*0\s*:\s*\2\.fallback\)\s*!==\s*null\s*\|\|\s*(\w+)\s*\|\|\s*(\w+)\s*\|\|\s*!\((\w+)\.length\s*>\s*0\)\s*\|\|\s*\((\w+)\s*=\s*(\w+)\.FallbackMode\.PRERENDER\)/g;
|
|
502
|
-
return workerCode.replace(minifiedPartialFallbackUpgradePattern, (_match, nextConfigVar, prerenderInfoVar, omittedFallbackParamVar, unresolvedRootParamsVar, remainingParamsVar, fallbackModeVar, fallbackEnumVar) => `true !== ${nextConfigVar}.experimental.partialFallbacks || (null == ${prerenderInfoVar} ? void 0 : ${prerenderInfoVar}.fallback) !== null || ${omittedFallbackParamVar} || ${unresolvedRootParamsVar} || !(${remainingParamsVar}.length > 0) || (${fallbackModeVar} = ${fallbackEnumVar}.FallbackMode.BLOCKING_STATIC_RENDER)`);
|
|
503
|
-
}
|
|
504
139
|
export async function bundleForWorkers(opts) {
|
|
505
140
|
// Patch Turbopack runtime BEFORE wrangler bundles.
|
|
506
141
|
// Turbopack's R.c() dynamically loads chunks from the filesystem.
|
|
507
142
|
// CF Workers has no filesystem, so we replace R.c() with a switch
|
|
508
143
|
// statement that maps chunk paths to static require() calls.
|
|
509
144
|
await patchTurbopackRuntime(opts.distDir);
|
|
510
|
-
await patchNodeExternalRequireConditions(opts.distDir);
|
|
511
|
-
await patchAppPageManifestSingletons(opts.distDir);
|
|
512
|
-
const ssrLazyRequireAliases = await collectSsrLazyRequireAliases(opts.distDir);
|
|
513
145
|
// Write the generated worker entry
|
|
514
146
|
const entryPath = path.join(opts.outputDir, "__entry.mjs");
|
|
515
147
|
await fs.writeFile(entryPath, opts.workerSource);
|
|
516
148
|
if (process.env.CREEK_DEBUG) {
|
|
517
149
|
await fs.writeFile(path.join(opts.outputDir, "__entry_debug.mjs"), opts.workerSource);
|
|
518
150
|
}
|
|
519
|
-
// Copy WASM files alongside the bundle BEFORE wrangler runs. wrangler's
|
|
520
|
-
// bundler needs the files resolvable from the entry `import __wasm_0 from
|
|
521
|
-
// "./wasm_<hex>.wasm"` to apply the CompiledWasm rule — copying later
|
|
522
|
-
// (after the bundle step) is too late.
|
|
523
|
-
for (const [name, absPath] of opts.wasmFiles) {
|
|
524
|
-
const destName = name.endsWith(".wasm") ? name : name + ".wasm";
|
|
525
|
-
const destPath = path.join(opts.outputDir, destName);
|
|
526
|
-
await fs.copyFile(absPath, destPath);
|
|
527
|
-
}
|
|
528
151
|
// Resolve adapter paths
|
|
529
152
|
const adapterDir = path.dirname(path.dirname(new URL(import.meta.url).pathname));
|
|
530
153
|
// Generate wrangler config for the bundle step
|
|
@@ -541,12 +164,7 @@ export async function bundleForWorkers(opts) {
|
|
|
541
164
|
},
|
|
542
165
|
// Mark optional/unavailable deps as external to prevent build errors.
|
|
543
166
|
// These are caught at runtime and handled gracefully.
|
|
544
|
-
//
|
|
545
|
-
// `ssrLazyRequireAliases` is spread FIRST so static entries below take
|
|
546
|
-
// precedence if they collide — our shims (fs, vm, critters, etc.)
|
|
547
|
-
// always win over a happenstance SSR lazy-require of the same name.
|
|
548
167
|
alias: {
|
|
549
|
-
...ssrLazyRequireAliases,
|
|
550
168
|
"@opentelemetry/api": path.join(adapterDir, "src", "shims", "opentelemetry.js"),
|
|
551
169
|
// fs shim — intercept both bare and node: prefixed imports.
|
|
552
170
|
// Turbopack runtime uses require("fs") which wrangler must redirect
|
|
@@ -557,48 +175,37 @@ export async function bundleForWorkers(opts) {
|
|
|
557
175
|
"node:vm": path.join(adapterDir, "src", "shims", "vm.js"),
|
|
558
176
|
// critters is bundled by Next.js for CSS inlining — not needed on Workers.
|
|
559
177
|
"critters": path.join(adapterDir, "src", "shims", "critters.js"),
|
|
560
|
-
// sharp has native .node bindings that workerd can't load.
|
|
561
|
-
// alias, wrangler pulls in ~1MB of sharp's JS wrapper and the module
|
|
562
|
-
// ends up non-callable at runtime — \`@vercel/og\`'s node path then
|
|
563
|
-
// throws \`sharp is not a function\`. Aliasing to a shim whose default
|
|
564
|
-
// is undefined makes \`@vercel/og\` fall back to its resvg.wasm path.
|
|
178
|
+
// sharp has native .node bindings that workerd can't load.
|
|
565
179
|
"sharp": path.join(adapterDir, "src", "shims", "sharp.js"),
|
|
566
|
-
//
|
|
567
|
-
// `if (Math.random() < 0) import("fail")` in their ESM entry. Turbopack
|
|
568
|
-
// externalizes the package and never resolves that branch, but wrangler
|
|
569
|
-
// follows it once we preload the concrete ESM file. Stub it so the dead
|
|
570
|
-
// branch remains harmless.
|
|
180
|
+
// Dead-branch dynamic imports.
|
|
571
181
|
"fail": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
572
|
-
//
|
|
573
|
-
// per-request AsyncLocalStorage version. The original keeps a
|
|
574
|
-
// module-level CacheSignal whose internal setImmediate closure
|
|
575
|
-
// leaks IoContext across requests on workerd — second-and-later
|
|
576
|
-
// requests throw "Cannot perform I/O on behalf of a different
|
|
577
|
-
// request" when CacheSignal.pendingTimeoutCleanup fires
|
|
578
|
-
// clearImmediate on an Immediate from the first request. Repros
|
|
579
|
-
// on any route that does dynamic imports during render (notably
|
|
580
|
-
// \`new ImageResponse(...)\` — every \`@vercel/og\` call triggers
|
|
581
|
-
// trackPendingImport). We alias \`.external\` as well because
|
|
582
|
-
// call sites import from that module (the internal relative
|
|
583
|
-
// \`./track-module-loading.instance\` import never passes through
|
|
584
|
-
// esbuild's bare-specifier alias map). See
|
|
585
|
-
// src/shims/track-module-loading.js.
|
|
182
|
+
// track-module-loading: per-request AsyncLocalStorage to avoid IoContext leak.
|
|
586
183
|
"next/dist/server/app-render/module-loading/track-module-loading.external": path.join(adapterDir, "src", "shims", "track-module-loading.js"),
|
|
587
184
|
"next/dist/server/app-render/module-loading/track-module-loading.external.js": path.join(adapterDir, "src", "shims", "track-module-loading.js"),
|
|
588
185
|
"next/dist/server/app-render/module-loading/track-module-loading.instance": path.join(adapterDir, "src", "shims", "track-module-loading.js"),
|
|
589
186
|
"next/dist/server/app-render/module-loading/track-module-loading.instance.js": path.join(adapterDir, "src", "shims", "track-module-loading.js"),
|
|
590
|
-
//
|
|
591
|
-
// ESM namespace is frozen in Workers. More importantly, workerd can run
|
|
592
|
-
// the next setTimeout(0) stage before a setImmediate scheduled by the
|
|
593
|
-
// previous cache-components stage. The shim keeps native setImmediate
|
|
594
|
-
// except during Next's explicit fast-immediate capture window.
|
|
187
|
+
// fast-set-immediate: workerd's frozen ESM namespace + scheduling order.
|
|
595
188
|
"next/dist/server/node-environment-extensions/fast-set-immediate.external": path.join(adapterDir, "src", "shims", "fast-set-immediate.js"),
|
|
596
189
|
"next/dist/server/node-environment-extensions/fast-set-immediate.external.js": path.join(adapterDir, "src", "shims", "fast-set-immediate.js"),
|
|
597
|
-
//
|
|
598
|
-
//
|
|
599
|
-
//
|
|
600
|
-
|
|
601
|
-
|
|
190
|
+
// CF Workers does NOT provide node:http / node:https even with
|
|
191
|
+
// nodejs_compat. TCP-server-shaped APIs have no Workers equivalent.
|
|
192
|
+
// Our shim provides IncomingMessage + ServerResponse stubs.
|
|
193
|
+
"http": path.join(adapterDir, "src", "shims", "http.js"),
|
|
194
|
+
"node:http": path.join(adapterDir, "src", "shims", "http.js"),
|
|
195
|
+
"https": path.join(adapterDir, "src", "shims", "http.js"),
|
|
196
|
+
"node:https": path.join(adapterDir, "src", "shims", "http.js"),
|
|
197
|
+
// net: Socket class needed by Next.js http bridge.
|
|
198
|
+
"net": path.join(adapterDir, "src", "shims", "net.js"),
|
|
199
|
+
"node:net": path.join(adapterDir, "src", "shims", "net.js"),
|
|
200
|
+
// Modules with no meaningful Workers runtime — empty stubs.
|
|
201
|
+
"inspector": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
202
|
+
"node:inspector": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
203
|
+
"tls": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
204
|
+
"node:tls": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
205
|
+
"dns": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
206
|
+
"node:dns": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
207
|
+
"child_process": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
208
|
+
"node:child_process": path.join(adapterDir, "src", "shims", "empty.js"),
|
|
602
209
|
},
|
|
603
210
|
};
|
|
604
211
|
const configPath = path.join(opts.outputDir, "__wrangler.json");
|
|
@@ -606,39 +213,18 @@ export async function bundleForWorkers(opts) {
|
|
|
606
213
|
// Bundle with wrangler --dry-run
|
|
607
214
|
// Wrangler internally uses esbuild but with Turbopack-aware resolution
|
|
608
215
|
// and proper CJS/ESM interop for CF Workers.
|
|
609
|
-
// Ensure @next/routing is resolvable from the project directory.
|
|
610
|
-
// dependency of the adapter, not the user's project
|
|
611
|
-
//
|
|
612
|
-
// rather than guessing \`adapterDir/node_modules/@next/routing\` — pnpm's
|
|
613
|
-
// virtual-store layout means that path often doesn't exist (the real
|
|
614
|
-
// install lives under \`node_modules/.pnpm/@next+routing@X/\`), and the
|
|
615
|
-
// guess only works for the link-protocol install of the adapter.
|
|
216
|
+
// Ensure @next/routing is resolvable from the project directory.
|
|
217
|
+
// It's a dependency of the adapter, not the user's project.
|
|
218
|
+
// Symlink it into the project's node_modules if missing.
|
|
616
219
|
const projectNodeModules = path.join(path.dirname(opts.distDir), "node_modules");
|
|
617
220
|
const routingDest = path.join(projectNodeModules, "@next", "routing");
|
|
618
|
-
const
|
|
619
|
-
let routingSrc;
|
|
620
|
-
try {
|
|
621
|
-
routingSrc = path.dirname(adapterRequire.resolve("@next/routing/package.json"));
|
|
622
|
-
}
|
|
623
|
-
catch {
|
|
624
|
-
// Fallback to the legacy guess so a classic link-install still works.
|
|
625
|
-
routingSrc = path.join(adapterDir, "node_modules", "@next", "routing");
|
|
626
|
-
}
|
|
221
|
+
const routingSrc = path.join(adapterDir, "node_modules", "@next", "routing");
|
|
627
222
|
try {
|
|
628
223
|
await fs.access(routingDest);
|
|
629
224
|
}
|
|
630
225
|
catch {
|
|
631
226
|
await fs.mkdir(path.join(projectNodeModules, "@next"), { recursive: true });
|
|
632
|
-
|
|
633
|
-
await fs.symlink(routingSrc, routingDest, "junction");
|
|
634
|
-
}
|
|
635
|
-
catch (err) {
|
|
636
|
-
// Racy repeat runs (same project dir rebuilt back-to-back) can leave a
|
|
637
|
-
// dangling symlink that \`access\` reports as missing while \`symlink\`
|
|
638
|
-
// still refuses to overwrite.
|
|
639
|
-
if (err?.code !== "EEXIST")
|
|
640
|
-
throw err;
|
|
641
|
-
}
|
|
227
|
+
await fs.symlink(routingSrc, routingDest, "junction");
|
|
642
228
|
}
|
|
643
229
|
const bundleDir = path.join(opts.outputDir, "__bundle");
|
|
644
230
|
// Resolve wrangler binary from the adapter's own node_modules
|
|
@@ -694,200 +280,13 @@ export async function bundleForWorkers(opts) {
|
|
|
694
280
|
// "Dynamic require of ... is not supported" without those codes.
|
|
695
281
|
// Patch the catch to also handle "is not supported" errors.
|
|
696
282
|
workerCode = workerCode.replace(/err\.code !== "ENOENT" && err\.code !== "MODULE_NOT_FOUND" && err\.code !== "ERR_MODULE_NOT_FOUND"/g, 'err.code !== "ENOENT" && err.code !== "MODULE_NOT_FOUND" && err.code !== "ERR_MODULE_NOT_FOUND" && !err.message?.includes("is not supported")');
|
|
697
|
-
// Some libraries hide builtin requires from the bundler, e.g.
|
|
698
|
-
// @typescript/vfs constructs `require("path")` via String.fromCharCode.
|
|
699
|
-
// Wrangler can't statically rewrite that, so esbuild's __require helper
|
|
700
|
-
// throws at runtime even though the node:path wrapper is already present
|
|
701
|
-
// elsewhere in the bundle. Route those builtin dynamic requires back to
|
|
702
|
-
// wrangler's generated require_path() module when available.
|
|
703
|
-
workerCode = workerCode.replace(/throw Error\('Dynamic require of "' \+ x \+ '" is not supported'\);/, 'if ((x === "path" || x === "node:path") && typeof require_path === "function") return require_path();\n throw Error(\'Dynamic require of "\' + x + \'" is not supported\');');
|
|
704
|
-
workerCode = workerCode.replace(/(throw Error\('Dynamic require of "' \+ x \+ '" is not supported'\);\n\}\);)/, '$1\n__require.resolve = (id) => id === "typescript" ? "node_modules/typescript/lib/typescript.js" : id;');
|
|
705
|
-
// `next/dist/server/node-environment.js` imports
|
|
706
|
-
// `./node-environment-extensions/fast-set-immediate.external` by relative
|
|
707
|
-
// path, so Wrangler's bare-specifier alias cannot intercept it. If the
|
|
708
|
-
// original module installs first, our scoped shim stores Next's patched
|
|
709
|
-
// timer functions as its "native" originals and the workerd ordering fix
|
|
710
|
-
// becomes re-entrant. Route that side-effect import to the aliased shim
|
|
711
|
-
// module that Wrangler emitted for Turbopack's external import.
|
|
712
|
-
workerCode = workerCode.replace(/\brequire_fast_set_immediate_external\(\);/g, 'typeof init_fast_set_immediate === "function" ? init_fast_set_immediate() : require_fast_set_immediate_external();');
|
|
713
|
-
// Next.js's \`getInstrumentationModule\` does
|
|
714
|
-
// \`await __require(path.join(projectDir, distDir, "server",
|
|
715
|
-
// \`\${INSTRUMENTATION_HOOK_FILENAME}.js\`))\`. workerd can't resolve
|
|
716
|
-
// dynamic-require paths — the call rejects, the catch above swallows it,
|
|
717
|
-
// and \`instrumentation.register()\` is never invoked. Worker-entry
|
|
718
|
-
// static-imports the user file onto \`globalThis.__CREEK_INSTRUMENTATION\`
|
|
719
|
-
// when present, so prefer that over \`__require\` here. Falls through
|
|
720
|
-
// to the original call when no user instrumentation is registered so
|
|
721
|
-
// the \`module.exports = {}\` placeholder path still works.
|
|
722
|
-
workerCode = workerCode.replace(/(cachedInstrumentationModule\s*=\s*\(0,\s*_interopdefault\.interopDefault\)\s*\()\s*await __require\(/g, "$1globalThis.__CREEK_INSTRUMENTATION || await __require(");
|
|
723
|
-
workerCode = patchBundledManifestSingleton(workerCode);
|
|
724
|
-
// depd (via raw-body) uses `eval("(function ("+args+") {...})")` to build a
|
|
725
|
-
// deprecation-wrapping thunk. workerd + CF Workers block code generation
|
|
726
|
-
// from strings ("Code generation from strings disallowed for this context"),
|
|
727
|
-
// so any module that pulls in raw-body — notably the Pages Router API
|
|
728
|
-
// body parser — throws at module load time, and the outer try/catch in
|
|
729
|
-
// parse-body.ts surfaces it to the browser as `400 Invalid body`.
|
|
730
|
-
//
|
|
731
|
-
// Replace the minified eval expression with a direct function literal
|
|
732
|
-
// that preserves the runtime semantics (log + call). We lose the
|
|
733
|
-
// `function.length` preservation the eval form achieved, but nothing
|
|
734
|
-
// in the raw-body → parse-body call chain reads it.
|
|
735
|
-
//
|
|
736
|
-
// Fixes middleware-redirects "should redirect to api route with locale"
|
|
737
|
-
// (and the /fr variant) — both fail because their navigation ends at
|
|
738
|
-
// an API route whose parse-body path can't load raw-body.
|
|
739
|
-
workerCode = workerCode.replace(/var\s+(\w+)\s*=\s*eval\("\(function \(".*?return\s+\1\s*;?\s*\}/s, "return function(){log.call(deprecate,message,site);return fn.apply(this,arguments)}}");
|
|
740
|
-
// Route \`externalImport(id)\` through \`globalThis.__CREEK_EXT_MODS\` so
|
|
741
|
-
// we can serve bundled-but-externalized-by-Turbopack modules from
|
|
742
|
-
// our worker-entry static imports. Turbopack emits chunks like
|
|
743
|
-
// \`[externals]_next_dist_compiled_@vercel_og_index_node_...\` that do
|
|
744
|
-
// \`await e.y("next/dist/compiled/@vercel/og/index.node.js")\`; on
|
|
745
|
-
// workerd that \`await import(id)\` path throws "No such module" and
|
|
746
|
-
// the handler 500s. When our entry registers the module in
|
|
747
|
-
// \`__CREEK_EXT_MODS\`, the patched \`externalImport\` returns it
|
|
748
|
-
// directly without going through workerd's external loader.
|
|
749
|
-
// Fixes og-api \`/og-node\` (node runtime) +
|
|
750
|
-
// use-cache-metadata-route-handler opengraph/icon tests.
|
|
751
|
-
workerCode = workerCode.replace(/async function externalImport\((\w+)\)\s*\{\s*let\s+raw;\s*try\s*\{\s*raw\s*=\s*await import\(\1\);/g, (match, idVar) => `async function externalImport(${idVar}) {\n` +
|
|
752
|
-
` let raw;\n` +
|
|
753
|
-
` { const __loaders = globalThis.__CREEK_EXT_LOADERS; if (__loaders && __loaders[${idVar}]) {\n` +
|
|
754
|
-
` const __cached = globalThis.__CREEK_EXT_MODS = globalThis.__CREEK_EXT_MODS || {};\n` +
|
|
755
|
-
` if (${idVar} in __cached) { raw = __cached[${idVar}]; }\n` +
|
|
756
|
-
` else { try { raw = await __loaders[${idVar}](); __cached[${idVar}] = raw; } catch (err) { throw new Error(\`Failed to load external module \${${idVar}}: \${err}\`); } }\n` +
|
|
757
|
-
` if (raw && raw.__esModule && raw.default && "default" in raw.default) { return interopEsm(raw.default, createNS(raw), true); }\n` +
|
|
758
|
-
` return raw;\n` +
|
|
759
|
-
` } }\n` +
|
|
760
|
-
` try {\n` +
|
|
761
|
-
` raw = await import(${idVar});`);
|
|
762
|
-
// \`@vercel/og/index.node.js\` evaluates at module load:
|
|
763
|
-
//
|
|
764
|
-
// var fontData = fs.readFileSync(fileURLToPath(new URL("./Geist-Regular.ttf", import.meta.url)));
|
|
765
|
-
// var resvg_wasm = fs.readFileSync(fileURLToPath(new URL("./resvg.wasm", import.meta.url)));
|
|
766
|
-
//
|
|
767
|
-
// workerd rejects \`new URL("./X", import.meta.url)\` with
|
|
768
|
-
// "Invalid URL string" in the bundled-worker context, so evaluation
|
|
769
|
-
// aborts before any request hits the route. Rewrite these two calls
|
|
770
|
-
// to pass literal paths into fs.readFileSync directly — our fs shim
|
|
771
|
-
// has a basename fallback for .wasm/.ttf, so the embedded bundled
|
|
772
|
-
// bytes resolve regardless of path.
|
|
773
|
-
workerCode = workerCode.replace(/fileURLToPath\(new URL\(("\.\/[^"]+\.(?:wasm|ttf|otf|woff2?|png|jpg|jpeg|gif|webp|svg|ico)")\s*,\s*import\.meta\.url\)\)/g, (_match, filename) => filename.replace(/^"\.\//, '"'));
|
|
774
|
-
// Strip `AsyncLocalStorage.snapshot()` bindings in Next's
|
|
775
|
-
// `server/app-render/async-local-storage.js` and its Turbopack-inlined
|
|
776
|
-
// variants. On workerd, `ALS.snapshot()` captures the CURRENT IoContext
|
|
777
|
-
// and invoking the returned function from a later request throws
|
|
778
|
-
// "Cannot perform I/O on behalf of a different request". Next uses
|
|
779
|
-
// `createSnapshot()` / `runInCleanSnapshot: ALS.snapshot()` for cache-
|
|
780
|
-
// invalidation + edge-action callback propagation — both paths cross
|
|
781
|
-
// request boundaries on workerd. Replace the snapshot binding with a
|
|
782
|
-
// direct passthrough, matching @opennextjs/cloudflare's `patchUseCacheIO`
|
|
783
|
-
// (their comment: "TODO: Find a better fix for this issue.").
|
|
784
|
-
//
|
|
785
|
-
// Four forms exist post-bundling:
|
|
786
|
-
// 1. `function createSnapshot() { if (X) return X.snapshot(); return function(fn,...args){...}; }`
|
|
787
|
-
// — clean esm form from next/dist/esm (one copy embedded by wrangler).
|
|
788
|
-
// 2. `function <name>() { return X ? X.snapshot() : function(a,...b){ return a(...b); }; }`
|
|
789
|
-
// — Turbopack's minified module-level shim (edge-side copies).
|
|
790
|
-
// 3. `a.s(["bindSnapshot", 0, q, "createSnapshot", 0, function(){ return p ? p.snapshot() : ...; }])`
|
|
791
|
-
// — Turbopack's export-binding form in the same shim.
|
|
792
|
-
// 4. `runInCleanSnapshot: X ? X.snapshot() : function(a,...b){ return a(...b); }`
|
|
793
|
-
// — inlined call-site in the bundled work-store constructor.
|
|
794
|
-
// A single regex `(\w+) ? \1.snapshot() : ` stripped from the minified
|
|
795
|
-
// forms covers 2/3/4; form 1 uses an if-guard so handle it separately.
|
|
796
|
-
workerCode = workerCode.replace(/if\s*\(\s*(\w+)\s*\)\s*\{\s*return\s+\1\.snapshot\(\);\s*\}/g, "// Ignored snapshot");
|
|
797
|
-
workerCode = stripAsyncLocalStorageSnapshotTernaries(workerCode);
|
|
798
|
-
// `use cache` arguments that contain an unresolved React thenable marker
|
|
799
|
-
// (`$@`) during a prerender are request-bound values. Next's dynamic
|
|
800
|
-
// access instrumentation usually catches this before cache fill, but an
|
|
801
|
-
// async params transform can hide the access until after serialization.
|
|
802
|
-
// Node eventually trips the 50s cache-fill timeout; under workerd that
|
|
803
|
-
// blocks the whole response. Treat the cache function as a dynamic hole
|
|
804
|
-
// immediately so the PPR fallback shell can resume.
|
|
805
|
-
workerCode = patchUseCachePrerenderDanglingPromiseBailout(workerCode);
|
|
806
|
-
// Null-fallback partial routes don't have a concrete fallback artifact
|
|
807
|
-
// on disk; under workerd the follow-up path that tries to specialize a
|
|
808
|
-
// generic shell on the first request can surface as a static-generation
|
|
809
|
-
// bailout instead of completing the request. Keep these routes on the
|
|
810
|
-
// blocking path and let the concrete request render/cache directly.
|
|
811
|
-
workerCode = patchNullFallbackPartialShellBlocking(workerCode);
|
|
812
|
-
// Unify Next's `*AsyncStorageInstance` singletons across Turbopack
|
|
813
|
-
// chunks via a `globalThis` key. Turbopack emits the `work-unit-async-
|
|
814
|
-
// storage-instance.js` / `work-async-storage-instance.js` / etc.
|
|
815
|
-
// factories INLINE into every chunk that references them (see the
|
|
816
|
-
// `'turbopack-transition': 'next-shared'` import attribute in the
|
|
817
|
-
// next-shared layer); each chunk evaluates its own
|
|
818
|
-
// `createAsyncLocalStorage()` call and gets a fresh ALS instance.
|
|
819
|
-
// On the edge-runtime server-action path this fragments the store:
|
|
820
|
-
// the action handler runs `workUnitAsyncStorage.run(d, fn)` inside
|
|
821
|
-
// chunk A's ALS, the inner `headers()` call reads from chunk B's
|
|
822
|
-
// ALS, finds no store, and throws "headers was called outside a
|
|
823
|
-
// request scope". Node action path survives because its caller +
|
|
824
|
-
// callee happen to resolve to the same chunk's copy.
|
|
825
|
-
//
|
|
826
|
-
// Fix: rewrite the module factory bodies to key the ALS on the
|
|
827
|
-
// exported instance name (`workUnitAsyncStorageInstance`, etc.)
|
|
828
|
-
// under a single `globalThis.__CREEK_ALS` bag, so all chunks share
|
|
829
|
-
// one instance per logical store. Name-keyed is narrower than
|
|
830
|
-
// "dedup every `new AsyncLocalStorage()`" — we don't accidentally
|
|
831
|
-
// merge unrelated per-store ALSes (tracing requestStorage,
|
|
832
|
-
// react-server-dom temporaryReferences, etc.).
|
|
833
|
-
//
|
|
834
|
-
// Target pattern (both `a.i(N)` and `a.r(N)` create-variants
|
|
835
|
-
// exist):
|
|
836
|
-
// let <v> = (0, a.i(43291).createAsyncLocalStorage)();
|
|
837
|
-
// …short gap…
|
|
838
|
-
// a.s(["<Name>AsyncStorageInstance", 0, <v>], …)
|
|
839
|
-
// Replacement keeps the original create call so the per-isolate
|
|
840
|
-
// fallback works if globalThis isn't carried through an unusual
|
|
841
|
-
// loader; the `??=` idempotently promotes the first copy into the
|
|
842
|
-
// shared bag.
|
|
843
|
-
workerCode = workerCode.replace(/let\s+(\w+)\s*=\s*(\(0,\s*a\.[ir]\(\d+\)\.createAsyncLocalStorage\)\(\));([\s\S]{0,400}?a\.s\(\["(\w+AsyncStorageInstance)",\s*0,\s*\1\])/g, (_match, varName, createCall, tail, storeName) => `let ${varName} = ((globalThis.__CREEK_ALS ??= {})["${storeName}"] ??= ${createCall});${tail}`);
|
|
844
|
-
// Same dedup for the CJS-wrapped variants esbuild produces when it
|
|
845
|
-
// bundles Next's non-Turbopack ESM copy. Two observed shapes:
|
|
846
|
-
// a. `var <Name>AsyncStorageInstance = (0, _als.createAsyncLocalStorage)();`
|
|
847
|
-
// — top-level from `work-unit-async-storage-instance.js` etc.
|
|
848
|
-
// b. `Object.defineProperty(c, "<Name>AsyncStorageInstance", {…get…return <v>…}); let <v> = (0, a.r(N).createAsyncLocalStorage)();`
|
|
849
|
-
// — Turbopack-emitted CJS compiled form (edge chunks).
|
|
850
|
-
workerCode = workerCode.replace(/var\s+(\w+AsyncStorageInstance)\s*=\s*(\(0,\s*\w+\.createAsyncLocalStorage\)\(\));/g, (_match, storeName, createCall) => `var ${storeName} = ((globalThis.__CREEK_ALS ??= {})["${storeName}"] ??= ${createCall});`);
|
|
851
|
-
workerCode = workerCode.replace(/(Object\.defineProperty\(c,\s*"(\w+AsyncStorageInstance)",\s*\{[^}]*get:\s*(?:\/\*[^*]*\*\/\s*)?(?:__name\()?function\(\)\s*\{\s*return (\w+);[^}]*\}[\s\S]*?\}\);)\s*let\s+\3\s*=\s*(\(0,\s*a\.[ir]\(\d+\)\.createAsyncLocalStorage\)\(\))/g, (_match, prefix, storeName, varName, createCall) => `${prefix}\nlet ${varName} = ((globalThis.__CREEK_ALS ??= {})["${storeName}"] ??= ${createCall})`);
|
|
852
|
-
// Turbopack's node.js runtime implements top-level-await by assigning
|
|
853
|
-
// \`module.exports = <Promise>\`. esbuild's \`__toESM\` wraps imports with
|
|
854
|
-
// \`__create(__getProtoOf(mod))\`, and for a Promise that yields a plain
|
|
855
|
-
// object whose \`__proto__\` is \`Promise.prototype\`. Awaiting that object
|
|
856
|
-
// invokes \`Promise.prototype.then\` with a non-Promise receiver — workerd
|
|
857
|
-
// rejects it as "incompatible receiver" and the route 500s. Detect a
|
|
858
|
-
// Promise input and return it unchanged so await resolves the real
|
|
859
|
-
// async-module promise instead. Fixes \`metadata-edge\` and
|
|
860
|
-
// \`metadata-dynamic-routes-async-deps\` \`opengraph-image\` routes.
|
|
861
|
-
workerCode = workerCode.replace(/var __toESM = \(mod, isNodeMode, target\) => \(target = mod != null \? __create\(__getProtoOf\(mod\)\) : \{\}, __copyProps\(\s*(?:\/\/[^\n]*\n\s*)*isNodeMode \|\| !mod \|\| !mod\.__esModule \? __defProp\(target, "default", \{ value: mod, enumerable: true \}\) : target,\s*mod\s*\)\);/, `var __toESM = (mod, isNodeMode, target) => {
|
|
862
|
-
if (mod != null && typeof mod === "object" && typeof mod.then === "function" && __getProtoOf(mod) === Promise.prototype) {
|
|
863
|
-
return mod;
|
|
864
|
-
}
|
|
865
|
-
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
866
|
-
return __copyProps(
|
|
867
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
868
|
-
mod
|
|
869
|
-
);
|
|
870
|
-
};`);
|
|
871
283
|
await fs.writeFile(workerPath, workerCode);
|
|
872
284
|
}
|
|
873
285
|
catch { }
|
|
874
|
-
//
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
//
|
|
879
|
-
// The file lives alongside worker.js in \`.creek/adapter-output/server/\`.
|
|
880
|
-
// Users can override specific values by creating a \`wrangler.toml\`
|
|
881
|
-
// at their project root — the adapter reads that at build time and
|
|
882
|
-
// merges over our defaults (support added in a follow-up TODO).
|
|
883
|
-
await emitWranglerConfig({
|
|
884
|
-
outputDir: opts.outputDir,
|
|
885
|
-
assetsRelPath: "../assets", // server/ → assets/
|
|
886
|
-
// Same normalization as the copy loop above — ensure every wasm
|
|
887
|
-
// filename declared in the rules ends with \`.wasm\` so wrangler's
|
|
888
|
-
// CompiledWasm rule discovers the file we actually wrote to disk.
|
|
889
|
-
wasmFilenames: [...opts.wasmFiles.keys()].map((n) => n.endsWith(".wasm") ? n : n + ".wasm"),
|
|
890
|
-
});
|
|
286
|
+
// Copy WASM files alongside the bundle
|
|
287
|
+
for (const [name, absPath] of opts.wasmFiles) {
|
|
288
|
+
await fs.copyFile(absPath, path.join(opts.outputDir, name));
|
|
289
|
+
}
|
|
891
290
|
// Clean up temp files
|
|
892
291
|
await fs.rm(entryPath, { force: true });
|
|
893
292
|
await fs.rm(configPath, { force: true });
|
|
@@ -896,96 +295,4 @@ export async function bundleForWorkers(opts) {
|
|
|
896
295
|
const files = await fs.readdir(opts.outputDir);
|
|
897
296
|
return files.filter(f => !f.startsWith("__"));
|
|
898
297
|
}
|
|
899
|
-
/**
|
|
900
|
-
* Write a wrangler.toml next to worker.js so the same config drives both
|
|
901
|
-
* \`wrangler dev\` (local dev / test harness) and \`wrangler deploy\` (prod).
|
|
902
|
-
*
|
|
903
|
-
* Contents:
|
|
904
|
-
* - \`name\`, \`main\`, \`compatibility_date\`, \`compatibility_flags\` — runtime
|
|
905
|
-
* - \`[assets]\` — binds the assets directory with \`run_worker_first\` so our
|
|
906
|
-
* worker handles routing before workerd's static asset shortcut
|
|
907
|
-
* - \`[[durable_objects.bindings]]\` + \`[[migrations]]\` — the three DO
|
|
908
|
-
* classes the worker entry declares (DOQueueHandler, DOShardedTagCache,
|
|
909
|
-
* BucketCachePurge)
|
|
910
|
-
* - \`[[rules]] type = "CompiledWasm"\` — declares wasm siblings (next/og
|
|
911
|
-
* yoga/resvg, sharp, etc.) as module imports for workerd's loader
|
|
912
|
-
*/
|
|
913
|
-
async function emitWranglerConfig(opts) {
|
|
914
|
-
const { outputDir, assetsRelPath, wasmFilenames } = opts;
|
|
915
|
-
// Turbopack + wrangler both emit wasm siblings (hashed \`wasm_<xxh3>\`
|
|
916
|
-
// from us, \`<sha1>-yoga.wasm\` / \`<sha1>-resvg.wasm\` from wrangler's
|
|
917
|
-
// own esbuild pass when the app imports next/og). A wildcard glob
|
|
918
|
-
// covers every sibling regardless of who emitted it. Listing specific
|
|
919
|
-
// filenames fails because wrangler normalises import paths with a
|
|
920
|
-
// leading \`./\` that literal-name globs don't match — the built-in
|
|
921
|
-
// \`**/*.wasm\` rule then fires and aborts the build with
|
|
922
|
-
// "ignored because a previous rule ... was not marked as fallthrough".
|
|
923
|
-
let hasWasm = wasmFilenames.length > 0;
|
|
924
|
-
if (!hasWasm) {
|
|
925
|
-
try {
|
|
926
|
-
const entries = await fs.readdir(outputDir);
|
|
927
|
-
hasWasm = entries.some((e) => e.endsWith(".wasm"));
|
|
928
|
-
}
|
|
929
|
-
catch { }
|
|
930
|
-
}
|
|
931
|
-
const wasmRule = hasWasm
|
|
932
|
-
? [
|
|
933
|
-
"",
|
|
934
|
-
"# Declare every wasm sibling as a CompiledWasm module so \`import",
|
|
935
|
-
"# foo from \"./<hash>.wasm\"\` in the bundle resolves at runtime.",
|
|
936
|
-
"# Without this, Turbopack's runtime registry returns undefined and",
|
|
937
|
-
"# throws \"dynamically loading WebAssembly is not supported\".",
|
|
938
|
-
"[[rules]]",
|
|
939
|
-
`globs = ["**/*.wasm"]`,
|
|
940
|
-
`type = "CompiledWasm"`,
|
|
941
|
-
`fallthrough = false`,
|
|
942
|
-
].join("\n")
|
|
943
|
-
: "";
|
|
944
|
-
const toml = `# Generated by @solcreek/adapter-creek. Hand edits will be overwritten on
|
|
945
|
-
# the next \`next build\`. To extend (add KV, queues, env vars, etc.), create
|
|
946
|
-
# a \`wrangler.toml\` at your project root — the adapter merges it on top.
|
|
947
|
-
|
|
948
|
-
name = "creek"
|
|
949
|
-
main = "worker.js"
|
|
950
|
-
compatibility_date = "2026-03-23"
|
|
951
|
-
compatibility_flags = ["nodejs_compat"]
|
|
952
|
-
|
|
953
|
-
# Static assets from Next.js's \`.next/static\` + \`public/\` live alongside
|
|
954
|
-
# this server dir under \`../assets/\`. \`run_worker_first = true\` tells
|
|
955
|
-
# workerd to invoke our worker BEFORE serving static files — the adapter
|
|
956
|
-
# handles middleware, routing, and cache headers before any asset shortcut.
|
|
957
|
-
[assets]
|
|
958
|
-
directory = "${assetsRelPath}"
|
|
959
|
-
binding = "ASSETS"
|
|
960
|
-
run_worker_first = true
|
|
961
|
-
|
|
962
|
-
# Durable Object classes declared by the adapter's worker-entry. The
|
|
963
|
-
# binding \`name\` (what runtime code reads via \`env.<NAME>\`) must match
|
|
964
|
-
# what Creek's control plane injects in production — see
|
|
965
|
-
# \`packages/control-plane/src/modules/deployments/deploy.ts:129-140\` in
|
|
966
|
-
# the Creek repo. Do NOT rename these without coordinating with Creek.
|
|
967
|
-
#
|
|
968
|
-
# \`class_name\` is our internal export name from worker-entry.ts.
|
|
969
|
-
# \`new_sqlite_classes\` (not \`new_classes\`) selects SQLite-backed DO
|
|
970
|
-
# storage — DOShardedTagCache and BucketCachePurge both use
|
|
971
|
-
# \`ctx.storage.sql\`. DOQueueHandler is currently a placeholder but
|
|
972
|
-
# declared under SQLite too so future queue persistence has no migration
|
|
973
|
-
# cost.
|
|
974
|
-
[[durable_objects.bindings]]
|
|
975
|
-
name = "NEXT_CACHE_DO_QUEUE"
|
|
976
|
-
class_name = "DOQueueHandler"
|
|
977
|
-
[[durable_objects.bindings]]
|
|
978
|
-
name = "NEXT_TAG_CACHE_DO_SHARDED"
|
|
979
|
-
class_name = "DOShardedTagCache"
|
|
980
|
-
[[durable_objects.bindings]]
|
|
981
|
-
name = "NEXT_CACHE_DO_BUCKET_PURGE"
|
|
982
|
-
class_name = "BucketCachePurge"
|
|
983
|
-
|
|
984
|
-
[[migrations]]
|
|
985
|
-
tag = "v1"
|
|
986
|
-
new_sqlite_classes = ["DOQueueHandler", "DOShardedTagCache", "BucketCachePurge"]
|
|
987
|
-
${wasmRule}
|
|
988
|
-
`;
|
|
989
|
-
await fs.writeFile(path.join(outputDir, "wrangler.toml"), toml);
|
|
990
|
-
}
|
|
991
298
|
//# sourceMappingURL=bundler.js.map
|
package/dist/worker-entry.js
CHANGED
|
@@ -333,8 +333,17 @@ if (typeof process !== "undefined") {
|
|
|
333
333
|
}, { bigint: () => BigInt(Math.floor(performance.now() * 1e6)) });
|
|
334
334
|
}
|
|
335
335
|
if (!process.on) process.on = () => process;
|
|
336
|
-
if (!process.removeListener) process.removeListener = () => process;
|
|
337
336
|
if (!process.off) process.off = () => process;
|
|
337
|
+
if (!process.removeListener) process.removeListener = () => process;
|
|
338
|
+
if (!process.removeAllListeners) process.removeAllListeners = () => process;
|
|
339
|
+
if (!process.listeners) process.listeners = () => [];
|
|
340
|
+
if (!process.listenerCount) process.listenerCount = () => 0;
|
|
341
|
+
if (!process.rawListeners) process.rawListeners = () => [];
|
|
342
|
+
if (!process.emit) process.emit = () => false;
|
|
343
|
+
if (!process.prependListener) process.prependListener = () => process;
|
|
344
|
+
if (!process.prependOnceListener) process.prependOnceListener = () => process;
|
|
345
|
+
if (!process.once) process.once = () => process;
|
|
346
|
+
if (!process.addListener) process.addListener = () => process;
|
|
338
347
|
}
|
|
339
348
|
|
|
340
349
|
import { resolveRoutes, responseToMiddlewareResult } from "@next/routing";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solcreek/adapter-creek",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Next.js deployment adapter for Creek (Cloudflare Workers)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@next/routing": "16.2.3",
|
|
48
|
-
"@node-rs/xxhash": "^1.7.6",
|
|
49
48
|
"sql.js": "^1.14.1",
|
|
50
49
|
"wrangler": "^4.82.2"
|
|
51
50
|
},
|
|
@@ -53,6 +52,7 @@
|
|
|
53
52
|
"next": ">=16.2.3"
|
|
54
53
|
},
|
|
55
54
|
"devDependencies": {
|
|
55
|
+
"@node-rs/xxhash": "^1.7.6",
|
|
56
56
|
"@types/node": "^22.13.10",
|
|
57
57
|
"miniflare": "^4.20260410.0",
|
|
58
58
|
"next": "16.2.3",
|
package/src/shims/net.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Minimal node:net shim for CF Workers.
|
|
2
|
+
// Only Socket is needed — Next.js passes it to the http shim's
|
|
3
|
+
// IncomingMessage as `socket` but never actually connects.
|
|
4
|
+
|
|
5
|
+
import { EventEmitter } from "node:events";
|
|
6
|
+
|
|
7
|
+
export class Socket extends EventEmitter {
|
|
8
|
+
constructor() {
|
|
9
|
+
super();
|
|
10
|
+
this.writable = true;
|
|
11
|
+
this.readable = true;
|
|
12
|
+
this.encrypted = false;
|
|
13
|
+
this.remoteAddress = "127.0.0.1";
|
|
14
|
+
this.remotePort = 0;
|
|
15
|
+
this.localAddress = "127.0.0.1";
|
|
16
|
+
this.localPort = 0;
|
|
17
|
+
}
|
|
18
|
+
address() { return { address: "127.0.0.1", family: "IPv4", port: 443 }; }
|
|
19
|
+
connect() { return this; }
|
|
20
|
+
write() { return true; }
|
|
21
|
+
end() { this.emit("close"); return this; }
|
|
22
|
+
destroy() { this.emit("close"); return this; }
|
|
23
|
+
setTimeout() { return this; }
|
|
24
|
+
setNoDelay() { return this; }
|
|
25
|
+
setKeepAlive() { return this; }
|
|
26
|
+
ref() { return this; }
|
|
27
|
+
unref() { return this; }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class Server extends EventEmitter {
|
|
31
|
+
constructor() { super(); }
|
|
32
|
+
listen() { return this; }
|
|
33
|
+
close() { return this; }
|
|
34
|
+
address() { return null; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function createServer() { return new Server(); }
|
|
38
|
+
export function createConnection() { return new Socket(); }
|
|
39
|
+
export function connect() { return new Socket(); }
|
|
40
|
+
export function isIP() { return 0; }
|
|
41
|
+
export function isIPv4() { return false; }
|
|
42
|
+
export function isIPv6() { return false; }
|
|
43
|
+
|
|
44
|
+
export default { Socket, Server, createServer, createConnection, connect, isIP, isIPv4, isIPv6 };
|