wrangler 2.0.25 → 2.0.28
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 +15 -3
- package/package.json +8 -6
- package/src/__tests__/configuration.test.ts +33 -29
- package/src/__tests__/dev.test.tsx +8 -6
- package/src/__tests__/generate.test.ts +2 -4
- package/src/__tests__/helpers/mock-cfetch.ts +33 -0
- 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 +30 -30
- package/src/__tests__/init.test.ts +537 -359
- package/src/__tests__/jest.setup.ts +7 -0
- package/src/__tests__/metrics.test.ts +1 -1
- package/src/__tests__/pages.test.ts +14 -0
- package/src/__tests__/publish.test.ts +59 -18
- package/src/__tests__/r2.test.ts +4 -3
- package/src/__tests__/tail.test.ts +53 -3
- package/src/__tests__/test-old-node-version.js +3 -3
- package/src/__tests__/user.test.ts +11 -0
- package/src/__tests__/worker-namespace.test.ts +37 -35
- package/src/api/dev.ts +1 -0
- package/src/bundle.ts +1 -1
- package/src/cfetch/internal.ts +118 -1
- package/src/config/environment.ts +1 -1
- package/src/config/index.ts +4 -4
- package/src/config/validation-helpers.ts +19 -6
- package/src/config/validation.ts +11 -5
- package/src/config-cache.ts +2 -1
- package/src/create-worker-upload-form.ts +29 -26
- package/src/dev/dev.tsx +4 -0
- package/src/dev/remote.tsx +10 -1
- package/src/dev.tsx +36 -8
- package/src/{worker-namespace.ts → dispatch-namespace.ts} +18 -18
- package/src/generate.ts +1 -1
- package/src/index.tsx +54 -8
- package/src/init.ts +111 -38
- package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
- package/src/metrics/metrics-config.ts +1 -1
- package/src/metrics/send-event.ts +5 -5
- package/src/miniflare-cli/assets.ts +8 -0
- package/src/miniflare-cli/index.ts +6 -3
- package/src/pages/build.tsx +41 -15
- package/src/pages/constants.ts +1 -0
- package/src/pages/dev.tsx +93 -37
- package/src/pages/errors.ts +22 -0
- package/src/pages/functions/routes-consolidation.test.ts +185 -1
- package/src/pages/functions/routes-consolidation.ts +46 -2
- package/src/pages/functions/routes-transformation.ts +0 -3
- package/src/pages/functions.tsx +96 -0
- package/src/pages/index.tsx +65 -55
- package/src/pages/publish.tsx +27 -16
- package/src/proxy.ts +10 -0
- package/src/publish.ts +19 -4
- package/src/r2.ts +4 -4
- package/src/tail/filters.ts +3 -1
- package/src/tail/printing.ts +2 -0
- package/src/user/user.tsx +6 -4
- package/src/whoami.tsx +5 -5
- package/src/worker.ts +3 -2
- package/src/zones.ts +91 -0
- package/templates/pages-template-plugin.ts +16 -4
- package/templates/pages-template-worker.ts +16 -5
- package/templates/service-bindings-module-facade.js +10 -7
- package/templates/service-bindings-sw-facade.js +10 -7
- package/wrangler-dist/cli.d.ts +8 -3
- package/wrangler-dist/cli.js +6757 -1639
package/src/cfetch/internal.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
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";
|
|
6
|
+
import { logger } from "../logger";
|
|
5
7
|
import { ParseError, parseJSON } from "../parse";
|
|
6
8
|
import { loginOrRefreshIfRequired, requireApiToken } from "../user";
|
|
7
9
|
import type { ApiCredentials } from "../user";
|
|
@@ -44,6 +46,13 @@ export async function fetchInternal<ResponseType>(
|
|
|
44
46
|
|
|
45
47
|
const queryString = queryParams ? `?${queryParams.toString()}` : "";
|
|
46
48
|
const method = init.method ?? "GET";
|
|
49
|
+
|
|
50
|
+
logger.debug(
|
|
51
|
+
`-- START CF API REQUEST: ${method} ${getCloudflareAPIBaseURL()}${resource}${queryString}`
|
|
52
|
+
);
|
|
53
|
+
logger.debug("HEADERS:", JSON.stringify(headers, null, 2));
|
|
54
|
+
logger.debug("INIT:", JSON.stringify(init, null, 2));
|
|
55
|
+
logger.debug("-- END CF API REQUEST");
|
|
47
56
|
const response = await fetch(
|
|
48
57
|
`${getCloudflareAPIBaseURL()}${resource}${queryString}`,
|
|
49
58
|
{
|
|
@@ -54,6 +63,15 @@ export async function fetchInternal<ResponseType>(
|
|
|
54
63
|
}
|
|
55
64
|
);
|
|
56
65
|
const jsonText = await response.text();
|
|
66
|
+
logger.debug(
|
|
67
|
+
"-- START CF API RESPONSE:",
|
|
68
|
+
response.statusText,
|
|
69
|
+
response.status
|
|
70
|
+
);
|
|
71
|
+
logger.debug("HEADERS:", JSON.stringify(response.headers, null, 2));
|
|
72
|
+
logger.debug("RESPONSE:", jsonText);
|
|
73
|
+
logger.debug("-- END CF API RESPONSE");
|
|
74
|
+
|
|
57
75
|
try {
|
|
58
76
|
return parseJSON<ResponseType>(jsonText);
|
|
59
77
|
} catch (err) {
|
|
@@ -179,3 +197,102 @@ export async function fetchR2Objects(
|
|
|
179
197
|
);
|
|
180
198
|
}
|
|
181
199
|
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* This is a wrapper STOPGAP for getting the script which returns a raw text response.
|
|
203
|
+
*/
|
|
204
|
+
export async function fetchDashboardScript(
|
|
205
|
+
resource: string,
|
|
206
|
+
bodyInit: RequestInit = {}
|
|
207
|
+
): Promise<string> {
|
|
208
|
+
await requireLoggedIn();
|
|
209
|
+
const auth = requireApiToken();
|
|
210
|
+
const headers = cloneHeaders(bodyInit.headers);
|
|
211
|
+
addAuthorizationHeaderIfUnspecified(headers, auth);
|
|
212
|
+
addUserAgent(headers);
|
|
213
|
+
|
|
214
|
+
const response = await fetch(`${getCloudflareAPIBaseURL()}${resource}`, {
|
|
215
|
+
...bodyInit,
|
|
216
|
+
headers,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (!response.ok || !response.body) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Failed to fetch ${resource} - ${response.status}: ${response.statusText});`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const usesModules = response.headers
|
|
226
|
+
.get("content-type")
|
|
227
|
+
?.startsWith("multipart");
|
|
228
|
+
|
|
229
|
+
if (usesModules) {
|
|
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 ?? "";
|
|
242
|
+
|
|
243
|
+
// Follow up on issue in Undici about multipart/form-data support & replace the workaround: https://github.com/nodejs/undici/issues/974
|
|
244
|
+
// This should be using a builtin formData() parser pattern.
|
|
245
|
+
} else {
|
|
246
|
+
return response.text();
|
|
247
|
+
}
|
|
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
|
+
}
|
|
@@ -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,
|
|
@@ -383,7 +383,7 @@ export const validateRequiredProperty = (
|
|
|
383
383
|
container: string,
|
|
384
384
|
key: string,
|
|
385
385
|
value: unknown,
|
|
386
|
-
type:
|
|
386
|
+
type: TypeofType,
|
|
387
387
|
choices?: unknown[]
|
|
388
388
|
): boolean => {
|
|
389
389
|
if (container) {
|
|
@@ -417,7 +417,7 @@ export const validateOptionalProperty = (
|
|
|
417
417
|
container: string,
|
|
418
418
|
key: string,
|
|
419
419
|
value: unknown,
|
|
420
|
-
type:
|
|
420
|
+
type: TypeofType,
|
|
421
421
|
choices?: unknown[]
|
|
422
422
|
): boolean => {
|
|
423
423
|
if (value !== undefined) {
|
|
@@ -440,7 +440,7 @@ export const validateTypedArray = (
|
|
|
440
440
|
diagnostics: Diagnostics,
|
|
441
441
|
container: string,
|
|
442
442
|
value: unknown,
|
|
443
|
-
type:
|
|
443
|
+
type: TypeofType
|
|
444
444
|
): boolean => {
|
|
445
445
|
let isValid = true;
|
|
446
446
|
if (!Array.isArray(value)) {
|
|
@@ -472,7 +472,7 @@ export const validateOptionalTypedArray = (
|
|
|
472
472
|
diagnostics: Diagnostics,
|
|
473
473
|
container: string,
|
|
474
474
|
value: unknown,
|
|
475
|
-
type:
|
|
475
|
+
type: TypeofType
|
|
476
476
|
) => {
|
|
477
477
|
if (value !== undefined) {
|
|
478
478
|
return validateTypedArray(diagnostics, container, value, type);
|
|
@@ -486,7 +486,7 @@ export const validateOptionalTypedArray = (
|
|
|
486
486
|
export const isRequiredProperty = <T extends object>(
|
|
487
487
|
obj: object,
|
|
488
488
|
prop: keyof T,
|
|
489
|
-
type:
|
|
489
|
+
type: TypeofType,
|
|
490
490
|
choices?: unknown[]
|
|
491
491
|
): obj is T =>
|
|
492
492
|
hasProperty<T>(obj, prop) &&
|
|
@@ -499,7 +499,7 @@ export const isRequiredProperty = <T extends object>(
|
|
|
499
499
|
export const isOptionalProperty = <T extends object>(
|
|
500
500
|
obj: object,
|
|
501
501
|
prop: keyof T,
|
|
502
|
-
type:
|
|
502
|
+
type: TypeofType
|
|
503
503
|
): obj is T => !hasProperty<T>(obj, prop) || typeof obj[prop] === type;
|
|
504
504
|
|
|
505
505
|
/**
|
|
@@ -582,3 +582,16 @@ const isRecord = (
|
|
|
582
582
|
value: unknown
|
|
583
583
|
): value is Record<string | number | symbol, unknown> =>
|
|
584
584
|
typeof value === "object" && value !== null && !Array.isArray(value);
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* JavaScript `typeof` operator return values.
|
|
588
|
+
*/
|
|
589
|
+
type TypeofType =
|
|
590
|
+
| "string"
|
|
591
|
+
| "number"
|
|
592
|
+
| "bigint"
|
|
593
|
+
| "boolean"
|
|
594
|
+
| "symbol"
|
|
595
|
+
| "undefined"
|
|
596
|
+
| "object"
|
|
597
|
+
| "function";
|
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
|
),
|
|
@@ -1630,7 +1630,10 @@ const validateKVBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1630
1630
|
);
|
|
1631
1631
|
isValid = false;
|
|
1632
1632
|
}
|
|
1633
|
-
if (
|
|
1633
|
+
if (
|
|
1634
|
+
!isRequiredProperty(value, "id", "string") ||
|
|
1635
|
+
(value as { id: string }).id.length === 0
|
|
1636
|
+
) {
|
|
1634
1637
|
diagnostics.errors.push(
|
|
1635
1638
|
`"${field}" bindings should have a string "id" field but got ${JSON.stringify(
|
|
1636
1639
|
value
|
|
@@ -1668,7 +1671,10 @@ const validateR2Binding: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1668
1671
|
);
|
|
1669
1672
|
isValid = false;
|
|
1670
1673
|
}
|
|
1671
|
-
if (
|
|
1674
|
+
if (
|
|
1675
|
+
!isRequiredProperty(value, "bucket_name", "string") ||
|
|
1676
|
+
(value as { bucket_name: string }).bucket_name.length === 0
|
|
1677
|
+
) {
|
|
1672
1678
|
diagnostics.errors.push(
|
|
1673
1679
|
`"${field}" bindings should have a string "bucket_name" field but got ${JSON.stringify(
|
|
1674
1680
|
value
|
package/src/config-cache.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import { findUpSync } from "find-up";
|
|
4
|
+
import { CI } from "./is-ci";
|
|
4
5
|
import isInteractive from "./is-interactive";
|
|
5
6
|
import { logger } from "./logger";
|
|
6
7
|
|
|
@@ -29,7 +30,7 @@ const arrayFormatter = new Intl.ListFormat("en", {
|
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
function showCacheMessage(fields: string[], folder: string) {
|
|
32
|
-
if (!cacheMessageShown && isInteractive()) {
|
|
33
|
+
if (!cacheMessageShown && isInteractive() && !CI.isCI()) {
|
|
33
34
|
if (fields.length > 0) {
|
|
34
35
|
logger.log(
|
|
35
36
|
`Retrieving cached values for ${arrayFormatter.format(
|
|
@@ -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
|
@@ -493,6 +493,10 @@ function useHotkeys(props: {
|
|
|
493
493
|
break;
|
|
494
494
|
// open browser
|
|
495
495
|
case "b": {
|
|
496
|
+
if (ip === "0.0.0.0") {
|
|
497
|
+
await openInBrowser(`${localProtocol}://127.0.0.1:${port}`);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
496
500
|
await openInBrowser(`${localProtocol}://${ip}:${port}`);
|
|
497
501
|
break;
|
|
498
502
|
}
|
package/src/dev/remote.tsx
CHANGED
|
@@ -182,7 +182,7 @@ export function useWorker(props: {
|
|
|
182
182
|
} = props;
|
|
183
183
|
const [session, setSession] = useState<CfPreviewSession | undefined>();
|
|
184
184
|
const [token, setToken] = useState<CfPreviewToken | undefined>();
|
|
185
|
-
|
|
185
|
+
const [restartCounter, setRestartCounter] = useState<number>(0);
|
|
186
186
|
// This is the most reliable way to detect whether
|
|
187
187
|
// something's "happened" in our system; We make a ref and
|
|
188
188
|
// mark it once we log our initial message. Refs are vars!
|
|
@@ -237,6 +237,7 @@ export function useWorker(props: {
|
|
|
237
237
|
props.routes,
|
|
238
238
|
props.zone,
|
|
239
239
|
props.sendMetrics,
|
|
240
|
+
restartCounter,
|
|
240
241
|
]);
|
|
241
242
|
|
|
242
243
|
// This effect uses the session to upload the worker and create a preview
|
|
@@ -314,6 +315,7 @@ export function useWorker(props: {
|
|
|
314
315
|
compatibility_date: compatibilityDate,
|
|
315
316
|
compatibility_flags: compatibilityFlags,
|
|
316
317
|
usage_model: usageModel,
|
|
318
|
+
keep_bindings: true,
|
|
317
319
|
};
|
|
318
320
|
|
|
319
321
|
const workerAccount: CfAccount = {
|
|
@@ -379,6 +381,13 @@ export function useWorker(props: {
|
|
|
379
381
|
logger.error(
|
|
380
382
|
`${errorMessage}\n${solutionMessage}\n${onboardingLink}`
|
|
381
383
|
);
|
|
384
|
+
} else if (err.code === 10049) {
|
|
385
|
+
logger.log("Preview token expired, fetching a new one");
|
|
386
|
+
// code 10049 happens when the preview token expires
|
|
387
|
+
// since we want a new preview token when this happens,
|
|
388
|
+
// lets increment the counter, and trigger a rerun of
|
|
389
|
+
// the useEffect above
|
|
390
|
+
setRestartCounter((prevCount) => prevCount + 1);
|
|
382
391
|
} else {
|
|
383
392
|
logger.error("Error on remote worker:", err);
|
|
384
393
|
}
|
package/src/dev.tsx
CHANGED
|
@@ -63,6 +63,8 @@ interface DevArgs {
|
|
|
63
63
|
tsconfig?: string;
|
|
64
64
|
local?: boolean;
|
|
65
65
|
minify?: boolean;
|
|
66
|
+
var?: string[];
|
|
67
|
+
define?: string[];
|
|
66
68
|
"node-compat"?: boolean;
|
|
67
69
|
"experimental-enable-local-persistence"?: boolean;
|
|
68
70
|
"live-reload"?: boolean;
|
|
@@ -129,7 +131,6 @@ export function devOptions(yargs: Argv): Argv<DevArgs> {
|
|
|
129
131
|
.option("ip", {
|
|
130
132
|
describe: "IP address to listen on",
|
|
131
133
|
type: "string",
|
|
132
|
-
default: "0.0.0.0",
|
|
133
134
|
})
|
|
134
135
|
.option("port", {
|
|
135
136
|
describe: "Port to listen on",
|
|
@@ -196,6 +197,19 @@ export function devOptions(yargs: Argv): Argv<DevArgs> {
|
|
|
196
197
|
describe: "Protocol to forward requests to host on, defaults to https.",
|
|
197
198
|
choices: ["http", "https"] as const,
|
|
198
199
|
})
|
|
200
|
+
.option("var", {
|
|
201
|
+
describe:
|
|
202
|
+
"A key-value pair to be injected into the script as a variable",
|
|
203
|
+
type: "string",
|
|
204
|
+
requiresArg: true,
|
|
205
|
+
array: true,
|
|
206
|
+
})
|
|
207
|
+
.option("define", {
|
|
208
|
+
describe: "A key-value pair to be substituted in the script",
|
|
209
|
+
type: "string",
|
|
210
|
+
requiresArg: true,
|
|
211
|
+
array: true,
|
|
212
|
+
})
|
|
199
213
|
.option("jsx-factory", {
|
|
200
214
|
describe: "The function that is called for each JSX element",
|
|
201
215
|
type: "string",
|
|
@@ -427,22 +441,36 @@ export async function startDev(args: StartDevOptions) {
|
|
|
427
441
|
const getLocalPort = memoizeGetPort(DEFAULT_LOCAL_PORT);
|
|
428
442
|
const getInspectorPort = memoizeGetPort(DEFAULT_INSPECTOR_PORT);
|
|
429
443
|
|
|
444
|
+
const cliVars =
|
|
445
|
+
args.var?.reduce<Record<string, string>>((collectVars, v) => {
|
|
446
|
+
const [key, ...value] = v.split(":");
|
|
447
|
+
collectVars[key] = value.join("");
|
|
448
|
+
return collectVars;
|
|
449
|
+
}, {}) || {};
|
|
450
|
+
|
|
451
|
+
const cliDefines =
|
|
452
|
+
args.define?.reduce<Record<string, string>>((collectDefines, d) => {
|
|
453
|
+
const [key, ...value] = d.split(":");
|
|
454
|
+
collectDefines[key] = value.join("");
|
|
455
|
+
return collectDefines;
|
|
456
|
+
}, {}) || {};
|
|
457
|
+
|
|
430
458
|
// eslint-disable-next-line no-inner-declarations
|
|
431
459
|
async function getDevReactElement(configParam: Config) {
|
|
432
460
|
// now log all available bindings into the terminal
|
|
433
461
|
const bindings = await getBindings(configParam, {
|
|
434
462
|
kv: args.kv,
|
|
435
|
-
vars: args.vars,
|
|
463
|
+
vars: { ...args.vars, ...cliVars },
|
|
436
464
|
durableObjects: args.durableObjects,
|
|
437
465
|
r2: args.r2,
|
|
438
466
|
});
|
|
439
467
|
|
|
440
|
-
// mask anything that was overridden in .dev.vars
|
|
468
|
+
// mask anything that was overridden in .dev.vars or cli args
|
|
441
469
|
// so that we don't log potential secrets into the terminal
|
|
442
470
|
const maskedVars = { ...bindings.vars };
|
|
443
471
|
for (const key of Object.keys(maskedVars)) {
|
|
444
472
|
if (maskedVars[key] !== configParam.vars[key]) {
|
|
445
|
-
// This means it was overridden in .dev.vars
|
|
473
|
+
// This means it was overridden in .dev.vars or cli args
|
|
446
474
|
// so let's mask it
|
|
447
475
|
maskedVars[key] = "(hidden)";
|
|
448
476
|
}
|
|
@@ -477,7 +505,7 @@ export async function startDev(args: StartDevOptions) {
|
|
|
477
505
|
minify={args.minify ?? config.minify}
|
|
478
506
|
nodeCompat={nodeCompat}
|
|
479
507
|
build={config.build || {}}
|
|
480
|
-
define={config.define}
|
|
508
|
+
define={{ ...config.define, ...cliDefines }}
|
|
481
509
|
initialMode={args.local ? "local" : "remote"}
|
|
482
510
|
jsxFactory={args["jsx-factory"] || config.jsx_factory}
|
|
483
511
|
jsxFragment={args["jsx-fragment"] || config.jsx_fragment}
|
|
@@ -502,10 +530,10 @@ export async function startDev(args: StartDevOptions) {
|
|
|
502
530
|
isWorkersSite={Boolean(args.site || config.site)}
|
|
503
531
|
compatibilityDate={getDevCompatibilityDate(
|
|
504
532
|
config,
|
|
505
|
-
args
|
|
533
|
+
args.compatibilityDate
|
|
506
534
|
)}
|
|
507
535
|
compatibilityFlags={
|
|
508
|
-
args
|
|
536
|
+
args.compatibilityFlags || config.compatibility_flags
|
|
509
537
|
}
|
|
510
538
|
usageModel={config.usage_model}
|
|
511
539
|
bindings={bindings}
|
|
@@ -615,7 +643,7 @@ async function getBindings(
|
|
|
615
643
|
) || []),
|
|
616
644
|
...(args.r2 || []),
|
|
617
645
|
],
|
|
618
|
-
|
|
646
|
+
dispatch_namespaces: configParam.dispatch_namespaces,
|
|
619
647
|
services: configParam.services,
|
|
620
648
|
unsafe: configParam.unsafe?.bindings,
|
|
621
649
|
logfwdr: configParam.logfwdr,
|
|
@@ -34,7 +34,7 @@ async function createWorkerNamespace(accountId: string, name: string) {
|
|
|
34
34
|
}
|
|
35
35
|
);
|
|
36
36
|
logger.log(
|
|
37
|
-
`Created
|
|
37
|
+
`Created dispatch namespace "${name}" with ID "${namespace.namespace_id}"`
|
|
38
38
|
);
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -46,7 +46,7 @@ async function deleteWorkerNamespace(accountId: string, name: string) {
|
|
|
46
46
|
`/accounts/${accountId}/workers/dispatch/namespaces/${name}`,
|
|
47
47
|
{ method: "DELETE" }
|
|
48
48
|
);
|
|
49
|
-
logger.log(`Deleted
|
|
49
|
+
logger.log(`Deleted dispatch namespace "${name}"`);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
@@ -101,7 +101,7 @@ async function renameWorkerNamespace(
|
|
|
101
101
|
body: JSON.stringify({ name: newName }),
|
|
102
102
|
}
|
|
103
103
|
);
|
|
104
|
-
logger.log(`Renamed
|
|
104
|
+
logger.log(`Renamed dispatch namespace "${oldName}" to "${newName}"`);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
export function workerNamespaceCommands(
|
|
@@ -110,20 +110,20 @@ export function workerNamespaceCommands(
|
|
|
110
110
|
): Argv {
|
|
111
111
|
return workerNamespaceYargs
|
|
112
112
|
.command(subHelp)
|
|
113
|
-
.command("list", "List all
|
|
113
|
+
.command("list", "List all dispatch namespaces", {}, async (args) => {
|
|
114
114
|
const config = readConfig(args.config as ConfigPath, args);
|
|
115
115
|
const accountId = await requireAuth(config);
|
|
116
116
|
await listWorkerNamespaces(accountId);
|
|
117
|
-
await metrics.sendMetricsEvent("list
|
|
117
|
+
await metrics.sendMetricsEvent("list dispatch namespaces", {
|
|
118
118
|
sendMetrics: config.send_metrics,
|
|
119
119
|
});
|
|
120
120
|
})
|
|
121
121
|
.command(
|
|
122
122
|
"get <name>",
|
|
123
|
-
"Get information about a
|
|
123
|
+
"Get information about a dispatch namespace",
|
|
124
124
|
(yargs) => {
|
|
125
125
|
return yargs.positional("name", {
|
|
126
|
-
describe: "Name of the
|
|
126
|
+
describe: "Name of the dispatch namespace",
|
|
127
127
|
type: "string",
|
|
128
128
|
demandOption: true,
|
|
129
129
|
});
|
|
@@ -132,17 +132,17 @@ export function workerNamespaceCommands(
|
|
|
132
132
|
const config = readConfig(args.config as ConfigPath, args);
|
|
133
133
|
const accountId = await requireAuth(config);
|
|
134
134
|
await getWorkerNamespaceInfo(accountId, args.name);
|
|
135
|
-
await metrics.sendMetricsEvent("view
|
|
135
|
+
await metrics.sendMetricsEvent("view dispatch namespace", {
|
|
136
136
|
sendMetrics: config.send_metrics,
|
|
137
137
|
});
|
|
138
138
|
}
|
|
139
139
|
)
|
|
140
140
|
.command(
|
|
141
141
|
"create <name>",
|
|
142
|
-
"Create a
|
|
142
|
+
"Create a dispatch namespace",
|
|
143
143
|
(yargs) => {
|
|
144
144
|
return yargs.positional("name", {
|
|
145
|
-
describe: "Name of the
|
|
145
|
+
describe: "Name of the dispatch namespace",
|
|
146
146
|
type: "string",
|
|
147
147
|
demandOption: true,
|
|
148
148
|
});
|
|
@@ -152,17 +152,17 @@ export function workerNamespaceCommands(
|
|
|
152
152
|
const config = readConfig(args.config as ConfigPath, args);
|
|
153
153
|
const accountId = await requireAuth(config);
|
|
154
154
|
await createWorkerNamespace(accountId, args.name);
|
|
155
|
-
await metrics.sendMetricsEvent("create
|
|
155
|
+
await metrics.sendMetricsEvent("create dispatch namespace", {
|
|
156
156
|
sendMetrics: config.send_metrics,
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
159
|
)
|
|
160
160
|
.command(
|
|
161
161
|
"delete <name>",
|
|
162
|
-
"Delete a
|
|
162
|
+
"Delete a dispatch namespace",
|
|
163
163
|
(yargs) => {
|
|
164
164
|
return yargs.positional("name", {
|
|
165
|
-
describe: "Name of the
|
|
165
|
+
describe: "Name of the dispatch namespace",
|
|
166
166
|
type: "string",
|
|
167
167
|
demandOption: true,
|
|
168
168
|
});
|
|
@@ -172,23 +172,23 @@ export function workerNamespaceCommands(
|
|
|
172
172
|
const config = readConfig(args.config as ConfigPath, args);
|
|
173
173
|
const accountId = await requireAuth(config);
|
|
174
174
|
await deleteWorkerNamespace(accountId, args.name);
|
|
175
|
-
await metrics.sendMetricsEvent("delete
|
|
175
|
+
await metrics.sendMetricsEvent("delete dispatch namespace", {
|
|
176
176
|
sendMetrics: config.send_metrics,
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
179
|
)
|
|
180
180
|
.command(
|
|
181
181
|
"rename <old-name> <new-name>",
|
|
182
|
-
"Rename a
|
|
182
|
+
"Rename a dispatch namespace",
|
|
183
183
|
(yargs) => {
|
|
184
184
|
return yargs
|
|
185
185
|
.positional("old-name", {
|
|
186
|
-
describe: "Name of the
|
|
186
|
+
describe: "Name of the dispatch namespace",
|
|
187
187
|
type: "string",
|
|
188
188
|
demandOption: true,
|
|
189
189
|
})
|
|
190
190
|
.positional("new-name", {
|
|
191
|
-
describe: "New name of the
|
|
191
|
+
describe: "New name of the dispatch namespace",
|
|
192
192
|
type: "string",
|
|
193
193
|
demandOption: true,
|
|
194
194
|
});
|
|
@@ -198,7 +198,7 @@ export function workerNamespaceCommands(
|
|
|
198
198
|
const config = readConfig(args.config as ConfigPath, args);
|
|
199
199
|
const accountId = await requireAuth(config);
|
|
200
200
|
await renameWorkerNamespace(accountId, args.oldName, args.newName);
|
|
201
|
-
await metrics.sendMetricsEvent("rename
|
|
201
|
+
await metrics.sendMetricsEvent("rename dispatch namespace", {
|
|
202
202
|
sendMetrics: config.send_metrics,
|
|
203
203
|
});
|
|
204
204
|
}
|