wrangler 0.0.0-e33294b0 → 0.0.0-e33bea9b
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 +21 -4
- package/package.json +8 -8
- package/src/__tests__/configuration.test.ts +235 -6
- package/src/__tests__/d1/execute.test.ts +1 -1
- package/src/__tests__/dev.test.tsx +16 -1
- package/src/__tests__/guess-worker-format.test.ts +35 -0
- package/src/__tests__/init.test.ts +9 -8
- package/src/__tests__/middleware.test.ts +2 -1
- package/src/__tests__/pages/functions-build.test.ts +91 -41
- package/src/__tests__/pages/publish.test.ts +136 -61
- package/src/__tests__/publish.test.ts +169 -14
- package/src/__tests__/test-old-node-version.js +3 -3
- package/src/__tests__/tsconfig.tsbuildinfo +1 -1
- package/src/__tests__/type-generation.test.ts +1 -0
- package/src/__tests__/user.test.ts +1 -1
- package/src/api/dev.ts +3 -4
- package/src/api/pages/publish.tsx +38 -51
- package/src/bundle.ts +27 -9
- package/src/config/environment.ts +9 -4
- package/src/config/index.ts +12 -2
- package/src/config/validation-helpers.ts +1 -1
- package/src/config/validation.ts +58 -4
- package/src/create-worker-preview.ts +19 -8
- package/src/create-worker-upload-form.ts +10 -2
- package/src/dev/dev.tsx +4 -4
- package/src/dev/local.tsx +18 -21
- package/src/dev/start-server.ts +10 -8
- package/src/dev/use-esbuild.ts +9 -5
- package/src/dev/validate-dev-props.ts +9 -0
- package/src/dev.tsx +27 -13
- package/src/entry.ts +1 -2
- package/src/index.ts +1 -1
- package/src/init.ts +1 -0
- package/src/inspect.ts +16 -1
- package/src/miniflare-cli/assets.ts +8 -7
- package/src/miniflare-cli/index.ts +1 -1
- package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -1
- package/src/pages/build.ts +277 -99
- package/src/pages/buildFunctions.ts +20 -10
- package/src/pages/dev.ts +21 -21
- package/src/pages/errors.ts +1 -0
- package/src/pages/functions/buildPlugin.ts +56 -34
- package/src/pages/functions/buildWorker.ts +32 -23
- package/src/pages/functions/tsconfig.tsbuildinfo +1 -1
- package/src/pages/publish.tsx +1 -10
- package/src/pages/upload.tsx +5 -5
- package/src/paths.ts +1 -1
- package/src/publish/index.ts +1 -1
- package/src/publish/publish.ts +19 -8
- package/src/r2/helpers.ts +1 -0
- package/src/secret/index.ts +1 -1
- package/src/type-generation.ts +1 -1
- package/src/user/user.ts +1 -1
- package/src/worker.ts +8 -1
- package/templates/d1-beta-facade.js +5 -2
- package/templates/gitignore +1 -0
- package/templates/middleware/middleware-miniflare3-json-error.ts +17 -5
- package/templates/pages-template-plugin.ts +7 -3
- package/templates/tsconfig.init.json +1 -2
- package/templates/tsconfig.tsbuildinfo +1 -1
- package/wrangler-dist/cli.d.ts +523 -14
- package/wrangler-dist/cli.js +11473 -4544
|
@@ -85,7 +85,7 @@ describe("User", () => {
|
|
|
85
85
|
await expect(
|
|
86
86
|
requireAuth({} as Config)
|
|
87
87
|
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
88
|
-
`"In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/
|
|
88
|
+
`"In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/fundamentals/api/get-started/create-token/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."`
|
|
89
89
|
);
|
|
90
90
|
});
|
|
91
91
|
|
package/src/api/dev.ts
CHANGED
|
@@ -18,7 +18,7 @@ export interface UnstableDevOptions {
|
|
|
18
18
|
site?: string; // Root folder of static assets for Workers Sites
|
|
19
19
|
siteInclude?: string[]; // Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.
|
|
20
20
|
siteExclude?: string[]; // Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.
|
|
21
|
-
nodeCompat?: boolean; // Enable
|
|
21
|
+
nodeCompat?: boolean; // Enable Node.js compatibility
|
|
22
22
|
compatibilityDate?: string; // Date to use for compatibility checks
|
|
23
23
|
compatibilityFlags?: string[]; // Flags to use for compatibility checks
|
|
24
24
|
persist?: boolean; // Enable persistence for local mode, using default path: .wrangler/state
|
|
@@ -43,7 +43,6 @@ export interface UnstableDevOptions {
|
|
|
43
43
|
preview_bucket_name?: string;
|
|
44
44
|
}[];
|
|
45
45
|
logLevel?: "none" | "info" | "error" | "log" | "warn" | "debug"; // Specify logging level [choices: "debug", "info", "log", "warn", "error", "none"] [default: "log"]
|
|
46
|
-
logPrefix?: string;
|
|
47
46
|
inspect?: boolean;
|
|
48
47
|
local?: boolean;
|
|
49
48
|
accountId?: string;
|
|
@@ -163,7 +162,7 @@ export async function unstable_dev(
|
|
|
163
162
|
site: options?.site, // Root folder of static assets for Workers Sites
|
|
164
163
|
siteInclude: options?.siteInclude, // Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.
|
|
165
164
|
siteExclude: options?.siteExclude, // Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.
|
|
166
|
-
nodeCompat: options?.nodeCompat, // Enable
|
|
165
|
+
nodeCompat: options?.nodeCompat, // Enable Node.js compatibility
|
|
167
166
|
persist: options?.persist, // Enable persistence for local mode, using default path: .wrangler/state
|
|
168
167
|
persistTo: options?.persistTo, // Specify directory to use for local persistence (implies --persist)
|
|
169
168
|
experimentalJsonConfig: undefined,
|
|
@@ -252,7 +251,7 @@ export async function unstable_dev(
|
|
|
252
251
|
site: options?.site, // Root folder of static assets for Workers Sites
|
|
253
252
|
siteInclude: options?.siteInclude, // Array of .gitignore-style patterns that match file or directory names from the sites directory. Only matched items will be uploaded.
|
|
254
253
|
siteExclude: options?.siteExclude, // Array of .gitignore-style patterns that match file or directory names from the sites directory. Matched items will not be uploaded.
|
|
255
|
-
nodeCompat: options?.nodeCompat, // Enable
|
|
254
|
+
nodeCompat: options?.nodeCompat, // Enable Node.js compatibility
|
|
256
255
|
persist: options?.persist, // Enable persistence for local mode, using default path: .wrangler/state
|
|
257
256
|
persistTo: options?.persistTo, // Specify directory to use for local persistence (implies --persist)
|
|
258
257
|
experimentalJsonConfig: undefined,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
|
-
import {
|
|
3
|
+
import { join, resolve as resolvePath } from "node:path";
|
|
4
4
|
import { cwd } from "node:process";
|
|
5
5
|
import { File, FormData } from "undici";
|
|
6
6
|
import { fetchResult } from "../../cfetch";
|
|
@@ -69,14 +69,6 @@ interface PagesPublishOptions {
|
|
|
69
69
|
*/
|
|
70
70
|
bundle?: boolean;
|
|
71
71
|
|
|
72
|
-
/**
|
|
73
|
-
* Whether to process non-JS module imports or not, such as
|
|
74
|
-
* wasm/text/binary, when we run bundling on `functions` or
|
|
75
|
-
* `_worker.js`
|
|
76
|
-
* Default: false
|
|
77
|
-
*/
|
|
78
|
-
experimentalWorkerBundle?: boolean;
|
|
79
|
-
|
|
80
72
|
// TODO: Allow passing in the API key and plumb it through
|
|
81
73
|
// to the API calls so that the publish function does not
|
|
82
74
|
// rely on the `CLOUDFLARE_API_KEY` environment variable
|
|
@@ -98,7 +90,6 @@ export async function publish({
|
|
|
98
90
|
commitDirty,
|
|
99
91
|
functionsDirectory: customFunctionsDirectory,
|
|
100
92
|
bundle,
|
|
101
|
-
experimentalWorkerBundle,
|
|
102
93
|
}: PagesPublishOptions) {
|
|
103
94
|
let _headers: string | undefined,
|
|
104
95
|
_redirects: string | undefined,
|
|
@@ -137,6 +128,11 @@ export async function publish({
|
|
|
137
128
|
isProduction = project.production_branch === branch;
|
|
138
129
|
}
|
|
139
130
|
|
|
131
|
+
const deploymentConfig =
|
|
132
|
+
project.deployment_configs[isProduction ? "production" : "preview"];
|
|
133
|
+
const nodejsCompat =
|
|
134
|
+
deploymentConfig.compatibility_flags?.includes("nodejs_compat");
|
|
135
|
+
|
|
140
136
|
/**
|
|
141
137
|
* Evaluate if this is an Advanced Mode or Pages Functions project. If Advanced Mode, we'll
|
|
142
138
|
* go ahead and upload `_worker.js` as is, but if Pages Functions, we need to attempt to build
|
|
@@ -160,7 +156,6 @@ export async function publish({
|
|
|
160
156
|
);
|
|
161
157
|
|
|
162
158
|
if (!_workerJS && existsSync(functionsDirectory)) {
|
|
163
|
-
const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.js`);
|
|
164
159
|
const outputConfigPath = join(
|
|
165
160
|
tmpdir(),
|
|
166
161
|
`functions-filepath-routing-config-${Math.random()}.json`
|
|
@@ -168,18 +163,20 @@ export async function publish({
|
|
|
168
163
|
|
|
169
164
|
try {
|
|
170
165
|
workerBundle = await buildFunctions({
|
|
171
|
-
outfile,
|
|
172
166
|
outputConfigPath,
|
|
173
167
|
functionsDirectory,
|
|
174
168
|
onEnd: () => {},
|
|
175
|
-
buildOutputDirectory:
|
|
169
|
+
buildOutputDirectory: directory,
|
|
176
170
|
routesOutputPath,
|
|
177
171
|
local: false,
|
|
178
172
|
d1Databases,
|
|
179
|
-
|
|
173
|
+
nodejsCompat,
|
|
180
174
|
});
|
|
181
175
|
|
|
182
|
-
builtFunctions = readFileSync(
|
|
176
|
+
builtFunctions = readFileSync(
|
|
177
|
+
workerBundle.resolvedEntryPointPath,
|
|
178
|
+
"utf-8"
|
|
179
|
+
);
|
|
183
180
|
filepathRoutingConfig = readFileSync(outputConfigPath, "utf-8");
|
|
184
181
|
} catch (e) {
|
|
185
182
|
if (e instanceof FunctionsNoRoutesError) {
|
|
@@ -247,10 +244,7 @@ export async function publish({
|
|
|
247
244
|
* – this includes its routing and middleware characteristics.
|
|
248
245
|
*/
|
|
249
246
|
if (_workerJS) {
|
|
250
|
-
|
|
251
|
-
const enableBundling = bundle || experimentalWorkerBundle;
|
|
252
|
-
|
|
253
|
-
if (enableBundling) {
|
|
247
|
+
if (bundle) {
|
|
254
248
|
const outfile = join(tmpdir(), `./bundledWorker-${Math.random()}.mjs`);
|
|
255
249
|
workerBundle = await buildRawWorker({
|
|
256
250
|
workerScriptPath,
|
|
@@ -261,30 +255,29 @@ export async function publish({
|
|
|
261
255
|
watch: false,
|
|
262
256
|
onEnd: () => {},
|
|
263
257
|
betaD1Shims: d1Databases,
|
|
264
|
-
|
|
258
|
+
nodejsCompat,
|
|
265
259
|
});
|
|
266
|
-
workerFileContents = readFileSync(outfile, "utf8");
|
|
267
260
|
} else {
|
|
268
261
|
await checkRawWorker(workerScriptPath, () => {});
|
|
262
|
+
// TODO: Replace this with the cool new no-bundle stuff when that lands: https://github.com/cloudflare/workers-sdk/pull/2769
|
|
263
|
+
workerBundle = {
|
|
264
|
+
modules: [],
|
|
265
|
+
dependencies: {},
|
|
266
|
+
stop: undefined,
|
|
267
|
+
resolvedEntryPointPath: workerScriptPath,
|
|
268
|
+
bundleType: "esm",
|
|
269
|
+
};
|
|
269
270
|
}
|
|
270
271
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
);
|
|
272
|
+
const workerBundleContents = await createUploadWorkerBundleContents(
|
|
273
|
+
workerBundle as BundleResult
|
|
274
|
+
);
|
|
275
275
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
} else {
|
|
282
|
-
formData.append(
|
|
283
|
-
"_worker.js",
|
|
284
|
-
new File([workerFileContents], "_worker.js")
|
|
285
|
-
);
|
|
286
|
-
logger.log(`✨ Uploading _worker.js`);
|
|
287
|
-
}
|
|
276
|
+
formData.append(
|
|
277
|
+
"_worker.bundle",
|
|
278
|
+
new File([workerBundleContents], "_worker.bundle")
|
|
279
|
+
);
|
|
280
|
+
logger.log(`✨ Uploading Worker bundle`);
|
|
288
281
|
|
|
289
282
|
if (_routesCustom) {
|
|
290
283
|
// user provided a custom _routes.json file
|
|
@@ -310,21 +303,15 @@ export async function publish({
|
|
|
310
303
|
* https://developers.cloudflare.com/pages/platform/functions/
|
|
311
304
|
*/
|
|
312
305
|
if (builtFunctions && !_workerJS) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
);
|
|
306
|
+
const workerBundleContents = await createUploadWorkerBundleContents(
|
|
307
|
+
workerBundle as BundleResult
|
|
308
|
+
);
|
|
317
309
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
} else {
|
|
324
|
-
// if Functions were build successfully, proceed to uploading the build file
|
|
325
|
-
formData.append("_worker.js", new File([builtFunctions], "_worker.js"));
|
|
326
|
-
logger.log(`✨ Uploading Functions`);
|
|
327
|
-
}
|
|
310
|
+
formData.append(
|
|
311
|
+
"_worker.bundle",
|
|
312
|
+
new File([workerBundleContents], "_worker.bundle")
|
|
313
|
+
);
|
|
314
|
+
logger.log(`✨ Uploading Functions bundle`);
|
|
328
315
|
|
|
329
316
|
if (_routesCustom) {
|
|
330
317
|
// user provided a custom _routes.json file
|
package/src/bundle.ts
CHANGED
|
@@ -85,6 +85,15 @@ Add "node_compat = true" to your wrangler.toml file to enable Node compatibility
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
const nodejsCompatPlugin: esbuild.Plugin = {
|
|
89
|
+
name: "nodejs_compat Plugin",
|
|
90
|
+
setup(pluginBuild) {
|
|
91
|
+
pluginBuild.onResolve({ filter: /node:.*/ }, () => {
|
|
92
|
+
return { external: true };
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
|
|
88
97
|
/**
|
|
89
98
|
* Generate a bundle for the worker identified by the arguments passed in.
|
|
90
99
|
*/
|
|
@@ -98,11 +107,13 @@ export async function bundleWorker(
|
|
|
98
107
|
doBindings: DurableObjectBindings;
|
|
99
108
|
jsxFactory?: string;
|
|
100
109
|
jsxFragment?: string;
|
|
110
|
+
entryName?: string;
|
|
101
111
|
rules: Config["rules"];
|
|
102
112
|
watch?: esbuild.WatchMode | boolean;
|
|
103
113
|
tsconfig?: string;
|
|
104
114
|
minify?: boolean;
|
|
105
|
-
|
|
115
|
+
legacyNodeCompat?: boolean;
|
|
116
|
+
nodejsCompat?: boolean;
|
|
106
117
|
define: Config["define"];
|
|
107
118
|
checkFetch: boolean;
|
|
108
119
|
services?: Config["services"];
|
|
@@ -127,11 +138,13 @@ export async function bundleWorker(
|
|
|
127
138
|
doBindings,
|
|
128
139
|
jsxFactory,
|
|
129
140
|
jsxFragment,
|
|
141
|
+
entryName,
|
|
130
142
|
rules,
|
|
131
143
|
watch,
|
|
132
144
|
tsconfig,
|
|
133
145
|
minify,
|
|
134
|
-
|
|
146
|
+
legacyNodeCompat,
|
|
147
|
+
nodejsCompat,
|
|
135
148
|
checkFetch,
|
|
136
149
|
local,
|
|
137
150
|
assets,
|
|
@@ -277,7 +290,7 @@ export async function bundleWorker(
|
|
|
277
290
|
currentEntry,
|
|
278
291
|
tmpDir.path,
|
|
279
292
|
betaD1Shims,
|
|
280
|
-
local,
|
|
293
|
+
local && !experimentalLocal,
|
|
281
294
|
doBindings
|
|
282
295
|
);
|
|
283
296
|
}),
|
|
@@ -324,16 +337,19 @@ export async function bundleWorker(
|
|
|
324
337
|
bundle: true,
|
|
325
338
|
absWorkingDir: entry.directory,
|
|
326
339
|
outdir: destination,
|
|
340
|
+
entryNames: entryName || path.parse(entry.file).name,
|
|
327
341
|
...(isOutfile
|
|
328
342
|
? {
|
|
329
343
|
outdir: undefined,
|
|
330
344
|
outfile: destination,
|
|
345
|
+
entryNames: undefined,
|
|
331
346
|
}
|
|
332
347
|
: {}),
|
|
333
348
|
inject,
|
|
334
349
|
external: ["__STATIC_CONTENT_MANIFEST"],
|
|
335
350
|
format: entry.format === "modules" ? "esm" : "iife",
|
|
336
|
-
|
|
351
|
+
// Our workerd runtime uses the same V8 version as recent Chrome, which is highly ES2022 compliant: https://kangax.github.io/compat-table/es2016plus/
|
|
352
|
+
target: "es2022",
|
|
337
353
|
sourcemap: sourcemap ?? true, // this needs to use ?? to accept false
|
|
338
354
|
// Include a reference to the output folder in the sourcemap.
|
|
339
355
|
// This is omitted by default, but we need it to properly resolve source paths in error output.
|
|
@@ -346,7 +362,7 @@ export async function bundleWorker(
|
|
|
346
362
|
// use process.env["NODE_ENV" + ""] so that esbuild doesn't replace it
|
|
347
363
|
// when we do a build of wrangler. (re: https://github.com/cloudflare/workers-sdk/issues/1477)
|
|
348
364
|
"process.env.NODE_ENV": `"${process.env["NODE_ENV" + ""]}"`,
|
|
349
|
-
...(
|
|
365
|
+
...(legacyNodeCompat ? { global: "globalThis" } : {}),
|
|
350
366
|
...(checkFetch ? { fetch: "checkedFetch" } : {}),
|
|
351
367
|
...options.define,
|
|
352
368
|
},
|
|
@@ -359,9 +375,10 @@ export async function bundleWorker(
|
|
|
359
375
|
},
|
|
360
376
|
plugins: [
|
|
361
377
|
moduleCollector.plugin,
|
|
362
|
-
...(
|
|
378
|
+
...(legacyNodeCompat
|
|
363
379
|
? [NodeGlobalsPolyfills({ buffer: true }), NodeModulesPolyfills()]
|
|
364
380
|
: []),
|
|
381
|
+
...(nodejsCompat ? [nodejsCompatPlugin] : []),
|
|
365
382
|
...(plugins || []),
|
|
366
383
|
],
|
|
367
384
|
...(jsxFactory && { jsxFactory }),
|
|
@@ -377,7 +394,8 @@ export async function bundleWorker(
|
|
|
377
394
|
try {
|
|
378
395
|
result = await esbuild.build(buildOptions);
|
|
379
396
|
} catch (e) {
|
|
380
|
-
if (!
|
|
397
|
+
if (!legacyNodeCompat && isBuildFailure(e))
|
|
398
|
+
rewriteNodeCompatBuildFailure(e);
|
|
381
399
|
throw e;
|
|
382
400
|
}
|
|
383
401
|
|
|
@@ -802,7 +820,7 @@ async function applyD1BetaFacade(
|
|
|
802
820
|
entry: Entry,
|
|
803
821
|
tmpDirPath: string,
|
|
804
822
|
betaD1Shims: string[],
|
|
805
|
-
|
|
823
|
+
miniflare2: boolean,
|
|
806
824
|
doBindings: DurableObjectBindings
|
|
807
825
|
): Promise<Entry> {
|
|
808
826
|
let entrypointPath = path.resolve(
|
|
@@ -853,7 +871,7 @@ async function applyD1BetaFacade(
|
|
|
853
871
|
],
|
|
854
872
|
define: {
|
|
855
873
|
__D1_IMPORTS__: JSON.stringify(betaD1Shims),
|
|
856
|
-
__LOCAL_MODE__: JSON.stringify(
|
|
874
|
+
__LOCAL_MODE__: JSON.stringify(miniflare2),
|
|
857
875
|
},
|
|
858
876
|
outfile: targetPath,
|
|
859
877
|
});
|
|
@@ -463,7 +463,6 @@ interface EnvironmentNonInheritable {
|
|
|
463
463
|
* NOTE: This field is not automatically inherited from the top level environment,
|
|
464
464
|
* and so must be specified in every named environment.
|
|
465
465
|
*
|
|
466
|
-
* @default `{ bindings: [] }`
|
|
467
466
|
* @nonInheritable
|
|
468
467
|
*/
|
|
469
468
|
unsafe: {
|
|
@@ -471,14 +470,20 @@ interface EnvironmentNonInheritable {
|
|
|
471
470
|
* A set of bindings that should be put into a Worker's upload metadata without changes. These
|
|
472
471
|
* can be used to implement bindings for features that haven't released and aren't supported
|
|
473
472
|
* directly by wrangler or miniflare.
|
|
474
|
-
*
|
|
475
|
-
* @default []
|
|
476
473
|
*/
|
|
477
|
-
bindings
|
|
474
|
+
bindings?: {
|
|
478
475
|
name: string;
|
|
479
476
|
type: string;
|
|
480
477
|
[key: string]: unknown;
|
|
481
478
|
}[];
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Arbitrary key/value pairs that will be included in the uploaded metadata. Values specified
|
|
482
|
+
* here will always be applied to metadata last, so can add new or override existing fields.
|
|
483
|
+
*/
|
|
484
|
+
metadata?: {
|
|
485
|
+
[key: string]: string;
|
|
486
|
+
};
|
|
482
487
|
};
|
|
483
488
|
|
|
484
489
|
mtls_certificates: {
|
package/src/config/index.ts
CHANGED
|
@@ -255,10 +255,10 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
|
255
255
|
});
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
-
if (unsafe !== undefined && unsafe.length > 0) {
|
|
258
|
+
if (unsafe?.bindings !== undefined && unsafe.bindings.length > 0) {
|
|
259
259
|
output.push({
|
|
260
260
|
type: "Unsafe",
|
|
261
|
-
entries: unsafe.map(({ name, type }) => ({
|
|
261
|
+
entries: unsafe.bindings.map(({ name, type }) => ({
|
|
262
262
|
key: type,
|
|
263
263
|
value: name,
|
|
264
264
|
})),
|
|
@@ -319,6 +319,16 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
|
|
|
319
319
|
});
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
if (unsafe?.metadata !== undefined) {
|
|
323
|
+
output.push({
|
|
324
|
+
type: "Unsafe Metadata",
|
|
325
|
+
entries: Object.entries(unsafe.metadata).map(([key, value]) => ({
|
|
326
|
+
key,
|
|
327
|
+
value: `${value}`,
|
|
328
|
+
})),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
322
332
|
if (output.length === 0) {
|
|
323
333
|
return;
|
|
324
334
|
}
|
|
@@ -550,7 +550,7 @@ export const getBindingNames = (value: unknown): string[] => {
|
|
|
550
550
|
} else if (isNamespaceList(value)) {
|
|
551
551
|
return value.map(({ binding }) => binding);
|
|
552
552
|
} else if (isRecord(value)) {
|
|
553
|
-
return Object.keys(value);
|
|
553
|
+
return Object.keys(value).filter((k) => value[k] !== undefined);
|
|
554
554
|
} else {
|
|
555
555
|
return [];
|
|
556
556
|
}
|
package/src/config/validation.ts
CHANGED
|
@@ -1162,10 +1162,8 @@ function normalizeAndValidateEnvironment(
|
|
|
1162
1162
|
rawEnv,
|
|
1163
1163
|
envName,
|
|
1164
1164
|
"unsafe",
|
|
1165
|
-
|
|
1166
|
-
{
|
|
1167
|
-
bindings: [],
|
|
1168
|
-
}
|
|
1165
|
+
validateUnsafeSettings(envName),
|
|
1166
|
+
{}
|
|
1169
1167
|
),
|
|
1170
1168
|
zone_id: rawEnv.zone_id,
|
|
1171
1169
|
no_bundle: inheritable(
|
|
@@ -1495,6 +1493,62 @@ const validateBindingsProperty =
|
|
|
1495
1493
|
return isValid;
|
|
1496
1494
|
};
|
|
1497
1495
|
|
|
1496
|
+
const validateUnsafeSettings =
|
|
1497
|
+
(envName: string): ValidatorFn =>
|
|
1498
|
+
(diagnostics, field, value, config) => {
|
|
1499
|
+
const fieldPath =
|
|
1500
|
+
config === undefined ? `${field}` : `env.${envName}.${field}`;
|
|
1501
|
+
|
|
1502
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1503
|
+
diagnostics.errors.push(
|
|
1504
|
+
`The field "${fieldPath}" should be an object but got ${JSON.stringify(
|
|
1505
|
+
value
|
|
1506
|
+
)}.`
|
|
1507
|
+
);
|
|
1508
|
+
return false;
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
// At least one of bindings and metadata must exist
|
|
1512
|
+
if (!hasProperty(value, "bindings") && !hasProperty(value, "metadata")) {
|
|
1513
|
+
diagnostics.errors.push(
|
|
1514
|
+
`The field "${fieldPath}" should contain at least one of "bindings" or "metadata" properties but got ${JSON.stringify(
|
|
1515
|
+
value
|
|
1516
|
+
)}.`
|
|
1517
|
+
);
|
|
1518
|
+
return false;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
// unsafe.bindings
|
|
1522
|
+
if (hasProperty(value, "bindings") && value.bindings !== undefined) {
|
|
1523
|
+
const validateBindingsFn = validateBindingsProperty(
|
|
1524
|
+
envName,
|
|
1525
|
+
validateUnsafeBinding
|
|
1526
|
+
);
|
|
1527
|
+
const valid = validateBindingsFn(diagnostics, field, value, config);
|
|
1528
|
+
if (!valid) {
|
|
1529
|
+
return false;
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
// unsafe.metadata
|
|
1534
|
+
if (
|
|
1535
|
+
hasProperty(value, "metadata") &&
|
|
1536
|
+
value.metadata !== undefined &&
|
|
1537
|
+
(typeof value.metadata !== "object" ||
|
|
1538
|
+
value.metadata === null ||
|
|
1539
|
+
Array.isArray(value.metadata))
|
|
1540
|
+
) {
|
|
1541
|
+
diagnostics.errors.push(
|
|
1542
|
+
`The field "${fieldPath}.metadata" should be an object but got ${JSON.stringify(
|
|
1543
|
+
value.metadata
|
|
1544
|
+
)}.`
|
|
1545
|
+
);
|
|
1546
|
+
return false;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
return true;
|
|
1550
|
+
};
|
|
1551
|
+
|
|
1498
1552
|
/**
|
|
1499
1553
|
* Check that the given field is a valid "durable_object" binding object.
|
|
1500
1554
|
*/
|
|
@@ -109,6 +109,14 @@ function randomId(): string {
|
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
// URLs are often relative to the zone. Sometimes the base zone
|
|
113
|
+
// will be grey-clouded, and so the host must be swapped out for
|
|
114
|
+
// the worker route host, which is more likely to be orange-clouded
|
|
115
|
+
function switchHost(originalUrl: string, host?: string): URL {
|
|
116
|
+
const url = new URL(originalUrl);
|
|
117
|
+
url.hostname = host ?? url.hostname;
|
|
118
|
+
return url;
|
|
119
|
+
}
|
|
112
120
|
/**
|
|
113
121
|
* Generates a preview session token.
|
|
114
122
|
*/
|
|
@@ -129,9 +137,13 @@ export async function createPreviewSession(
|
|
|
129
137
|
abortSignal
|
|
130
138
|
);
|
|
131
139
|
|
|
132
|
-
|
|
140
|
+
const switchedExchangeUrl = switchHost(exchange_url, ctx.host).toString();
|
|
141
|
+
|
|
142
|
+
logger.debug(`-- START EXCHANGE API REQUEST: GET ${switchedExchangeUrl}`);
|
|
133
143
|
logger.debug("-- END EXCHANGE API REQUEST");
|
|
134
|
-
const exchangeResponse = await fetch(
|
|
144
|
+
const exchangeResponse = await fetch(switchedExchangeUrl, {
|
|
145
|
+
signal: abortSignal,
|
|
146
|
+
});
|
|
135
147
|
const bodyText = await exchangeResponse.text();
|
|
136
148
|
logger.debug(
|
|
137
149
|
"-- START EXCHANGE API RESPONSE:",
|
|
@@ -147,16 +159,15 @@ export async function createPreviewSession(
|
|
|
147
159
|
token: string;
|
|
148
160
|
prewarm: string;
|
|
149
161
|
}>(bodyText);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const query = `cf_workers_preview_token=${token}`;
|
|
162
|
+
const inspector = new URL(inspector_websocket);
|
|
163
|
+
inspector.searchParams.append("cf_workers_preview_token", token);
|
|
153
164
|
|
|
154
165
|
return {
|
|
155
166
|
id: randomId(),
|
|
156
167
|
value: token,
|
|
157
|
-
host,
|
|
158
|
-
inspectorUrl:
|
|
159
|
-
prewarmUrl:
|
|
168
|
+
host: ctx.host ?? inspector.host,
|
|
169
|
+
inspectorUrl: switchHost(inspector.href, ctx.host),
|
|
170
|
+
prewarmUrl: switchHost(prewarm, ctx.host),
|
|
160
171
|
};
|
|
161
172
|
}
|
|
162
173
|
|
|
@@ -65,6 +65,8 @@ export interface WorkerMetadata {
|
|
|
65
65
|
bindings: WorkerMetadataBinding[];
|
|
66
66
|
keep_bindings?: WorkerMetadataBinding["type"][];
|
|
67
67
|
logpush?: boolean;
|
|
68
|
+
// Allow unsafe.metadata to add arbitary properties at runtime
|
|
69
|
+
[key: string]: unknown;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
/**
|
|
@@ -284,9 +286,9 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
284
286
|
}
|
|
285
287
|
}
|
|
286
288
|
|
|
287
|
-
if (bindings.unsafe) {
|
|
289
|
+
if (bindings.unsafe?.bindings) {
|
|
288
290
|
// @ts-expect-error unsafe bindings don't need to match a specific type here
|
|
289
|
-
metadataBindings.push(...bindings.unsafe);
|
|
291
|
+
metadataBindings.push(...bindings.unsafe.bindings);
|
|
290
292
|
}
|
|
291
293
|
|
|
292
294
|
const metadata: WorkerMetadata = {
|
|
@@ -303,6 +305,12 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
303
305
|
...(logpush !== undefined && { logpush }),
|
|
304
306
|
};
|
|
305
307
|
|
|
308
|
+
if (bindings.unsafe?.metadata !== undefined) {
|
|
309
|
+
for (const key of Object.keys(bindings.unsafe.metadata)) {
|
|
310
|
+
metadata[key] = bindings.unsafe.metadata[key];
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
306
314
|
formData.set("metadata", JSON.stringify(metadata));
|
|
307
315
|
|
|
308
316
|
if (main.type === "commonjs" && modules && modules.length > 0) {
|
package/src/dev/dev.tsx
CHANGED
|
@@ -138,7 +138,8 @@ export type DevProps = {
|
|
|
138
138
|
compatibilityFlags: string[] | undefined;
|
|
139
139
|
usageModel: "bundled" | "unbound" | undefined;
|
|
140
140
|
minify: boolean | undefined;
|
|
141
|
-
|
|
141
|
+
legacyNodeCompat: boolean | undefined;
|
|
142
|
+
nodejsCompat: boolean | undefined;
|
|
142
143
|
build: Config["build"];
|
|
143
144
|
env: string | undefined;
|
|
144
145
|
legacyEnv: boolean;
|
|
@@ -146,7 +147,6 @@ export type DevProps = {
|
|
|
146
147
|
host: string | undefined;
|
|
147
148
|
routes: Route[] | undefined;
|
|
148
149
|
inspect: boolean;
|
|
149
|
-
logPrefix?: string;
|
|
150
150
|
onReady: ((ip: string, port: number) => void) | undefined;
|
|
151
151
|
showInteractiveDevSession: boolean | undefined;
|
|
152
152
|
forceLocal: boolean | undefined;
|
|
@@ -278,7 +278,8 @@ function DevSession(props: DevSessionProps) {
|
|
|
278
278
|
),
|
|
279
279
|
tsconfig: props.tsconfig,
|
|
280
280
|
minify: props.minify,
|
|
281
|
-
|
|
281
|
+
legacyNodeCompat: props.legacyNodeCompat,
|
|
282
|
+
nodejsCompat: props.nodejsCompat,
|
|
282
283
|
betaD1Shims,
|
|
283
284
|
define: props.define,
|
|
284
285
|
noBundle: props.noBundle,
|
|
@@ -341,7 +342,6 @@ function DevSession(props: DevSessionProps) {
|
|
|
341
342
|
queueConsumers={props.queueConsumers}
|
|
342
343
|
localProtocol={props.localProtocol}
|
|
343
344
|
localUpstream={props.localUpstream}
|
|
344
|
-
logPrefix={props.logPrefix}
|
|
345
345
|
inspect={props.inspect}
|
|
346
346
|
onReady={announceAndOnReady}
|
|
347
347
|
enablePagesAssetsServiceBinding={props.enablePagesAssetsServiceBinding}
|