wrangler 2.12.3 → 2.14.0
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/package.json +11 -7
- package/src/__tests__/api-devregistry.test.ts +121 -0
- package/src/__tests__/api.test.ts +27 -5
- package/src/__tests__/configuration.test.ts +59 -12
- package/src/__tests__/deployments.test.ts +335 -95
- package/src/__tests__/helpers/msw/handlers/deployments.ts +70 -3
- package/src/__tests__/helpers/msw/index.ts +4 -2
- package/src/__tests__/index.test.ts +10 -4
- package/src/__tests__/jest.setup.ts +4 -0
- package/src/__tests__/mtls-certificates.test.ts +5 -2
- package/src/__tests__/pages/publish.test.ts +67 -23
- package/src/__tests__/publish.test.ts +138 -59
- package/src/__tests__/queues.test.ts +5 -2
- package/src/__tests__/traverse-module-graph.test.ts +220 -0
- package/src/api/dev.ts +7 -18
- package/src/api/pages/create-worker-bundle-contents.ts +1 -0
- package/src/bundle.ts +19 -5
- package/src/config/environment.ts +27 -0
- package/src/config/index.ts +18 -0
- package/src/config/validation.ts +91 -1
- package/src/create-worker-upload-form.ts +18 -1
- package/src/d1/execute.tsx +1 -1
- package/src/d1/migrations/apply.tsx +2 -1
- package/src/deployments.ts +260 -8
- package/src/dev/start-server.ts +2 -8
- package/src/dev/use-esbuild.ts +2 -8
- package/src/dev-registry.ts +2 -1
- package/src/dev.tsx +1 -0
- package/src/entry.ts +18 -8
- package/src/index.ts +75 -22
- package/src/init.ts +144 -135
- package/src/metrics/send-event.ts +2 -1
- package/src/module-collection.ts +91 -25
- package/src/pages/functions/buildPlugin.ts +1 -0
- package/src/pages/functions/buildWorker.ts +2 -8
- package/src/publish/publish.ts +10 -26
- package/src/queues/cli/commands/consumer/add.ts +6 -0
- package/src/queues/client.ts +1 -0
- package/src/secret/index.ts +1 -0
- package/src/traverse-module-graph.ts +53 -0
- package/src/worker.ts +10 -0
- package/wrangler-dist/cli.d.ts +24 -0
- package/wrangler-dist/cli.js +19083 -18680
- package/src/__tests__/api-devregistry.test.js +0 -64
- package/src/__tests__/tsconfig.tsbuildinfo +0 -1
- package/src/miniflare-cli/tsconfig.tsbuildinfo +0 -1
- package/src/pages/functions/tsconfig.tsbuildinfo +0 -1
- package/templates/__tests__/tsconfig.tsbuildinfo +0 -1
- package/templates/tsconfig.tsbuildinfo +0 -1
package/src/api/dev.ts
CHANGED
|
@@ -143,7 +143,6 @@ export async function unstable_dev(
|
|
|
143
143
|
enablePagesAssetsServiceBinding,
|
|
144
144
|
liveReload,
|
|
145
145
|
showInteractiveDevSession,
|
|
146
|
-
forceLocal,
|
|
147
146
|
onReady: (address, port) => {
|
|
148
147
|
readyPort = port;
|
|
149
148
|
readyAddress = address;
|
|
@@ -232,6 +231,7 @@ export async function unstable_dev(
|
|
|
232
231
|
experimentalLocal: experimentalLocal ?? false,
|
|
233
232
|
experimentalLocalRemoteKv: experimentalLocalRemoteKv ?? false,
|
|
234
233
|
enablePagesAssetsServiceBinding,
|
|
234
|
+
forceLocal,
|
|
235
235
|
liveReload,
|
|
236
236
|
onReady: (address, port) => {
|
|
237
237
|
readyPort = port;
|
|
@@ -301,27 +301,16 @@ export async function unstable_dev(
|
|
|
301
301
|
export function parseRequestInput(
|
|
302
302
|
readyAddress: string,
|
|
303
303
|
readyPort: number,
|
|
304
|
-
input
|
|
304
|
+
input: RequestInfo = "/",
|
|
305
305
|
init?: RequestInit,
|
|
306
306
|
protocol: "http" | "https" = "http"
|
|
307
307
|
): [RequestInfo, RequestInit | undefined] {
|
|
308
308
|
if (input instanceof Request) {
|
|
309
309
|
return [input, undefined];
|
|
310
|
-
} else if (input instanceof URL) {
|
|
311
|
-
input = `${protocol}://${readyAddress}:${readyPort}${input.pathname}`;
|
|
312
|
-
} else if (typeof input === "string") {
|
|
313
|
-
try {
|
|
314
|
-
// Want to strip the URL to only get the pathname, but the user could pass in only the pathname
|
|
315
|
-
// Will error if we try and pass "/something" into new URL("/something")
|
|
316
|
-
input = `${protocol}://${readyAddress}:${readyPort}${
|
|
317
|
-
new URL(input).pathname
|
|
318
|
-
}`;
|
|
319
|
-
} catch {
|
|
320
|
-
input = `${protocol}://${readyAddress}:${readyPort}${input}`;
|
|
321
|
-
}
|
|
322
|
-
} else {
|
|
323
|
-
input = `${protocol}://${readyAddress}:${readyPort}`;
|
|
324
310
|
}
|
|
325
|
-
|
|
326
|
-
|
|
311
|
+
const url = new URL(`${input}`, `${protocol}://${readyAddress}:${readyPort}`);
|
|
312
|
+
url.protocol = protocol;
|
|
313
|
+
url.hostname = readyAddress;
|
|
314
|
+
url.port = readyPort.toString();
|
|
315
|
+
return [url, init];
|
|
327
316
|
}
|
package/src/bundle.ts
CHANGED
|
@@ -13,6 +13,13 @@ import type { DurableObjectBindings } from "./config/environment";
|
|
|
13
13
|
import type { WorkerRegistry } from "./dev-registry";
|
|
14
14
|
import type { Entry } from "./entry";
|
|
15
15
|
import type { CfModule } from "./worker";
|
|
16
|
+
|
|
17
|
+
export const COMMON_ESBUILD_OPTIONS = {
|
|
18
|
+
// Our workerd runtime uses the same V8 version as recent Chrome, which is highly ES2022 compliant: https://kangax.github.io/compat-table/es2016plus/
|
|
19
|
+
target: "es2022",
|
|
20
|
+
loader: { ".js": "jsx", ".mjs": "jsx", ".cjs": "jsx" },
|
|
21
|
+
} as const;
|
|
22
|
+
|
|
16
23
|
export type BundleResult = {
|
|
17
24
|
modules: CfModule[];
|
|
18
25
|
dependencies: esbuild.Metafile["outputs"][string]["inputs"];
|
|
@@ -94,6 +101,15 @@ const nodejsCompatPlugin: esbuild.Plugin = {
|
|
|
94
101
|
},
|
|
95
102
|
};
|
|
96
103
|
|
|
104
|
+
const cloudflareJsPlugin: esbuild.Plugin = {
|
|
105
|
+
name: "cloudflare javascript Plugin",
|
|
106
|
+
setup(pluginBuild) {
|
|
107
|
+
pluginBuild.onResolve({ filter: /^cloudflare:.*/ }, () => {
|
|
108
|
+
return { external: true };
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
97
113
|
/**
|
|
98
114
|
* Generate a bundle for the worker identified by the arguments passed in.
|
|
99
115
|
*/
|
|
@@ -348,8 +364,7 @@ export async function bundleWorker(
|
|
|
348
364
|
inject,
|
|
349
365
|
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
350
366
|
format: entry.format === "modules" ? "esm" : "iife",
|
|
351
|
-
|
|
352
|
-
target: "es2022",
|
|
367
|
+
target: COMMON_ESBUILD_OPTIONS.target,
|
|
353
368
|
sourcemap: sourcemap ?? true, // this needs to use ?? to accept false
|
|
354
369
|
// Include a reference to the output folder in the sourcemap.
|
|
355
370
|
// This is omitted by default, but we need it to properly resolve source paths in error output.
|
|
@@ -368,9 +383,7 @@ export async function bundleWorker(
|
|
|
368
383
|
},
|
|
369
384
|
}),
|
|
370
385
|
loader: {
|
|
371
|
-
|
|
372
|
-
".mjs": "jsx",
|
|
373
|
-
".cjs": "jsx",
|
|
386
|
+
...COMMON_ESBUILD_OPTIONS.loader,
|
|
374
387
|
...(loader || {}),
|
|
375
388
|
},
|
|
376
389
|
plugins: [
|
|
@@ -379,6 +392,7 @@ export async function bundleWorker(
|
|
|
379
392
|
? [NodeGlobalsPolyfills({ buffer: true }), NodeModulesPolyfills()]
|
|
380
393
|
: []),
|
|
381
394
|
...(nodejsCompat ? [nodejsCompatPlugin] : []),
|
|
395
|
+
...[cloudflareJsPlugin],
|
|
382
396
|
...(plugins || []),
|
|
383
397
|
],
|
|
384
398
|
...(jsxFactory && { jsxFactory }),
|
|
@@ -74,6 +74,12 @@ interface EnvironmentInheritable {
|
|
|
74
74
|
*/
|
|
75
75
|
main: string | undefined;
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* The directory in which module rules should be evaluated in a `--no-bundle` worker
|
|
79
|
+
* This defaults to dirname(main) when left undefined
|
|
80
|
+
*/
|
|
81
|
+
base_dir: string | undefined;
|
|
82
|
+
|
|
77
83
|
/**
|
|
78
84
|
* Whether we use <name>.<subdomain>.workers.dev to
|
|
79
85
|
* test and deploy your worker.
|
|
@@ -339,6 +345,24 @@ interface EnvironmentNonInheritable {
|
|
|
339
345
|
preview_id?: string;
|
|
340
346
|
}[];
|
|
341
347
|
|
|
348
|
+
/**
|
|
349
|
+
* These specify bindings to send email from inside your Worker.
|
|
350
|
+
*
|
|
351
|
+
* NOTE: This field is not automatically inherited from the top level environment,
|
|
352
|
+
* and so must be specified in every named environment.
|
|
353
|
+
*
|
|
354
|
+
* @default `[]`
|
|
355
|
+
* @nonInheritable
|
|
356
|
+
*/
|
|
357
|
+
send_email: {
|
|
358
|
+
/** The binding name used to refer to the this binding */
|
|
359
|
+
name: string;
|
|
360
|
+
/** If this binding should be restricted to a specific verified address */
|
|
361
|
+
destination_address?: string;
|
|
362
|
+
/** If this binding should be restricted to a set of verified addresses */
|
|
363
|
+
allowed_destination_addresses?: string[];
|
|
364
|
+
}[];
|
|
365
|
+
|
|
342
366
|
/**
|
|
343
367
|
* Specifies Queues that are bound to this Worker environment.
|
|
344
368
|
*
|
|
@@ -374,6 +398,9 @@ interface EnvironmentNonInheritable {
|
|
|
374
398
|
|
|
375
399
|
/** The queue to send messages that failed to be consumed. */
|
|
376
400
|
dead_letter_queue?: string;
|
|
401
|
+
|
|
402
|
+
/** The maximum number of concurrent consumer Worker invocations. Leaving this unset will allow your consumer to scale to the maximum concurrency needed to keep up with the message backlog. */
|
|
403
|
+
max_concurrency?: number | null;
|
|
377
404
|
}[];
|
|
378
405
|
};
|
|
379
406
|
|
package/src/config/index.ts
CHANGED
|
@@ -97,6 +97,7 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
|
97
97
|
data_blobs,
|
|
98
98
|
durable_objects,
|
|
99
99
|
kv_namespaces,
|
|
100
|
+
send_email,
|
|
100
101
|
queues,
|
|
101
102
|
d1_databases,
|
|
102
103
|
r2_buckets,
|
|
@@ -155,6 +156,23 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
|
155
156
|
});
|
|
156
157
|
}
|
|
157
158
|
|
|
159
|
+
if (send_email !== undefined && send_email.length > 0) {
|
|
160
|
+
output.push({
|
|
161
|
+
type: "Send Email",
|
|
162
|
+
entries: send_email.map(
|
|
163
|
+
({ name, destination_address, allowed_destination_addresses }) => {
|
|
164
|
+
return {
|
|
165
|
+
key: name,
|
|
166
|
+
value:
|
|
167
|
+
destination_address ||
|
|
168
|
+
allowed_destination_addresses?.join(", ") ||
|
|
169
|
+
"unrestricted",
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
158
176
|
if (queues !== undefined && queues.length > 0) {
|
|
159
177
|
output.push({
|
|
160
178
|
type: "Queues",
|
package/src/config/validation.ts
CHANGED
|
@@ -358,6 +358,26 @@ function normalizeAndValidateMainField(
|
|
|
358
358
|
}
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Validate the `base_dir` field and return the normalized values.
|
|
363
|
+
*/
|
|
364
|
+
function normalizeAndValidateBaseDirField(
|
|
365
|
+
configPath: string | undefined,
|
|
366
|
+
rawDir: string | undefined
|
|
367
|
+
): string | undefined {
|
|
368
|
+
const configDir = path.dirname(configPath ?? "wrangler.toml");
|
|
369
|
+
if (rawDir !== undefined) {
|
|
370
|
+
if (typeof rawDir === "string") {
|
|
371
|
+
const directory = path.resolve(configDir);
|
|
372
|
+
return path.resolve(directory, rawDir);
|
|
373
|
+
} else {
|
|
374
|
+
return rawDir;
|
|
375
|
+
}
|
|
376
|
+
} else {
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
361
381
|
/**
|
|
362
382
|
* Validate the `dev` configuration and return the normalized values.
|
|
363
383
|
*/
|
|
@@ -1011,6 +1031,17 @@ function normalizeAndValidateEnvironment(
|
|
|
1011
1031
|
),
|
|
1012
1032
|
deprecatedUpload
|
|
1013
1033
|
),
|
|
1034
|
+
base_dir: normalizeAndValidateBaseDirField(
|
|
1035
|
+
configPath,
|
|
1036
|
+
inheritable(
|
|
1037
|
+
diagnostics,
|
|
1038
|
+
topLevelEnv,
|
|
1039
|
+
rawEnv,
|
|
1040
|
+
"base_dir",
|
|
1041
|
+
isString,
|
|
1042
|
+
undefined
|
|
1043
|
+
)
|
|
1044
|
+
),
|
|
1014
1045
|
route,
|
|
1015
1046
|
routes,
|
|
1016
1047
|
triggers: inheritable(
|
|
@@ -1074,6 +1105,16 @@ function normalizeAndValidateEnvironment(
|
|
|
1074
1105
|
validateBindingArray(envName, validateKVBinding),
|
|
1075
1106
|
[]
|
|
1076
1107
|
),
|
|
1108
|
+
send_email: notInheritable(
|
|
1109
|
+
diagnostics,
|
|
1110
|
+
topLevelEnv,
|
|
1111
|
+
rawConfig,
|
|
1112
|
+
rawEnv,
|
|
1113
|
+
envName,
|
|
1114
|
+
"send_email",
|
|
1115
|
+
validateBindingArray(envName, validateSendEmailBinding),
|
|
1116
|
+
[]
|
|
1117
|
+
),
|
|
1077
1118
|
queues: notInheritable(
|
|
1078
1119
|
diagnostics,
|
|
1079
1120
|
topLevelEnv,
|
|
@@ -1765,6 +1806,53 @@ const validateKVBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1765
1806
|
return isValid;
|
|
1766
1807
|
};
|
|
1767
1808
|
|
|
1809
|
+
const validateSendEmailBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
1810
|
+
if (typeof value !== "object" || value === null) {
|
|
1811
|
+
diagnostics.errors.push(
|
|
1812
|
+
`"send_email" bindings should be objects, but got ${JSON.stringify(
|
|
1813
|
+
value
|
|
1814
|
+
)}`
|
|
1815
|
+
);
|
|
1816
|
+
return false;
|
|
1817
|
+
}
|
|
1818
|
+
let isValid = true;
|
|
1819
|
+
// send email bindings must have a name.
|
|
1820
|
+
if (!isRequiredProperty(value, "name", "string")) {
|
|
1821
|
+
diagnostics.errors.push(
|
|
1822
|
+
`"${field}" bindings should have a string "name" field but got ${JSON.stringify(
|
|
1823
|
+
value
|
|
1824
|
+
)}.`
|
|
1825
|
+
);
|
|
1826
|
+
isValid = false;
|
|
1827
|
+
}
|
|
1828
|
+
if (!isOptionalProperty(value, "destination_address", "string")) {
|
|
1829
|
+
diagnostics.errors.push(
|
|
1830
|
+
`"${field}" bindings should, optionally, have a string "destination_address" field but got ${JSON.stringify(
|
|
1831
|
+
value
|
|
1832
|
+
)}.`
|
|
1833
|
+
);
|
|
1834
|
+
isValid = false;
|
|
1835
|
+
}
|
|
1836
|
+
if (!isOptionalProperty(value, "allowed_destination_addresses", "object")) {
|
|
1837
|
+
diagnostics.errors.push(
|
|
1838
|
+
`"${field}" bindings should, optionally, have a []string "allowed_destination_addresses" field but got ${JSON.stringify(
|
|
1839
|
+
value
|
|
1840
|
+
)}.`
|
|
1841
|
+
);
|
|
1842
|
+
isValid = false;
|
|
1843
|
+
}
|
|
1844
|
+
if (
|
|
1845
|
+
"destination_address" in value &&
|
|
1846
|
+
"allowed_destination_addresses" in value
|
|
1847
|
+
) {
|
|
1848
|
+
diagnostics.errors.push(
|
|
1849
|
+
`"${field}" bindings should have either a "destination_address" or "allowed_destination_addresses" field, but not both.`
|
|
1850
|
+
);
|
|
1851
|
+
isValid = false;
|
|
1852
|
+
}
|
|
1853
|
+
return isValid;
|
|
1854
|
+
};
|
|
1855
|
+
|
|
1768
1856
|
const validateQueueBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
1769
1857
|
if (typeof value !== "object" || value === null) {
|
|
1770
1858
|
diagnostics.errors.push(
|
|
@@ -2215,6 +2303,7 @@ const validateConsumer: ValidatorFn = (diagnostics, field, value, _config) => {
|
|
|
2215
2303
|
"max_batch_timeout",
|
|
2216
2304
|
"max_retries",
|
|
2217
2305
|
"dead_letter_queue",
|
|
2306
|
+
"max_concurrency",
|
|
2218
2307
|
])
|
|
2219
2308
|
) {
|
|
2220
2309
|
isValid = false;
|
|
@@ -2230,12 +2319,13 @@ const validateConsumer: ValidatorFn = (diagnostics, field, value, _config) => {
|
|
|
2230
2319
|
|
|
2231
2320
|
const options: {
|
|
2232
2321
|
key: string;
|
|
2233
|
-
type: "number" | "string";
|
|
2322
|
+
type: "number" | "string" | "boolean";
|
|
2234
2323
|
}[] = [
|
|
2235
2324
|
{ key: "max_batch_size", type: "number" },
|
|
2236
2325
|
{ key: "max_batch_timeout", type: "number" },
|
|
2237
2326
|
{ key: "max_retries", type: "number" },
|
|
2238
2327
|
{ key: "dead_letter_queue", type: "string" },
|
|
2328
|
+
{ key: "max_concurrency", type: "number" },
|
|
2239
2329
|
];
|
|
2240
2330
|
for (const optionalOpt of options) {
|
|
2241
2331
|
if (!isOptionalProperty(value, optionalOpt.key, optionalOpt.type)) {
|
|
@@ -23,7 +23,7 @@ export function toMimeType(type: CfModuleType): string {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
type WorkerMetadataBinding =
|
|
26
|
+
export type WorkerMetadataBinding =
|
|
27
27
|
// If you add any new binding types here, also add it to safeBindings
|
|
28
28
|
// under validateUnsafeBinding in config/validation.ts
|
|
29
29
|
| { type: "plain_text"; name: string; text: string }
|
|
@@ -32,6 +32,12 @@ type WorkerMetadataBinding =
|
|
|
32
32
|
| { type: "text_blob"; name: string; part: string }
|
|
33
33
|
| { type: "data_blob"; name: string; part: string }
|
|
34
34
|
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
35
|
+
| {
|
|
36
|
+
type: "send_email";
|
|
37
|
+
name: string;
|
|
38
|
+
destination_address?: string;
|
|
39
|
+
allowed_destination_addresses?: string[];
|
|
40
|
+
}
|
|
35
41
|
| {
|
|
36
42
|
type: "durable_object_namespace";
|
|
37
43
|
name: string;
|
|
@@ -105,6 +111,17 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
105
111
|
});
|
|
106
112
|
});
|
|
107
113
|
|
|
114
|
+
bindings.send_email?.forEach(
|
|
115
|
+
({ name, destination_address, allowed_destination_addresses }) => {
|
|
116
|
+
metadataBindings.push({
|
|
117
|
+
name: name,
|
|
118
|
+
type: "send_email",
|
|
119
|
+
destination_address,
|
|
120
|
+
allowed_destination_addresses,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
|
|
108
125
|
bindings.durable_objects?.bindings.forEach(
|
|
109
126
|
({ name, class_name, script_name, environment }) => {
|
|
110
127
|
metadataBindings.push({
|
package/src/d1/execute.tsx
CHANGED
|
@@ -37,7 +37,7 @@ export type QueryResult = {
|
|
|
37
37
|
};
|
|
38
38
|
query?: string;
|
|
39
39
|
};
|
|
40
|
-
// Max number of
|
|
40
|
+
// Max number of statements to send in a single /execute call
|
|
41
41
|
const QUERY_LIMIT = 10_000;
|
|
42
42
|
|
|
43
43
|
export function Options(yargs: CommonYargsArgv) {
|
|
@@ -172,10 +172,11 @@ Your database may not be available to serve requests during the migration, conti
|
|
|
172
172
|
}
|
|
173
173
|
} catch (e) {
|
|
174
174
|
const err = e as ParseError;
|
|
175
|
+
const maybeCause = (err.cause ?? err) as Error;
|
|
175
176
|
|
|
176
177
|
success = false;
|
|
177
178
|
errorNotes = err.notes?.map((msg) => msg.text) ?? [
|
|
178
|
-
|
|
179
|
+
maybeCause?.message ?? maybeCause.toString(),
|
|
179
180
|
];
|
|
180
181
|
}
|
|
181
182
|
|