wrangler 2.0.5 → 2.0.8
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/README.md +1 -1
- package/bin/wrangler.js +16 -4
- package/package.json +6 -4
- package/pages/functions/buildPlugin.ts +13 -0
- package/pages/functions/buildWorker.ts +13 -0
- package/src/__tests__/configuration.test.ts +335 -86
- package/src/__tests__/dev.test.tsx +166 -15
- package/src/__tests__/helpers/mock-dialogs.ts +41 -1
- package/src/__tests__/index.test.ts +30 -16
- package/src/__tests__/init.test.ts +249 -131
- package/src/__tests__/kv.test.ts +101 -101
- package/src/__tests__/package-manager.test.ts +154 -7
- package/src/__tests__/pages.test.ts +369 -39
- package/src/__tests__/parse.test.ts +5 -1
- package/src/__tests__/publish.test.ts +556 -84
- package/src/__tests__/r2.test.ts +47 -24
- package/src/__tests__/secret.test.ts +39 -4
- package/src/abort.d.ts +3 -0
- package/src/bundle.ts +32 -1
- package/src/cfetch/index.ts +21 -4
- package/src/cfetch/internal.ts +14 -9
- package/src/config/environment.ts +40 -14
- package/src/config/index.ts +162 -0
- package/src/config/validation.ts +179 -64
- package/src/create-worker-preview.ts +17 -7
- package/src/create-worker-upload-form.ts +22 -8
- package/src/dev/dev.tsx +2 -4
- package/src/dev/local.tsx +6 -0
- package/src/dev/remote.tsx +15 -1
- package/src/dialogs.tsx +48 -0
- package/src/durable.ts +102 -0
- package/src/index.tsx +314 -144
- package/src/inspect.ts +39 -0
- package/src/kv.ts +77 -13
- package/src/open-in-browser.ts +5 -12
- package/src/package-manager.ts +50 -3
- package/src/pages.tsx +210 -65
- package/src/parse.ts +21 -4
- package/src/proxy.ts +38 -22
- package/src/publish.ts +227 -113
- package/src/sites.tsx +11 -9
- package/src/worker.ts +8 -0
- package/templates/new-worker-scheduled.js +17 -0
- package/templates/new-worker-scheduled.ts +32 -0
- package/templates/new-worker.ts +16 -1
- package/wrangler-dist/cli.js +35466 -22362
package/src/config/validation.ts
CHANGED
|
@@ -94,22 +94,6 @@ export function normalizeAndValidateConfig(
|
|
|
94
94
|
"boolean"
|
|
95
95
|
);
|
|
96
96
|
|
|
97
|
-
validateOptionalProperty(
|
|
98
|
-
diagnostics,
|
|
99
|
-
"",
|
|
100
|
-
"minify",
|
|
101
|
-
rawConfig.minify,
|
|
102
|
-
"boolean"
|
|
103
|
-
);
|
|
104
|
-
|
|
105
|
-
validateOptionalProperty(
|
|
106
|
-
diagnostics,
|
|
107
|
-
"",
|
|
108
|
-
"node_compat",
|
|
109
|
-
rawConfig.node_compat,
|
|
110
|
-
"boolean"
|
|
111
|
-
);
|
|
112
|
-
|
|
113
97
|
// TODO: set the default to false to turn on service environments as the default
|
|
114
98
|
const isLegacyEnv =
|
|
115
99
|
(args as { "legacy-env": boolean | undefined })["legacy-env"] ??
|
|
@@ -193,7 +177,8 @@ export function normalizeAndValidateConfig(
|
|
|
193
177
|
dev: normalizeAndValidateDev(diagnostics, rawConfig.dev ?? {}),
|
|
194
178
|
migrations: normalizeAndValidateMigrations(
|
|
195
179
|
diagnostics,
|
|
196
|
-
rawConfig.migrations ?? []
|
|
180
|
+
rawConfig.migrations ?? [],
|
|
181
|
+
activeEnv.durable_objects
|
|
197
182
|
),
|
|
198
183
|
site: normalizeAndValidateSite(
|
|
199
184
|
diagnostics,
|
|
@@ -387,7 +372,8 @@ function normalizeAndValidateDev(
|
|
|
387
372
|
*/
|
|
388
373
|
function normalizeAndValidateMigrations(
|
|
389
374
|
diagnostics: Diagnostics,
|
|
390
|
-
rawMigrations: Config["migrations"]
|
|
375
|
+
rawMigrations: Config["migrations"],
|
|
376
|
+
durableObjects: Config["durable_objects"]
|
|
391
377
|
): Config["migrations"] {
|
|
392
378
|
if (!Array.isArray(rawMigrations)) {
|
|
393
379
|
diagnostics.errors.push(
|
|
@@ -398,29 +384,38 @@ function normalizeAndValidateMigrations(
|
|
|
398
384
|
return [];
|
|
399
385
|
} else {
|
|
400
386
|
for (let i = 0; i < rawMigrations.length; i++) {
|
|
401
|
-
const
|
|
387
|
+
const { tag, new_classes, renamed_classes, deleted_classes, ...rest } =
|
|
388
|
+
rawMigrations[i];
|
|
389
|
+
|
|
390
|
+
validateAdditionalProperties(
|
|
391
|
+
diagnostics,
|
|
392
|
+
"migrations",
|
|
393
|
+
Object.keys(rest),
|
|
394
|
+
[]
|
|
395
|
+
);
|
|
396
|
+
|
|
402
397
|
validateRequiredProperty(
|
|
403
398
|
diagnostics,
|
|
404
399
|
`migrations[${i}]`,
|
|
405
400
|
`tag`,
|
|
406
|
-
|
|
401
|
+
tag,
|
|
407
402
|
"string"
|
|
408
403
|
);
|
|
409
404
|
validateOptionalTypedArray(
|
|
410
405
|
diagnostics,
|
|
411
406
|
`migrations[${i}].new_classes`,
|
|
412
|
-
|
|
407
|
+
new_classes,
|
|
413
408
|
"string"
|
|
414
409
|
);
|
|
415
|
-
if (
|
|
416
|
-
if (!Array.isArray(
|
|
410
|
+
if (renamed_classes !== undefined) {
|
|
411
|
+
if (!Array.isArray(renamed_classes)) {
|
|
417
412
|
diagnostics.errors.push(
|
|
418
413
|
`Expected "migrations[${i}].renamed_classes" to be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
|
|
419
|
-
|
|
414
|
+
renamed_classes
|
|
420
415
|
)}.`
|
|
421
416
|
);
|
|
422
417
|
} else if (
|
|
423
|
-
|
|
418
|
+
renamed_classes.some(
|
|
424
419
|
(c) =>
|
|
425
420
|
typeof c !== "object" ||
|
|
426
421
|
!isRequiredProperty(c, "from", "string") ||
|
|
@@ -429,7 +424,7 @@ function normalizeAndValidateMigrations(
|
|
|
429
424
|
) {
|
|
430
425
|
diagnostics.errors.push(
|
|
431
426
|
`Expected "migrations[${i}].renamed_classes" to be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
|
|
432
|
-
|
|
427
|
+
renamed_classes
|
|
433
428
|
)}.`
|
|
434
429
|
);
|
|
435
430
|
}
|
|
@@ -437,10 +432,49 @@ function normalizeAndValidateMigrations(
|
|
|
437
432
|
validateOptionalTypedArray(
|
|
438
433
|
diagnostics,
|
|
439
434
|
`migrations[${i}].deleted_classes`,
|
|
440
|
-
|
|
435
|
+
deleted_classes,
|
|
441
436
|
"string"
|
|
442
437
|
);
|
|
443
438
|
}
|
|
439
|
+
|
|
440
|
+
if (
|
|
441
|
+
Array.isArray(durableObjects?.bindings) &&
|
|
442
|
+
durableObjects.bindings.length > 0
|
|
443
|
+
) {
|
|
444
|
+
// intrinsic [durable_objects] implies [migrations]
|
|
445
|
+
const exportedDurableObjects = (durableObjects.bindings || []).filter(
|
|
446
|
+
(binding) => !binding.script_name
|
|
447
|
+
);
|
|
448
|
+
if (exportedDurableObjects.length > 0 && rawMigrations.length === 0) {
|
|
449
|
+
if (
|
|
450
|
+
!exportedDurableObjects.some(
|
|
451
|
+
(exportedDurableObject) =>
|
|
452
|
+
typeof exportedDurableObject.class_name !== "string"
|
|
453
|
+
)
|
|
454
|
+
) {
|
|
455
|
+
const durableObjectClassnames = exportedDurableObjects.map(
|
|
456
|
+
(durable) => durable.class_name
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
diagnostics.warnings.push(
|
|
460
|
+
`In wrangler.toml, you have configured [durable_objects] exported by this Worker (${durableObjectClassnames.join(
|
|
461
|
+
", "
|
|
462
|
+
)}), but no [migrations] for them. This may not work as expected until you add a [migrations] section to your wrangler.toml. Add this configuration to your wrangler.toml:
|
|
463
|
+
|
|
464
|
+
\`\`\`
|
|
465
|
+
[[migrations]]
|
|
466
|
+
tag = "v1" # Should be unique for each entry
|
|
467
|
+
new_classes = [${durableObjectClassnames
|
|
468
|
+
.map((name) => `"${name}"`)
|
|
469
|
+
.join(", ")}]
|
|
470
|
+
\`\`\`
|
|
471
|
+
|
|
472
|
+
Refer to https://developers.cloudflare.com/workers/learning/using-durable-objects/#durable-object-migrations-in-wranglertoml for more details.`
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
444
478
|
return rawMigrations;
|
|
445
479
|
}
|
|
446
480
|
}
|
|
@@ -562,19 +596,37 @@ function normalizeAndValidateModulePaths(
|
|
|
562
596
|
* or an object that looks like {pattern: string, zone_id: string }
|
|
563
597
|
*/
|
|
564
598
|
function isValidRouteValue(item: unknown): boolean {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
599
|
+
if (!item) {
|
|
600
|
+
return false;
|
|
601
|
+
}
|
|
602
|
+
if (typeof item === "string") {
|
|
603
|
+
return true;
|
|
604
|
+
}
|
|
605
|
+
if (typeof item === "object") {
|
|
606
|
+
if (!hasProperty(item, "pattern") || typeof item.pattern !== "string") {
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
const otherKeys = Object.keys(item).length - 1; // minus one to subtract "pattern"
|
|
611
|
+
|
|
612
|
+
const hasZoneId =
|
|
613
|
+
hasProperty(item, "zone_id") && typeof item.zone_id === "string";
|
|
614
|
+
const hasZoneName =
|
|
615
|
+
hasProperty(item, "zone_name") && typeof item.zone_name === "string";
|
|
616
|
+
const hasCustomDomainFlag =
|
|
617
|
+
hasProperty(item, "custom_domain") &&
|
|
618
|
+
typeof item.custom_domain === "boolean";
|
|
619
|
+
|
|
620
|
+
if (otherKeys === 2 && hasCustomDomainFlag && (hasZoneId || hasZoneName)) {
|
|
621
|
+
return true;
|
|
622
|
+
} else if (
|
|
623
|
+
otherKeys === 1 &&
|
|
624
|
+
(hasZoneId || hasZoneName || hasCustomDomainFlag)
|
|
625
|
+
) {
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
return false;
|
|
578
630
|
}
|
|
579
631
|
|
|
580
632
|
/**
|
|
@@ -583,7 +635,7 @@ function isValidRouteValue(item: unknown): boolean {
|
|
|
583
635
|
const isRoute: ValidatorFn = (diagnostics, field, value) => {
|
|
584
636
|
if (value !== undefined && !isValidRouteValue(value)) {
|
|
585
637
|
diagnostics.errors.push(
|
|
586
|
-
`Expected "${field}" to be either a string, or an object with shape { pattern, zone_id | zone_name }, but got ${JSON.stringify(
|
|
638
|
+
`Expected "${field}" to be either a string, or an object with shape { pattern, custom_domain, zone_id | zone_name }, but got ${JSON.stringify(
|
|
587
639
|
value
|
|
588
640
|
)}.`
|
|
589
641
|
);
|
|
@@ -613,7 +665,7 @@ const isRouteArray: ValidatorFn = (diagnostics, field, value) => {
|
|
|
613
665
|
}
|
|
614
666
|
if (invalidRoutes.length > 0) {
|
|
615
667
|
diagnostics.errors.push(
|
|
616
|
-
`Expected "${field}" to be an array of either strings or objects with the shape { pattern, zone_id | zone_name }, but these weren't valid: ${JSON.stringify(
|
|
668
|
+
`Expected "${field}" to be an array of either strings or objects with the shape { pattern, custom_domain, zone_id | zone_name }, but these weren't valid: ${JSON.stringify(
|
|
617
669
|
invalidRoutes,
|
|
618
670
|
null,
|
|
619
671
|
2
|
|
@@ -696,27 +748,12 @@ function normalizeAndValidateEnvironment(
|
|
|
696
748
|
diagnostics,
|
|
697
749
|
rawEnv,
|
|
698
750
|
"experimental_services",
|
|
699
|
-
`The "experimental_services" field is no longer supported.
|
|
700
|
-
"```\n" +
|
|
701
|
-
TOML.stringify({
|
|
702
|
-
unsafe: {
|
|
703
|
-
bindings: (rawEnv?.experimental_services || []).map(
|
|
704
|
-
(serviceDefinition) => {
|
|
705
|
-
return {
|
|
706
|
-
name: serviceDefinition.name,
|
|
707
|
-
type: "service",
|
|
708
|
-
service: serviceDefinition.service,
|
|
709
|
-
environment: serviceDefinition.environment,
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
|
-
),
|
|
713
|
-
},
|
|
714
|
-
}) +
|
|
715
|
-
"```",
|
|
751
|
+
`The "experimental_services" field is no longer supported. Simply rename the [experimental_services] field to [services].`,
|
|
716
752
|
true
|
|
717
753
|
);
|
|
718
754
|
|
|
719
755
|
experimental(diagnostics, rawEnv, "unsafe");
|
|
756
|
+
experimental(diagnostics, rawEnv, "services");
|
|
720
757
|
|
|
721
758
|
const route = validateRoute(diagnostics, topLevelEnv, rawEnv);
|
|
722
759
|
|
|
@@ -880,6 +917,16 @@ function normalizeAndValidateEnvironment(
|
|
|
880
917
|
validateBindingArray(envName, validateR2Binding),
|
|
881
918
|
[]
|
|
882
919
|
),
|
|
920
|
+
services: notInheritable(
|
|
921
|
+
diagnostics,
|
|
922
|
+
topLevelEnv,
|
|
923
|
+
rawConfig,
|
|
924
|
+
rawEnv,
|
|
925
|
+
envName,
|
|
926
|
+
"services",
|
|
927
|
+
validateBindingArray(envName, validateServiceBinding),
|
|
928
|
+
[]
|
|
929
|
+
),
|
|
883
930
|
unsafe: notInheritable(
|
|
884
931
|
diagnostics,
|
|
885
932
|
topLevelEnv,
|
|
@@ -893,8 +940,22 @@ function normalizeAndValidateEnvironment(
|
|
|
893
940
|
}
|
|
894
941
|
),
|
|
895
942
|
zone_id: rawEnv.zone_id,
|
|
896
|
-
minify:
|
|
897
|
-
|
|
943
|
+
minify: inheritable(
|
|
944
|
+
diagnostics,
|
|
945
|
+
topLevelEnv,
|
|
946
|
+
rawEnv,
|
|
947
|
+
"minify",
|
|
948
|
+
isBoolean,
|
|
949
|
+
undefined
|
|
950
|
+
),
|
|
951
|
+
node_compat: inheritable(
|
|
952
|
+
diagnostics,
|
|
953
|
+
topLevelEnv,
|
|
954
|
+
rawEnv,
|
|
955
|
+
"node_compat",
|
|
956
|
+
isBoolean,
|
|
957
|
+
undefined
|
|
958
|
+
),
|
|
898
959
|
};
|
|
899
960
|
|
|
900
961
|
return environment;
|
|
@@ -1014,7 +1075,7 @@ const validateRule: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1014
1075
|
|
|
1015
1076
|
if (!isOptionalProperty(rule, "fallthrough", "boolean")) {
|
|
1016
1077
|
diagnostics.errors.push(
|
|
1017
|
-
`
|
|
1078
|
+
`the field "fallthrough", when present, should be a boolean.`
|
|
1018
1079
|
);
|
|
1019
1080
|
isValid = false;
|
|
1020
1081
|
}
|
|
@@ -1135,7 +1196,7 @@ const validateDurableObjectBinding: ValidatorFn = (
|
|
|
1135
1196
|
return false;
|
|
1136
1197
|
}
|
|
1137
1198
|
|
|
1138
|
-
// Durable Object bindings must have a name and class_name, and optionally a script_name.
|
|
1199
|
+
// Durable Object bindings must have a name and class_name, and optionally a script_name and an environment.
|
|
1139
1200
|
let isValid = true;
|
|
1140
1201
|
if (!isRequiredProperty(value, "name", "string")) {
|
|
1141
1202
|
diagnostics.errors.push(`binding should have a string "name" field.`);
|
|
@@ -1147,7 +1208,21 @@ const validateDurableObjectBinding: ValidatorFn = (
|
|
|
1147
1208
|
}
|
|
1148
1209
|
if (!isOptionalProperty(value, "script_name", "string")) {
|
|
1149
1210
|
diagnostics.errors.push(
|
|
1150
|
-
`
|
|
1211
|
+
`the field "script_name", when present, should be a string.`
|
|
1212
|
+
);
|
|
1213
|
+
isValid = false;
|
|
1214
|
+
}
|
|
1215
|
+
// environment requires a script_name
|
|
1216
|
+
if (!isOptionalProperty(value, "environment", "string")) {
|
|
1217
|
+
diagnostics.errors.push(
|
|
1218
|
+
`the field "environment", when present, should be a string.`
|
|
1219
|
+
);
|
|
1220
|
+
isValid = false;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
if ("environment" in value && !("script_name" in value)) {
|
|
1224
|
+
diagnostics.errors.push(
|
|
1225
|
+
`binding should have a "script_name" field if "environment" is present.`
|
|
1151
1226
|
);
|
|
1152
1227
|
isValid = false;
|
|
1153
1228
|
}
|
|
@@ -1178,8 +1253,13 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
|
1178
1253
|
const safeBindings = [
|
|
1179
1254
|
"plain_text",
|
|
1180
1255
|
"json",
|
|
1256
|
+
"wasm_module",
|
|
1257
|
+
"data_blob",
|
|
1258
|
+
"text_blob",
|
|
1181
1259
|
"kv_namespace",
|
|
1182
1260
|
"durable_object_namespace",
|
|
1261
|
+
"r2_bucket",
|
|
1262
|
+
"service",
|
|
1183
1263
|
];
|
|
1184
1264
|
|
|
1185
1265
|
if (safeBindings.includes(value.type)) {
|
|
@@ -1422,3 +1502,38 @@ const validateBindingsHaveUniqueNames = (
|
|
|
1422
1502
|
|
|
1423
1503
|
return !hasDuplicates;
|
|
1424
1504
|
};
|
|
1505
|
+
const validateServiceBinding: ValidatorFn = (diagnostics, field, value) => {
|
|
1506
|
+
if (typeof value !== "object" || value === null) {
|
|
1507
|
+
diagnostics.errors.push(
|
|
1508
|
+
`"services" bindings should be objects, but got ${JSON.stringify(value)}`
|
|
1509
|
+
);
|
|
1510
|
+
return false;
|
|
1511
|
+
}
|
|
1512
|
+
let isValid = true;
|
|
1513
|
+
// Service bindings must have a binding, service, and environment.
|
|
1514
|
+
if (!isRequiredProperty(value, "binding", "string")) {
|
|
1515
|
+
diagnostics.errors.push(
|
|
1516
|
+
`"${field}" bindings should have a string "binding" field but got ${JSON.stringify(
|
|
1517
|
+
value
|
|
1518
|
+
)}.`
|
|
1519
|
+
);
|
|
1520
|
+
isValid = false;
|
|
1521
|
+
}
|
|
1522
|
+
if (!isRequiredProperty(value, "service", "string")) {
|
|
1523
|
+
diagnostics.errors.push(
|
|
1524
|
+
`"${field}" bindings should have a string "service" field but got ${JSON.stringify(
|
|
1525
|
+
value
|
|
1526
|
+
)}.`
|
|
1527
|
+
);
|
|
1528
|
+
isValid = false;
|
|
1529
|
+
}
|
|
1530
|
+
if (!isOptionalProperty(value, "environment", "string")) {
|
|
1531
|
+
diagnostics.errors.push(
|
|
1532
|
+
`"${field}" bindings should have a string "environment" field but got ${JSON.stringify(
|
|
1533
|
+
value
|
|
1534
|
+
)}.`
|
|
1535
|
+
);
|
|
1536
|
+
isValid = false;
|
|
1537
|
+
}
|
|
1538
|
+
return isValid;
|
|
1539
|
+
};
|
|
@@ -66,7 +66,12 @@ async function sessionToken(
|
|
|
66
66
|
? `/zones/${ctx.zone.id}/workers/edge-preview`
|
|
67
67
|
: `/accounts/${accountId}/workers/subdomain/edge-preview`;
|
|
68
68
|
|
|
69
|
-
const { exchange_url } = await fetchResult<{ exchange_url: string }>(
|
|
69
|
+
const { exchange_url } = await fetchResult<{ exchange_url: string }>(
|
|
70
|
+
initUrl,
|
|
71
|
+
undefined,
|
|
72
|
+
undefined,
|
|
73
|
+
abortSignal
|
|
74
|
+
);
|
|
70
75
|
const { inspector_websocket, prewarm, token } = (await (
|
|
71
76
|
await fetch(exchange_url, { signal: abortSignal })
|
|
72
77
|
).json()) as { inspector_websocket: string; token: string; prewarm: string };
|
|
@@ -119,13 +124,18 @@ async function createPreviewToken(
|
|
|
119
124
|
const formData = createWorkerUploadForm(worker);
|
|
120
125
|
formData.set("wrangler-session-config", JSON.stringify(mode));
|
|
121
126
|
|
|
122
|
-
const { preview_token } = await fetchResult<{ preview_token: string }>(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
const { preview_token } = await fetchResult<{ preview_token: string }>(
|
|
128
|
+
url,
|
|
129
|
+
{
|
|
130
|
+
method: "POST",
|
|
131
|
+
body: formData,
|
|
132
|
+
headers: {
|
|
133
|
+
"cf-preview-upload-config-token": value,
|
|
134
|
+
},
|
|
127
135
|
},
|
|
128
|
-
|
|
136
|
+
undefined,
|
|
137
|
+
abortSignal
|
|
138
|
+
);
|
|
129
139
|
|
|
130
140
|
return {
|
|
131
141
|
value: preview_token,
|
|
@@ -32,20 +32,24 @@ export interface WorkerMetadata {
|
|
|
32
32
|
compatibility_flags?: string[];
|
|
33
33
|
usage_model?: "bundled" | "unbound";
|
|
34
34
|
migrations?: CfDurableObjectMigrations;
|
|
35
|
+
// If you add any new binding types here, also add it to safeBindings
|
|
36
|
+
// under validateUnsafeBinding in config/validation.ts
|
|
35
37
|
bindings: (
|
|
36
|
-
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
37
38
|
| { type: "plain_text"; name: string; text: string }
|
|
38
39
|
| { type: "json"; name: string; json: unknown }
|
|
39
40
|
| { type: "wasm_module"; name: string; part: string }
|
|
40
41
|
| { type: "text_blob"; name: string; part: string }
|
|
41
42
|
| { type: "data_blob"; name: string; part: string }
|
|
43
|
+
| { type: "kv_namespace"; name: string; namespace_id: string }
|
|
42
44
|
| {
|
|
43
45
|
type: "durable_object_namespace";
|
|
44
46
|
name: string;
|
|
45
47
|
class_name: string;
|
|
46
48
|
script_name?: string;
|
|
49
|
+
environment?: string;
|
|
47
50
|
}
|
|
48
51
|
| { type: "r2_bucket"; name: string; bucket_name: string }
|
|
52
|
+
| { type: "service"; name: string; service: string; environment?: string }
|
|
49
53
|
)[];
|
|
50
54
|
}
|
|
51
55
|
|
|
@@ -67,6 +71,14 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
67
71
|
|
|
68
72
|
const metadataBindings: WorkerMetadata["bindings"] = [];
|
|
69
73
|
|
|
74
|
+
Object.entries(bindings.vars || {})?.forEach(([key, value]) => {
|
|
75
|
+
if (typeof value === "string") {
|
|
76
|
+
metadataBindings.push({ name: key, type: "plain_text", text: value });
|
|
77
|
+
} else {
|
|
78
|
+
metadataBindings.push({ name: key, type: "json", json: value });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
70
82
|
bindings.kv_namespaces?.forEach(({ id, binding }) => {
|
|
71
83
|
metadataBindings.push({
|
|
72
84
|
name: binding,
|
|
@@ -76,12 +88,13 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
76
88
|
});
|
|
77
89
|
|
|
78
90
|
bindings.durable_objects?.bindings.forEach(
|
|
79
|
-
({ name, class_name, script_name }) => {
|
|
91
|
+
({ name, class_name, script_name, environment }) => {
|
|
80
92
|
metadataBindings.push({
|
|
81
93
|
name,
|
|
82
94
|
type: "durable_object_namespace",
|
|
83
95
|
class_name: class_name,
|
|
84
96
|
...(script_name && { script_name }),
|
|
97
|
+
...(environment && { environment }),
|
|
85
98
|
});
|
|
86
99
|
}
|
|
87
100
|
);
|
|
@@ -94,12 +107,13 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
|
|
|
94
107
|
});
|
|
95
108
|
});
|
|
96
109
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
110
|
+
bindings.services?.forEach(({ binding, service, environment }) => {
|
|
111
|
+
metadataBindings.push({
|
|
112
|
+
name: binding,
|
|
113
|
+
type: "service",
|
|
114
|
+
service,
|
|
115
|
+
...(environment && { environment }),
|
|
116
|
+
});
|
|
103
117
|
});
|
|
104
118
|
|
|
105
119
|
for (const [name, filePath] of Object.entries(bindings.wasm_modules || {})) {
|
package/src/dev/dev.tsx
CHANGED
|
@@ -10,6 +10,7 @@ import onExit from "signal-exit";
|
|
|
10
10
|
import tmp from "tmp-promise";
|
|
11
11
|
import { fetch } from "undici";
|
|
12
12
|
import { runCustomBuild } from "../entry";
|
|
13
|
+
import { openInspector } from "../inspect";
|
|
13
14
|
import { logger } from "../logger";
|
|
14
15
|
import openInBrowser from "../open-in-browser";
|
|
15
16
|
import { getAPIToken } from "../user";
|
|
@@ -378,10 +379,7 @@ function useHotkeys(
|
|
|
378
379
|
}
|
|
379
380
|
// toggle inspector
|
|
380
381
|
case "d": {
|
|
381
|
-
await
|
|
382
|
-
`https://built-devtools.pages.dev/js_app?experiments=true&v8only=true&ws=localhost:${inspectorPort}/ws`,
|
|
383
|
-
{ forceChromium: true }
|
|
384
|
-
);
|
|
382
|
+
await openInspector(inspectorPort);
|
|
385
383
|
break;
|
|
386
384
|
}
|
|
387
385
|
// toggle local
|
package/src/dev/local.tsx
CHANGED
|
@@ -87,6 +87,11 @@ function useLocalWorker({
|
|
|
87
87
|
'⎔ A "public" folder is not yet supported in local mode.'
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
|
+
if (bindings.services && bindings.services.length > 0) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
"⎔ Service bindings are not yet supported in local mode."
|
|
93
|
+
);
|
|
94
|
+
}
|
|
90
95
|
|
|
91
96
|
// In local mode, we want to copy all referenced modules into
|
|
92
97
|
// the output bundle directory before starting up
|
|
@@ -297,6 +302,7 @@ function useLocalWorker({
|
|
|
297
302
|
bindings.durable_objects?.bindings,
|
|
298
303
|
bindings.kv_namespaces,
|
|
299
304
|
bindings.vars,
|
|
305
|
+
bindings.services,
|
|
300
306
|
compatibilityDate,
|
|
301
307
|
compatibilityFlags,
|
|
302
308
|
localPersistencePath,
|
package/src/dev/remote.tsx
CHANGED
|
@@ -185,7 +185,21 @@ export function useWorker(props: {
|
|
|
185
185
|
// we want to log the error, but not end the process
|
|
186
186
|
// since it could recover after the developer fixes whatever's wrong
|
|
187
187
|
if ((err as { code: string }).code !== "ABORT_ERR") {
|
|
188
|
-
|
|
188
|
+
// instead of logging the raw API error to the user,
|
|
189
|
+
// give them friendly instructions
|
|
190
|
+
// for error 10063 (workers.dev subdomain required)
|
|
191
|
+
if (err.code === 10063) {
|
|
192
|
+
const errorMessage =
|
|
193
|
+
"Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
|
|
194
|
+
const solutionMessage =
|
|
195
|
+
"You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
|
|
196
|
+
const onboardingLink = `https://dash.cloudflare.com/${accountId}/workers/onboarding`;
|
|
197
|
+
logger.error(
|
|
198
|
+
`${errorMessage}\n${solutionMessage}\n${onboardingLink}`
|
|
199
|
+
);
|
|
200
|
+
} else {
|
|
201
|
+
logger.error("Error on remote worker:", err);
|
|
202
|
+
}
|
|
189
203
|
}
|
|
190
204
|
});
|
|
191
205
|
|
package/src/dialogs.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { Box, Text, useInput, render } from "ink";
|
|
3
|
+
import SelectInput from "ink-select-input";
|
|
3
4
|
import TextInput from "ink-text-input";
|
|
4
5
|
import * as React from "react";
|
|
5
6
|
import { useState } from "react";
|
|
@@ -85,3 +86,50 @@ export async function prompt(
|
|
|
85
86
|
);
|
|
86
87
|
});
|
|
87
88
|
}
|
|
89
|
+
|
|
90
|
+
type SelectOption = {
|
|
91
|
+
value: string;
|
|
92
|
+
label: string;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
type SelectProps = {
|
|
96
|
+
text: string;
|
|
97
|
+
options: SelectOption[];
|
|
98
|
+
initialIndex: number;
|
|
99
|
+
onSelect: (value: string) => void;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
function Select(props: SelectProps) {
|
|
103
|
+
return (
|
|
104
|
+
<Box flexDirection="column">
|
|
105
|
+
<Text>{props.text}</Text>
|
|
106
|
+
<SelectInput
|
|
107
|
+
initialIndex={props.initialIndex}
|
|
108
|
+
items={props.options}
|
|
109
|
+
onSelect={async (selected) => {
|
|
110
|
+
props.onSelect(selected.value);
|
|
111
|
+
}}
|
|
112
|
+
/>
|
|
113
|
+
</Box>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function select(
|
|
118
|
+
text: string,
|
|
119
|
+
options: SelectOption[],
|
|
120
|
+
initialIndex: number
|
|
121
|
+
): Promise<string> {
|
|
122
|
+
return new Promise((resolve) => {
|
|
123
|
+
const { unmount } = render(
|
|
124
|
+
<Select
|
|
125
|
+
text={text}
|
|
126
|
+
options={options}
|
|
127
|
+
initialIndex={initialIndex}
|
|
128
|
+
onSelect={(option: string) => {
|
|
129
|
+
unmount();
|
|
130
|
+
resolve(option);
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
}
|