wrangler 2.0.24 → 2.0.27
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/miniflare-dist/index.mjs +142 -16
- package/package.json +3 -3
- package/src/__tests__/configuration.test.ts +7 -3
- package/src/__tests__/dev.test.tsx +26 -4
- package/src/__tests__/generate.test.ts +2 -4
- package/src/__tests__/helpers/mock-cfetch.ts +35 -2
- package/src/__tests__/init.test.ts +537 -359
- package/src/__tests__/jest.setup.ts +7 -0
- package/src/__tests__/metrics.test.ts +1 -1
- package/src/__tests__/pages.test.ts +14 -0
- package/src/__tests__/r2.test.ts +22 -3
- package/src/__tests__/tail.test.ts +112 -42
- package/src/__tests__/user.test.ts +11 -0
- package/src/api/dev.ts +7 -0
- package/src/bundle.ts +3 -2
- package/src/cfetch/internal.ts +56 -0
- package/src/config/config.ts +1 -1
- package/src/config/validation-helpers.ts +19 -6
- package/src/config/validation.ts +9 -3
- package/src/config-cache.ts +2 -1
- package/src/dev/dev.tsx +16 -2
- package/src/dev/local.tsx +69 -5
- package/src/dev/use-esbuild.ts +3 -0
- package/src/dev-registry.tsx +3 -0
- package/src/dev.tsx +28 -19
- package/src/generate.ts +1 -1
- package/src/index.tsx +51 -21
- package/src/init.ts +111 -38
- package/src/inspect.ts +1 -4
- package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
- package/src/metrics/metrics-config.ts +1 -1
- package/src/miniflare-cli/assets.ts +27 -16
- package/src/miniflare-cli/index.ts +124 -2
- package/src/pages/build.tsx +75 -41
- package/src/pages/constants.ts +4 -0
- package/src/pages/deployments.tsx +9 -9
- package/src/pages/dev.tsx +178 -64
- 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 +75 -22
- package/src/pages/types.ts +9 -0
- package/src/pages/upload.tsx +6 -8
- package/src/proxy.ts +10 -0
- package/src/r2.ts +17 -4
- 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 +6 -4
- package/src/whoami.tsx +5 -5
- package/templates/pages-template-plugin.ts +16 -4
- package/templates/pages-template-worker.ts +16 -5
- package/templates/service-bindings-module-facade.js +10 -7
- package/templates/service-bindings-sw-facade.js +10 -7
- package/wrangler-dist/cli.d.ts +7 -0
- package/wrangler-dist/cli.js +1681 -1091
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
|
@@ -320,10 +320,7 @@ export default function useInspector(props: InspectorProps) {
|
|
|
320
320
|
const convertedFnName =
|
|
321
321
|
pos.name || functionName || "";
|
|
322
322
|
exceptionLines.push(
|
|
323
|
-
` at ${convertedFnName} (${pos.source
|
|
324
|
-
`${mapContent.sourceRoot}/`,
|
|
325
|
-
""
|
|
326
|
-
)}:${pos.line}:${pos.column})`
|
|
323
|
+
` at ${convertedFnName} (${pos.source}:${pos.line}:${pos.column})`
|
|
327
324
|
);
|
|
328
325
|
}
|
|
329
326
|
}
|
|
File without changes
|
|
@@ -6,10 +6,10 @@ import { fetchResult } from "../cfetch";
|
|
|
6
6
|
import { getConfigCache, saveToConfigCache } from "../config-cache";
|
|
7
7
|
import { confirm } from "../dialogs";
|
|
8
8
|
import { getEnvironmentVariableFactory } from "../environment-variables";
|
|
9
|
+
import { CI } from "../is-ci";
|
|
9
10
|
import isInteractive from "../is-interactive";
|
|
10
11
|
import { logger } from "../logger";
|
|
11
12
|
import { getAPIToken } from "../user";
|
|
12
|
-
import { CI } from "./is-ci";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* The date that the metrics being gathered was last updated in a way that would require
|
|
@@ -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/pages/build.tsx
CHANGED
|
@@ -1,32 +1,29 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
+
import { FatalError } from "../errors";
|
|
4
5
|
import { logger } from "../logger";
|
|
5
6
|
import * as metrics from "../metrics";
|
|
6
7
|
import { toUrlPath } from "../paths";
|
|
7
8
|
import { isInPagesCI } from "./constants";
|
|
9
|
+
import {
|
|
10
|
+
EXIT_CODE_FUNCTIONS_NO_ROUTES_ERROR,
|
|
11
|
+
FunctionsNoRoutesError,
|
|
12
|
+
getFunctionsNoRoutesWarning,
|
|
13
|
+
} from "./errors";
|
|
8
14
|
import { buildPlugin } from "./functions/buildPlugin";
|
|
9
15
|
import { buildWorker } from "./functions/buildWorker";
|
|
10
16
|
import { generateConfigFromFileTree } from "./functions/filepath-routing";
|
|
11
17
|
import { writeRoutesModule } from "./functions/routes";
|
|
18
|
+
import { convertRoutesToRoutesJSONSpec } from "./functions/routes-transformation";
|
|
12
19
|
import { pagesBetaWarning, RUNNING_BUILDERS } from "./utils";
|
|
13
20
|
import type { Config } from "./functions/routes";
|
|
14
|
-
import type {
|
|
21
|
+
import type { YargsOptionsToInterface } from "./types";
|
|
22
|
+
import type { Argv } from "yargs";
|
|
15
23
|
|
|
16
|
-
type PagesBuildArgs =
|
|
17
|
-
directory: string;
|
|
18
|
-
outfile: string;
|
|
19
|
-
"output-config-path"?: string;
|
|
20
|
-
minify: boolean;
|
|
21
|
-
sourcemap: boolean;
|
|
22
|
-
"fallback-service": string;
|
|
23
|
-
watch: boolean;
|
|
24
|
-
plugin: boolean;
|
|
25
|
-
"build-output-directory"?: string;
|
|
26
|
-
"node-compat": boolean;
|
|
27
|
-
};
|
|
24
|
+
type PagesBuildArgs = YargsOptionsToInterface<typeof Options>;
|
|
28
25
|
|
|
29
|
-
export function Options(yargs: Argv)
|
|
26
|
+
export function Options(yargs: Argv) {
|
|
30
27
|
return yargs
|
|
31
28
|
.positional("directory", {
|
|
32
29
|
type: "string",
|
|
@@ -43,6 +40,10 @@ export function Options(yargs: Argv): Argv<PagesBuildArgs> {
|
|
|
43
40
|
type: "string",
|
|
44
41
|
description: "The location for the output config file",
|
|
45
42
|
},
|
|
43
|
+
"output-routes-path": {
|
|
44
|
+
type: "string",
|
|
45
|
+
description: "The location for the output _routes.json file",
|
|
46
|
+
},
|
|
46
47
|
minify: {
|
|
47
48
|
type: "boolean",
|
|
48
49
|
default: false,
|
|
@@ -87,15 +88,16 @@ export function Options(yargs: Argv): Argv<PagesBuildArgs> {
|
|
|
87
88
|
export const Handler = async ({
|
|
88
89
|
directory,
|
|
89
90
|
outfile,
|
|
90
|
-
|
|
91
|
+
outputConfigPath,
|
|
92
|
+
outputRoutesPath: routesOutputPath,
|
|
91
93
|
minify,
|
|
92
94
|
sourcemap,
|
|
93
95
|
fallbackService,
|
|
94
96
|
watch,
|
|
95
97
|
plugin,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}:
|
|
98
|
+
buildOutputDirectory,
|
|
99
|
+
nodeCompat,
|
|
100
|
+
}: PagesBuildArgs) => {
|
|
99
101
|
if (!isInPagesCI) {
|
|
100
102
|
// Beta message for `wrangler pages <commands>` usage
|
|
101
103
|
logger.log(pagesBetaWarning);
|
|
@@ -108,22 +110,37 @@ export const Handler = async ({
|
|
|
108
110
|
}
|
|
109
111
|
|
|
110
112
|
buildOutputDirectory ??= dirname(outfile);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
113
|
+
try {
|
|
114
|
+
await buildFunctions({
|
|
115
|
+
outfile,
|
|
116
|
+
outputConfigPath,
|
|
117
|
+
functionsDirectory: directory,
|
|
118
|
+
minify,
|
|
119
|
+
sourcemap,
|
|
120
|
+
fallbackService,
|
|
121
|
+
watch,
|
|
122
|
+
plugin,
|
|
123
|
+
buildOutputDirectory,
|
|
124
|
+
nodeCompat,
|
|
125
|
+
routesOutputPath,
|
|
126
|
+
});
|
|
127
|
+
} catch (e) {
|
|
128
|
+
if (e instanceof FunctionsNoRoutesError) {
|
|
129
|
+
throw new FatalError(
|
|
130
|
+
getFunctionsNoRoutesWarning(directory),
|
|
131
|
+
EXIT_CODE_FUNCTIONS_NO_ROUTES_ERROR
|
|
132
|
+
);
|
|
133
|
+
} else {
|
|
134
|
+
throw e;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
124
137
|
await metrics.sendMetricsEvent("build pages functions");
|
|
125
138
|
};
|
|
126
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Builds a Functions worker based on the functions directory, with filepath and handler based routing.
|
|
142
|
+
* @throws FunctionsNoRoutesError when there are no routes found in the functions directory
|
|
143
|
+
*/
|
|
127
144
|
export async function buildFunctions({
|
|
128
145
|
outfile,
|
|
129
146
|
outputConfigPath,
|
|
@@ -135,19 +152,25 @@ export async function buildFunctions({
|
|
|
135
152
|
onEnd,
|
|
136
153
|
plugin = false,
|
|
137
154
|
buildOutputDirectory,
|
|
155
|
+
routesOutputPath,
|
|
138
156
|
nodeCompat,
|
|
139
|
-
}:
|
|
140
|
-
|
|
141
|
-
|
|
157
|
+
}: Partial<
|
|
158
|
+
Pick<
|
|
159
|
+
PagesBuildArgs,
|
|
160
|
+
| "outputConfigPath"
|
|
161
|
+
| "minify"
|
|
162
|
+
| "sourcemap"
|
|
163
|
+
| "fallbackService"
|
|
164
|
+
| "watch"
|
|
165
|
+
| "plugin"
|
|
166
|
+
| "buildOutputDirectory"
|
|
167
|
+
| "nodeCompat"
|
|
168
|
+
>
|
|
169
|
+
> & {
|
|
142
170
|
functionsDirectory: string;
|
|
143
|
-
minify?: boolean;
|
|
144
|
-
sourcemap?: boolean;
|
|
145
|
-
fallbackService?: string;
|
|
146
|
-
watch?: boolean;
|
|
147
171
|
onEnd?: () => void;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
nodeCompat?: boolean;
|
|
172
|
+
outfile: Required<PagesBuildArgs>["outfile"];
|
|
173
|
+
routesOutputPath?: PagesBuildArgs["outputRoutesPath"];
|
|
151
174
|
}) {
|
|
152
175
|
RUNNING_BUILDERS.forEach(
|
|
153
176
|
(runningBuilder) => runningBuilder.stop && runningBuilder.stop()
|
|
@@ -161,6 +184,17 @@ export async function buildFunctions({
|
|
|
161
184
|
baseURL,
|
|
162
185
|
});
|
|
163
186
|
|
|
187
|
+
if (!config.routes || config.routes.length === 0) {
|
|
188
|
+
throw new FunctionsNoRoutesError(
|
|
189
|
+
`Failed to find any routes while compiling Functions in: ${functionsDirectory}`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (routesOutputPath) {
|
|
194
|
+
const routesJSON = convertRoutesToRoutesJSONSpec(config.routes);
|
|
195
|
+
writeFileSync(routesOutputPath, JSON.stringify(routesJSON, null, 2));
|
|
196
|
+
}
|
|
197
|
+
|
|
164
198
|
if (outputConfigPath) {
|
|
165
199
|
writeFileSync(
|
|
166
200
|
outputConfigPath,
|