wrangler 0.0.0-e2b7352 → 0.0.0-e2f9bb2

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 (86) hide show
  1. package/README.md +24 -13
  2. package/bin/wrangler.js +3 -0
  3. package/miniflare-dist/index.mjs +4828 -21
  4. package/package.json +29 -22
  5. package/pages/functions/buildPlugin.ts +92 -0
  6. package/pages/functions/buildWorker.ts +80 -4
  7. package/pages/functions/filepath-routing.test.ts +11 -0
  8. package/pages/functions/filepath-routing.ts +8 -0
  9. package/pages/functions/routes.ts +6 -1
  10. package/pages/functions/template-plugin.ts +146 -0
  11. package/pages/functions/template-worker.ts +21 -8
  12. package/src/__tests__/config-cache.test.ts +36 -0
  13. package/src/__tests__/configuration.test.ts +386 -81
  14. package/src/__tests__/dev.test.tsx +242 -21
  15. package/src/__tests__/guess-worker-format.test.ts +18 -1
  16. package/src/__tests__/helpers/mock-cfetch.ts +5 -1
  17. package/src/__tests__/helpers/mock-console.ts +20 -3
  18. package/src/__tests__/helpers/mock-dialogs.ts +24 -0
  19. package/src/__tests__/helpers/mock-http-server.ts +46 -0
  20. package/src/__tests__/helpers/mock-istty.ts +27 -0
  21. package/src/__tests__/helpers/mock-oauth-flow.ts +255 -0
  22. package/src/__tests__/helpers/run-in-tmp.ts +7 -0
  23. package/src/__tests__/helpers/write-worker-source.ts +31 -0
  24. package/src/__tests__/https-options.test.ts +5 -2
  25. package/src/__tests__/index.test.ts +25 -759
  26. package/src/__tests__/init.test.ts +1042 -0
  27. package/src/__tests__/jest.setup.ts +4 -4
  28. package/src/__tests__/kv.test.ts +130 -56
  29. package/src/__tests__/logger.test.ts +115 -0
  30. package/src/__tests__/pages.test.ts +290 -43
  31. package/src/__tests__/publish.test.ts +1699 -333
  32. package/src/__tests__/r2.test.ts +24 -8
  33. package/src/__tests__/route.test.ts +4 -4
  34. package/src/__tests__/secret.test.ts +55 -34
  35. package/src/__tests__/tail.test.ts +97 -45
  36. package/src/__tests__/user.test.ts +131 -0
  37. package/src/__tests__/whoami.test.tsx +14 -9
  38. package/src/bundle.ts +15 -1
  39. package/src/cfetch/internal.ts +0 -2
  40. package/src/cli.ts +5 -11
  41. package/src/config/README.md +1 -1
  42. package/src/config/config.ts +20 -4
  43. package/src/config/environment.ts +26 -3
  44. package/src/config/index.ts +2 -1
  45. package/src/config/validation-helpers.ts +26 -5
  46. package/src/config/validation.ts +233 -41
  47. package/src/config-cache.ts +45 -0
  48. package/src/create-worker-preview.ts +22 -13
  49. package/src/create-worker-upload-form.ts +32 -9
  50. package/src/dev/dev-vars.ts +36 -0
  51. package/src/dev/dev.tsx +60 -34
  52. package/src/dev/local.tsx +101 -21
  53. package/src/dev/remote.tsx +23 -13
  54. package/src/dev/use-esbuild.ts +19 -5
  55. package/src/dialogs.tsx +16 -6
  56. package/src/entry.ts +38 -11
  57. package/src/environment-variables.ts +3 -1
  58. package/src/errors.ts +11 -0
  59. package/src/https-options.ts +3 -2
  60. package/src/index.tsx +529 -303
  61. package/src/inspect.ts +17 -11
  62. package/src/logger.ts +66 -0
  63. package/src/miniflare-cli/index.ts +4 -0
  64. package/src/miniflare-cli/request-context.ts +40 -0
  65. package/src/module-collection.ts +7 -7
  66. package/src/open-in-browser.ts +5 -4
  67. package/src/package-manager.ts +9 -8
  68. package/src/pages.tsx +860 -55
  69. package/src/parse.ts +1 -1
  70. package/src/proxy.ts +25 -11
  71. package/src/publish.ts +260 -141
  72. package/src/sites.tsx +27 -14
  73. package/src/tail/index.ts +1 -1
  74. package/src/tail/printing.ts +13 -8
  75. package/src/update-check.ts +19 -0
  76. package/src/user.tsx +104 -38
  77. package/src/whoami.tsx +2 -1
  78. package/src/worker.ts +15 -6
  79. package/templates/gitignore +171 -0
  80. package/templates/static-asset-facade.js +1 -1
  81. package/wrangler-dist/cli.js +20948 -50169
  82. package/miniflare-dist/index.mjs.map +0 -7
  83. package/src/__tests__/logout.test.ts +0 -52
  84. package/src/__tests__/sentry.test.ts +0 -146
  85. package/src/reporting.ts +0 -131
  86. package/wrangler-dist/cli.js.map +0 -7
@@ -24,6 +24,7 @@ import {
24
24
  inheritableInLegacyEnvironments,
25
25
  appendEnvName,
26
26
  getBindingNames,
27
+ isValidName,
27
28
  } from "./validation-helpers";
28
29
  import type { Config, DevConfig, RawConfig, RawDevConfig } from "./config";
29
30
  import type {
@@ -61,26 +62,28 @@ export function normalizeAndValidateConfig(
61
62
  deprecated(
62
63
  diagnostics,
63
64
  rawConfig,
64
- "type",
65
- "DO NOT USE THIS. Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
66
- true
65
+ "miniflare",
66
+ "Wrangler does not use configuration in the `miniflare` section. Unless you are using Miniflare directly you can remove this section.",
67
+ true,
68
+ "😶 Ignored"
67
69
  );
68
70
 
69
71
  deprecated(
70
72
  diagnostics,
71
73
  rawConfig,
72
- "webpack_config",
73
- "DO NOT USE THIS. Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
74
- true
74
+ "type",
75
+ "Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
76
+ true,
77
+ "😶 Ignored"
75
78
  );
76
79
 
77
80
  deprecated(
78
81
  diagnostics,
79
82
  rawConfig,
80
- `site.entry-point`,
81
- `The \`site.entry-point\` config field is no longer used.\nThe entry-point should be specified via the command line or the \`main\` config field.`,
82
- false,
83
- true
83
+ "webpack_config",
84
+ "Most common features now work out of the box with wrangler, including modules, jsx, typescript, etc. If you need anything more, use a custom build.",
85
+ true,
86
+ "😶 Ignored"
84
87
  );
85
88
 
86
89
  validateOptionalProperty(
@@ -91,12 +94,35 @@ export function normalizeAndValidateConfig(
91
94
  "boolean"
92
95
  );
93
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
+
94
113
  // TODO: set the default to false to turn on service environments as the default
95
114
  const isLegacyEnv =
96
115
  (args as { "legacy-env": boolean | undefined })["legacy-env"] ??
97
116
  rawConfig.legacy_env ??
98
117
  true;
99
118
 
119
+ // TODO: remove this once service environments goes GA.
120
+ if (!isLegacyEnv) {
121
+ diagnostics.warnings.push(
122
+ "Experimental: Service environments are in beta, and their behaviour is guaranteed to change in the future. DO NOT USE IN PRODUCTION."
123
+ );
124
+ }
125
+
100
126
  const topLevelEnv = normalizeAndValidateEnvironment(
101
127
  diagnostics,
102
128
  configPath,
@@ -169,7 +195,12 @@ export function normalizeAndValidateConfig(
169
195
  diagnostics,
170
196
  rawConfig.migrations ?? []
171
197
  ),
172
- site: normalizeAndValidateSite(diagnostics, rawConfig.site),
198
+ site: normalizeAndValidateSite(
199
+ diagnostics,
200
+ configPath,
201
+ rawConfig,
202
+ activeEnv.main
203
+ ),
173
204
  wasm_modules: normalizeAndValidateModulePaths(
174
205
  diagnostics,
175
206
  configPath,
@@ -182,6 +213,12 @@ export function normalizeAndValidateConfig(
182
213
  "text_blobs",
183
214
  rawConfig.text_blobs
184
215
  ),
216
+ data_blobs: normalizeAndValidateModulePaths(
217
+ diagnostics,
218
+ configPath,
219
+ "data_blobs",
220
+ rawConfig.data_blobs
221
+ ),
185
222
  };
186
223
 
187
224
  validateBindingsHaveUniqueNames(diagnostics, config);
@@ -190,7 +227,7 @@ export function normalizeAndValidateConfig(
190
227
  diagnostics,
191
228
  "top-level",
192
229
  Object.keys(rawConfig),
193
- [...Object.keys(config), "env", "miniflare"]
230
+ [...Object.keys(config), "env"]
194
231
  );
195
232
 
196
233
  return { config, diagnostics };
@@ -413,26 +450,91 @@ function normalizeAndValidateMigrations(
413
450
  */
414
451
  function normalizeAndValidateSite(
415
452
  diagnostics: Diagnostics,
416
- rawSite: Config["site"]
453
+ configPath: string | undefined,
454
+ rawConfig: RawConfig,
455
+ mainEntryPoint: string | undefined
417
456
  ): Config["site"] {
418
- if (rawSite !== undefined) {
419
- const { bucket, include = [], exclude = [], ...rest } = rawSite;
420
- validateAdditionalProperties(diagnostics, "site", Object.keys(rest), []);
457
+ if (rawConfig?.site !== undefined) {
458
+ const { bucket, include = [], exclude = [], ...rest } = rawConfig.site;
459
+
460
+ validateAdditionalProperties(diagnostics, "site", Object.keys(rest), [
461
+ "entry-point",
462
+ ]);
421
463
  validateRequiredProperty(diagnostics, "site", "bucket", bucket, "string");
422
464
  validateTypedArray(diagnostics, "sites.include", include, "string");
423
465
  validateTypedArray(diagnostics, "sites.exclude", exclude, "string");
424
- return { bucket, include, exclude };
466
+ validateOptionalProperty(
467
+ diagnostics,
468
+ "site",
469
+ "entry-point",
470
+ rawConfig.site["entry-point"],
471
+ "string"
472
+ );
473
+
474
+ deprecated(
475
+ diagnostics,
476
+ rawConfig,
477
+ `site.entry-point`,
478
+ `Delete the \`site.entry-point\` field, then add the top level \`main\` field to your configuration file:\n` +
479
+ `\`\`\`\n` +
480
+ `main = "${path.join(
481
+ String(rawConfig.site["entry-point"]) || "workers-site",
482
+ path.extname(String(rawConfig.site["entry-point"]) || "workers-site")
483
+ ? ""
484
+ : "index.js"
485
+ )}"\n` +
486
+ `\`\`\``,
487
+ false,
488
+ undefined,
489
+ "warning"
490
+ );
491
+
492
+ let siteEntryPoint = rawConfig.site["entry-point"];
493
+
494
+ if (!mainEntryPoint && !siteEntryPoint) {
495
+ // this means that we're defaulting to "workers-site"
496
+ // so let's add the deprecation warning
497
+ diagnostics.warnings.push(
498
+ `Because you've defined a [site] configuration, we're defaulting to "workers-site" for the deprecated \`site.entry-point\`field.\n` +
499
+ `Add the top level \`main\` field to your configuration file:\n` +
500
+ `\`\`\`\n` +
501
+ `main = "workers-site/index.js"\n` +
502
+ `\`\`\``
503
+ );
504
+ siteEntryPoint = "workers-site";
505
+ } else if (mainEntryPoint && siteEntryPoint) {
506
+ diagnostics.errors.push(
507
+ `Don't define both the \`main\` and \`site.entry-point\` fields in your configuration.\n` +
508
+ `They serve the same purpose: to point to the entry-point of your worker.\n` +
509
+ `Delete the deprecated \`site.entry-point\` field from your config.`
510
+ );
511
+ }
512
+
513
+ if (configPath && siteEntryPoint) {
514
+ // rewrite the path to be relative to the working directory
515
+ siteEntryPoint = path.relative(
516
+ process.cwd(),
517
+ path.join(path.dirname(configPath), siteEntryPoint)
518
+ );
519
+ }
520
+
521
+ return {
522
+ bucket,
523
+ "entry-point": siteEntryPoint,
524
+ include,
525
+ exclude,
526
+ };
425
527
  }
426
528
  return undefined;
427
529
  }
428
530
 
429
531
  /**
430
- * Map the paths of the `wasm_modules` or `text_blobs` configuration to be relative to the current working directory.
532
+ * Map the paths of the `wasm_modules`, `text_blobs` or `data_blobs` configuration to be relative to the current working directory.
431
533
  */
432
534
  function normalizeAndValidateModulePaths(
433
535
  diagnostics: Diagnostics,
434
536
  configPath: string | undefined,
435
- field: "wasm_modules" | "text_blobs",
537
+ field: "wasm_modules" | "text_blobs" | "data_blobs",
436
538
  rawMapping: Record<string, string> | undefined
437
539
  ): Record<string, string> | undefined {
438
540
  if (rawMapping === undefined) {
@@ -455,6 +557,102 @@ function normalizeAndValidateModulePaths(
455
557
  return mapping;
456
558
  }
457
559
 
560
+ /**
561
+ * Check whether a value has the shape of a route, which can be a string
562
+ * or an object that looks like {pattern: string, zone_id: string }
563
+ */
564
+ 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
+ );
578
+ }
579
+
580
+ /**
581
+ * Validate that the field is a route.
582
+ */
583
+ const isRoute: ValidatorFn = (diagnostics, field, value) => {
584
+ if (value !== undefined && !isValidRouteValue(value)) {
585
+ 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(
587
+ value
588
+ )}.`
589
+ );
590
+ return false;
591
+ }
592
+ return true;
593
+ };
594
+
595
+ /**
596
+ * Validate that the field is an array of routes.
597
+ */
598
+ const isRouteArray: ValidatorFn = (diagnostics, field, value) => {
599
+ if (value === undefined) {
600
+ return true;
601
+ }
602
+ if (!Array.isArray(value)) {
603
+ diagnostics.errors.push(
604
+ `Expected "${field}" to be an array but got ${JSON.stringify(value)}.`
605
+ );
606
+ return false;
607
+ }
608
+ const invalidRoutes = [];
609
+ for (const item of value) {
610
+ if (!isValidRouteValue(item)) {
611
+ invalidRoutes.push(item);
612
+ }
613
+ }
614
+ if (invalidRoutes.length > 0) {
615
+ 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(
617
+ invalidRoutes,
618
+ null,
619
+ 2
620
+ )}.`
621
+ );
622
+ }
623
+ return invalidRoutes.length === 0;
624
+ };
625
+
626
+ function validateRoute(
627
+ diagnostics: Diagnostics,
628
+ topLevelEnv: Environment | undefined,
629
+ rawEnv: RawEnvironment
630
+ ): Config["route"] {
631
+ return inheritable(
632
+ diagnostics,
633
+ topLevelEnv,
634
+ rawEnv,
635
+ "route",
636
+ isRoute,
637
+ undefined
638
+ );
639
+ }
640
+
641
+ function validateRoutes(
642
+ diagnostics: Diagnostics,
643
+ topLevelEnv: Environment | undefined,
644
+ rawEnv: RawEnvironment
645
+ ): Config["routes"] {
646
+ return inheritable(
647
+ diagnostics,
648
+ topLevelEnv,
649
+ rawEnv,
650
+ "routes",
651
+ all(isRouteArray, isMutuallyExclusiveWith(rawEnv, "route")),
652
+ undefined
653
+ );
654
+ }
655
+
458
656
  /**
459
657
  * Validate top-level environment configuration and return the normalized values.
460
658
  */
@@ -520,22 +718,10 @@ function normalizeAndValidateEnvironment(
520
718
 
521
719
  experimental(diagnostics, rawEnv, "unsafe");
522
720
 
523
- const route = inheritable(
524
- diagnostics,
525
- topLevelEnv,
526
- rawEnv,
527
- "route",
528
- isString,
529
- undefined
530
- );
531
- const routes = inheritable(
532
- diagnostics,
533
- topLevelEnv,
534
- rawEnv,
535
- "routes",
536
- all(isStringArray, isMutuallyExclusiveWith(rawEnv, "route")),
537
- undefined
538
- );
721
+ const route = validateRoute(diagnostics, topLevelEnv, rawEnv);
722
+
723
+ const routes = validateRoutes(diagnostics, topLevelEnv, rawEnv);
724
+
539
725
  const workers_dev = inheritable(
540
726
  diagnostics,
541
727
  topLevelEnv,
@@ -554,12 +740,14 @@ function normalizeAndValidateEnvironment(
554
740
 
555
741
  const environment: Environment = {
556
742
  // Inherited fields
557
- account_id: inheritable(
743
+ account_id: inheritableInLegacyEnvironments(
558
744
  diagnostics,
745
+ isLegacyEnv,
559
746
  topLevelEnv,
560
747
  rawEnv,
561
748
  "account_id",
562
749
  isString,
750
+ undefined,
563
751
  undefined
564
752
  ),
565
753
  compatibility_date: inheritable(
@@ -613,7 +801,7 @@ function normalizeAndValidateEnvironment(
613
801
  topLevelEnv,
614
802
  rawEnv,
615
803
  "name",
616
- isString,
804
+ isValidName,
617
805
  appendEnvName(envName),
618
806
  undefined
619
807
  ),
@@ -705,6 +893,8 @@ function normalizeAndValidateEnvironment(
705
893
  }
706
894
  ),
707
895
  zone_id: rawEnv.zone_id,
896
+ minify: rawEnv.minify,
897
+ node_compat: rawEnv.node_compat,
708
898
  };
709
899
 
710
900
  return environment;
@@ -748,7 +938,7 @@ const validateAndNormalizeRules = (
748
938
  );
749
939
  } else if (deprecatedRules) {
750
940
  diagnostics.warnings.push(
751
- `DEPRECATION: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:\n` +
941
+ `Deprecation: The \`build.upload.rules\` config field is no longer used, the rules should be specified via the \`rules\` config field. Delete the \`build.upload\` field from the configuration file, and add this:\n` +
752
942
  "```\n" +
753
943
  TOML.stringify({ rules: deprecatedRules }) +
754
944
  "```"
@@ -921,7 +1111,7 @@ const validateBindingsProperty =
921
1111
  diagnostics.warnings.push(
922
1112
  `The following bindings are at the top level, but not on "env.${envName}".\n` +
923
1113
  `This is not what you probably want, since "${field}" configuration is not inherited by environments.\n` +
924
- `Please add a binding for each to "${fieldPath}.bindings".` +
1114
+ `Please add a binding for each to "${fieldPath}.bindings":\n` +
925
1115
  missingBindings.map((name) => `- ${name}`).join("\n")
926
1116
  );
927
1117
  }
@@ -1100,7 +1290,7 @@ const validateKVBinding: ValidatorFn = (diagnostics, field, value) => {
1100
1290
  const validateR2Binding: ValidatorFn = (diagnostics, field, value) => {
1101
1291
  if (typeof value !== "object" || value === null) {
1102
1292
  diagnostics.errors.push(
1103
- `"kv_namespaces" bindings should be objects, but got ${JSON.stringify(
1293
+ `"r2_buckets" bindings should be objects, but got ${JSON.stringify(
1104
1294
  value
1105
1295
  )}`
1106
1296
  );
@@ -1140,7 +1330,7 @@ const validateR2Binding: ValidatorFn = (diagnostics, field, value) => {
1140
1330
  *
1141
1331
  * We don't want to have, for example, a KV namespace named "DATA"
1142
1332
  * and a Durable Object also named "DATA". Then it would be ambiguous
1143
- * what exactly would live at `env.DATA` (or in the case of service workers,
1333
+ * what exactly would live at `env.DATA` (or in the case of service-workers,
1144
1334
  * the `DATA` global).
1145
1335
  */
1146
1336
  const validateBindingsHaveUniqueNames = (
@@ -1153,6 +1343,7 @@ const validateBindingsHaveUniqueNames = (
1153
1343
  unsafe,
1154
1344
  vars,
1155
1345
  wasm_modules,
1346
+ data_blobs,
1156
1347
  }: Partial<Config>
1157
1348
  ): boolean => {
1158
1349
  let hasDuplicates = false;
@@ -1165,6 +1356,7 @@ const validateBindingsHaveUniqueNames = (
1165
1356
  Unsafe: getBindingNames(unsafe),
1166
1357
  "Environment Variable": getBindingNames(vars),
1167
1358
  "WASM Module": getBindingNames(wasm_modules),
1359
+ "Data Blob": getBindingNames(data_blobs),
1168
1360
  } as Record<string, string[]>;
1169
1361
 
1170
1362
  const bindingsGroupedByName: Record<string, string[]> = {};
@@ -0,0 +1,45 @@
1
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
2
+ import { dirname, join } from "path";
3
+
4
+ let cacheMessageShown = false;
5
+
6
+ const cacheFolder = "node_modules/.cache/wrangler";
7
+
8
+ const showCacheMessage = () => {
9
+ if (!cacheMessageShown) {
10
+ console.log(
11
+ `Using cached values in '${cacheFolder}'. This is used as a temporary store to improve the developer experience for some commands. It may be purged at any time. It doesn't contain any sensitive information, but it should not be commited into source control.`
12
+ );
13
+ cacheMessageShown = true;
14
+ }
15
+ };
16
+
17
+ export const getConfigCache = <T>(fileName: string): Partial<T> => {
18
+ try {
19
+ const configCacheLocation = join(cacheFolder, fileName);
20
+ const configCache = JSON.parse(readFileSync(configCacheLocation, "utf-8"));
21
+ showCacheMessage();
22
+ return configCache;
23
+ } catch {
24
+ return {};
25
+ }
26
+ };
27
+
28
+ export const saveToConfigCache = <T>(
29
+ fileName: string,
30
+ newValues: Partial<T>
31
+ ) => {
32
+ const configCacheLocation = join(cacheFolder, fileName);
33
+ const existingValues = getConfigCache(fileName);
34
+
35
+ mkdirSync(dirname(configCacheLocation), { recursive: true });
36
+ writeFileSync(
37
+ configCacheLocation,
38
+ JSON.stringify({ ...existingValues, ...newValues }, null, 2)
39
+ );
40
+ showCacheMessage();
41
+ };
42
+
43
+ export const purgeConfigCaches = () => {
44
+ rmSync(cacheFolder, { recursive: true, force: true });
45
+ };
@@ -2,6 +2,7 @@ import { URL } from "node:url";
2
2
  import { fetch } from "undici";
3
3
  import { fetchResult } from "./cfetch";
4
4
  import { createWorkerUploadForm } from "./create-worker-upload-form";
5
+ import { logger } from "./logger";
5
6
  import type { CfAccount, CfWorkerContext, CfWorkerInit } from "./worker";
6
7
 
7
8
  /**
@@ -57,7 +58,8 @@ export interface CfPreviewToken {
57
58
  */
58
59
  async function sessionToken(
59
60
  account: CfAccount,
60
- ctx: CfWorkerContext
61
+ ctx: CfWorkerContext,
62
+ abortSignal: AbortSignal
61
63
  ): Promise<CfPreviewToken> {
62
64
  const { accountId } = account;
63
65
  const initUrl = ctx.zone
@@ -65,9 +67,9 @@ async function sessionToken(
65
67
  : `/accounts/${accountId}/workers/subdomain/edge-preview`;
66
68
 
67
69
  const { exchange_url } = await fetchResult<{ exchange_url: string }>(initUrl);
68
- const { inspector_websocket, token } = (await (
69
- await fetch(exchange_url)
70
- ).json()) as { inspector_websocket: string; token: string };
70
+ const { inspector_websocket, prewarm, token } = (await (
71
+ await fetch(exchange_url, { signal: abortSignal })
72
+ ).json()) as { inspector_websocket: string; token: string; prewarm: string };
71
73
  const { host } = new URL(inspector_websocket);
72
74
  const query = `cf_workers_preview_token=${token}`;
73
75
 
@@ -75,9 +77,7 @@ async function sessionToken(
75
77
  value: token,
76
78
  host,
77
79
  inspectorUrl: new URL(`${inspector_websocket}?${query}`),
78
- prewarmUrl: new URL(
79
- `https://${host}/cdn-cgi/workers/preview/prewarm?${query}`
80
- ),
80
+ prewarmUrl: new URL(prewarm),
81
81
  };
82
82
  }
83
83
 
@@ -96,11 +96,13 @@ function randomId(): string {
96
96
  async function createPreviewToken(
97
97
  account: CfAccount,
98
98
  worker: CfWorkerInit,
99
- ctx: CfWorkerContext
99
+ ctx: CfWorkerContext,
100
+ abortSignal: AbortSignal
100
101
  ): Promise<CfPreviewToken> {
101
102
  const { value, host, inspectorUrl, prewarmUrl } = await sessionToken(
102
103
  account,
103
- ctx
104
+ ctx,
105
+ abortSignal
104
106
  );
105
107
 
106
108
  const { accountId } = account;
@@ -155,12 +157,19 @@ async function createPreviewToken(
155
157
  export async function createWorkerPreview(
156
158
  init: CfWorkerInit,
157
159
  account: CfAccount,
158
- ctx: CfWorkerContext
160
+ ctx: CfWorkerContext,
161
+ abortSignal: AbortSignal
159
162
  ): Promise<CfPreviewToken> {
160
- const token = await createPreviewToken(account, init, ctx);
161
- const response = await fetch(token.prewarmUrl.href, { method: "POST" });
163
+ const token = await createPreviewToken(account, init, ctx, abortSignal);
164
+ const response = await fetch(token.prewarmUrl.href, {
165
+ method: "POST",
166
+ signal: abortSignal,
167
+ headers: {
168
+ "cf-workers-preview-token": token.value,
169
+ },
170
+ });
162
171
  if (!response.ok) {
163
- // console.error("worker failed to prewarm: ", response.statusText);
172
+ logger.warn("worker failed to prewarm: ", response.statusText);
164
173
  }
165
174
  return token;
166
175
  }
@@ -26,7 +26,7 @@ export function toMimeType(type: CfModuleType): string {
26
26
  export interface WorkerMetadata {
27
27
  /** The name of the entry point module. Only exists when the worker is in the ES module format */
28
28
  main_module?: string;
29
- /** The name of the entry point module. Only exists when the worker is in the Service Worker format */
29
+ /** The name of the entry point module. Only exists when the worker is in the service-worker format */
30
30
  body_part?: string;
31
31
  compatibility_date?: string;
32
32
  compatibility_flags?: string[];
@@ -38,6 +38,7 @@ export interface WorkerMetadata {
38
38
  | { type: "json"; name: string; json: unknown }
39
39
  | { type: "wasm_module"; name: string; part: string }
40
40
  | { type: "text_blob"; name: string; part: string }
41
+ | { type: "data_blob"; name: string; part: string }
41
42
  | {
42
43
  type: "durable_object_namespace";
43
44
  name: string;
@@ -133,6 +134,21 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
133
134
  }
134
135
  }
135
136
 
137
+ for (const [name, filePath] of Object.entries(bindings.data_blobs || {})) {
138
+ metadataBindings.push({
139
+ name,
140
+ type: "data_blob",
141
+ part: name,
142
+ });
143
+
144
+ formData.set(
145
+ name,
146
+ new File([readFileSync(filePath)], filePath, {
147
+ type: "application/octet-stream",
148
+ })
149
+ );
150
+ }
151
+
136
152
  if (main.type === "commonjs") {
137
153
  // This is a service-worker format worker.
138
154
  for (const module of Object.values([...(modules || [])])) {
@@ -146,8 +162,12 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
146
162
  );
147
163
  // And then remove it from the modules collection
148
164
  modules = modules?.filter((m) => m !== module);
149
- } else if (module.type === "compiled-wasm" || module.type === "text") {
150
- // Convert all wasm/text modules into `wasm_module`/`text_blob` bindings.
165
+ } else if (
166
+ module.type === "compiled-wasm" ||
167
+ module.type === "text" ||
168
+ module.type === "buffer"
169
+ ) {
170
+ // Convert all wasm/text/data modules into `wasm_module`/`text_blob`/`data_blob` bindings.
151
171
  // The "name" of the module is a file path. We use it
152
172
  // to instead be a "part" of the body, and a reference
153
173
  // that we can use inside our source. This identifier has to be a valid
@@ -156,7 +176,12 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
156
176
  const name = module.name.replace(/[^a-zA-Z0-9_$]/g, "_");
157
177
  metadataBindings.push({
158
178
  name,
159
- type: module.type === "compiled-wasm" ? "wasm_module" : "text_blob",
179
+ type:
180
+ module.type === "compiled-wasm"
181
+ ? "wasm_module"
182
+ : module.type === "text"
183
+ ? "text_blob"
184
+ : "data_blob",
160
185
  part: name,
161
186
  });
162
187
 
@@ -167,15 +192,13 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
167
192
  type:
168
193
  module.type === "compiled-wasm"
169
194
  ? "application/wasm"
170
- : "text/plain",
195
+ : module.type === "text"
196
+ ? "text/plain"
197
+ : "application/octet-stream",
171
198
  })
172
199
  );
173
200
  // And then remove it from the modules collection
174
201
  modules = modules?.filter((m) => m !== module);
175
- } else if (module.type === "buffer") {
176
- throw new Error(
177
- 'The "buffer" module type is not yet supported in service worker format workers'
178
- );
179
202
  }
180
203
  }
181
204
  }
@@ -0,0 +1,36 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import dotenv from "dotenv";
4
+ import { logger } from "../logger";
5
+ import type { Config } from "../config";
6
+
7
+ /**
8
+ * Get the Worker `vars` bindings for a `wrangler dev` instance of a Worker.
9
+ *
10
+ * The `vars` bindings can be specified in the `wrangler.toml` configuration file.
11
+ * But "secret" `vars` are usually only provided at the server -
12
+ * either by creating them in the Dashboard UI, or using the `wrangler secret` command.
13
+ *
14
+ * It is useful during development, to provide these types of variable locally.
15
+ * When running `wrangler dev` we will look for a file called `.dev.vars`, situated
16
+ * next to the `wrangler.toml` file (or in the current working directory if there is no
17
+ * `wrangler.toml`).
18
+ *
19
+ * Any values in this file, formatted like a `dotenv` file, will add to or override `vars`
20
+ * bindings provided in the `wrangler.toml`.
21
+ */
22
+ export function getVarsForDev(config: Config): Config["vars"] {
23
+ const configDir = path.resolve(path.dirname(config.configPath ?? "."));
24
+ const devVarsPath = path.resolve(configDir, ".dev.vars");
25
+ if (fs.existsSync(devVarsPath)) {
26
+ const devVarsRelativePath = path.relative(process.cwd(), devVarsPath);
27
+ logger.log(`Using vars defined in ${devVarsRelativePath}`);
28
+ const devVars = dotenv.parse(fs.readFileSync(devVarsPath, "utf8"));
29
+ return {
30
+ ...config.vars,
31
+ ...devVars,
32
+ };
33
+ } else {
34
+ return config.vars;
35
+ }
36
+ }