wrangler 0.0.0-e2f9bb2 → 0.0.0-e322475

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.
Files changed (103) hide show
  1. package/Cloudflare_CA.pem +18 -0
  2. package/README.md +3 -1
  3. package/bin/wrangler.js +36 -4
  4. package/kv-asset-handler.js +1 -0
  5. package/package.json +15 -7
  6. package/pages/functions/buildPlugin.ts +13 -0
  7. package/pages/functions/buildWorker.ts +14 -1
  8. package/pages/functions/template-plugin.ts +3 -2
  9. package/src/__tests__/configuration.test.ts +652 -229
  10. package/src/__tests__/dev.test.tsx +372 -79
  11. package/src/__tests__/helpers/mock-cfetch.ts +2 -2
  12. package/src/__tests__/helpers/mock-console.ts +11 -1
  13. package/src/__tests__/helpers/mock-dialogs.ts +75 -11
  14. package/src/__tests__/helpers/mock-process.ts +24 -0
  15. package/src/__tests__/index.test.ts +81 -72
  16. package/src/__tests__/init.test.ts +1360 -410
  17. package/src/__tests__/jest.setup.ts +38 -0
  18. package/src/__tests__/kv.test.ts +218 -152
  19. package/src/__tests__/logger.test.ts +26 -0
  20. package/src/__tests__/package-manager.test.ts +154 -7
  21. package/src/__tests__/pages.test.ts +478 -53
  22. package/src/__tests__/parse.test.ts +8 -0
  23. package/src/__tests__/publish.test.ts +1614 -333
  24. package/src/__tests__/r2.test.ts +55 -36
  25. package/src/__tests__/secret.test.ts +39 -4
  26. package/src/__tests__/user.test.ts +1 -0
  27. package/src/__tests__/whoami.test.tsx +34 -0
  28. package/src/abort.d.ts +3 -0
  29. package/src/bundle-reporter.tsx +29 -0
  30. package/src/bundle.ts +42 -7
  31. package/src/cfetch/index.ts +21 -4
  32. package/src/cfetch/internal.ts +25 -20
  33. package/src/config/config.ts +11 -2
  34. package/src/config/environment.ts +41 -15
  35. package/src/config/index.ts +162 -0
  36. package/src/config/validation-helpers.ts +10 -1
  37. package/src/config/validation.ts +234 -78
  38. package/src/create-worker-preview.ts +32 -22
  39. package/src/create-worker-upload-form.ts +22 -8
  40. package/src/dev/dev.tsx +48 -67
  41. package/src/dev/local.tsx +16 -7
  42. package/src/dev/remote.tsx +57 -21
  43. package/src/dev/use-esbuild.ts +1 -7
  44. package/src/dialogs.tsx +48 -0
  45. package/src/durable.ts +102 -0
  46. package/src/entry.ts +71 -10
  47. package/src/generate-auth-url.ts +33 -0
  48. package/src/generate-random-state.ts +16 -0
  49. package/src/git-client.ts +42 -0
  50. package/src/index.tsx +619 -338
  51. package/src/inspect.ts +39 -0
  52. package/src/kv.ts +116 -27
  53. package/src/logger.ts +8 -1
  54. package/src/open-in-browser.ts +3 -12
  55. package/src/package-manager.ts +50 -3
  56. package/src/pages.tsx +434 -207
  57. package/src/parse.ts +44 -5
  58. package/src/proxy.ts +66 -44
  59. package/src/publish.ts +407 -137
  60. package/src/sites.tsx +103 -28
  61. package/src/user.tsx +35 -26
  62. package/src/whoami.tsx +3 -2
  63. package/src/worker.ts +10 -1
  64. package/src/zones.ts +73 -0
  65. package/templates/new-worker-scheduled.js +17 -0
  66. package/templates/new-worker-scheduled.ts +32 -0
  67. package/templates/new-worker.ts +16 -1
  68. package/templates/no-op-worker.js +10 -0
  69. package/templates/static-asset-facade.js +2 -6
  70. package/wrangler-dist/cli.js +46122 -32396
  71. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  72. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  73. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  74. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  75. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  76. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  77. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  78. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  79. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  80. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  81. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  82. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  83. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  84. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  85. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  86. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  87. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  88. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  89. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  90. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  91. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  92. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  93. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  94. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  95. package/vendor/wrangler-mime/LICENSE +0 -21
  96. package/vendor/wrangler-mime/Mime.js +0 -97
  97. package/vendor/wrangler-mime/README.md +0 -187
  98. package/vendor/wrangler-mime/cli.js +0 -46
  99. package/vendor/wrangler-mime/index.js +0 -4
  100. package/vendor/wrangler-mime/lite.js +0 -4
  101. package/vendor/wrangler-mime/package.json +0 -52
  102. package/vendor/wrangler-mime/types/other.js +0 -1
  103. package/vendor/wrangler-mime/types/standard.js +0 -1
@@ -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,
@@ -201,6 +186,7 @@ export function normalizeAndValidateConfig(
201
186
  rawConfig,
202
187
  activeEnv.main
203
188
  ),
189
+ assets: normalizeAndValidateAssets(diagnostics, configPath, rawConfig),
204
190
  wasm_modules: normalizeAndValidateModulePaths(
205
191
  diagnostics,
206
192
  configPath,
@@ -230,6 +216,8 @@ export function normalizeAndValidateConfig(
230
216
  [...Object.keys(config), "env"]
231
217
  );
232
218
 
219
+ experimental(diagnostics, rawConfig, "assets");
220
+
233
221
  return { config, diagnostics };
234
222
  }
235
223
 
@@ -242,19 +230,23 @@ function normalizeAndValidateBuild(
242
230
  rawBuild: Config["build"],
243
231
  configPath: string | undefined
244
232
  ): Config["build"] & { deprecatedUpload: DeprecatedUpload } {
245
- const { command, cwd, watch_dir, upload, ...rest } = rawBuild;
233
+ const { command, cwd, watch_dir = "./src", upload, ...rest } = rawBuild;
246
234
  const deprecatedUpload: DeprecatedUpload = { ...upload };
247
235
  validateAdditionalProperties(diagnostics, "build", Object.keys(rest), []);
248
236
 
249
237
  validateOptionalProperty(diagnostics, "build", "command", command, "string");
250
238
  validateOptionalProperty(diagnostics, "build", "cwd", cwd, "string");
251
- validateOptionalProperty(
252
- diagnostics,
253
- "build",
254
- "watch_dir",
255
- watch_dir,
256
- "string"
257
- );
239
+ if (Array.isArray(watch_dir)) {
240
+ validateTypedArray(diagnostics, "build.watch_dir", watch_dir, "string");
241
+ } else {
242
+ validateOptionalProperty(
243
+ diagnostics,
244
+ "build",
245
+ "watch_dir",
246
+ watch_dir,
247
+ "string"
248
+ );
249
+ }
258
250
 
259
251
  deprecated(
260
252
  diagnostics,
@@ -303,13 +295,18 @@ function normalizeAndValidateBuild(
303
295
  // - `configPath` will always be defined since `build` can only
304
296
  // be configured in `wrangler.toml`, but who knows, that may
305
297
  // change in the future, so we do a check anyway
306
- command
307
- ? configPath
308
- ? path.relative(
298
+ command && configPath
299
+ ? Array.isArray(watch_dir)
300
+ ? watch_dir.map((dir) =>
301
+ path.relative(
302
+ process.cwd(),
303
+ path.join(path.dirname(configPath), `${dir}`)
304
+ )
305
+ )
306
+ : path.relative(
309
307
  process.cwd(),
310
- path.join(path.dirname(configPath), watch_dir || "./src")
308
+ path.join(path.dirname(configPath), `${watch_dir}`)
311
309
  )
312
- : watch_dir || "./src"
313
310
  : watch_dir,
314
311
  cwd,
315
312
  deprecatedUpload,
@@ -352,7 +349,7 @@ function normalizeAndValidateDev(
352
349
  ): DevConfig {
353
350
  const {
354
351
  ip = "localhost",
355
- port = 8787,
352
+ port,
356
353
  local_protocol = "http",
357
354
  upstream_protocol = "https",
358
355
  host,
@@ -387,7 +384,8 @@ function normalizeAndValidateDev(
387
384
  */
388
385
  function normalizeAndValidateMigrations(
389
386
  diagnostics: Diagnostics,
390
- rawMigrations: Config["migrations"]
387
+ rawMigrations: Config["migrations"],
388
+ durableObjects: Config["durable_objects"]
391
389
  ): Config["migrations"] {
392
390
  if (!Array.isArray(rawMigrations)) {
393
391
  diagnostics.errors.push(
@@ -398,29 +396,38 @@ function normalizeAndValidateMigrations(
398
396
  return [];
399
397
  } else {
400
398
  for (let i = 0; i < rawMigrations.length; i++) {
401
- const migration = rawMigrations[i];
399
+ const { tag, new_classes, renamed_classes, deleted_classes, ...rest } =
400
+ rawMigrations[i];
401
+
402
+ validateAdditionalProperties(
403
+ diagnostics,
404
+ "migrations",
405
+ Object.keys(rest),
406
+ []
407
+ );
408
+
402
409
  validateRequiredProperty(
403
410
  diagnostics,
404
411
  `migrations[${i}]`,
405
412
  `tag`,
406
- migration.tag,
413
+ tag,
407
414
  "string"
408
415
  );
409
416
  validateOptionalTypedArray(
410
417
  diagnostics,
411
418
  `migrations[${i}].new_classes`,
412
- migration.new_classes,
419
+ new_classes,
413
420
  "string"
414
421
  );
415
- if (migration.renamed_classes !== undefined) {
416
- if (!Array.isArray(migration.renamed_classes)) {
422
+ if (renamed_classes !== undefined) {
423
+ if (!Array.isArray(renamed_classes)) {
417
424
  diagnostics.errors.push(
418
425
  `Expected "migrations[${i}].renamed_classes" to be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
419
- migration.renamed_classes
426
+ renamed_classes
420
427
  )}.`
421
428
  );
422
429
  } else if (
423
- migration.renamed_classes.some(
430
+ renamed_classes.some(
424
431
  (c) =>
425
432
  typeof c !== "object" ||
426
433
  !isRequiredProperty(c, "from", "string") ||
@@ -429,7 +436,7 @@ function normalizeAndValidateMigrations(
429
436
  ) {
430
437
  diagnostics.errors.push(
431
438
  `Expected "migrations[${i}].renamed_classes" to be an array of "{from: string, to: string}" objects but got ${JSON.stringify(
432
- migration.renamed_classes
439
+ renamed_classes
433
440
  )}.`
434
441
  );
435
442
  }
@@ -437,10 +444,49 @@ function normalizeAndValidateMigrations(
437
444
  validateOptionalTypedArray(
438
445
  diagnostics,
439
446
  `migrations[${i}].deleted_classes`,
440
- migration.deleted_classes,
447
+ deleted_classes,
441
448
  "string"
442
449
  );
443
450
  }
451
+
452
+ if (
453
+ Array.isArray(durableObjects?.bindings) &&
454
+ durableObjects.bindings.length > 0
455
+ ) {
456
+ // intrinsic [durable_objects] implies [migrations]
457
+ const exportedDurableObjects = (durableObjects.bindings || []).filter(
458
+ (binding) => !binding.script_name
459
+ );
460
+ if (exportedDurableObjects.length > 0 && rawMigrations.length === 0) {
461
+ if (
462
+ !exportedDurableObjects.some(
463
+ (exportedDurableObject) =>
464
+ typeof exportedDurableObject.class_name !== "string"
465
+ )
466
+ ) {
467
+ const durableObjectClassnames = exportedDurableObjects.map(
468
+ (durable) => durable.class_name
469
+ );
470
+
471
+ diagnostics.warnings.push(
472
+ `In wrangler.toml, you have configured [durable_objects] exported by this Worker (${durableObjectClassnames.join(
473
+ ", "
474
+ )}), 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:
475
+
476
+ \`\`\`
477
+ [[migrations]]
478
+ tag = "v1" # Should be unique for each entry
479
+ new_classes = [${durableObjectClassnames
480
+ .map((name) => `"${name}"`)
481
+ .join(", ")}]
482
+ \`\`\`
483
+
484
+ Refer to https://developers.cloudflare.com/workers/learning/using-durable-objects/#durable-object-migrations-in-wranglertoml for more details.`
485
+ );
486
+ }
487
+ }
488
+ }
489
+
444
490
  return rawMigrations;
445
491
  }
446
492
  }
@@ -528,6 +574,35 @@ function normalizeAndValidateSite(
528
574
  return undefined;
529
575
  }
530
576
 
577
+ /**
578
+ * Validate the `assets` configuration and return normalized values.
579
+ */
580
+ function normalizeAndValidateAssets(
581
+ diagnostics: Diagnostics,
582
+ configPath: string | undefined,
583
+ rawConfig: RawConfig
584
+ ) {
585
+ if (
586
+ typeof rawConfig?.assets === "string" ||
587
+ rawConfig?.assets === undefined
588
+ ) {
589
+ return rawConfig?.assets;
590
+ }
591
+
592
+ const { bucket, include = [], exclude = [], ...rest } = rawConfig.assets;
593
+
594
+ validateAdditionalProperties(diagnostics, "assets", Object.keys(rest), []);
595
+ validateRequiredProperty(diagnostics, "assets", "bucket", bucket, "string");
596
+ validateTypedArray(diagnostics, "assets.include", include, "string");
597
+ validateTypedArray(diagnostics, "assets.exclude", exclude, "string");
598
+
599
+ return {
600
+ bucket,
601
+ include,
602
+ exclude,
603
+ };
604
+ }
605
+
531
606
  /**
532
607
  * Map the paths of the `wasm_modules`, `text_blobs` or `data_blobs` configuration to be relative to the current working directory.
533
608
  */
@@ -562,19 +637,37 @@ function normalizeAndValidateModulePaths(
562
637
  * or an object that looks like {pattern: string, zone_id: string }
563
638
  */
564
639
  function isValidRouteValue(item: unknown): boolean {
565
- return (
566
- !!item &&
567
- (typeof item === "string" ||
568
- (typeof item === "object" &&
569
- hasProperty(item, "pattern") &&
570
- typeof item.pattern === "string" &&
571
- // it could have a zone_name
572
- ((hasProperty(item, "zone_name") &&
573
- typeof item.zone_name === "string") ||
574
- // or a zone_id
575
- (hasProperty(item, "zone_id") && typeof item.zone_id === "string")) &&
576
- Object.keys(item).length === 2))
577
- );
640
+ if (!item) {
641
+ return false;
642
+ }
643
+ if (typeof item === "string") {
644
+ return true;
645
+ }
646
+ if (typeof item === "object") {
647
+ if (!hasProperty(item, "pattern") || typeof item.pattern !== "string") {
648
+ return false;
649
+ }
650
+
651
+ const otherKeys = Object.keys(item).length - 1; // minus one to subtract "pattern"
652
+
653
+ const hasZoneId =
654
+ hasProperty(item, "zone_id") && typeof item.zone_id === "string";
655
+ const hasZoneName =
656
+ hasProperty(item, "zone_name") && typeof item.zone_name === "string";
657
+ const hasCustomDomainFlag =
658
+ hasProperty(item, "custom_domain") &&
659
+ typeof item.custom_domain === "boolean";
660
+
661
+ if (otherKeys === 2 && hasCustomDomainFlag && (hasZoneId || hasZoneName)) {
662
+ return true;
663
+ } else if (
664
+ otherKeys === 1 &&
665
+ (hasZoneId || hasZoneName || hasCustomDomainFlag)
666
+ ) {
667
+ return true;
668
+ }
669
+ }
670
+ return false;
578
671
  }
579
672
 
580
673
  /**
@@ -583,7 +676,7 @@ function isValidRouteValue(item: unknown): boolean {
583
676
  const isRoute: ValidatorFn = (diagnostics, field, value) => {
584
677
  if (value !== undefined && !isValidRouteValue(value)) {
585
678
  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(
679
+ `Expected "${field}" to be either a string, or an object with shape { pattern, custom_domain, zone_id | zone_name }, but got ${JSON.stringify(
587
680
  value
588
681
  )}.`
589
682
  );
@@ -613,7 +706,7 @@ const isRouteArray: ValidatorFn = (diagnostics, field, value) => {
613
706
  }
614
707
  if (invalidRoutes.length > 0) {
615
708
  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(
709
+ `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
710
  invalidRoutes,
618
711
  null,
619
712
  2
@@ -696,27 +789,12 @@ function normalizeAndValidateEnvironment(
696
789
  diagnostics,
697
790
  rawEnv,
698
791
  "experimental_services",
699
- `The "experimental_services" field is no longer supported. Instead, use [[unsafe.bindings]] to enable experimental features. Add this to your wrangler.toml:\n` +
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
- "```",
792
+ `The "experimental_services" field is no longer supported. Simply rename the [experimental_services] field to [services].`,
716
793
  true
717
794
  );
718
795
 
719
796
  experimental(diagnostics, rawEnv, "unsafe");
797
+ experimental(diagnostics, rawEnv, "services");
720
798
 
721
799
  const route = validateRoute(diagnostics, topLevelEnv, rawEnv);
722
800
 
@@ -880,6 +958,16 @@ function normalizeAndValidateEnvironment(
880
958
  validateBindingArray(envName, validateR2Binding),
881
959
  []
882
960
  ),
961
+ services: notInheritable(
962
+ diagnostics,
963
+ topLevelEnv,
964
+ rawConfig,
965
+ rawEnv,
966
+ envName,
967
+ "services",
968
+ validateBindingArray(envName, validateServiceBinding),
969
+ []
970
+ ),
883
971
  unsafe: notInheritable(
884
972
  diagnostics,
885
973
  topLevelEnv,
@@ -893,8 +981,22 @@ function normalizeAndValidateEnvironment(
893
981
  }
894
982
  ),
895
983
  zone_id: rawEnv.zone_id,
896
- minify: rawEnv.minify,
897
- node_compat: rawEnv.node_compat,
984
+ minify: inheritable(
985
+ diagnostics,
986
+ topLevelEnv,
987
+ rawEnv,
988
+ "minify",
989
+ isBoolean,
990
+ undefined
991
+ ),
992
+ node_compat: inheritable(
993
+ diagnostics,
994
+ topLevelEnv,
995
+ rawEnv,
996
+ "node_compat",
997
+ isBoolean,
998
+ undefined
999
+ ),
898
1000
  };
899
1001
 
900
1002
  return environment;
@@ -1014,7 +1116,7 @@ const validateRule: ValidatorFn = (diagnostics, field, value) => {
1014
1116
 
1015
1117
  if (!isOptionalProperty(rule, "fallthrough", "boolean")) {
1016
1118
  diagnostics.errors.push(
1017
- `binding should, optionally, have a boolean "fallthrough" field.`
1119
+ `the field "fallthrough", when present, should be a boolean.`
1018
1120
  );
1019
1121
  isValid = false;
1020
1122
  }
@@ -1135,7 +1237,7 @@ const validateDurableObjectBinding: ValidatorFn = (
1135
1237
  return false;
1136
1238
  }
1137
1239
 
1138
- // Durable Object bindings must have a name and class_name, and optionally a script_name.
1240
+ // Durable Object bindings must have a name and class_name, and optionally a script_name and an environment.
1139
1241
  let isValid = true;
1140
1242
  if (!isRequiredProperty(value, "name", "string")) {
1141
1243
  diagnostics.errors.push(`binding should have a string "name" field.`);
@@ -1147,7 +1249,21 @@ const validateDurableObjectBinding: ValidatorFn = (
1147
1249
  }
1148
1250
  if (!isOptionalProperty(value, "script_name", "string")) {
1149
1251
  diagnostics.errors.push(
1150
- `binding should, optionally, have a string "script_name" field.`
1252
+ `the field "script_name", when present, should be a string.`
1253
+ );
1254
+ isValid = false;
1255
+ }
1256
+ // environment requires a script_name
1257
+ if (!isOptionalProperty(value, "environment", "string")) {
1258
+ diagnostics.errors.push(
1259
+ `the field "environment", when present, should be a string.`
1260
+ );
1261
+ isValid = false;
1262
+ }
1263
+
1264
+ if ("environment" in value && !("script_name" in value)) {
1265
+ diagnostics.errors.push(
1266
+ `binding should have a "script_name" field if "environment" is present.`
1151
1267
  );
1152
1268
  isValid = false;
1153
1269
  }
@@ -1178,8 +1294,13 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => {
1178
1294
  const safeBindings = [
1179
1295
  "plain_text",
1180
1296
  "json",
1297
+ "wasm_module",
1298
+ "data_blob",
1299
+ "text_blob",
1181
1300
  "kv_namespace",
1182
1301
  "durable_object_namespace",
1302
+ "r2_bucket",
1303
+ "service",
1183
1304
  ];
1184
1305
 
1185
1306
  if (safeBindings.includes(value.type)) {
@@ -1422,3 +1543,38 @@ const validateBindingsHaveUniqueNames = (
1422
1543
 
1423
1544
  return !hasDuplicates;
1424
1545
  };
1546
+ const validateServiceBinding: ValidatorFn = (diagnostics, field, value) => {
1547
+ if (typeof value !== "object" || value === null) {
1548
+ diagnostics.errors.push(
1549
+ `"services" bindings should be objects, but got ${JSON.stringify(value)}`
1550
+ );
1551
+ return false;
1552
+ }
1553
+ let isValid = true;
1554
+ // Service bindings must have a binding, service, and environment.
1555
+ if (!isRequiredProperty(value, "binding", "string")) {
1556
+ diagnostics.errors.push(
1557
+ `"${field}" bindings should have a string "binding" field but got ${JSON.stringify(
1558
+ value
1559
+ )}.`
1560
+ );
1561
+ isValid = false;
1562
+ }
1563
+ if (!isRequiredProperty(value, "service", "string")) {
1564
+ diagnostics.errors.push(
1565
+ `"${field}" bindings should have a string "service" field but got ${JSON.stringify(
1566
+ value
1567
+ )}.`
1568
+ );
1569
+ isValid = false;
1570
+ }
1571
+ if (!isOptionalProperty(value, "environment", "string")) {
1572
+ diagnostics.errors.push(
1573
+ `"${field}" bindings should have a string "environment" field but got ${JSON.stringify(
1574
+ value
1575
+ )}.`
1576
+ );
1577
+ isValid = false;
1578
+ }
1579
+ return isValid;
1580
+ };
@@ -63,10 +63,15 @@ async function sessionToken(
63
63
  ): Promise<CfPreviewToken> {
64
64
  const { accountId } = account;
65
65
  const initUrl = ctx.zone
66
- ? `/zones/${ctx.zone.id}/workers/edge-preview`
66
+ ? `/zones/${ctx.zone}/workers/edge-preview`
67
67
  : `/accounts/${accountId}/workers/subdomain/edge-preview`;
68
68
 
69
- const { exchange_url } = await fetchResult<{ exchange_url: string }>(initUrl);
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 };
@@ -106,7 +111,7 @@ async function createPreviewToken(
106
111
  );
107
112
 
108
113
  const { accountId } = account;
109
- const scriptId = ctx.zone ? randomId() : worker.name || host.split(".")[0];
114
+ const scriptId = worker.name || (ctx.zone ? randomId() : host.split(".")[0]);
110
115
  const url =
111
116
  ctx.env && !ctx.legacyEnv
112
117
  ? `/accounts/${accountId}/workers/services/${scriptId}/environments/${ctx.env}/edge-preview`
@@ -119,29 +124,34 @@ 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 }>(url, {
123
- method: "POST",
124
- body: formData,
125
- headers: {
126
- "cf-preview-upload-config-token": value,
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,
132
- host: ctx.zone
133
- ? ctx.zone.host
134
- : worker.name
135
- ? `${
136
- worker.name
137
- // TODO: this should also probably have the env prefix
138
- // but it doesn't appear to work yet, instead giving us the
139
- // "There is nothing here yet" screen
140
- // ctx.env && !ctx.legacyEnv
141
- // ? `${ctx.env}.${worker.name}`
142
- // : worker.name
143
- }.${host.split(".").slice(1).join(".")}`
144
- : host,
142
+ host:
143
+ ctx.host ??
144
+ (worker.name
145
+ ? `${
146
+ worker.name
147
+ // TODO: this should also probably have the env prefix
148
+ // but it doesn't appear to work yet, instead giving us the
149
+ // "There is nothing here yet" screen
150
+ // ctx.env && !ctx.legacyEnv
151
+ // ? `${ctx.env}.${worker.name}`
152
+ // : worker.name
153
+ }.${host.split(".").slice(1).join(".")}`
154
+ : host),
145
155
 
146
156
  inspectorUrl,
147
157
  prewarmUrl,
@@ -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
- Object.entries(bindings.vars || {})?.forEach(([key, value]) => {
98
- if (typeof value === "string") {
99
- metadataBindings.push({ name: key, type: "plain_text", text: value });
100
- } else {
101
- metadataBindings.push({ name: key, type: "json", json: value });
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 || {})) {