wrangler 0.0.0-e33bea9b → 0.0.0-e351afcf
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 +167 -93
- package/package.json +14 -9
- 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__/constellation.test.ts +371 -0
- package/src/__tests__/d1/execute.test.ts +2 -0
- package/src/__tests__/d1/migrate.test.ts +102 -0
- package/src/__tests__/delete.test.ts +6 -0
- package/src/__tests__/deployments.test.ts +335 -95
- package/src/__tests__/dev.test.tsx +71 -56
- package/src/__tests__/generate.test.ts +3 -4
- package/src/__tests__/helpers/mock-console.ts +6 -0
- package/src/__tests__/helpers/msw/handlers/deployments.ts +70 -3
- package/src/__tests__/helpers/msw/index.ts +4 -2
- package/src/__tests__/helpers/worker-scripts/parent-worker.js +4 -1
- package/src/__tests__/index.test.ts +14 -6
- package/src/__tests__/init.test.ts +127 -96
- package/src/__tests__/jest.setup.ts +4 -0
- package/src/__tests__/kv.test.ts +9 -9
- package/src/__tests__/logger.test.ts +68 -2
- package/src/__tests__/middleware.scheduled.test.ts +2 -2
- package/src/__tests__/middleware.test.ts +2 -2
- package/src/__tests__/mtls-certificates.test.ts +7 -3
- package/src/__tests__/pages/deployment-list.test.ts +2 -0
- package/src/__tests__/pages/functions-build.test.ts +70 -0
- package/src/__tests__/pages/project-list.test.ts +2 -0
- package/src/__tests__/pages/project-upload.test.ts +43 -24
- package/src/__tests__/pages/publish.test.ts +136 -92
- package/src/__tests__/publish.test.ts +878 -332
- package/src/__tests__/pubsub.test.ts +3 -0
- package/src/__tests__/queues.test.ts +5 -2
- package/src/__tests__/traverse-module-graph.test.ts +220 -0
- package/src/__tests__/tsconfig.tsbuildinfo +1 -1
- package/src/__tests__/user.test.ts +1 -1
- package/src/__tests__/worker-namespace.test.ts +1 -0
- package/src/api/dev.ts +12 -18
- package/src/api/pages/create-worker-bundle-contents.ts +1 -0
- package/src/api/pages/publish.tsx +28 -12
- package/src/bundle.ts +26 -9
- package/src/config/environment.ts +27 -0
- package/src/config/index.ts +18 -0
- package/src/config/validation.ts +91 -1
- package/src/constellation/createProject.tsx +51 -0
- package/src/constellation/deleteProject.ts +51 -0
- package/src/constellation/deleteProjectModel.ts +68 -0
- package/src/constellation/index.ts +75 -0
- package/src/constellation/listCatalog.tsx +35 -0
- package/src/constellation/listModel.tsx +41 -0
- package/src/constellation/listProject.tsx +28 -0
- package/src/constellation/listRuntime.tsx +28 -0
- package/src/constellation/options.ts +17 -0
- package/src/constellation/types.ts +17 -0
- package/src/constellation/uploadModel.tsx +64 -0
- package/src/constellation/utils.ts +90 -0
- package/src/create-worker-upload-form.ts +18 -1
- package/src/d1/backups.tsx +19 -15
- package/src/d1/create.tsx +45 -39
- package/src/d1/delete.ts +1 -1
- package/src/d1/execute.tsx +23 -20
- package/src/d1/list.tsx +14 -12
- package/src/d1/migrations/apply.tsx +36 -30
- package/src/d1/migrations/create.tsx +15 -12
- package/src/d1/migrations/helpers.ts +5 -8
- package/src/d1/migrations/list.tsx +10 -7
- package/src/deployments.ts +260 -8
- package/src/dev/dev.tsx +2 -0
- package/src/dev/local.tsx +48 -20
- package/src/dev/start-server.ts +49 -46
- package/src/dev/use-esbuild.ts +52 -49
- package/src/dev-registry.ts +2 -1
- package/src/dev.tsx +11 -5
- package/src/docs/helpers.ts +50 -0
- package/src/docs/index.ts +25 -68
- package/src/entry.ts +19 -8
- package/src/environment-variables/factory.ts +2 -1
- package/src/generate/index.ts +2 -2
- package/src/index.ts +86 -23
- package/src/init.ts +144 -135
- package/src/kv/helpers.ts +7 -4
- package/src/logger.ts +23 -4
- package/src/metrics/send-event.ts +2 -1
- package/src/miniflare-cli/index.ts +2 -2
- package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -1
- package/src/module-collection.ts +92 -26
- package/src/pages/build.ts +29 -17
- package/src/pages/deployments.tsx +3 -5
- package/src/pages/dev.ts +24 -6
- package/src/pages/functions/buildPlugin.ts +1 -0
- package/src/pages/functions/buildWorker.ts +59 -8
- package/src/pages/functions/tsconfig.tsbuildinfo +1 -1
- package/src/pages/projects.tsx +2 -5
- package/src/pages/upload.tsx +24 -4
- 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/sites.ts +164 -52
- package/src/traverse-module-graph.ts +53 -0
- package/src/user/user.ts +1 -0
- package/src/utils/render.ts +93 -0
- package/src/worker.ts +10 -0
- package/wrangler-dist/cli.d.ts +29 -2
- package/wrangler-dist/cli.js +23270 -22110
- package/src/__tests__/api-devregistry.test.js +0 -64
|
@@ -60,7 +60,7 @@ describe("User", () => {
|
|
|
60
60
|
expect(counter).toBe(1);
|
|
61
61
|
expect(std.out).toMatchInlineSnapshot(`
|
|
62
62
|
"Attempting to login via OAuth...
|
|
63
|
-
Opening a link in your default browser: https://dash.cloudflare.com/oauth2/auth?response_type=code&client_id=54d11594-84e4-41aa-b438-e81b8fa78ee7&redirect_uri=http%3A%2F%2Flocalhost%3A8976%2Foauth%2Fcallback&scope=account%3Aread%20user%3Aread%20workers%3Awrite%20workers_kv%3Awrite%20workers_routes%3Awrite%20workers_scripts%3Awrite%20workers_tail%3Aread%20d1%3Awrite%20pages%3Awrite%20zone%3Aread%20ssl_certs%3Awrite%20offline_access&state=MOCK_STATE_PARAM&code_challenge=MOCK_CODE_CHALLENGE&code_challenge_method=S256
|
|
63
|
+
Opening a link in your default browser: https://dash.cloudflare.com/oauth2/auth?response_type=code&client_id=54d11594-84e4-41aa-b438-e81b8fa78ee7&redirect_uri=http%3A%2F%2Flocalhost%3A8976%2Foauth%2Fcallback&scope=account%3Aread%20user%3Aread%20workers%3Awrite%20workers_kv%3Awrite%20workers_routes%3Awrite%20workers_scripts%3Awrite%20workers_tail%3Aread%20d1%3Awrite%20pages%3Awrite%20zone%3Aread%20ssl_certs%3Awrite%20constellation%3Awrite%20offline_access&state=MOCK_STATE_PARAM&code_challenge=MOCK_CODE_CHALLENGE&code_challenge_method=S256
|
|
64
64
|
Successfully logged in."
|
|
65
65
|
`);
|
|
66
66
|
expect(readAuthConfigFile()).toEqual<UserAuthConfig>({
|
package/src/api/dev.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { startApiDev, startDev } from "../dev";
|
|
|
3
3
|
import { logger } from "../logger";
|
|
4
4
|
|
|
5
5
|
import type { Environment } from "../config";
|
|
6
|
+
import type { Rule } from "../config/environment";
|
|
6
7
|
import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli/types";
|
|
7
8
|
import type { RequestInit, Response, RequestInfo } from "undici";
|
|
8
9
|
|
|
@@ -42,6 +43,9 @@ export interface UnstableDevOptions {
|
|
|
42
43
|
bucket_name: string;
|
|
43
44
|
preview_bucket_name?: string;
|
|
44
45
|
}[];
|
|
46
|
+
processEntrypoint?: boolean;
|
|
47
|
+
moduleRoot?: string;
|
|
48
|
+
rules?: Rule[];
|
|
45
49
|
logLevel?: "none" | "info" | "error" | "log" | "warn" | "debug"; // Specify logging level [choices: "debug", "info", "log", "warn", "error", "none"] [default: "log"]
|
|
46
50
|
inspect?: boolean;
|
|
47
51
|
local?: boolean;
|
|
@@ -143,7 +147,6 @@ export async function unstable_dev(
|
|
|
143
147
|
enablePagesAssetsServiceBinding,
|
|
144
148
|
liveReload,
|
|
145
149
|
showInteractiveDevSession,
|
|
146
|
-
forceLocal,
|
|
147
150
|
onReady: (address, port) => {
|
|
148
151
|
readyPort = port;
|
|
149
152
|
readyAddress = address;
|
|
@@ -151,6 +154,7 @@ export async function unstable_dev(
|
|
|
151
154
|
},
|
|
152
155
|
config: options?.config,
|
|
153
156
|
env: options?.env,
|
|
157
|
+
processEntrypoint: !!options?.processEntrypoint,
|
|
154
158
|
bundle: options?.bundle,
|
|
155
159
|
compatibilityDate: options?.compatibilityDate,
|
|
156
160
|
compatibilityFlags: options?.compatibilityFlags,
|
|
@@ -232,6 +236,7 @@ export async function unstable_dev(
|
|
|
232
236
|
experimentalLocal: experimentalLocal ?? false,
|
|
233
237
|
experimentalLocalRemoteKv: experimentalLocalRemoteKv ?? false,
|
|
234
238
|
enablePagesAssetsServiceBinding,
|
|
239
|
+
forceLocal,
|
|
235
240
|
liveReload,
|
|
236
241
|
onReady: (address, port) => {
|
|
237
242
|
readyPort = port;
|
|
@@ -301,27 +306,16 @@ export async function unstable_dev(
|
|
|
301
306
|
export function parseRequestInput(
|
|
302
307
|
readyAddress: string,
|
|
303
308
|
readyPort: number,
|
|
304
|
-
input
|
|
309
|
+
input: RequestInfo = "/",
|
|
305
310
|
init?: RequestInit,
|
|
306
311
|
protocol: "http" | "https" = "http"
|
|
307
312
|
): [RequestInfo, RequestInit | undefined] {
|
|
308
313
|
if (input instanceof Request) {
|
|
309
314
|
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
315
|
}
|
|
325
|
-
|
|
326
|
-
|
|
316
|
+
const url = new URL(`${input}`, `${protocol}://${readyAddress}:${readyPort}`);
|
|
317
|
+
url.protocol = protocol;
|
|
318
|
+
url.hostname = readyAddress;
|
|
319
|
+
url.port = readyPort.toString();
|
|
320
|
+
return [url, init];
|
|
327
321
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
1
|
+
import { existsSync, lstatSync, readFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join, resolve as resolvePath } from "node:path";
|
|
4
4
|
import { cwd } from "node:process";
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import {
|
|
15
15
|
buildRawWorker,
|
|
16
16
|
checkRawWorker,
|
|
17
|
+
traverseAndBuildWorkerJSDirectory,
|
|
17
18
|
} from "../../pages/functions/buildWorker";
|
|
18
19
|
import { validateRoutes } from "../../pages/functions/routes-validation";
|
|
19
20
|
import { upload } from "../../pages/upload";
|
|
@@ -65,7 +66,7 @@ interface PagesPublishOptions {
|
|
|
65
66
|
|
|
66
67
|
/**
|
|
67
68
|
* Whether to run bundling on `_worker.js` before deploying.
|
|
68
|
-
* Default:
|
|
69
|
+
* Default: true
|
|
69
70
|
*/
|
|
70
71
|
bundle?: boolean;
|
|
71
72
|
|
|
@@ -95,9 +96,12 @@ export async function publish({
|
|
|
95
96
|
_redirects: string | undefined,
|
|
96
97
|
_routesGenerated: string | undefined,
|
|
97
98
|
_routesCustom: string | undefined,
|
|
99
|
+
_workerJSIsDirectory = false,
|
|
98
100
|
_workerJS: string | undefined;
|
|
99
101
|
|
|
100
|
-
|
|
102
|
+
bundle = bundle ?? true;
|
|
103
|
+
|
|
104
|
+
const _workerPath = resolvePath(directory, "_worker.js");
|
|
101
105
|
|
|
102
106
|
try {
|
|
103
107
|
_headers = readFileSync(join(directory, "_headers"), "utf-8");
|
|
@@ -116,7 +120,10 @@ export async function publish({
|
|
|
116
120
|
} catch {}
|
|
117
121
|
|
|
118
122
|
try {
|
|
119
|
-
|
|
123
|
+
_workerJSIsDirectory = lstatSync(_workerPath).isDirectory();
|
|
124
|
+
if (!_workerJSIsDirectory) {
|
|
125
|
+
_workerJS = readFileSync(_workerPath, "utf-8");
|
|
126
|
+
}
|
|
120
127
|
} catch {}
|
|
121
128
|
|
|
122
129
|
// Grab the bindings from the API, we need these for shims and other such hacky inserts
|
|
@@ -240,16 +247,23 @@ export async function publish({
|
|
|
240
247
|
* Advanced Mode
|
|
241
248
|
* https://developers.cloudflare.com/pages/platform/functions/#advanced-mode
|
|
242
249
|
*
|
|
243
|
-
* When using a _worker.js file, the entire /functions directory is ignored
|
|
250
|
+
* When using a _worker.js file or _worker.js/ directory, the entire /functions directory is ignored
|
|
244
251
|
* – this includes its routing and middleware characteristics.
|
|
245
252
|
*/
|
|
246
|
-
if (
|
|
253
|
+
if (_workerJSIsDirectory) {
|
|
254
|
+
workerBundle = await traverseAndBuildWorkerJSDirectory({
|
|
255
|
+
workerJSDirectory: _workerPath,
|
|
256
|
+
buildOutputDirectory: directory,
|
|
257
|
+
d1Databases,
|
|
258
|
+
nodejsCompat,
|
|
259
|
+
});
|
|
260
|
+
} else if (_workerJS) {
|
|
247
261
|
if (bundle) {
|
|
248
262
|
const outfile = join(tmpdir(), `./bundledWorker-${Math.random()}.mjs`);
|
|
249
263
|
workerBundle = await buildRawWorker({
|
|
250
|
-
workerScriptPath,
|
|
264
|
+
workerScriptPath: _workerPath,
|
|
251
265
|
outfile,
|
|
252
|
-
directory
|
|
266
|
+
directory,
|
|
253
267
|
local: false,
|
|
254
268
|
sourcemap: true,
|
|
255
269
|
watch: false,
|
|
@@ -258,17 +272,19 @@ export async function publish({
|
|
|
258
272
|
nodejsCompat,
|
|
259
273
|
});
|
|
260
274
|
} else {
|
|
261
|
-
await checkRawWorker(
|
|
262
|
-
// TODO:
|
|
275
|
+
await checkRawWorker(_workerPath, () => {});
|
|
276
|
+
// TODO: Let users configure this in the future.
|
|
263
277
|
workerBundle = {
|
|
264
278
|
modules: [],
|
|
265
279
|
dependencies: {},
|
|
266
280
|
stop: undefined,
|
|
267
|
-
resolvedEntryPointPath:
|
|
281
|
+
resolvedEntryPointPath: _workerPath,
|
|
268
282
|
bundleType: "esm",
|
|
269
283
|
};
|
|
270
284
|
}
|
|
285
|
+
}
|
|
271
286
|
|
|
287
|
+
if (_workerJS || _workerJSIsDirectory) {
|
|
272
288
|
const workerBundleContents = await createUploadWorkerBundleContents(
|
|
273
289
|
workerBundle as BundleResult
|
|
274
290
|
);
|
|
@@ -302,7 +318,7 @@ export async function publish({
|
|
|
302
318
|
* Pages Functions
|
|
303
319
|
* https://developers.cloudflare.com/pages/platform/functions/
|
|
304
320
|
*/
|
|
305
|
-
if (builtFunctions && !_workerJS) {
|
|
321
|
+
if (builtFunctions && !_workerJS && !_workerJSIsDirectory) {
|
|
306
322
|
const workerBundleContents = await createUploadWorkerBundleContents(
|
|
307
323
|
workerBundle as BundleResult
|
|
308
324
|
);
|
package/src/bundle.ts
CHANGED
|
@@ -7,12 +7,19 @@ import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
|
|
|
7
7
|
import * as esbuild from "esbuild";
|
|
8
8
|
import tmp from "tmp-promise";
|
|
9
9
|
import createModuleCollector from "./module-collection";
|
|
10
|
-
import { getBasePath
|
|
10
|
+
import { getBasePath } from "./paths";
|
|
11
11
|
import type { Config } from "./config";
|
|
12
12
|
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
|
*/
|
|
@@ -101,6 +117,8 @@ export async function bundleWorker(
|
|
|
101
117
|
entry: Entry,
|
|
102
118
|
destination: string,
|
|
103
119
|
options: {
|
|
120
|
+
// When `bundle` is set to false, we apply shims to the Worker, but won't pull in any imports
|
|
121
|
+
bundle?: boolean;
|
|
104
122
|
serveAssetsFromWorker: boolean;
|
|
105
123
|
assets?: StaticAssetsConfig;
|
|
106
124
|
betaD1Shims?: string[];
|
|
@@ -133,6 +151,7 @@ export async function bundleWorker(
|
|
|
133
151
|
}
|
|
134
152
|
): Promise<BundleResult> {
|
|
135
153
|
const {
|
|
154
|
+
bundle = true,
|
|
136
155
|
serveAssetsFromWorker,
|
|
137
156
|
betaD1Shims,
|
|
138
157
|
doBindings,
|
|
@@ -334,7 +353,7 @@ export async function bundleWorker(
|
|
|
334
353
|
|
|
335
354
|
const buildOptions: esbuild.BuildOptions & { metafile: true } = {
|
|
336
355
|
entryPoints: [inputEntry.file],
|
|
337
|
-
bundle
|
|
356
|
+
bundle,
|
|
338
357
|
absWorkingDir: entry.directory,
|
|
339
358
|
outdir: destination,
|
|
340
359
|
entryNames: entryName || path.parse(entry.file).name,
|
|
@@ -346,10 +365,9 @@ export async function bundleWorker(
|
|
|
346
365
|
}
|
|
347
366
|
: {}),
|
|
348
367
|
inject,
|
|
349
|
-
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
368
|
+
external: bundle ? ["__STATIC_CONTENT_MANIFEST"] : undefined,
|
|
350
369
|
format: entry.format === "modules" ? "esm" : "iife",
|
|
351
|
-
|
|
352
|
-
target: "es2022",
|
|
370
|
+
target: COMMON_ESBUILD_OPTIONS.target,
|
|
353
371
|
sourcemap: sourcemap ?? true, // this needs to use ?? to accept false
|
|
354
372
|
// Include a reference to the output folder in the sourcemap.
|
|
355
373
|
// This is omitted by default, but we need it to properly resolve source paths in error output.
|
|
@@ -368,9 +386,7 @@ export async function bundleWorker(
|
|
|
368
386
|
},
|
|
369
387
|
}),
|
|
370
388
|
loader: {
|
|
371
|
-
|
|
372
|
-
".mjs": "jsx",
|
|
373
|
-
".cjs": "jsx",
|
|
389
|
+
...COMMON_ESBUILD_OPTIONS.loader,
|
|
374
390
|
...(loader || {}),
|
|
375
391
|
},
|
|
376
392
|
plugins: [
|
|
@@ -379,6 +395,7 @@ export async function bundleWorker(
|
|
|
379
395
|
? [NodeGlobalsPolyfills({ buffer: true }), NodeModulesPolyfills()]
|
|
380
396
|
: []),
|
|
381
397
|
...(nodejsCompat ? [nodejsCompatPlugin] : []),
|
|
398
|
+
...[cloudflareJsPlugin],
|
|
382
399
|
...(plugins || []),
|
|
383
400
|
],
|
|
384
401
|
...(jsxFactory && { jsxFactory }),
|
|
@@ -585,7 +602,7 @@ async function applyMiddlewareLoaderFacade(
|
|
|
585
602
|
...Object.fromEntries(
|
|
586
603
|
middleware.map((val, index) => [
|
|
587
604
|
middlewareIdentifiers[index],
|
|
588
|
-
|
|
605
|
+
path.resolve(getBasePath(), val.path),
|
|
589
606
|
])
|
|
590
607
|
),
|
|
591
608
|
}),
|
|
@@ -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)) {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { fetchResult } from "../cfetch";
|
|
2
|
+
import { withConfig } from "../config";
|
|
3
|
+
import { logger } from "../logger";
|
|
4
|
+
import { requireAuth } from "../user";
|
|
5
|
+
import { takeName } from "./options";
|
|
6
|
+
import { constellationBetaWarning } from "./utils";
|
|
7
|
+
import type {
|
|
8
|
+
CommonYargsArgv,
|
|
9
|
+
StrictYargsOptionsToInterface,
|
|
10
|
+
} from "../yargs-types";
|
|
11
|
+
import type { Project } from "./types";
|
|
12
|
+
|
|
13
|
+
export function options(yargs: CommonYargsArgv) {
|
|
14
|
+
return takeName(yargs)
|
|
15
|
+
.positional("runtime", {
|
|
16
|
+
describe: "The name of the runtime to use",
|
|
17
|
+
type: "string",
|
|
18
|
+
demandOption: true,
|
|
19
|
+
})
|
|
20
|
+
.epilogue(constellationBetaWarning);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type HandlerOptions = StrictYargsOptionsToInterface<typeof options>;
|
|
24
|
+
export const handler = withConfig<HandlerOptions>(
|
|
25
|
+
async ({ name, runtime, config }): Promise<void> => {
|
|
26
|
+
const accountId = await requireAuth(config);
|
|
27
|
+
|
|
28
|
+
logger.log(constellationBetaWarning);
|
|
29
|
+
|
|
30
|
+
let proj: Project;
|
|
31
|
+
try {
|
|
32
|
+
proj = await fetchResult(`/accounts/${accountId}/constellation/project`, {
|
|
33
|
+
method: "POST",
|
|
34
|
+
headers: {
|
|
35
|
+
"Content-Type": "application/json",
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify({
|
|
38
|
+
name,
|
|
39
|
+
runtime,
|
|
40
|
+
}),
|
|
41
|
+
});
|
|
42
|
+
} catch (e) {
|
|
43
|
+
if ((e as { code: number }).code === 7409) {
|
|
44
|
+
throw new Error("A project with that name already exists");
|
|
45
|
+
}
|
|
46
|
+
throw e;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
logger.log(`✅ Successfully created Project "${proj.name}"!`);
|
|
50
|
+
}
|
|
51
|
+
);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { fetchResult } from "../cfetch";
|
|
2
|
+
import { withConfig } from "../config";
|
|
3
|
+
import { confirm } from "../dialogs";
|
|
4
|
+
import { logger } from "../logger";
|
|
5
|
+
import { requireAuth } from "../user";
|
|
6
|
+
import { takeName } from "./options";
|
|
7
|
+
import { constellationBetaWarning, getProjectByName } from "./utils";
|
|
8
|
+
import type {
|
|
9
|
+
CommonYargsArgv,
|
|
10
|
+
StrictYargsOptionsToInterface,
|
|
11
|
+
} from "../yargs-types";
|
|
12
|
+
|
|
13
|
+
export function options(yargs: CommonYargsArgv) {
|
|
14
|
+
return takeName(yargs)
|
|
15
|
+
.option("force", {
|
|
16
|
+
describe: "Skip confirmation",
|
|
17
|
+
type: "boolean",
|
|
18
|
+
alias: "f",
|
|
19
|
+
default: false,
|
|
20
|
+
})
|
|
21
|
+
.epilogue(constellationBetaWarning);
|
|
22
|
+
}
|
|
23
|
+
type HandlerOptions = StrictYargsOptionsToInterface<typeof options>;
|
|
24
|
+
export const handler = withConfig<HandlerOptions>(
|
|
25
|
+
async ({ name, force, config }): Promise<void> => {
|
|
26
|
+
const accountId = await requireAuth(config);
|
|
27
|
+
logger.log(constellationBetaWarning);
|
|
28
|
+
|
|
29
|
+
const proj = await getProjectByName(config, accountId, name);
|
|
30
|
+
|
|
31
|
+
logger.log(`About to delete Project '${name}' (${proj.id}).`);
|
|
32
|
+
if (!force) {
|
|
33
|
+
const response = await confirm(`Ok to proceed?`);
|
|
34
|
+
if (!response) {
|
|
35
|
+
logger.log(`Not deleting.`);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
logger.log("Deleting...");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
await fetchResult(
|
|
43
|
+
`/accounts/${accountId}/constellation/project/${proj.id}`,
|
|
44
|
+
{
|
|
45
|
+
method: "DELETE",
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
logger.log(`Deleted '${name}' successfully.`);
|
|
50
|
+
}
|
|
51
|
+
);
|