wrangler 2.0.27 → 2.1.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/bin/wrangler.js +1 -1
- package/miniflare-dist/index.mjs +1141 -369
- package/package.json +6 -4
- package/src/__tests__/api-dev.test.ts +19 -0
- package/src/__tests__/configuration.test.ts +27 -27
- package/src/__tests__/dev.test.tsx +8 -6
- package/src/__tests__/helpers/hello-world-worker.js +5 -0
- package/src/__tests__/helpers/mock-cfetch.ts +4 -4
- package/src/__tests__/helpers/mock-console.ts +11 -2
- package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
- package/src/__tests__/helpers/mock-known-routes.ts +7 -0
- package/src/__tests__/index.test.ts +37 -37
- package/src/__tests__/init.test.ts +356 -5
- package/src/__tests__/jest.setup.ts +13 -0
- package/src/__tests__/middleware.test.ts +768 -0
- package/src/__tests__/pages.test.ts +829 -104
- package/src/__tests__/paths.test.ts +17 -0
- package/src/__tests__/publish.test.ts +512 -445
- package/src/__tests__/tail.test.ts +79 -72
- package/src/__tests__/test-old-node-version.js +3 -3
- package/src/__tests__/worker-namespace.test.ts +37 -35
- package/src/api/dev.ts +93 -28
- package/src/bundle.ts +239 -12
- package/src/cfetch/internal.ts +64 -3
- package/src/cli.ts +1 -1
- package/src/config/environment.ts +1 -1
- package/src/config/index.ts +4 -4
- package/src/config/validation.ts +3 -3
- package/src/create-worker-upload-form.ts +29 -26
- package/src/dev/dev.tsx +3 -1
- package/src/dev/local.tsx +319 -171
- package/src/dev/remote.tsx +16 -4
- package/src/dev/start-server.ts +416 -0
- package/src/dev/use-esbuild.ts +4 -0
- package/src/dev.tsx +340 -166
- package/src/dialogs.tsx +12 -0
- package/src/{worker-namespace.ts → dispatch-namespace.ts} +18 -18
- package/src/entry.ts +2 -1
- package/src/index.tsx +59 -12
- package/src/init.ts +291 -16
- package/src/metrics/send-event.ts +6 -5
- package/src/miniflare-cli/assets.ts +130 -476
- package/src/miniflare-cli/index.ts +39 -33
- package/src/pages/constants.ts +3 -0
- package/src/pages/dev.tsx +8 -3
- package/src/pages/functions/buildPlugin.ts +2 -1
- package/src/pages/functions/buildWorker.ts +2 -1
- package/src/pages/functions/routes-transformation.test.ts +12 -1
- package/src/pages/functions/routes-transformation.ts +7 -1
- package/src/pages/hash.tsx +13 -0
- package/src/pages/publish.tsx +82 -38
- package/src/pages/upload.tsx +3 -18
- package/src/paths.ts +20 -1
- package/src/publish.ts +49 -8
- package/src/tail/filters.ts +1 -5
- package/src/tail/index.ts +6 -3
- package/src/worker.ts +10 -9
- package/src/zones.ts +91 -0
- package/templates/middleware/common.ts +62 -0
- package/templates/middleware/loader-modules.ts +84 -0
- package/templates/middleware/loader-sw.ts +213 -0
- package/templates/middleware/middleware-pretty-error.ts +40 -0
- package/templates/middleware/middleware-scheduled.ts +14 -0
- package/wrangler-dist/cli.d.ts +22 -8
- package/wrangler-dist/cli.js +71020 -65212
package/src/bundle.ts
CHANGED
|
@@ -7,6 +7,7 @@ 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 } from "./paths";
|
|
10
11
|
import type { Config } from "./config";
|
|
11
12
|
import type { WorkerRegistry } from "./dev-registry";
|
|
12
13
|
import type { Entry } from "./entry";
|
|
@@ -71,9 +72,10 @@ export async function bundleWorker(
|
|
|
71
72
|
nodeCompat: boolean | undefined;
|
|
72
73
|
define: Config["define"];
|
|
73
74
|
checkFetch: boolean;
|
|
74
|
-
services: Config["services"];
|
|
75
|
+
services: Config["services"] | undefined;
|
|
75
76
|
workerDefinitions: WorkerRegistry | undefined;
|
|
76
77
|
firstPartyWorkerDevFacade: boolean | undefined;
|
|
78
|
+
targetConsumer: "dev" | "publish";
|
|
77
79
|
}
|
|
78
80
|
): Promise<BundleResult> {
|
|
79
81
|
const {
|
|
@@ -90,6 +92,7 @@ export async function bundleWorker(
|
|
|
90
92
|
workerDefinitions,
|
|
91
93
|
services,
|
|
92
94
|
firstPartyWorkerDevFacade,
|
|
95
|
+
targetConsumer,
|
|
93
96
|
} = options;
|
|
94
97
|
|
|
95
98
|
// We create a temporary directory for any oneoff files we
|
|
@@ -129,7 +132,7 @@ export async function bundleWorker(
|
|
|
129
132
|
});
|
|
130
133
|
fs.writeFileSync(
|
|
131
134
|
checkedFetchFileToInject,
|
|
132
|
-
fs.readFileSync(path.resolve(
|
|
135
|
+
fs.readFileSync(path.resolve(getBasePath(), "templates/checked-fetch.js"))
|
|
133
136
|
);
|
|
134
137
|
}
|
|
135
138
|
|
|
@@ -139,6 +142,19 @@ export async function bundleWorker(
|
|
|
139
142
|
// a new entry point, that we call "middleware" or "facades".
|
|
140
143
|
// Look at implementations of these functions to learn more.
|
|
141
144
|
|
|
145
|
+
// We also have middleware that uses a more "traditional" middleware stack,
|
|
146
|
+
// which is all loaded as one in a stack.
|
|
147
|
+
const middlewareToLoad: MiddlewareLoader[] = [
|
|
148
|
+
// {
|
|
149
|
+
// path: "templates/middleware/middleware-pretty-error.ts",
|
|
150
|
+
// publish: true,
|
|
151
|
+
// dev: false,
|
|
152
|
+
// },
|
|
153
|
+
// {
|
|
154
|
+
// path: "../templates/middleware/middleware-scheduled.ts",
|
|
155
|
+
// },
|
|
156
|
+
];
|
|
157
|
+
|
|
142
158
|
type MiddlewareFn = (arg0: Entry) => Promise<Entry>;
|
|
143
159
|
const middleware: (false | undefined | MiddlewareFn)[] = [
|
|
144
160
|
// serve static assets
|
|
@@ -172,6 +188,32 @@ export async function bundleWorker(
|
|
|
172
188
|
((currentEntry: Entry) => {
|
|
173
189
|
return applyFirstPartyWorkerDevFacade(currentEntry, tmpDir.path);
|
|
174
190
|
}),
|
|
191
|
+
|
|
192
|
+
// Middleware loader: to add middleware, we add the path to the middleware
|
|
193
|
+
// Currently for demonstration purposes we have two example middlewares
|
|
194
|
+
// Middlewares are togglable by changing the `publish` (default=false) and `dev` (default=true) options
|
|
195
|
+
// As we are not yet supporting user created middlewares yet, if no wrangler applied middleware
|
|
196
|
+
// are found, we will not load any middleware. We also need to check if there are middlewares compatible with
|
|
197
|
+
// the target consumer (dev / publish).
|
|
198
|
+
(middlewareToLoad.filter(
|
|
199
|
+
(m) =>
|
|
200
|
+
(m.publish && targetConsumer === "publish") ||
|
|
201
|
+
(m.dev !== false && targetConsumer === "dev")
|
|
202
|
+
).length > 0 ||
|
|
203
|
+
process.env.EXPERIMENTAL_MIDDLEWARE === "true") &&
|
|
204
|
+
((currentEntry: Entry) => {
|
|
205
|
+
return applyMiddlewareLoaderFacade(
|
|
206
|
+
currentEntry,
|
|
207
|
+
tmpDir.path,
|
|
208
|
+
middlewareToLoad.filter(
|
|
209
|
+
// We dynamically filter the middleware depending on where we are bundling for
|
|
210
|
+
(m) =>
|
|
211
|
+
(targetConsumer === "dev" && m.dev !== false) ||
|
|
212
|
+
(m.publish && targetConsumer === "publish")
|
|
213
|
+
),
|
|
214
|
+
moduleCollector.plugin
|
|
215
|
+
);
|
|
216
|
+
}),
|
|
175
217
|
].filter(Boolean);
|
|
176
218
|
|
|
177
219
|
let inputEntry = entry;
|
|
@@ -214,7 +256,11 @@ export async function bundleWorker(
|
|
|
214
256
|
".cjs": "jsx",
|
|
215
257
|
},
|
|
216
258
|
plugins: [
|
|
217
|
-
moduleCollector
|
|
259
|
+
// We run the moduleCollector plugin for service workers as part of the middleware loader
|
|
260
|
+
// so we only run here for modules or with no middleware to load
|
|
261
|
+
...(entry.format === "modules" || middlewareToLoad.length === 0
|
|
262
|
+
? [moduleCollector.plugin]
|
|
263
|
+
: []),
|
|
218
264
|
...(nodeCompat
|
|
219
265
|
? [NodeGlobalsPolyfills({ buffer: true }), NodeModulesPolyfills()]
|
|
220
266
|
: // we use checkForNodeBuiltinsPlugin to throw a nicer error
|
|
@@ -302,7 +348,9 @@ async function applyFormatDevErrorsFacade(
|
|
|
302
348
|
): Promise<Entry> {
|
|
303
349
|
const targetPath = path.join(tmpDirPath, "format-dev-errors.entry.js");
|
|
304
350
|
await esbuild.build({
|
|
305
|
-
entryPoints: [
|
|
351
|
+
entryPoints: [
|
|
352
|
+
path.resolve(getBasePath(), "templates/format-dev-errors.ts"),
|
|
353
|
+
],
|
|
306
354
|
bundle: true,
|
|
307
355
|
sourcemap: true,
|
|
308
356
|
format: "esm",
|
|
@@ -320,6 +368,185 @@ async function applyFormatDevErrorsFacade(
|
|
|
320
368
|
};
|
|
321
369
|
}
|
|
322
370
|
|
|
371
|
+
/**
|
|
372
|
+
* A facade that acts as a "middleware loader".
|
|
373
|
+
* Instead of needing to apply a facade for each individual middleware, this allows
|
|
374
|
+
* middleware to be written in a more traditional manner and then be applied all
|
|
375
|
+
* at once, requiring just two esbuild steps, rather than 1 per middleware.
|
|
376
|
+
*/
|
|
377
|
+
|
|
378
|
+
interface MiddlewareLoader {
|
|
379
|
+
path: string;
|
|
380
|
+
// By default all middleware will run on dev, but will not be run when published
|
|
381
|
+
publish?: boolean;
|
|
382
|
+
dev?: boolean;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function applyMiddlewareLoaderFacade(
|
|
386
|
+
entry: Entry,
|
|
387
|
+
tmpDirPath: string,
|
|
388
|
+
middleware: MiddlewareLoader[], // a list of paths to middleware files
|
|
389
|
+
moduleCollectorPlugin: esbuild.Plugin
|
|
390
|
+
): Promise<Entry> {
|
|
391
|
+
// Firstly we need to insert the middleware array into the project,
|
|
392
|
+
// and then we load the middleware - this insertion and loading is
|
|
393
|
+
// different for each format.
|
|
394
|
+
|
|
395
|
+
// STEP 1: Insert the middleware
|
|
396
|
+
const targetPathInsertion = path.join(
|
|
397
|
+
tmpDirPath,
|
|
398
|
+
"middleware-insertion.entry.js"
|
|
399
|
+
);
|
|
400
|
+
|
|
401
|
+
// We need to import each of the middlewares, so we need to generate a
|
|
402
|
+
// random, unique identifier that we can use for the import.
|
|
403
|
+
// Middlewares are required to be default exports so we can import to any name.
|
|
404
|
+
const middlewareIdentifiers = middleware.map(
|
|
405
|
+
(_, index) => `__MIDDLEWARE_${index}__`
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
const dynamicFacadePath = path.join(
|
|
409
|
+
tmpDirPath,
|
|
410
|
+
"middleware-insertion-facade.js"
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
if (entry.format === "modules") {
|
|
414
|
+
// We use a facade to expose the required middleware alongside any user defined
|
|
415
|
+
// middleware on the worker object
|
|
416
|
+
|
|
417
|
+
const imports = middlewareIdentifiers
|
|
418
|
+
.map((m) => `import ${m} from "${m}";`)
|
|
419
|
+
.join("\n");
|
|
420
|
+
|
|
421
|
+
// write a file with all of the imports required
|
|
422
|
+
fs.writeFileSync(
|
|
423
|
+
dynamicFacadePath,
|
|
424
|
+
`import worker from "__ENTRY_POINT__";
|
|
425
|
+
${imports}
|
|
426
|
+
const facade = {
|
|
427
|
+
...worker,
|
|
428
|
+
middleware: [
|
|
429
|
+
${middlewareIdentifiers.join(",")}${middlewareIdentifiers.length > 0 ? "," : ""}
|
|
430
|
+
...(worker.middleware ? worker.middleware : []),
|
|
431
|
+
]
|
|
432
|
+
}
|
|
433
|
+
export * from "__ENTRY_POINT__";
|
|
434
|
+
export default facade;`
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
await esbuild.build({
|
|
438
|
+
entryPoints: [path.resolve(getBasePath(), dynamicFacadePath)],
|
|
439
|
+
bundle: true,
|
|
440
|
+
sourcemap: true,
|
|
441
|
+
format: "esm",
|
|
442
|
+
plugins: [
|
|
443
|
+
esbuildAliasExternalPlugin({
|
|
444
|
+
__ENTRY_POINT__: entry.file,
|
|
445
|
+
...Object.fromEntries(
|
|
446
|
+
middleware.map((val, index) => [
|
|
447
|
+
middlewareIdentifiers[index],
|
|
448
|
+
path.resolve(getBasePath(), val.path),
|
|
449
|
+
])
|
|
450
|
+
),
|
|
451
|
+
}),
|
|
452
|
+
],
|
|
453
|
+
outfile: targetPathInsertion,
|
|
454
|
+
});
|
|
455
|
+
} else {
|
|
456
|
+
// We handle service workers slightly differently as we have to overwrite
|
|
457
|
+
// the event listeners and reimplement them
|
|
458
|
+
|
|
459
|
+
await esbuild.build({
|
|
460
|
+
entryPoints: [entry.file],
|
|
461
|
+
bundle: true,
|
|
462
|
+
sourcemap: true,
|
|
463
|
+
define: {
|
|
464
|
+
"process.env.NODE_ENV": `"${process.env["NODE_ENV" + ""]}"`,
|
|
465
|
+
},
|
|
466
|
+
format: "esm",
|
|
467
|
+
outfile: targetPathInsertion,
|
|
468
|
+
plugins: [moduleCollectorPlugin],
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
const imports = middlewareIdentifiers
|
|
472
|
+
.map(
|
|
473
|
+
(m, i) =>
|
|
474
|
+
`import ${m} from "${path.resolve(
|
|
475
|
+
getBasePath(),
|
|
476
|
+
middleware[i].path
|
|
477
|
+
)}";`
|
|
478
|
+
)
|
|
479
|
+
.join("\n");
|
|
480
|
+
|
|
481
|
+
// We add the new modules with imports and then register using the
|
|
482
|
+
// addMiddleware function (which gets rewritten in the next build step)
|
|
483
|
+
|
|
484
|
+
// We choose to run middleware inserted in wrangler before user inserted
|
|
485
|
+
// middleware in the stack
|
|
486
|
+
// To do this, we either need to execute the addMiddleware function first
|
|
487
|
+
// before any user middleware, or use a separate handling function.
|
|
488
|
+
// We choose to do the latter as to prepend, we would have to load the entire
|
|
489
|
+
// script into memory as a prepend function doesn't exist or work in the same
|
|
490
|
+
// way that an append function does.
|
|
491
|
+
|
|
492
|
+
fs.copyFileSync(targetPathInsertion, dynamicFacadePath);
|
|
493
|
+
fs.appendFileSync(
|
|
494
|
+
dynamicFacadePath,
|
|
495
|
+
`
|
|
496
|
+
${imports}
|
|
497
|
+
addMiddlewareInternal([${middlewareIdentifiers.join(",")}])
|
|
498
|
+
`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// STEP 2: Load the middleware
|
|
503
|
+
// We want to get the filename of the orginal entry point
|
|
504
|
+
let targetPathLoader = path.join(tmpDirPath, path.basename(entry.file));
|
|
505
|
+
if (path.extname(entry.file) === "") targetPathLoader += ".js";
|
|
506
|
+
|
|
507
|
+
const loaderPath =
|
|
508
|
+
entry.format === "modules"
|
|
509
|
+
? path.resolve(getBasePath(), "templates/middleware/loader-modules.ts")
|
|
510
|
+
: dynamicFacadePath;
|
|
511
|
+
|
|
512
|
+
await esbuild.build({
|
|
513
|
+
entryPoints: [loaderPath],
|
|
514
|
+
bundle: true,
|
|
515
|
+
sourcemap: true,
|
|
516
|
+
format: "esm",
|
|
517
|
+
...(entry.format === "service-worker"
|
|
518
|
+
? {
|
|
519
|
+
inject: [
|
|
520
|
+
path.resolve(getBasePath(), "templates/middleware/loader-sw.ts"),
|
|
521
|
+
],
|
|
522
|
+
define: {
|
|
523
|
+
addEventListener: "__facade_addEventListener__",
|
|
524
|
+
removeEventListener: "__facade_removeEventListener__",
|
|
525
|
+
dispatchEvent: "__facade_dispatchEvent__",
|
|
526
|
+
addMiddleware: "__facade_register__",
|
|
527
|
+
addMiddlewareInternal: "__facade_registerInternal__",
|
|
528
|
+
},
|
|
529
|
+
}
|
|
530
|
+
: {
|
|
531
|
+
plugins: [
|
|
532
|
+
esbuildAliasExternalPlugin({
|
|
533
|
+
__ENTRY_POINT__: targetPathInsertion,
|
|
534
|
+
"./common": path.resolve(
|
|
535
|
+
getBasePath(),
|
|
536
|
+
"templates/middleware/common.ts"
|
|
537
|
+
),
|
|
538
|
+
}),
|
|
539
|
+
],
|
|
540
|
+
}),
|
|
541
|
+
outfile: targetPathLoader,
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
return {
|
|
545
|
+
...entry,
|
|
546
|
+
file: targetPathLoader,
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
323
550
|
/**
|
|
324
551
|
* A middleware that serves static assets from a worker.
|
|
325
552
|
* This powers --assets / config.assets
|
|
@@ -334,7 +561,7 @@ async function applyStaticAssetFacade(
|
|
|
334
561
|
|
|
335
562
|
await esbuild.build({
|
|
336
563
|
entryPoints: [
|
|
337
|
-
path.resolve(
|
|
564
|
+
path.resolve(getBasePath(), "templates/serve-static-assets.ts"),
|
|
338
565
|
],
|
|
339
566
|
bundle: true,
|
|
340
567
|
format: "esm",
|
|
@@ -342,7 +569,7 @@ async function applyStaticAssetFacade(
|
|
|
342
569
|
plugins: [
|
|
343
570
|
esbuildAliasExternalPlugin({
|
|
344
571
|
__ENTRY_POINT__: entry.file,
|
|
345
|
-
__KV_ASSET_HANDLER__: path.join(
|
|
572
|
+
__KV_ASSET_HANDLER__: path.join(getBasePath(), "kv-asset-handler.js"),
|
|
346
573
|
__STATIC_CONTENT_MANIFEST: "__STATIC_CONTENT_MANIFEST",
|
|
347
574
|
}),
|
|
348
575
|
],
|
|
@@ -380,7 +607,7 @@ async function applyMultiWorkerDevFacade(
|
|
|
380
607
|
services: Config["services"],
|
|
381
608
|
workerDefinitions: WorkerRegistry
|
|
382
609
|
) {
|
|
383
|
-
const targetPath = path.join(tmpDirPath, "
|
|
610
|
+
const targetPath = path.join(tmpDirPath, "multiworker-dev-facade.entry.js");
|
|
384
611
|
const serviceMap = Object.fromEntries(
|
|
385
612
|
(services || []).map((serviceBinding) => [
|
|
386
613
|
serviceBinding.binding,
|
|
@@ -391,10 +618,10 @@ async function applyMultiWorkerDevFacade(
|
|
|
391
618
|
await esbuild.build({
|
|
392
619
|
entryPoints: [
|
|
393
620
|
path.join(
|
|
394
|
-
|
|
621
|
+
getBasePath(),
|
|
395
622
|
entry.format === "modules"
|
|
396
|
-
? "
|
|
397
|
-
: "
|
|
623
|
+
? "templates/service-bindings-module-facade.js"
|
|
624
|
+
: "templates/service-bindings-sw-facade.js"
|
|
398
625
|
),
|
|
399
626
|
],
|
|
400
627
|
bundle: true,
|
|
@@ -440,8 +667,8 @@ async function applyFirstPartyWorkerDevFacade(
|
|
|
440
667
|
await esbuild.build({
|
|
441
668
|
entryPoints: [
|
|
442
669
|
path.resolve(
|
|
443
|
-
|
|
444
|
-
"
|
|
670
|
+
getBasePath(),
|
|
671
|
+
"templates/first-party-worker-module-facade.ts"
|
|
445
672
|
),
|
|
446
673
|
],
|
|
447
674
|
bundle: true,
|
package/src/cfetch/internal.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import assert from "node:assert";
|
|
2
|
-
import
|
|
2
|
+
import Busboy from "busboy";
|
|
3
|
+
import { fetch, File, FormData, Headers } from "undici";
|
|
3
4
|
import { version as wranglerVersion } from "../../package.json";
|
|
4
5
|
import { getEnvironmentVariableFactory } from "../environment-variables";
|
|
5
6
|
import { logger } from "../logger";
|
|
@@ -226,12 +227,72 @@ export async function fetchDashboardScript(
|
|
|
226
227
|
?.startsWith("multipart");
|
|
227
228
|
|
|
228
229
|
if (usesModules) {
|
|
229
|
-
|
|
230
|
+
// Response from edge contains generic "name = worker.js" for dashboard created scripts
|
|
231
|
+
const form = await formData(response);
|
|
232
|
+
const entries = Array.from(form.entries());
|
|
233
|
+
if (entries.length > 1)
|
|
234
|
+
throw new RangeError("Expected only one entry in multipart response");
|
|
235
|
+
const [_, file] = entries[0];
|
|
236
|
+
|
|
237
|
+
if (file instanceof File) {
|
|
238
|
+
return await file.text();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return file ?? "";
|
|
230
242
|
|
|
231
243
|
// Follow up on issue in Undici about multipart/form-data support & replace the workaround: https://github.com/nodejs/undici/issues/974
|
|
232
244
|
// This should be using a builtin formData() parser pattern.
|
|
233
|
-
return file.split("\n").slice(4, -4).join("\n");
|
|
234
245
|
} else {
|
|
235
246
|
return response.text();
|
|
236
247
|
}
|
|
237
248
|
}
|
|
249
|
+
|
|
250
|
+
async function formData({ headers, body }: Response): Promise<FormData> {
|
|
251
|
+
// undici doesn't include a multipart/form-data parser yet, so we parse
|
|
252
|
+
// form data with busboy instead
|
|
253
|
+
const contentType = headers.get("Content-Type") ?? "";
|
|
254
|
+
if (!/multipart\/form-data/.test(contentType))
|
|
255
|
+
throw Error("Need Content-Type for multipart/form-data");
|
|
256
|
+
|
|
257
|
+
const responseFormData = new FormData();
|
|
258
|
+
|
|
259
|
+
let busboy: Busboy.Busboy;
|
|
260
|
+
|
|
261
|
+
const parsedHeaders = Object.fromEntries(
|
|
262
|
+
Array.from(headers).map(([header, value]) => [header.toLowerCase(), value])
|
|
263
|
+
);
|
|
264
|
+
try {
|
|
265
|
+
busboy = Busboy({ headers: parsedHeaders });
|
|
266
|
+
} catch (err) {
|
|
267
|
+
// Error due to headers:
|
|
268
|
+
throw Object.assign(new TypeError(), { cause: err });
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
busboy.on("field", (name, value) => {
|
|
272
|
+
responseFormData.append(name, value);
|
|
273
|
+
});
|
|
274
|
+
busboy.on("file", (name, value, info) => {
|
|
275
|
+
const { filename, encoding, mimeType } = info;
|
|
276
|
+
const base64 = encoding.toLowerCase() === "base64";
|
|
277
|
+
const chunks: Buffer[] = [];
|
|
278
|
+
value.on("data", (chunk) => {
|
|
279
|
+
if (base64) chunk = Buffer.from(chunk.toString(), "base64");
|
|
280
|
+
chunks.push(chunk);
|
|
281
|
+
});
|
|
282
|
+
value.on("end", () => {
|
|
283
|
+
const file = new File(chunks, filename, { type: mimeType });
|
|
284
|
+
responseFormData.append(name, file);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
const busboyResolve = new Promise((resolve, reject) => {
|
|
289
|
+
busboy.on("finish", resolve);
|
|
290
|
+
busboy.on("error", (err) => reject(err));
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
if (body !== null) for await (const chunk of body) busboy.write(chunk);
|
|
294
|
+
busboy.end();
|
|
295
|
+
await busboyResolve;
|
|
296
|
+
|
|
297
|
+
return responseFormData;
|
|
298
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -210,7 +210,7 @@ interface EnvironmentInheritable {
|
|
|
210
210
|
* @default `[]`
|
|
211
211
|
* @nonInheritable
|
|
212
212
|
*/
|
|
213
|
-
|
|
213
|
+
dispatch_namespaces: {
|
|
214
214
|
/** The binding name used to refer to the bound service. */
|
|
215
215
|
binding: string;
|
|
216
216
|
/** The namespace to bind to. */
|
package/src/config/index.ts
CHANGED
|
@@ -91,7 +91,7 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
|
91
91
|
unsafe,
|
|
92
92
|
vars,
|
|
93
93
|
wasm_modules,
|
|
94
|
-
|
|
94
|
+
dispatch_namespaces,
|
|
95
95
|
} = bindings;
|
|
96
96
|
|
|
97
97
|
if (data_blobs !== undefined && Object.keys(data_blobs).length > 0) {
|
|
@@ -219,10 +219,10 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
|
219
219
|
});
|
|
220
220
|
}
|
|
221
221
|
|
|
222
|
-
if (
|
|
222
|
+
if (dispatch_namespaces !== undefined && dispatch_namespaces.length > 0) {
|
|
223
223
|
output.push({
|
|
224
|
-
type: "
|
|
225
|
-
entries:
|
|
224
|
+
type: "dispatch namespaces",
|
|
225
|
+
entries: dispatch_namespaces.map(({ binding, namespace }) => {
|
|
226
226
|
return {
|
|
227
227
|
key: binding,
|
|
228
228
|
value: namespace,
|
package/src/config/validation.ts
CHANGED
|
@@ -899,7 +899,7 @@ function normalizeAndValidateEnvironment(
|
|
|
899
899
|
|
|
900
900
|
experimental(diagnostics, rawEnv, "unsafe");
|
|
901
901
|
experimental(diagnostics, rawEnv, "services");
|
|
902
|
-
experimental(diagnostics, rawEnv, "
|
|
902
|
+
experimental(diagnostics, rawEnv, "dispatch_namespaces");
|
|
903
903
|
|
|
904
904
|
const route = normalizeAndValidateRoute(diagnostics, topLevelEnv, rawEnv);
|
|
905
905
|
|
|
@@ -1085,13 +1085,13 @@ function normalizeAndValidateEnvironment(
|
|
|
1085
1085
|
validateBindingArray(envName, validateServiceBinding),
|
|
1086
1086
|
[]
|
|
1087
1087
|
),
|
|
1088
|
-
|
|
1088
|
+
dispatch_namespaces: notInheritable(
|
|
1089
1089
|
diagnostics,
|
|
1090
1090
|
topLevelEnv,
|
|
1091
1091
|
rawConfig,
|
|
1092
1092
|
rawEnv,
|
|
1093
1093
|
envName,
|
|
1094
|
-
"
|
|
1094
|
+
"dispatch_namespaces",
|
|
1095
1095
|
validateBindingArray(envName, validateWorkerNamespaceBinding),
|
|
1096
1096
|
[]
|
|
1097
1097
|
),
|
|
@@ -23,6 +23,31 @@ export function toMimeType(type: CfModuleType): string {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
type WorkerMetadataBinding =
|
|
27
|
+
// If you add any new binding types here, also add it to safeBindings
|
|
28
|
+
// under validateUnsafeBinding in config/validation.ts
|
|
29
|
+
| { type: "plain_text"; name: string; text: string }
|
|
30
|
+
| { type: "json"; name: string; json: unknown }
|
|
31
|
+
| { type: "wasm_module"; name: string; part: string }
|
|
32
|
+
| { type: "text_blob"; name: string; part: string }
|
|
33
|
+
| { type: "data_blob"; name: string; part: string }
|
|
34
|
+
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
35
|
+
| {
|
|
36
|
+
type: "durable_object_namespace";
|
|
37
|
+
name: string;
|
|
38
|
+
class_name: string;
|
|
39
|
+
script_name?: string;
|
|
40
|
+
environment?: string;
|
|
41
|
+
}
|
|
42
|
+
| { type: "r2_bucket"; name: string; bucket_name: string }
|
|
43
|
+
| { type: "service"; name: string; service: string; environment?: string }
|
|
44
|
+
| { type: "namespace"; name: string; namespace: string }
|
|
45
|
+
| {
|
|
46
|
+
type: "logfwdr";
|
|
47
|
+
name: string;
|
|
48
|
+
destination: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
26
51
|
export interface WorkerMetadata {
|
|
27
52
|
/** The name of the entry point module. Only exists when the worker is in the ES module format */
|
|
28
53
|
main_module?: string;
|
|
@@ -33,31 +58,8 @@ export interface WorkerMetadata {
|
|
|
33
58
|
usage_model?: "bundled" | "unbound";
|
|
34
59
|
migrations?: CfDurableObjectMigrations;
|
|
35
60
|
capnp_schema?: string;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
bindings: (
|
|
39
|
-
| { type: "plain_text"; name: string; text: string }
|
|
40
|
-
| { type: "json"; name: string; json: unknown }
|
|
41
|
-
| { type: "wasm_module"; name: string; part: string }
|
|
42
|
-
| { type: "text_blob"; name: string; part: string }
|
|
43
|
-
| { type: "data_blob"; name: string; part: string }
|
|
44
|
-
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
45
|
-
| {
|
|
46
|
-
type: "durable_object_namespace";
|
|
47
|
-
name: string;
|
|
48
|
-
class_name: string;
|
|
49
|
-
script_name?: string;
|
|
50
|
-
environment?: string;
|
|
51
|
-
}
|
|
52
|
-
| { type: "r2_bucket"; name: string; bucket_name: string }
|
|
53
|
-
| { type: "service"; name: string; service: string; environment?: string }
|
|
54
|
-
| { type: "namespace"; name: string; namespace: string }
|
|
55
|
-
| {
|
|
56
|
-
type: "logfwdr";
|
|
57
|
-
name: string;
|
|
58
|
-
destination: string;
|
|
59
|
-
}
|
|
60
|
-
)[];
|
|
61
|
+
bindings: WorkerMetadataBinding[];
|
|
62
|
+
keep_bindings: WorkerMetadataBinding["type"][];
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
/**
|
|
@@ -123,7 +125,7 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
123
125
|
});
|
|
124
126
|
});
|
|
125
127
|
|
|
126
|
-
bindings.
|
|
128
|
+
bindings.dispatch_namespaces?.forEach(({ binding, namespace }) => {
|
|
127
129
|
metadataBindings.push({
|
|
128
130
|
name: binding,
|
|
129
131
|
type: "namespace",
|
|
@@ -255,6 +257,7 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
255
257
|
...(usage_model && { usage_model }),
|
|
256
258
|
...(migrations && { migrations }),
|
|
257
259
|
capnp_schema: bindings.logfwdr?.schema,
|
|
260
|
+
keep_bindings: ["plain_text", "json"],
|
|
258
261
|
};
|
|
259
262
|
|
|
260
263
|
formData.set("metadata", JSON.stringify(metadata));
|
package/src/dev/dev.tsx
CHANGED
|
@@ -158,7 +158,7 @@ export type DevProps = {
|
|
|
158
158
|
inspect: boolean;
|
|
159
159
|
logLevel: "none" | "error" | "log" | "warn" | "debug" | undefined;
|
|
160
160
|
logPrefix?: string;
|
|
161
|
-
onReady: (() => void) | undefined;
|
|
161
|
+
onReady: ((ip: string, port: number) => void) | undefined;
|
|
162
162
|
showInteractiveDevSession: boolean | undefined;
|
|
163
163
|
forceLocal: boolean | undefined;
|
|
164
164
|
enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
|
|
@@ -257,6 +257,8 @@ function DevSession(props: DevSessionProps) {
|
|
|
257
257
|
services: props.bindings.services,
|
|
258
258
|
durableObjects: props.bindings.durable_objects || { bindings: [] },
|
|
259
259
|
firstPartyWorkerDevFacade: props.firstPartyWorker,
|
|
260
|
+
// Enable the bundling to know whether we are using dev or publish
|
|
261
|
+
targetConsumer: "dev",
|
|
260
262
|
});
|
|
261
263
|
|
|
262
264
|
return props.local ? (
|