wrangler 2.0.24 → 2.0.25
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 +130 -16
- package/package.json +1 -1
- package/src/__tests__/configuration.test.ts +1 -1
- package/src/__tests__/dev.test.tsx +26 -4
- package/src/__tests__/helpers/mock-cfetch.ts +2 -2
- package/src/__tests__/r2.test.ts +18 -0
- package/src/__tests__/tail.test.ts +93 -39
- package/src/api/dev.ts +6 -0
- package/src/bundle.ts +3 -2
- package/src/config/config.ts +1 -1
- package/src/config/validation.ts +1 -1
- package/src/dev/dev.tsx +12 -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 +26 -17
- package/src/index.tsx +51 -21
- package/src/inspect.ts +1 -4
- package/src/miniflare-cli/assets.ts +19 -16
- package/src/miniflare-cli/index.ts +121 -2
- package/src/pages/build.tsx +36 -28
- package/src/pages/constants.ts +3 -0
- package/src/pages/deployments.tsx +9 -9
- package/src/pages/dev.tsx +85 -27
- package/src/pages/functions/buildPlugin.ts +4 -0
- package/src/pages/functions/buildWorker.ts +4 -0
- package/src/pages/functions/routes-consolidation.test.ts +66 -0
- package/src/pages/functions/routes-consolidation.ts +29 -0
- package/src/pages/functions/routes-transformation.test.ts +271 -0
- package/src/pages/functions/routes-transformation.ts +125 -0
- package/src/pages/projects.tsx +9 -3
- package/src/pages/publish.tsx +56 -14
- package/src/pages/types.ts +9 -0
- package/src/pages/upload.tsx +6 -8
- package/src/r2.ts +13 -0
- package/src/tail/index.ts +15 -2
- package/src/tail/printing.ts +41 -3
- package/wrangler-dist/cli.d.ts +6 -0
- package/wrangler-dist/cli.js +385 -89
|
@@ -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,45 @@ 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 url = `http://${host}${port ? `:${port}` : ""}`;
|
|
80
|
+
const request = new MiniflareRequest(
|
|
81
|
+
url,
|
|
82
|
+
new MiniflareRequest(...reqArgs)
|
|
83
|
+
);
|
|
84
|
+
request.headers.set("x-miniflare-durable-object-name", name);
|
|
85
|
+
request.headers.set("x-miniflare-durable-object-id", id.toString());
|
|
86
|
+
|
|
87
|
+
return fetch(request);
|
|
88
|
+
};
|
|
89
|
+
return stub;
|
|
90
|
+
};
|
|
91
|
+
return [binding, namespace];
|
|
92
|
+
})
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
|
|
47
96
|
let mf: Miniflare | undefined;
|
|
97
|
+
let durableObjectsMf: Miniflare | undefined = undefined;
|
|
98
|
+
let durableObjectsMfPort: number | undefined = undefined;
|
|
48
99
|
|
|
49
100
|
try {
|
|
50
101
|
if (args._[1]) {
|
|
@@ -73,12 +124,80 @@ async function main() {
|
|
|
73
124
|
// Start Miniflare development server
|
|
74
125
|
await mf.startServer();
|
|
75
126
|
await mf.startScheduler();
|
|
76
|
-
|
|
127
|
+
|
|
128
|
+
const internalDurableObjectClassNames = Object.values(
|
|
129
|
+
config.durableObjects as Record<string, string>
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
if (internalDurableObjectClassNames.length > 0) {
|
|
133
|
+
durableObjectsMf = new Miniflare({
|
|
134
|
+
host: config.host,
|
|
135
|
+
port: 0,
|
|
136
|
+
script: `
|
|
137
|
+
export default {
|
|
138
|
+
fetch(request, env) {
|
|
139
|
+
return env.DO.fetch(request)
|
|
140
|
+
}
|
|
141
|
+
}`,
|
|
142
|
+
serviceBindings: {
|
|
143
|
+
DO: async (request: MiniflareRequest) => {
|
|
144
|
+
request = new MiniflareRequest(request);
|
|
145
|
+
|
|
146
|
+
const name = request.headers.get("x-miniflare-durable-object-name");
|
|
147
|
+
const idString = request.headers.get(
|
|
148
|
+
"x-miniflare-durable-object-id"
|
|
149
|
+
);
|
|
150
|
+
request.headers.delete("x-miniflare-durable-object-name");
|
|
151
|
+
request.headers.delete("x-miniflare-durable-object-id");
|
|
152
|
+
|
|
153
|
+
if (!name || !idString) {
|
|
154
|
+
return new MiniflareResponse(
|
|
155
|
+
"[durable-object-proxy-err] Missing `x-miniflare-durable-object-name` or `x-miniflare-durable-object-id` headers.",
|
|
156
|
+
{ status: 400 }
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const namespace = await mf?.getDurableObjectNamespace(name);
|
|
161
|
+
const id = namespace?.idFromString(idString);
|
|
162
|
+
|
|
163
|
+
if (!id) {
|
|
164
|
+
return new MiniflareResponse(
|
|
165
|
+
"[durable-object-proxy-err] Could not generate an ID. Possibly due to a mismatched DO name and ID?",
|
|
166
|
+
{ status: 500 }
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const stub = namespace?.get(id);
|
|
171
|
+
|
|
172
|
+
if (!stub) {
|
|
173
|
+
return new MiniflareResponse(
|
|
174
|
+
"[durable-object-proxy-err] Could not generate a stub. Possibly due to a mismatched DO name and ID?",
|
|
175
|
+
{ status: 500 }
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return stub.fetch(request);
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
modules: true,
|
|
183
|
+
});
|
|
184
|
+
const server = await durableObjectsMf.startServer();
|
|
185
|
+
durableObjectsMfPort = (server.address() as AddressInfo).port;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
process.send &&
|
|
189
|
+
process.send(
|
|
190
|
+
JSON.stringify({
|
|
191
|
+
ready: true,
|
|
192
|
+
durableObjectsPort: durableObjectsMfPort,
|
|
193
|
+
})
|
|
194
|
+
);
|
|
77
195
|
} catch (e) {
|
|
78
196
|
mf?.log.error(e as Error);
|
|
79
197
|
process.exitCode = 1;
|
|
80
198
|
// Unmount any mounted workers
|
|
81
199
|
await mf?.dispose();
|
|
200
|
+
await durableObjectsMf?.dispose();
|
|
82
201
|
}
|
|
83
202
|
}
|
|
84
203
|
|
package/src/pages/build.tsx
CHANGED
|
@@ -9,24 +9,15 @@ import { buildPlugin } from "./functions/buildPlugin";
|
|
|
9
9
|
import { buildWorker } from "./functions/buildWorker";
|
|
10
10
|
import { generateConfigFromFileTree } from "./functions/filepath-routing";
|
|
11
11
|
import { writeRoutesModule } from "./functions/routes";
|
|
12
|
+
import { convertRoutesToRoutesJSONSpec } from "./functions/routes-transformation";
|
|
12
13
|
import { pagesBetaWarning, RUNNING_BUILDERS } from "./utils";
|
|
13
14
|
import type { Config } from "./functions/routes";
|
|
14
|
-
import type {
|
|
15
|
+
import type { YargsOptionsToInterface } from "./types";
|
|
16
|
+
import type { Argv } from "yargs";
|
|
15
17
|
|
|
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
|
-
};
|
|
18
|
+
type PagesBuildArgs = YargsOptionsToInterface<typeof Options>;
|
|
28
19
|
|
|
29
|
-
export function Options(yargs: Argv)
|
|
20
|
+
export function Options(yargs: Argv) {
|
|
30
21
|
return yargs
|
|
31
22
|
.positional("directory", {
|
|
32
23
|
type: "string",
|
|
@@ -43,6 +34,10 @@ export function Options(yargs: Argv): Argv<PagesBuildArgs> {
|
|
|
43
34
|
type: "string",
|
|
44
35
|
description: "The location for the output config file",
|
|
45
36
|
},
|
|
37
|
+
"output-routes-path": {
|
|
38
|
+
type: "string",
|
|
39
|
+
description: "The location for the output _routes.json file",
|
|
40
|
+
},
|
|
46
41
|
minify: {
|
|
47
42
|
type: "boolean",
|
|
48
43
|
default: false,
|
|
@@ -87,15 +82,16 @@ export function Options(yargs: Argv): Argv<PagesBuildArgs> {
|
|
|
87
82
|
export const Handler = async ({
|
|
88
83
|
directory,
|
|
89
84
|
outfile,
|
|
90
|
-
|
|
85
|
+
outputConfigPath,
|
|
86
|
+
outputRoutesPath: routesOutputPath,
|
|
91
87
|
minify,
|
|
92
88
|
sourcemap,
|
|
93
89
|
fallbackService,
|
|
94
90
|
watch,
|
|
95
91
|
plugin,
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}:
|
|
92
|
+
buildOutputDirectory,
|
|
93
|
+
nodeCompat,
|
|
94
|
+
}: PagesBuildArgs) => {
|
|
99
95
|
if (!isInPagesCI) {
|
|
100
96
|
// Beta message for `wrangler pages <commands>` usage
|
|
101
97
|
logger.log(pagesBetaWarning);
|
|
@@ -120,6 +116,7 @@ export const Handler = async ({
|
|
|
120
116
|
plugin,
|
|
121
117
|
buildOutputDirectory,
|
|
122
118
|
nodeCompat,
|
|
119
|
+
routesOutputPath,
|
|
123
120
|
});
|
|
124
121
|
await metrics.sendMetricsEvent("build pages functions");
|
|
125
122
|
};
|
|
@@ -135,19 +132,25 @@ export async function buildFunctions({
|
|
|
135
132
|
onEnd,
|
|
136
133
|
plugin = false,
|
|
137
134
|
buildOutputDirectory,
|
|
135
|
+
routesOutputPath,
|
|
138
136
|
nodeCompat,
|
|
139
|
-
}:
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
}: Partial<
|
|
138
|
+
Pick<
|
|
139
|
+
PagesBuildArgs,
|
|
140
|
+
| "outputConfigPath"
|
|
141
|
+
| "minify"
|
|
142
|
+
| "sourcemap"
|
|
143
|
+
| "fallbackService"
|
|
144
|
+
| "watch"
|
|
145
|
+
| "plugin"
|
|
146
|
+
| "buildOutputDirectory"
|
|
147
|
+
| "nodeCompat"
|
|
148
|
+
>
|
|
149
|
+
> & {
|
|
142
150
|
functionsDirectory: string;
|
|
143
|
-
minify?: boolean;
|
|
144
|
-
sourcemap?: boolean;
|
|
145
|
-
fallbackService?: string;
|
|
146
|
-
watch?: boolean;
|
|
147
151
|
onEnd?: () => void;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
nodeCompat?: boolean;
|
|
152
|
+
outfile: Required<PagesBuildArgs>["outfile"];
|
|
153
|
+
routesOutputPath?: PagesBuildArgs["outputRoutesPath"];
|
|
151
154
|
}) {
|
|
152
155
|
RUNNING_BUILDERS.forEach(
|
|
153
156
|
(runningBuilder) => runningBuilder.stop && runningBuilder.stop()
|
|
@@ -161,6 +164,11 @@ export async function buildFunctions({
|
|
|
161
164
|
baseURL,
|
|
162
165
|
});
|
|
163
166
|
|
|
167
|
+
if (config.routes && routesOutputPath) {
|
|
168
|
+
const routesJSON = convertRoutesToRoutesJSONSpec(config.routes);
|
|
169
|
+
writeFileSync(routesOutputPath, JSON.stringify(routesJSON, null, 2));
|
|
170
|
+
}
|
|
171
|
+
|
|
164
172
|
if (outputConfigPath) {
|
|
165
173
|
writeFileSync(
|
|
166
174
|
outputConfigPath,
|
package/src/pages/constants.ts
CHANGED
|
@@ -6,3 +6,6 @@ export const MAX_UPLOAD_ATTEMPTS = 5;
|
|
|
6
6
|
export const MAX_CHECK_MISSING_ATTEMPTS = 5;
|
|
7
7
|
export const SECONDS_TO_WAIT_FOR_PROXY = 5;
|
|
8
8
|
export const isInPagesCI = !!process.env.CF_PAGES;
|
|
9
|
+
/** The max number of rules in _routes.json */
|
|
10
|
+
export const MAX_FUNCTIONS_ROUTES_RULES = 100;
|
|
11
|
+
export const ROUTES_SPEC_VERSION = 1;
|
|
@@ -11,14 +11,16 @@ import { requireAuth } from "../user";
|
|
|
11
11
|
import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
|
|
12
12
|
import { listProjects } from "./projects";
|
|
13
13
|
import { pagesBetaWarning } from "./utils";
|
|
14
|
-
import type {
|
|
15
|
-
|
|
14
|
+
import type {
|
|
15
|
+
Deployment,
|
|
16
|
+
PagesConfigCache,
|
|
17
|
+
YargsOptionsToInterface,
|
|
18
|
+
} from "./types";
|
|
19
|
+
import type { Argv } from "yargs";
|
|
16
20
|
|
|
17
|
-
type ListArgs =
|
|
18
|
-
"project-name"?: string;
|
|
19
|
-
};
|
|
21
|
+
type ListArgs = YargsOptionsToInterface<typeof ListOptions>;
|
|
20
22
|
|
|
21
|
-
export function ListOptions(yargs: Argv)
|
|
23
|
+
export function ListOptions(yargs: Argv) {
|
|
22
24
|
return yargs
|
|
23
25
|
.options({
|
|
24
26
|
"project-name": {
|
|
@@ -30,9 +32,7 @@ export function ListOptions(yargs: Argv): Argv<ListArgs> {
|
|
|
30
32
|
.epilogue(pagesBetaWarning);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
export async function ListHandler({
|
|
34
|
-
projectName,
|
|
35
|
-
}: ArgumentsCamelCase<ListArgs>) {
|
|
35
|
+
export async function ListHandler({ projectName }: ListArgs) {
|
|
36
36
|
const config = getConfigCache<PagesConfigCache>(PAGES_CONFIG_CACHE_FILENAME);
|
|
37
37
|
const accountId = await requireAuth(config);
|
|
38
38
|
|
package/src/pages/dev.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
import { watch } from "chokidar";
|
|
6
|
+
import { build as workerJsBuild } from "esbuild";
|
|
6
7
|
import { unstable_dev } from "../api";
|
|
7
8
|
import { FatalError } from "../errors";
|
|
8
9
|
import { logger } from "../logger";
|
|
@@ -10,25 +11,18 @@ import * as metrics from "../metrics";
|
|
|
10
11
|
import { buildFunctions } from "./build";
|
|
11
12
|
import { SECONDS_TO_WAIT_FOR_PROXY } from "./constants";
|
|
12
13
|
import { CLEANUP, CLEANUP_CALLBACKS, pagesBetaWarning } from "./utils";
|
|
13
|
-
import type {
|
|
14
|
-
|
|
15
|
-
type
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"script-path": string;
|
|
22
|
-
binding?: (string | number)[];
|
|
23
|
-
kv?: (string | number)[];
|
|
24
|
-
do?: (string | number)[];
|
|
25
|
-
"live-reload": boolean;
|
|
26
|
-
"local-protocol"?: "https" | "http";
|
|
27
|
-
"experimental-enable-local-persistence": boolean;
|
|
28
|
-
"node-compat": boolean;
|
|
29
|
-
};
|
|
14
|
+
import type { AdditionalDevProps } from "../dev";
|
|
15
|
+
import type { YargsOptionsToInterface } from "./types";
|
|
16
|
+
import type { Plugin } from "esbuild";
|
|
17
|
+
import type { Argv } from "yargs";
|
|
18
|
+
|
|
19
|
+
const DURABLE_OBJECTS_BINDING_REGEXP = new RegExp(
|
|
20
|
+
/^(?<binding>[^=]+)=(?<className>[^@\s]+)(@(?<scriptName>.*)$)?$/
|
|
21
|
+
);
|
|
30
22
|
|
|
31
|
-
|
|
23
|
+
type PagesDevArgs = YargsOptionsToInterface<typeof Options>;
|
|
24
|
+
|
|
25
|
+
export function Options(yargs: Argv) {
|
|
32
26
|
return yargs
|
|
33
27
|
.positional("directory", {
|
|
34
28
|
type: "string",
|
|
@@ -46,11 +40,20 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
|
|
|
46
40
|
default: true,
|
|
47
41
|
description: "Run on my machine",
|
|
48
42
|
},
|
|
43
|
+
ip: {
|
|
44
|
+
type: "string",
|
|
45
|
+
default: "0.0.0.0",
|
|
46
|
+
description: "The IP address to listen on",
|
|
47
|
+
},
|
|
49
48
|
port: {
|
|
50
49
|
type: "number",
|
|
51
50
|
default: 8788,
|
|
52
51
|
description: "The port to listen on (serve from)",
|
|
53
52
|
},
|
|
53
|
+
"inspector-port": {
|
|
54
|
+
type: "number",
|
|
55
|
+
describe: "Port for devtools to connect to",
|
|
56
|
+
},
|
|
54
57
|
proxy: {
|
|
55
58
|
type: "number",
|
|
56
59
|
description: "The port to proxy (where the static assets are served)",
|
|
@@ -68,14 +71,18 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
|
|
|
68
71
|
},
|
|
69
72
|
kv: {
|
|
70
73
|
type: "array",
|
|
71
|
-
description: "KV namespace to bind",
|
|
74
|
+
description: "KV namespace to bind (--kv KV_BINDING)",
|
|
72
75
|
alias: "k",
|
|
73
76
|
},
|
|
74
77
|
do: {
|
|
75
78
|
type: "array",
|
|
76
|
-
description: "Durable Object to bind (NAME=CLASS)",
|
|
79
|
+
description: "Durable Object to bind (--do NAME=CLASS)",
|
|
77
80
|
alias: "o",
|
|
78
81
|
},
|
|
82
|
+
r2: {
|
|
83
|
+
type: "array",
|
|
84
|
+
description: "R2 bucket to bind (--r2 R2_BINDING)",
|
|
85
|
+
},
|
|
79
86
|
"live-reload": {
|
|
80
87
|
type: "boolean",
|
|
81
88
|
default: false,
|
|
@@ -108,19 +115,22 @@ export function Options(yargs: Argv): Argv<PagesDevArgs> {
|
|
|
108
115
|
export const Handler = async ({
|
|
109
116
|
local,
|
|
110
117
|
directory,
|
|
118
|
+
ip,
|
|
111
119
|
port,
|
|
120
|
+
"inspector-port": inspectorPort,
|
|
112
121
|
proxy: requestedProxyPort,
|
|
113
122
|
"script-path": singleWorkerScriptPath,
|
|
114
123
|
binding: bindings = [],
|
|
115
124
|
kv: kvs = [],
|
|
116
125
|
do: durableObjects = [],
|
|
126
|
+
r2: r2s = [],
|
|
117
127
|
"live-reload": liveReload,
|
|
118
128
|
"local-protocol": localProtocol,
|
|
119
129
|
"experimental-enable-local-persistence": experimentalEnableLocalPersistence,
|
|
120
130
|
"node-compat": nodeCompat,
|
|
121
131
|
config: config,
|
|
122
132
|
_: [_pages, _dev, ...remaining],
|
|
123
|
-
}:
|
|
133
|
+
}: PagesDevArgs) => {
|
|
124
134
|
// Beta message for `wrangler pages <commands>` usage
|
|
125
135
|
logger.log(pagesBetaWarning);
|
|
126
136
|
|
|
@@ -213,6 +223,23 @@ export const Handler = async ({
|
|
|
213
223
|
if (!existsSync(scriptPath)) {
|
|
214
224
|
logger.log("No functions. Shimming...");
|
|
215
225
|
scriptPath = resolve(__dirname, "../templates/pages-shim.ts");
|
|
226
|
+
} else {
|
|
227
|
+
const runBuild = async () => {
|
|
228
|
+
try {
|
|
229
|
+
await workerJsBuild({
|
|
230
|
+
entryPoints: [scriptPath],
|
|
231
|
+
write: false,
|
|
232
|
+
plugins: [blockWorkerJsImports],
|
|
233
|
+
});
|
|
234
|
+
} catch {}
|
|
235
|
+
};
|
|
236
|
+
await runBuild();
|
|
237
|
+
watch([scriptPath], {
|
|
238
|
+
persistent: true,
|
|
239
|
+
ignoreInitial: true,
|
|
240
|
+
}).on("all", async () => {
|
|
241
|
+
await runBuild();
|
|
242
|
+
});
|
|
216
243
|
}
|
|
217
244
|
}
|
|
218
245
|
|
|
@@ -221,7 +248,9 @@ export const Handler = async ({
|
|
|
221
248
|
const { stop, waitUntilExit } = await unstable_dev(
|
|
222
249
|
scriptPath,
|
|
223
250
|
{
|
|
251
|
+
ip,
|
|
224
252
|
port,
|
|
253
|
+
inspectorPort,
|
|
225
254
|
watch: true,
|
|
226
255
|
localProtocol,
|
|
227
256
|
liveReload,
|
|
@@ -237,12 +266,29 @@ export const Handler = async ({
|
|
|
237
266
|
binding: val.toString(),
|
|
238
267
|
id: "",
|
|
239
268
|
})),
|
|
240
|
-
durableObjects: durableObjects
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
269
|
+
durableObjects: durableObjects
|
|
270
|
+
.map((durableObject) => {
|
|
271
|
+
const { binding, className, scriptName } =
|
|
272
|
+
DURABLE_OBJECTS_BINDING_REGEXP.exec(durableObject.toString())
|
|
273
|
+
?.groups || {};
|
|
274
|
+
|
|
275
|
+
if (!binding || !className) {
|
|
276
|
+
logger.warn(
|
|
277
|
+
"Could not parse Durable Object binding:",
|
|
278
|
+
durableObject.toString()
|
|
279
|
+
);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return {
|
|
284
|
+
name: binding,
|
|
285
|
+
class_name: className,
|
|
286
|
+
script_name: scriptName,
|
|
287
|
+
};
|
|
288
|
+
})
|
|
289
|
+
.filter(Boolean) as AdditionalDevProps["durableObjects"],
|
|
290
|
+
r2: r2s.map((binding) => {
|
|
291
|
+
return { binding: binding.toString(), bucket_name: "" };
|
|
246
292
|
}),
|
|
247
293
|
|
|
248
294
|
enablePagesAssetsServiceBinding: {
|
|
@@ -412,3 +458,15 @@ async function spawnProxyProcess({
|
|
|
412
458
|
|
|
413
459
|
return port;
|
|
414
460
|
}
|
|
461
|
+
|
|
462
|
+
const blockWorkerJsImports: Plugin = {
|
|
463
|
+
name: "block-worker-js-imports",
|
|
464
|
+
setup(build) {
|
|
465
|
+
build.onResolve({ filter: /.*/g }, (_args) => {
|
|
466
|
+
logger.error(
|
|
467
|
+
`_worker.js is importing from another file. This will throw an error if deployed.\nYou should bundle your Worker or remove the import if it is unused.`
|
|
468
|
+
);
|
|
469
|
+
return null;
|
|
470
|
+
});
|
|
471
|
+
},
|
|
472
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { consolidateRoutes } from "./routes-consolidation";
|
|
2
|
+
|
|
3
|
+
describe("route-consolidation", () => {
|
|
4
|
+
describe("consolidateRoutes()", () => {
|
|
5
|
+
it("should consolidate redundant routes", () => {
|
|
6
|
+
expect(consolidateRoutes(["/api/foo", "/api/*"])).toEqual(["/api/*"]);
|
|
7
|
+
expect(
|
|
8
|
+
consolidateRoutes([
|
|
9
|
+
"/api/foo",
|
|
10
|
+
"/api/foo/*",
|
|
11
|
+
"/api/bar/*",
|
|
12
|
+
"/api/*",
|
|
13
|
+
"/foo",
|
|
14
|
+
"/foo/bar",
|
|
15
|
+
"/bar/*",
|
|
16
|
+
"/bar/baz/*",
|
|
17
|
+
"/bar/baz/hello",
|
|
18
|
+
])
|
|
19
|
+
).toEqual(["/api/*", "/foo", "/foo/bar", "/bar/*"]);
|
|
20
|
+
});
|
|
21
|
+
it("should consolidate thousands of redundant routes", () => {
|
|
22
|
+
// Test to make sure the consolidator isn't horribly slow
|
|
23
|
+
const routes: string[] = [];
|
|
24
|
+
const limit = 1000;
|
|
25
|
+
for (let i = 0; i < limit; i++) {
|
|
26
|
+
// Add 3 routes per id
|
|
27
|
+
const id = `some-id-${i}`;
|
|
28
|
+
routes.push(`/${id}/*`, `/${id}/foo`, `/${id}/bar/*`);
|
|
29
|
+
}
|
|
30
|
+
const consolidated = consolidateRoutes(routes);
|
|
31
|
+
expect(consolidated.length).toEqual(limit);
|
|
32
|
+
// Should be all unique
|
|
33
|
+
expect(Array.from(new Set(consolidated)).length).toEqual(limit);
|
|
34
|
+
// Should all have pattern `/$id/*`
|
|
35
|
+
expect(
|
|
36
|
+
consolidated.every((route) => route.match(/\/[a-z0-9-]+\/\*/) !== null)
|
|
37
|
+
).toEqual(true);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should consolidate many redundant sub-routes", () => {
|
|
41
|
+
const routes: string[] = [];
|
|
42
|
+
const limit = 15;
|
|
43
|
+
|
|
44
|
+
// Create $limit of top-level catch-all routes, with a lot of sub-routes
|
|
45
|
+
for (let i = 0; i < limit; i++) {
|
|
46
|
+
routes.push(`/foo-${i}/*`);
|
|
47
|
+
for (let j = 0; j < limit; j++) {
|
|
48
|
+
routes.push(`/foo-${i}/bar-${j}/hello`);
|
|
49
|
+
for (let k = 0; k < limit; k++) {
|
|
50
|
+
routes.push(`/foo-${i}/bar-${j}/baz-${k}/*`);
|
|
51
|
+
routes.push(`/foo-${i}/bar-${j}/baz-${k}/profile`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const consolidated = consolidateRoutes(routes);
|
|
57
|
+
expect(consolidated.length).toEqual(limit);
|
|
58
|
+
// Should be all unique
|
|
59
|
+
expect(Array.from(new Set(consolidated)).length).toEqual(limit);
|
|
60
|
+
// Should all have pattern `/$id/*`
|
|
61
|
+
expect(
|
|
62
|
+
consolidated.every((route) => route.match(/\/[a-z0-9-]+\/\*/) !== null)
|
|
63
|
+
).toEqual(true);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* consolidateRoutes consolidates redundant routes - eg. ["/api/*"", "/api/foo"] -> ["/api/*""]
|
|
3
|
+
* @param routes If this is the same order as Functions routes (with most-specific first),
|
|
4
|
+
* it will be more efficient to reverse it first. Should be in the format: /api/foo, /api/*
|
|
5
|
+
* @returns Non-redundant list of routes
|
|
6
|
+
*/
|
|
7
|
+
export function consolidateRoutes(routes: string[]): string[] {
|
|
8
|
+
// create a map of the routes
|
|
9
|
+
const routesMap = new Map<string, boolean>();
|
|
10
|
+
for (const route of routes) {
|
|
11
|
+
routesMap.set(route, true);
|
|
12
|
+
}
|
|
13
|
+
// Find routes that might render other routes redundant
|
|
14
|
+
for (const route of routes.filter((r) => r.endsWith("/*"))) {
|
|
15
|
+
// Make sure the route still exists in the map
|
|
16
|
+
if (routesMap.has(route)) {
|
|
17
|
+
// Remove splat at the end, leaving the /
|
|
18
|
+
// eg. /api/* -> /api/
|
|
19
|
+
const routeTrimmed = route.substring(0, route.length - 1);
|
|
20
|
+
for (const nextRoute of routesMap.keys()) {
|
|
21
|
+
// Delete any route that has the wildcard route as a prefix
|
|
22
|
+
if (nextRoute !== route && nextRoute.startsWith(routeTrimmed)) {
|
|
23
|
+
routesMap.delete(nextRoute);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return Array.from(routesMap.keys());
|
|
29
|
+
}
|