wrangler 2.0.23 → 2.0.26
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 +20 -2
- package/bin/wrangler.js +1 -1
- package/miniflare-dist/index.mjs +235 -47
- package/package.json +11 -6
- package/src/__tests__/configuration.test.ts +89 -17
- package/src/__tests__/dev.test.tsx +29 -4
- package/src/__tests__/generate.test.ts +93 -0
- package/src/__tests__/helpers/mock-cfetch.ts +87 -2
- package/src/__tests__/index.test.ts +10 -27
- package/src/__tests__/init.test.ts +537 -359
- package/src/__tests__/jest.setup.ts +34 -1
- package/src/__tests__/kv.test.ts +2 -2
- package/src/__tests__/metrics.test.ts +5 -0
- package/src/__tests__/pages.test.ts +14 -0
- package/src/__tests__/publish.test.ts +497 -254
- package/src/__tests__/r2.test.ts +173 -71
- package/src/__tests__/tail.test.ts +112 -42
- package/src/__tests__/user.test.ts +1 -0
- package/src/__tests__/validate-dev-props.test.ts +56 -0
- package/src/__tests__/whoami.test.tsx +60 -1
- package/src/api/dev.ts +7 -0
- package/src/bundle.ts +279 -44
- package/src/cfetch/internal.ts +73 -2
- package/src/config/config.ts +8 -3
- package/src/config/environment.ts +40 -8
- package/src/config/index.ts +13 -0
- package/src/config/validation.ts +102 -8
- package/src/create-worker-upload-form.ts +25 -0
- package/src/dev/dev.tsx +121 -28
- package/src/dev/local.tsx +88 -14
- package/src/dev/remote.tsx +39 -8
- package/src/dev/use-esbuild.ts +28 -0
- package/src/dev/validate-dev-props.ts +31 -0
- package/src/dev-registry.tsx +160 -0
- package/src/dev.tsx +107 -80
- package/src/generate.ts +112 -14
- package/src/index.tsx +212 -4
- package/src/init.ts +111 -38
- package/src/inspect.ts +90 -5
- package/src/metrics/index.ts +1 -0
- package/src/metrics/metrics-dispatcher.ts +1 -0
- package/src/metrics/metrics-usage-headers.ts +24 -0
- package/src/metrics/send-event.ts +2 -2
- package/src/miniflare-cli/assets.ts +27 -16
- package/src/miniflare-cli/index.ts +124 -2
- package/src/module-collection.ts +3 -3
- package/src/pages/build.tsx +75 -41
- package/src/pages/constants.ts +5 -0
- package/src/pages/deployments.tsx +10 -10
- package/src/pages/dev.tsx +177 -52
- package/src/pages/errors.ts +22 -0
- package/src/pages/functions/buildPlugin.ts +4 -0
- package/src/pages/functions/buildWorker.ts +4 -0
- package/src/pages/functions/routes-consolidation.test.ts +250 -0
- package/src/pages/functions/routes-consolidation.ts +73 -0
- package/src/pages/functions/routes-transformation.test.ts +271 -0
- package/src/pages/functions/routes-transformation.ts +122 -0
- package/src/pages/functions.tsx +96 -0
- package/src/pages/index.tsx +65 -55
- package/src/pages/projects.tsx +9 -3
- package/src/pages/publish.tsx +76 -23
- package/src/pages/types.ts +9 -0
- package/src/pages/upload.tsx +38 -21
- package/src/publish.ts +126 -112
- package/src/r2.ts +81 -0
- package/src/tail/filters.ts +3 -1
- package/src/tail/index.ts +15 -2
- package/src/tail/printing.ts +43 -3
- package/src/user/user.tsx +20 -2
- package/src/whoami.tsx +79 -1
- package/src/worker.ts +12 -0
- package/templates/first-party-worker-module-facade.ts +18 -0
- package/templates/format-dev-errors.ts +32 -0
- package/templates/pages-template-plugin.ts +16 -4
- package/templates/pages-template-worker.ts +16 -5
- package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
- package/templates/service-bindings-module-facade.js +54 -0
- package/templates/service-bindings-sw-facade.js +42 -0
- package/wrangler-dist/cli.d.ts +7 -0
- package/wrangler-dist/cli.js +40851 -15332
package/src/init.ts
CHANGED
|
@@ -5,12 +5,16 @@ import TOML from "@iarna/toml";
|
|
|
5
5
|
import { findUp } from "find-up";
|
|
6
6
|
import { version as wranglerVersion } from "../package.json";
|
|
7
7
|
|
|
8
|
+
import { fetchDashboardScript } from "./cfetch/internal";
|
|
9
|
+
import { readConfig } from "./config";
|
|
8
10
|
import { confirm, select } from "./dialogs";
|
|
9
11
|
import { initializeGit, isGitInstalled, isInsideGitRepo } from "./git-client";
|
|
10
12
|
import { logger } from "./logger";
|
|
11
13
|
import { getPackageManager } from "./package-manager";
|
|
12
14
|
import { parsePackageJSON, parseTOML, readFileSync } from "./parse";
|
|
15
|
+
import { requireAuth } from "./user";
|
|
13
16
|
import { CommandLineArgsError, printWranglerBanner } from "./index";
|
|
17
|
+
import type { ConfigPath } from "./index";
|
|
14
18
|
|
|
15
19
|
import type { Argv, ArgumentsCamelCase } from "yargs";
|
|
16
20
|
|
|
@@ -36,6 +40,12 @@ export async function initOptions(yargs: Argv) {
|
|
|
36
40
|
describe: 'Answer "yes" to any prompts for new projects',
|
|
37
41
|
type: "boolean",
|
|
38
42
|
alias: "y",
|
|
43
|
+
})
|
|
44
|
+
.option("from-dash", {
|
|
45
|
+
describe: "Download script from the dashboard for local development",
|
|
46
|
+
type: "string",
|
|
47
|
+
requiresArg: true,
|
|
48
|
+
hidden: true,
|
|
39
49
|
});
|
|
40
50
|
}
|
|
41
51
|
|
|
@@ -61,7 +71,11 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
|
|
|
61
71
|
const devDepsToInstall: string[] = [];
|
|
62
72
|
const instructions: string[] = [];
|
|
63
73
|
let shouldRunPackageManagerInstall = false;
|
|
64
|
-
const
|
|
74
|
+
const fromDashScriptName = args["from-dash"] as string;
|
|
75
|
+
const creationDirectory = path.resolve(
|
|
76
|
+
process.cwd(),
|
|
77
|
+
(args.name ? args.name : fromDashScriptName) ?? ""
|
|
78
|
+
);
|
|
65
79
|
|
|
66
80
|
if (args.site) {
|
|
67
81
|
const gitDirectory =
|
|
@@ -90,6 +104,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
|
|
|
90
104
|
|
|
91
105
|
// TODO: ask which directory to make the worker in (defaults to args.name)
|
|
92
106
|
// TODO: if args.name isn't provided, ask what to name the worker
|
|
107
|
+
// Note: `--from-dash` will be a fallback creationDir/Worker name if none is provided.
|
|
93
108
|
|
|
94
109
|
const wranglerTomlDestination = path.join(
|
|
95
110
|
creationDirectory,
|
|
@@ -98,12 +113,15 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
|
|
|
98
113
|
let justCreatedWranglerToml = false;
|
|
99
114
|
|
|
100
115
|
if (fs.existsSync(wranglerTomlDestination)) {
|
|
116
|
+
let shouldContinue = false;
|
|
101
117
|
logger.warn(
|
|
102
118
|
`${path.relative(process.cwd(), wranglerTomlDestination)} already exists!`
|
|
103
119
|
);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
120
|
+
if (!fromDashScriptName) {
|
|
121
|
+
shouldContinue = await confirm(
|
|
122
|
+
"Do you want to continue initializing this project?"
|
|
123
|
+
);
|
|
124
|
+
}
|
|
107
125
|
if (!shouldContinue) {
|
|
108
126
|
return;
|
|
109
127
|
}
|
|
@@ -438,27 +456,23 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
|
|
|
438
456
|
process.cwd(),
|
|
439
457
|
path.join(creationDirectory, "./src/index.ts")
|
|
440
458
|
);
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
: await getNewWorkerType(newWorkerFilename);
|
|
445
|
-
|
|
446
|
-
if (newWorkerType !== "none") {
|
|
447
|
-
const template = getNewWorkerTemplate("ts", newWorkerType);
|
|
448
|
-
|
|
459
|
+
if (fromDashScriptName) {
|
|
460
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
461
|
+
const accountId = await requireAuth(config);
|
|
449
462
|
await mkdir(path.join(creationDirectory, "./src"), {
|
|
450
463
|
recursive: true,
|
|
451
464
|
});
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
465
|
+
|
|
466
|
+
const dashScript = await fetchDashboardScript(
|
|
467
|
+
`/accounts/${accountId}/workers/scripts/${fromDashScriptName}`,
|
|
468
|
+
{
|
|
469
|
+
method: "GET",
|
|
470
|
+
}
|
|
455
471
|
);
|
|
456
472
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
path.join(creationDirectory, "./src/index.ts")
|
|
461
|
-
)}`
|
|
473
|
+
await writeFile(
|
|
474
|
+
path.join(creationDirectory, "./src/index.ts"),
|
|
475
|
+
dashScript
|
|
462
476
|
);
|
|
463
477
|
|
|
464
478
|
await writePackageJsonScriptsAndUpdateWranglerToml(
|
|
@@ -466,8 +480,39 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
|
|
|
466
480
|
justCreatedWranglerToml,
|
|
467
481
|
pathToPackageJson,
|
|
468
482
|
"src/index.ts",
|
|
469
|
-
|
|
483
|
+
{}
|
|
470
484
|
);
|
|
485
|
+
} else {
|
|
486
|
+
const newWorkerType = yesFlag
|
|
487
|
+
? "fetch"
|
|
488
|
+
: await getNewWorkerType(newWorkerFilename);
|
|
489
|
+
|
|
490
|
+
if (newWorkerType !== "none") {
|
|
491
|
+
const template = getNewWorkerTemplate("ts", newWorkerType);
|
|
492
|
+
|
|
493
|
+
await mkdir(path.join(creationDirectory, "./src"), {
|
|
494
|
+
recursive: true,
|
|
495
|
+
});
|
|
496
|
+
await writeFile(
|
|
497
|
+
path.join(creationDirectory, "./src/index.ts"),
|
|
498
|
+
readFileSync(path.join(__dirname, `../templates/${template}`))
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
logger.log(
|
|
502
|
+
`✨ Created ${path.relative(
|
|
503
|
+
process.cwd(),
|
|
504
|
+
path.join(creationDirectory, "./src/index.ts")
|
|
505
|
+
)}`
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
await writePackageJsonScriptsAndUpdateWranglerToml(
|
|
509
|
+
shouldWritePackageJsonScripts,
|
|
510
|
+
justCreatedWranglerToml,
|
|
511
|
+
pathToPackageJson,
|
|
512
|
+
"src/index.ts",
|
|
513
|
+
getNewWorkerToml(newWorkerType)
|
|
514
|
+
);
|
|
515
|
+
}
|
|
471
516
|
}
|
|
472
517
|
}
|
|
473
518
|
} else {
|
|
@@ -477,35 +522,63 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
|
|
|
477
522
|
path.join(creationDirectory, "./src/index.js")
|
|
478
523
|
);
|
|
479
524
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
if (newWorkerType !== "none") {
|
|
485
|
-
const template = getNewWorkerTemplate("js", newWorkerType);
|
|
486
|
-
|
|
525
|
+
if (fromDashScriptName) {
|
|
526
|
+
const config = readConfig(args.config as ConfigPath, args);
|
|
527
|
+
const accountId = await requireAuth(config);
|
|
487
528
|
await mkdir(path.join(creationDirectory, "./src"), {
|
|
488
529
|
recursive: true,
|
|
489
530
|
});
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
531
|
+
|
|
532
|
+
const dashScript = await fetchDashboardScript(
|
|
533
|
+
`/accounts/${accountId}/workers/scripts/${fromDashScriptName}`,
|
|
534
|
+
{
|
|
535
|
+
method: "GET",
|
|
536
|
+
}
|
|
493
537
|
);
|
|
494
538
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
path.join(creationDirectory, "./src/index.js")
|
|
499
|
-
)}`
|
|
539
|
+
await writeFile(
|
|
540
|
+
path.join(creationDirectory, "./src/index.js"),
|
|
541
|
+
dashScript
|
|
500
542
|
);
|
|
501
543
|
|
|
502
544
|
await writePackageJsonScriptsAndUpdateWranglerToml(
|
|
503
545
|
shouldWritePackageJsonScripts,
|
|
504
546
|
justCreatedWranglerToml,
|
|
505
547
|
pathToPackageJson,
|
|
506
|
-
"src/index.
|
|
507
|
-
|
|
548
|
+
"src/index.ts",
|
|
549
|
+
{}
|
|
508
550
|
);
|
|
551
|
+
} else {
|
|
552
|
+
const newWorkerType = yesFlag
|
|
553
|
+
? "fetch"
|
|
554
|
+
: await getNewWorkerType(newWorkerFilename);
|
|
555
|
+
|
|
556
|
+
if (newWorkerType !== "none") {
|
|
557
|
+
const template = getNewWorkerTemplate("js", newWorkerType);
|
|
558
|
+
|
|
559
|
+
await mkdir(path.join(creationDirectory, "./src"), {
|
|
560
|
+
recursive: true,
|
|
561
|
+
});
|
|
562
|
+
await writeFile(
|
|
563
|
+
path.join(creationDirectory, "./src/index.js"),
|
|
564
|
+
readFileSync(path.join(__dirname, `../templates/${template}`))
|
|
565
|
+
);
|
|
566
|
+
|
|
567
|
+
logger.log(
|
|
568
|
+
`✨ Created ${path.relative(
|
|
569
|
+
process.cwd(),
|
|
570
|
+
path.join(creationDirectory, "./src/index.js")
|
|
571
|
+
)}`
|
|
572
|
+
);
|
|
573
|
+
|
|
574
|
+
await writePackageJsonScriptsAndUpdateWranglerToml(
|
|
575
|
+
shouldWritePackageJsonScripts,
|
|
576
|
+
justCreatedWranglerToml,
|
|
577
|
+
pathToPackageJson,
|
|
578
|
+
"src/index.js",
|
|
579
|
+
getNewWorkerToml(newWorkerType)
|
|
580
|
+
);
|
|
581
|
+
}
|
|
509
582
|
}
|
|
510
583
|
}
|
|
511
584
|
}
|
package/src/inspect.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
1
2
|
import assert from "node:assert";
|
|
2
3
|
import { createServer } from "node:http";
|
|
3
4
|
import os from "node:os";
|
|
@@ -5,6 +6,7 @@ import { URL } from "node:url";
|
|
|
5
6
|
|
|
6
7
|
import open from "open";
|
|
7
8
|
import { useEffect, useRef, useState } from "react";
|
|
9
|
+
import { SourceMapConsumer } from "source-map";
|
|
8
10
|
import WebSocket, { WebSocketServer } from "ws";
|
|
9
11
|
import { version } from "../package.json";
|
|
10
12
|
import { logger } from "./logger";
|
|
@@ -52,6 +54,10 @@ interface InspectorProps {
|
|
|
52
54
|
* logged to the terminal by nature of them actually running in node locally.)
|
|
53
55
|
*/
|
|
54
56
|
logToTerminal: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Sourcemap path, so that stacktraces can be interpretted
|
|
59
|
+
*/
|
|
60
|
+
sourceMapPath?: string | undefined;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
export default function useInspector(props: InspectorProps) {
|
|
@@ -252,15 +258,93 @@ export default function useInspector(props: InspectorProps) {
|
|
|
252
258
|
* without having to open the devtools).
|
|
253
259
|
*/
|
|
254
260
|
if (props.logToTerminal) {
|
|
255
|
-
ws.addEventListener("message", (event: MessageEvent) => {
|
|
261
|
+
ws.addEventListener("message", async (event: MessageEvent) => {
|
|
256
262
|
if (typeof event.data === "string") {
|
|
257
263
|
const evt = JSON.parse(event.data);
|
|
258
264
|
if (evt.method === "Runtime.exceptionThrown") {
|
|
259
265
|
const params = evt.params as Protocol.Runtime.ExceptionThrownEvent;
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
266
|
+
|
|
267
|
+
// Parse stack trace with source map.
|
|
268
|
+
if (props.sourceMapPath) {
|
|
269
|
+
// Parse in the sourcemap
|
|
270
|
+
const mapContent = JSON.parse(
|
|
271
|
+
await readFile(props.sourceMapPath, "utf-8")
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
// Create the lines for the exception details log
|
|
275
|
+
const exceptionLines = [
|
|
276
|
+
params.exceptionDetails.exception?.description?.split("\n")[0],
|
|
277
|
+
];
|
|
278
|
+
|
|
279
|
+
await SourceMapConsumer.with(
|
|
280
|
+
mapContent,
|
|
281
|
+
null,
|
|
282
|
+
async (consumer) => {
|
|
283
|
+
// Pass each of the callframes into the consumer, and format the error
|
|
284
|
+
const stack = params.exceptionDetails.stackTrace?.callFrames;
|
|
285
|
+
|
|
286
|
+
stack?.forEach(
|
|
287
|
+
({ functionName, lineNumber, columnNumber }, i) => {
|
|
288
|
+
try {
|
|
289
|
+
if (lineNumber) {
|
|
290
|
+
// The line and column numbers in the stackTrace are zero indexed,
|
|
291
|
+
// whereas the sourcemap consumer indexes from one.
|
|
292
|
+
const pos = consumer.originalPositionFor({
|
|
293
|
+
line: lineNumber + 1,
|
|
294
|
+
column: columnNumber + 1,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Print out line which caused error:
|
|
298
|
+
if (i === 0 && pos.source && pos.line) {
|
|
299
|
+
const fileSource = consumer.sourceContentFor(
|
|
300
|
+
pos.source
|
|
301
|
+
);
|
|
302
|
+
const fileSourceLine =
|
|
303
|
+
fileSource?.split("\n")[pos.line - 1] || "";
|
|
304
|
+
exceptionLines.push(fileSourceLine.trim());
|
|
305
|
+
|
|
306
|
+
// If we have a column, we can mark the position underneath
|
|
307
|
+
if (pos.column) {
|
|
308
|
+
exceptionLines.push(
|
|
309
|
+
`${" ".repeat(
|
|
310
|
+
pos.column - fileSourceLine.search(/\S/)
|
|
311
|
+
)}^`
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// From the way esbuild implements the "names" field:
|
|
317
|
+
// > To save space, the original name is only recorded when it's different from the final name.
|
|
318
|
+
// however, source-map consumer does not handle this
|
|
319
|
+
if (pos && pos.line != null) {
|
|
320
|
+
const convertedFnName =
|
|
321
|
+
pos.name || functionName || "";
|
|
322
|
+
exceptionLines.push(
|
|
323
|
+
` at ${convertedFnName} (${pos.source}:${pos.line}:${pos.column})`
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
} catch {
|
|
328
|
+
// Line failed to parse through the sourcemap consumer
|
|
329
|
+
// We should handle this better
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// Log the parsed stacktrace
|
|
337
|
+
logger.error(
|
|
338
|
+
params.exceptionDetails.text,
|
|
339
|
+
exceptionLines.join("\n")
|
|
340
|
+
);
|
|
341
|
+
} else {
|
|
342
|
+
// We log the stacktrace to the terminal
|
|
343
|
+
logger.error(
|
|
344
|
+
params.exceptionDetails.text,
|
|
345
|
+
params.exceptionDetails.exception?.description ?? ""
|
|
346
|
+
);
|
|
347
|
+
}
|
|
264
348
|
}
|
|
265
349
|
if (evt.method === "Runtime.consoleAPICalled") {
|
|
266
350
|
logConsoleMessage(
|
|
@@ -333,6 +417,7 @@ export default function useInspector(props: InspectorProps) {
|
|
|
333
417
|
}, [
|
|
334
418
|
props.inspectorUrl,
|
|
335
419
|
props.logToTerminal,
|
|
420
|
+
props.sourceMapPath,
|
|
336
421
|
wsServer,
|
|
337
422
|
// We use a state value as a sigil to trigger a retry of the
|
|
338
423
|
// remote websocket connection. It's not used inside the effect,
|
package/src/metrics/index.ts
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getMetricsConfig } from "./metrics-config";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add an additional header to publish requests if the user has opted into sending usage metrics.
|
|
5
|
+
*
|
|
6
|
+
* This allows us to estimate the number of instances of Wrangler that have opted-in
|
|
7
|
+
* without breaking our agreement not to send stuff if you have not opted-in.
|
|
8
|
+
*/
|
|
9
|
+
export async function getMetricsUsageHeaders(
|
|
10
|
+
sendMetrics: boolean | undefined
|
|
11
|
+
): Promise<Record<string, string> | undefined> {
|
|
12
|
+
const metricsEnabled = (
|
|
13
|
+
await getMetricsConfig({
|
|
14
|
+
sendMetrics,
|
|
15
|
+
})
|
|
16
|
+
).enabled;
|
|
17
|
+
if (metricsEnabled) {
|
|
18
|
+
return {
|
|
19
|
+
metricsEnabled: "true",
|
|
20
|
+
};
|
|
21
|
+
} else {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -47,8 +47,8 @@ export type EventNames =
|
|
|
47
47
|
| "rename worker namespace"
|
|
48
48
|
| "create pages project"
|
|
49
49
|
| "list pages projects"
|
|
50
|
-
| "
|
|
51
|
-
| "list pages
|
|
50
|
+
| "create pages deployment"
|
|
51
|
+
| "list pages deployments"
|
|
52
52
|
| "build pages functions"
|
|
53
53
|
| "run dev"
|
|
54
54
|
| "run pages dev";
|
|
@@ -335,6 +335,11 @@ async function generateAssetsFetch(
|
|
|
335
335
|
|
|
336
336
|
const generateResponse = (request: MiniflareRequest) => {
|
|
337
337
|
const url = new URL(request.url);
|
|
338
|
+
let assetName = url.pathname;
|
|
339
|
+
try {
|
|
340
|
+
//it's possible for someone to send a URL like http://fakehost/abc%2 which would fail to decode
|
|
341
|
+
assetName = decodeURIComponent(url.pathname);
|
|
342
|
+
} catch {}
|
|
338
343
|
|
|
339
344
|
const deconstructedResponse: {
|
|
340
345
|
status: number;
|
|
@@ -377,7 +382,7 @@ async function generateAssetsFetch(
|
|
|
377
382
|
}
|
|
378
383
|
|
|
379
384
|
const notFound = () => {
|
|
380
|
-
let cwd =
|
|
385
|
+
let cwd = assetName;
|
|
381
386
|
while (cwd) {
|
|
382
387
|
cwd = cwd.slice(0, cwd.lastIndexOf("/"));
|
|
383
388
|
|
|
@@ -407,38 +412,36 @@ async function generateAssetsFetch(
|
|
|
407
412
|
|
|
408
413
|
let asset;
|
|
409
414
|
|
|
410
|
-
if (
|
|
411
|
-
if ((asset = getAsset(`${
|
|
415
|
+
if (assetName.endsWith("/")) {
|
|
416
|
+
if ((asset = getAsset(`${assetName}/index.html`))) {
|
|
412
417
|
deconstructedResponse.body = serveAsset(asset);
|
|
413
418
|
deconstructedResponse.headers.set(
|
|
414
419
|
"Content-Type",
|
|
415
420
|
getType(asset) || "application/octet-stream"
|
|
416
421
|
);
|
|
417
422
|
return deconstructedResponse;
|
|
418
|
-
} else if (
|
|
419
|
-
(asset = getAsset(`${url.pathname.replace(/\/$/, ".html")}`))
|
|
420
|
-
) {
|
|
423
|
+
} else if ((asset = getAsset(`${assetName.replace(/\/$/, ".html")}`))) {
|
|
421
424
|
deconstructedResponse.status = 301;
|
|
422
425
|
deconstructedResponse.headers.set(
|
|
423
426
|
"Location",
|
|
424
|
-
`${
|
|
427
|
+
`${assetName.slice(0, -1)}${url.search}`
|
|
425
428
|
);
|
|
426
429
|
return deconstructedResponse;
|
|
427
430
|
}
|
|
428
431
|
}
|
|
429
432
|
|
|
430
|
-
if (
|
|
433
|
+
if (assetName.endsWith("/index")) {
|
|
431
434
|
deconstructedResponse.status = 301;
|
|
432
435
|
deconstructedResponse.headers.set(
|
|
433
436
|
"Location",
|
|
434
|
-
`${
|
|
437
|
+
`${assetName.slice(0, -"index".length)}${url.search}`
|
|
435
438
|
);
|
|
436
439
|
return deconstructedResponse;
|
|
437
440
|
}
|
|
438
441
|
|
|
439
|
-
if ((asset = getAsset(
|
|
440
|
-
if (
|
|
441
|
-
const extensionlessPath =
|
|
442
|
+
if ((asset = getAsset(assetName))) {
|
|
443
|
+
if (assetName.endsWith(".html")) {
|
|
444
|
+
const extensionlessPath = assetName.slice(0, -".html".length);
|
|
442
445
|
if (getAsset(extensionlessPath) || extensionlessPath === "/") {
|
|
443
446
|
deconstructedResponse.body = serveAsset(asset);
|
|
444
447
|
deconstructedResponse.headers.set(
|
|
@@ -462,12 +465,20 @@ async function generateAssetsFetch(
|
|
|
462
465
|
);
|
|
463
466
|
return deconstructedResponse;
|
|
464
467
|
}
|
|
465
|
-
} else if (hasFileExtension(
|
|
468
|
+
} else if (hasFileExtension(assetName)) {
|
|
469
|
+
if ((asset = getAsset(assetName + ".html"))) {
|
|
470
|
+
deconstructedResponse.body = serveAsset(asset);
|
|
471
|
+
deconstructedResponse.headers.set(
|
|
472
|
+
"Content-Type",
|
|
473
|
+
getType(asset) || "application/octet-stream"
|
|
474
|
+
);
|
|
475
|
+
return deconstructedResponse;
|
|
476
|
+
}
|
|
466
477
|
notFound();
|
|
467
478
|
return deconstructedResponse;
|
|
468
479
|
}
|
|
469
480
|
|
|
470
|
-
if ((asset = getAsset(`${
|
|
481
|
+
if ((asset = getAsset(`${assetName}.html`))) {
|
|
471
482
|
deconstructedResponse.body = serveAsset(asset);
|
|
472
483
|
deconstructedResponse.headers.set(
|
|
473
484
|
"Content-Type",
|
|
@@ -476,11 +487,11 @@ async function generateAssetsFetch(
|
|
|
476
487
|
return deconstructedResponse;
|
|
477
488
|
}
|
|
478
489
|
|
|
479
|
-
if ((asset = getAsset(`${
|
|
490
|
+
if ((asset = getAsset(`${assetName}/index.html`))) {
|
|
480
491
|
deconstructedResponse.status = 301;
|
|
481
492
|
deconstructedResponse.headers.set(
|
|
482
493
|
"Location",
|
|
483
|
-
`${
|
|
494
|
+
`${assetName}/${url.search}`
|
|
484
495
|
);
|
|
485
496
|
return deconstructedResponse;
|
|
486
497
|
} else {
|
|
@@ -1,10 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { fetch } from "@miniflare/core";
|
|
2
|
+
import {
|
|
3
|
+
DurableObjectNamespace,
|
|
4
|
+
DurableObjectStub,
|
|
5
|
+
} from "@miniflare/durable-objects";
|
|
6
|
+
import {
|
|
7
|
+
Log,
|
|
8
|
+
LogLevel,
|
|
9
|
+
Miniflare,
|
|
10
|
+
Response as MiniflareResponse,
|
|
11
|
+
Request as MiniflareRequest,
|
|
12
|
+
} from "miniflare";
|
|
2
13
|
import yargs from "yargs";
|
|
3
14
|
import { hideBin } from "yargs/helpers";
|
|
15
|
+
import { FatalError } from "../errors";
|
|
4
16
|
import generateASSETSBinding from "./assets";
|
|
5
17
|
import { enumKeys } from "./enum-keys";
|
|
6
18
|
import { getRequestContextCheckOptions } from "./request-context";
|
|
7
19
|
import type { Options } from "./assets";
|
|
20
|
+
import type { AddressInfo } from "net";
|
|
8
21
|
|
|
9
22
|
export interface EnablePagesAssetsServiceBindingOptions {
|
|
10
23
|
proxyPort?: number;
|
|
@@ -44,7 +57,48 @@ async function main() {
|
|
|
44
57
|
console.log("OPTIONS:\n", JSON.stringify(config, null, 2));
|
|
45
58
|
}
|
|
46
59
|
|
|
60
|
+
config.bindings = {
|
|
61
|
+
...config.bindings,
|
|
62
|
+
...Object.fromEntries(
|
|
63
|
+
Object.entries(
|
|
64
|
+
config.externalDurableObjects as Record<
|
|
65
|
+
string,
|
|
66
|
+
{ name: string; host: string; port: number }
|
|
67
|
+
>
|
|
68
|
+
).map(([binding, { name, host, port }]) => {
|
|
69
|
+
const factory = () => {
|
|
70
|
+
throw new FatalError(
|
|
71
|
+
"An external Durable Object instance's state has somehow been attempted to be accessed.",
|
|
72
|
+
1
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
const namespace = new DurableObjectNamespace(name as string, factory);
|
|
76
|
+
namespace.get = (id) => {
|
|
77
|
+
const stub = new DurableObjectStub(factory, id);
|
|
78
|
+
stub.fetch = (...reqArgs) => {
|
|
79
|
+
const requestFromArgs = new MiniflareRequest(...reqArgs);
|
|
80
|
+
const url = new URL(requestFromArgs.url);
|
|
81
|
+
url.host = host;
|
|
82
|
+
if (port !== undefined) url.port = port.toString();
|
|
83
|
+
const request = new MiniflareRequest(
|
|
84
|
+
url.toString(),
|
|
85
|
+
requestFromArgs
|
|
86
|
+
);
|
|
87
|
+
request.headers.set("x-miniflare-durable-object-name", name);
|
|
88
|
+
request.headers.set("x-miniflare-durable-object-id", id.toString());
|
|
89
|
+
|
|
90
|
+
return fetch(request);
|
|
91
|
+
};
|
|
92
|
+
return stub;
|
|
93
|
+
};
|
|
94
|
+
return [binding, namespace];
|
|
95
|
+
})
|
|
96
|
+
),
|
|
97
|
+
};
|
|
98
|
+
|
|
47
99
|
let mf: Miniflare | undefined;
|
|
100
|
+
let durableObjectsMf: Miniflare | undefined = undefined;
|
|
101
|
+
let durableObjectsMfPort: number | undefined = undefined;
|
|
48
102
|
|
|
49
103
|
try {
|
|
50
104
|
if (args._[1]) {
|
|
@@ -73,12 +127,80 @@ async function main() {
|
|
|
73
127
|
// Start Miniflare development server
|
|
74
128
|
await mf.startServer();
|
|
75
129
|
await mf.startScheduler();
|
|
76
|
-
|
|
130
|
+
|
|
131
|
+
const internalDurableObjectClassNames = Object.values(
|
|
132
|
+
config.durableObjects as Record<string, string>
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
if (internalDurableObjectClassNames.length > 0) {
|
|
136
|
+
durableObjectsMf = new Miniflare({
|
|
137
|
+
host: config.host,
|
|
138
|
+
port: 0,
|
|
139
|
+
script: `
|
|
140
|
+
export default {
|
|
141
|
+
fetch(request, env) {
|
|
142
|
+
return env.DO.fetch(request)
|
|
143
|
+
}
|
|
144
|
+
}`,
|
|
145
|
+
serviceBindings: {
|
|
146
|
+
DO: async (request: MiniflareRequest) => {
|
|
147
|
+
request = new MiniflareRequest(request);
|
|
148
|
+
|
|
149
|
+
const name = request.headers.get("x-miniflare-durable-object-name");
|
|
150
|
+
const idString = request.headers.get(
|
|
151
|
+
"x-miniflare-durable-object-id"
|
|
152
|
+
);
|
|
153
|
+
request.headers.delete("x-miniflare-durable-object-name");
|
|
154
|
+
request.headers.delete("x-miniflare-durable-object-id");
|
|
155
|
+
|
|
156
|
+
if (!name || !idString) {
|
|
157
|
+
return new MiniflareResponse(
|
|
158
|
+
"[durable-object-proxy-err] Missing `x-miniflare-durable-object-name` or `x-miniflare-durable-object-id` headers.",
|
|
159
|
+
{ status: 400 }
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const namespace = await mf?.getDurableObjectNamespace(name);
|
|
164
|
+
const id = namespace?.idFromString(idString);
|
|
165
|
+
|
|
166
|
+
if (!id) {
|
|
167
|
+
return new MiniflareResponse(
|
|
168
|
+
"[durable-object-proxy-err] Could not generate an ID. Possibly due to a mismatched DO name and ID?",
|
|
169
|
+
{ status: 500 }
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const stub = namespace?.get(id);
|
|
174
|
+
|
|
175
|
+
if (!stub) {
|
|
176
|
+
return new MiniflareResponse(
|
|
177
|
+
"[durable-object-proxy-err] Could not generate a stub. Possibly due to a mismatched DO name and ID?",
|
|
178
|
+
{ status: 500 }
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return stub.fetch(request);
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
modules: true,
|
|
186
|
+
});
|
|
187
|
+
const server = await durableObjectsMf.startServer();
|
|
188
|
+
durableObjectsMfPort = (server.address() as AddressInfo).port;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
process.send &&
|
|
192
|
+
process.send(
|
|
193
|
+
JSON.stringify({
|
|
194
|
+
ready: true,
|
|
195
|
+
durableObjectsPort: durableObjectsMfPort,
|
|
196
|
+
})
|
|
197
|
+
);
|
|
77
198
|
} catch (e) {
|
|
78
199
|
mf?.log.error(e as Error);
|
|
79
200
|
process.exitCode = 1;
|
|
80
201
|
// Unmount any mounted workers
|
|
81
202
|
await mf?.dispose();
|
|
203
|
+
await durableObjectsMf?.dispose();
|
|
82
204
|
}
|
|
83
205
|
}
|
|
84
206
|
|
package/src/module-collection.ts
CHANGED
|
@@ -123,9 +123,9 @@ export default function createModuleCollector(props: {
|
|
|
123
123
|
{
|
|
124
124
|
filter: new RegExp(
|
|
125
125
|
"^(" +
|
|
126
|
-
[...props.wrangler1xlegacyModuleReferences.fileNames]
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
[...props.wrangler1xlegacyModuleReferences.fileNames]
|
|
127
|
+
.map((name) => name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
|
|
128
|
+
.join("|") +
|
|
129
129
|
")$"
|
|
130
130
|
),
|
|
131
131
|
},
|