wrangler 2.0.23 → 2.0.24

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 (55) hide show
  1. package/README.md +20 -2
  2. package/bin/wrangler.js +1 -1
  3. package/miniflare-dist/index.mjs +96 -34
  4. package/package.json +9 -4
  5. package/src/__tests__/configuration.test.ts +88 -16
  6. package/src/__tests__/dev.test.tsx +3 -0
  7. package/src/__tests__/generate.test.ts +93 -0
  8. package/src/__tests__/helpers/mock-cfetch.ts +54 -2
  9. package/src/__tests__/index.test.ts +10 -27
  10. package/src/__tests__/jest.setup.ts +31 -1
  11. package/src/__tests__/kv.test.ts +2 -2
  12. package/src/__tests__/metrics.test.ts +5 -0
  13. package/src/__tests__/publish.test.ts +497 -254
  14. package/src/__tests__/r2.test.ts +155 -71
  15. package/src/__tests__/user.test.ts +1 -0
  16. package/src/__tests__/validate-dev-props.test.ts +56 -0
  17. package/src/__tests__/whoami.test.tsx +60 -1
  18. package/src/bundle.ts +278 -44
  19. package/src/cfetch/internal.ts +34 -2
  20. package/src/config/config.ts +7 -2
  21. package/src/config/environment.ts +40 -8
  22. package/src/config/index.ts +13 -0
  23. package/src/config/validation.ts +101 -7
  24. package/src/create-worker-upload-form.ts +25 -0
  25. package/src/dev/dev.tsx +107 -28
  26. package/src/dev/local.tsx +20 -10
  27. package/src/dev/remote.tsx +39 -8
  28. package/src/dev/use-esbuild.ts +25 -0
  29. package/src/dev/validate-dev-props.ts +31 -0
  30. package/src/dev-registry.tsx +157 -0
  31. package/src/dev.tsx +93 -75
  32. package/src/generate.ts +112 -14
  33. package/src/index.tsx +182 -4
  34. package/src/inspect.ts +93 -5
  35. package/src/metrics/index.ts +1 -0
  36. package/src/metrics/metrics-dispatcher.ts +1 -0
  37. package/src/metrics/metrics-usage-headers.ts +24 -0
  38. package/src/metrics/send-event.ts +2 -2
  39. package/src/module-collection.ts +3 -3
  40. package/src/pages/constants.ts +1 -0
  41. package/src/pages/deployments.tsx +1 -1
  42. package/src/pages/dev.tsx +6 -1
  43. package/src/pages/publish.tsx +1 -1
  44. package/src/pages/upload.tsx +32 -13
  45. package/src/publish.ts +126 -112
  46. package/src/r2.ts +68 -0
  47. package/src/user/user.tsx +20 -2
  48. package/src/whoami.tsx +79 -1
  49. package/src/worker.ts +12 -0
  50. package/templates/first-party-worker-module-facade.ts +18 -0
  51. package/templates/format-dev-errors.ts +32 -0
  52. package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
  53. package/templates/service-bindings-module-facade.js +51 -0
  54. package/templates/service-bindings-sw-facade.js +39 -0
  55. package/wrangler-dist/cli.js +40192 -15265
@@ -598,25 +598,68 @@ function normalizeAndValidateAssets(
598
598
  diagnostics: Diagnostics,
599
599
  configPath: string | undefined,
600
600
  rawConfig: RawConfig
601
- ) {
602
- if (
603
- typeof rawConfig?.assets === "string" ||
604
- rawConfig?.assets === undefined
605
- ) {
606
- return rawConfig?.assets;
601
+ ): Config["assets"] {
602
+ // Even though the type doesn't say it,
603
+ // we allow for a string input in the config,
604
+ // so let's normalise it
605
+ if (typeof rawConfig?.assets === "string") {
606
+ return {
607
+ bucket: rawConfig.assets,
608
+ include: [],
609
+ exclude: [],
610
+ browser_TTL: undefined,
611
+ serve_single_page_app: false,
612
+ };
607
613
  }
608
614
 
609
- const { bucket, include = [], exclude = [], ...rest } = rawConfig.assets;
615
+ if (rawConfig?.assets === undefined) {
616
+ return undefined;
617
+ }
618
+
619
+ if (typeof rawConfig.assets !== "object") {
620
+ diagnostics.errors.push(
621
+ `Expected the \`assets\` field to be a string or an object, but got ${typeof rawConfig.assets}.`
622
+ );
623
+ return undefined;
624
+ }
625
+
626
+ const {
627
+ bucket,
628
+ include = [],
629
+ exclude = [],
630
+ browser_TTL,
631
+ serve_single_page_app,
632
+ ...rest
633
+ } = rawConfig.assets;
610
634
 
611
635
  validateAdditionalProperties(diagnostics, "assets", Object.keys(rest), []);
636
+
612
637
  validateRequiredProperty(diagnostics, "assets", "bucket", bucket, "string");
613
638
  validateTypedArray(diagnostics, "assets.include", include, "string");
614
639
  validateTypedArray(diagnostics, "assets.exclude", exclude, "string");
615
640
 
641
+ validateOptionalProperty(
642
+ diagnostics,
643
+ "assets",
644
+ "browser_TTL",
645
+ browser_TTL,
646
+ "number"
647
+ );
648
+
649
+ validateOptionalProperty(
650
+ diagnostics,
651
+ "assets",
652
+ "serve_single_page_app",
653
+ serve_single_page_app,
654
+ "boolean"
655
+ );
656
+
616
657
  return {
617
658
  bucket,
618
659
  include,
619
660
  exclude,
661
+ browser_TTL,
662
+ serve_single_page_app,
620
663
  };
621
664
  }
622
665
 
@@ -830,6 +873,13 @@ function normalizeAndValidateEnvironment(
830
873
  isLegacyEnv?: boolean,
831
874
  rawConfig?: RawConfig | undefined
832
875
  ): Environment {
876
+ deprecated(
877
+ diagnostics,
878
+ rawEnv,
879
+ "kv-namespaces",
880
+ `The "kv-namespaces" field is no longer supported, please rename to "kv_namespaces"`,
881
+ true
882
+ );
833
883
  deprecated(
834
884
  diagnostics,
835
885
  rawEnv,
@@ -1045,6 +1095,17 @@ function normalizeAndValidateEnvironment(
1045
1095
  validateBindingArray(envName, validateWorkerNamespaceBinding),
1046
1096
  []
1047
1097
  ),
1098
+ logfwdr: inheritable(
1099
+ diagnostics,
1100
+ topLevelEnv,
1101
+ rawEnv,
1102
+ "logfwdr",
1103
+ validateBindingsProperty(envName, validateCflogfwdrBinding),
1104
+ {
1105
+ schema: undefined,
1106
+ bindings: [],
1107
+ }
1108
+ ),
1048
1109
  unsafe: notInheritable(
1049
1110
  diagnostics,
1050
1111
  topLevelEnv,
@@ -1082,6 +1143,14 @@ function normalizeAndValidateEnvironment(
1082
1143
  isBoolean,
1083
1144
  undefined
1084
1145
  ),
1146
+ first_party_worker: inheritable(
1147
+ diagnostics,
1148
+ topLevelEnv,
1149
+ rawEnv,
1150
+ "first_party_worker",
1151
+ isBoolean,
1152
+ undefined
1153
+ ),
1085
1154
  };
1086
1155
 
1087
1156
  return environment;
@@ -1418,6 +1487,30 @@ const validateDurableObjectBinding: ValidatorFn = (
1418
1487
  return isValid;
1419
1488
  };
1420
1489
 
1490
+ const validateCflogfwdrBinding: ValidatorFn = (diagnostics, field, value) => {
1491
+ if (typeof value !== "object" || value === null) {
1492
+ diagnostics.errors.push(
1493
+ `Expected "${field}" to be an object but got ${JSON.stringify(value)}`
1494
+ );
1495
+ return false;
1496
+ }
1497
+
1498
+ let isValid = true;
1499
+ if (!isRequiredProperty(value, "name", "string")) {
1500
+ diagnostics.errors.push(`binding should have a string "name" field.`);
1501
+ isValid = false;
1502
+ }
1503
+
1504
+ if (!isRequiredProperty(value, "destination", "string")) {
1505
+ diagnostics.errors.push(
1506
+ `binding should have a string "destination" field.`
1507
+ );
1508
+ isValid = false;
1509
+ }
1510
+
1511
+ return isValid;
1512
+ };
1513
+
1421
1514
  /**
1422
1515
  * Check that the given field is a valid "unsafe" binding object.
1423
1516
  *
@@ -1448,6 +1541,7 @@ const validateUnsafeBinding: ValidatorFn = (diagnostics, field, value) => {
1448
1541
  "durable_object_namespace",
1449
1542
  "r2_bucket",
1450
1543
  "service",
1544
+ "logfwdr",
1451
1545
  ];
1452
1546
 
1453
1547
  if (safeBindings.includes(value.type)) {
@@ -32,6 +32,7 @@ export interface WorkerMetadata {
32
32
  compatibility_flags?: string[];
33
33
  usage_model?: "bundled" | "unbound";
34
34
  migrations?: CfDurableObjectMigrations;
35
+ capnp_schema?: string;
35
36
  // If you add any new binding types here, also add it to safeBindings
36
37
  // under validateUnsafeBinding in config/validation.ts
37
38
  bindings: (
@@ -51,6 +52,11 @@ export interface WorkerMetadata {
51
52
  | { type: "r2_bucket"; name: string; bucket_name: string }
52
53
  | { type: "service"; name: string; service: string; environment?: string }
53
54
  | { type: "namespace"; name: string; namespace: string }
55
+ | {
56
+ type: "logfwdr";
57
+ name: string;
58
+ destination: string;
59
+ }
54
60
  )[];
55
61
  }
56
62
 
@@ -125,6 +131,14 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
125
131
  });
126
132
  });
127
133
 
134
+ bindings.logfwdr?.bindings.forEach(({ name, destination }) => {
135
+ metadataBindings.push({
136
+ name: name,
137
+ type: "logfwdr",
138
+ destination,
139
+ });
140
+ });
141
+
128
142
  for (const [name, filePath] of Object.entries(bindings.wasm_modules || {})) {
129
143
  metadataBindings.push({
130
144
  name,
@@ -240,6 +254,7 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
240
254
  ...(compatibility_flags && { compatibility_flags }),
241
255
  ...(usage_model && { usage_model }),
242
256
  ...(migrations && { migrations }),
257
+ capnp_schema: bindings.logfwdr?.schema,
243
258
  };
244
259
 
245
260
  formData.set("metadata", JSON.stringify(metadata));
@@ -259,5 +274,15 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
259
274
  );
260
275
  }
261
276
 
277
+ if (bindings.logfwdr && bindings.logfwdr.schema) {
278
+ const filePath = bindings.logfwdr.schema;
279
+ formData.set(
280
+ filePath,
281
+ new File([readFileSync(filePath)], filePath, {
282
+ type: "application/octet-stream",
283
+ })
284
+ );
285
+ }
286
+
262
287
  return formData;
263
288
  }
package/src/dev/dev.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import { spawn } from "node:child_process";
2
2
  import * as path from "node:path";
3
+ import * as util from "node:util";
3
4
  import { watch } from "chokidar";
4
5
  import clipboardy from "clipboardy";
5
6
  import commandExists from "command-exists";
@@ -9,6 +10,12 @@ import { withErrorBoundary, useErrorHandler } from "react-error-boundary";
9
10
  import onExit from "signal-exit";
10
11
  import tmp from "tmp-promise";
11
12
  import { fetch } from "undici";
13
+ import {
14
+ getRegisteredWorkers,
15
+ startWorkerRegistry,
16
+ stopWorkerRegistry,
17
+ unregisterWorker,
18
+ } from "../dev-registry";
12
19
  import { runCustomBuild } from "../entry";
13
20
  import { openInspector } from "../inspect";
14
21
  import { logger } from "../logger";
@@ -16,13 +23,97 @@ import openInBrowser from "../open-in-browser";
16
23
  import { Local } from "./local";
17
24
  import { Remote } from "./remote";
18
25
  import { useEsbuild } from "./use-esbuild";
26
+ import { validateDevProps } from "./validate-dev-props";
19
27
  import type { Config } from "../config";
20
28
  import type { Route } from "../config/environment";
29
+ import type { WorkerRegistry } from "../dev-registry";
21
30
  import type { Entry } from "../entry";
22
31
  import type { EnablePagesAssetsServiceBindingOptions } from "../miniflare-cli";
23
32
  import type { AssetPaths } from "../sites";
24
33
  import type { CfWorkerInit } from "../worker";
25
34
 
35
+ /**
36
+ * This hooks establishes a connection with the dev registry,
37
+ * and periodically updates itself with details of workers currently
38
+ * running a dev session on this system.
39
+ */
40
+ function useDevRegistry(
41
+ name: string | undefined,
42
+ services: Config["services"] | undefined,
43
+ mode: "local" | "remote"
44
+ ): WorkerRegistry {
45
+ const [workers, setWorkers] = useState<WorkerRegistry>({});
46
+
47
+ useEffect(() => {
48
+ // Let's try to start registry
49
+ // TODO: we should probably call this in a loop
50
+ // in case the registry dies elsewhere
51
+ startWorkerRegistry().catch((err) => {
52
+ logger.error("failed to start worker registry", err);
53
+ });
54
+
55
+ const serviceNames = (services || []).map(
56
+ (serviceBinding) => serviceBinding.service
57
+ );
58
+
59
+ const interval =
60
+ // TODO: enable this for remote mode as well
61
+ // https://github.com/cloudflare/wrangler2/issues/1182
62
+ mode === "local"
63
+ ? setInterval(() => {
64
+ getRegisteredWorkers().then(
65
+ (workerDefinitions: WorkerRegistry | undefined) => {
66
+ // We only want the workers that we're bound to
67
+ // so let's filter out the others
68
+ const filteredWorkers = Object.fromEntries(
69
+ Object.entries(workerDefinitions || {}).filter(
70
+ ([key, _value]) => serviceNames.includes(key)
71
+ )
72
+ );
73
+ setWorkers((prevWorkers) => {
74
+ if (!util.isDeepStrictEqual(filteredWorkers, prevWorkers)) {
75
+ return filteredWorkers;
76
+ }
77
+ return prevWorkers;
78
+ });
79
+ },
80
+ (err) => {
81
+ logger.warn("Failed to get worker definitions", err);
82
+ }
83
+ );
84
+ }, 300)
85
+ : undefined;
86
+
87
+ return () => {
88
+ interval && clearInterval(interval);
89
+ Promise.allSettled([
90
+ name ? unregisterWorker(name) : Promise.resolve(),
91
+ stopWorkerRegistry(),
92
+ ]).then(
93
+ ([unregisterResult, stopRegistryResult]) => {
94
+ if (unregisterResult.status === "rejected") {
95
+ logger.error(
96
+ "Failed to unregister worker",
97
+ unregisterResult.reason
98
+ );
99
+ }
100
+ if (stopRegistryResult.status === "rejected") {
101
+ logger.error(
102
+ "Failed to stop worker registry",
103
+ stopRegistryResult.reason
104
+ );
105
+ }
106
+ },
107
+ (err) => {
108
+ logger.error("Failed to clear dev registry effect", err);
109
+ }
110
+ );
111
+ };
112
+ }, [name, services, mode]);
113
+
114
+ return workers;
115
+ }
116
+
26
117
  export type DevProps = {
27
118
  name: string | undefined;
28
119
  noBundle: boolean;
@@ -46,6 +137,7 @@ export type DevProps = {
46
137
  crons: Config["triggers"]["crons"];
47
138
  isWorkersSite: boolean;
48
139
  assetPaths: AssetPaths | undefined;
140
+ assetsConfig: Config["assets"];
49
141
  compatibilityDate: string;
50
142
  compatibilityFlags: string[] | undefined;
51
143
  usageModel: "bundled" | "unbound" | undefined;
@@ -64,36 +156,12 @@ export type DevProps = {
64
156
  showInteractiveDevSession: boolean | undefined;
65
157
  forceLocal: boolean | undefined;
66
158
  enablePagesAssetsServiceBinding?: EnablePagesAssetsServiceBindingOptions;
159
+ firstPartyWorker: boolean | undefined;
160
+ sendMetrics: boolean | undefined;
67
161
  };
68
162
 
69
163
  export function DevImplementation(props: DevProps): JSX.Element {
70
- if (
71
- !props.isWorkersSite &&
72
- props.assetPaths &&
73
- props.entry.format === "service-worker"
74
- ) {
75
- throw new Error(
76
- "You cannot use the service-worker format with an `assets` directory yet. For information on how to migrate to the module-worker format, see: https://developers.cloudflare.com/workers/learning/migrating-to-module-workers/"
77
- );
78
- }
79
-
80
- if (props.bindings.wasm_modules && props.entry.format === "modules") {
81
- throw new Error(
82
- "You cannot configure [wasm_modules] with an ES module worker. Instead, import the .wasm module directly in your code"
83
- );
84
- }
85
-
86
- if (props.bindings.text_blobs && props.entry.format === "modules") {
87
- throw new Error(
88
- "You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
89
- );
90
- }
91
-
92
- if (props.bindings.data_blobs && props.entry.format === "modules") {
93
- throw new Error(
94
- "You cannot configure [data_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[rules]` in your wrangler.toml"
95
- );
96
- }
164
+ validateDevProps(props);
97
165
 
98
166
  // only load the UI if we're running in a supported environment
99
167
  const { isRawModeSupported } = useStdin();
@@ -157,6 +225,12 @@ function DevSession(props: DevSessionProps) {
157
225
 
158
226
  const directory = useTmpDir();
159
227
 
228
+ const workerDefinitions = useDevRegistry(
229
+ props.name,
230
+ props.bindings.services,
231
+ props.local ? "local" : "remote"
232
+ );
233
+
160
234
  const bundle = useEsbuild({
161
235
  entry: props.entry,
162
236
  destination: directory,
@@ -171,6 +245,10 @@ function DevSession(props: DevSessionProps) {
171
245
  nodeCompat: props.nodeCompat,
172
246
  define: props.define,
173
247
  noBundle: props.noBundle,
248
+ assets: props.assetsConfig,
249
+ workerDefinitions,
250
+ services: props.bindings.services,
251
+ firstPartyWorkerDevFacade: props.firstPartyWorker,
174
252
  });
175
253
 
176
254
  return props.local ? (
@@ -182,7 +260,6 @@ function DevSession(props: DevSessionProps) {
182
260
  compatibilityFlags={props.compatibilityFlags}
183
261
  bindings={props.bindings}
184
262
  assetPaths={props.assetPaths}
185
- isWorkersSite={props.isWorkersSite}
186
263
  port={props.port}
187
264
  ip={props.ip}
188
265
  rules={props.rules}
@@ -223,6 +300,8 @@ function DevSession(props: DevSessionProps) {
223
300
  host={props.host}
224
301
  routes={props.routes}
225
302
  onReady={props.onReady}
303
+ sourceMapPath={bundle?.sourceMapPath}
304
+ sendMetrics={props.sendMetrics}
226
305
  />
227
306
  );
228
307
  }
package/src/dev/local.tsx CHANGED
@@ -4,6 +4,7 @@ import { writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { useState, useEffect, useRef } from "react";
6
6
  import onExit from "signal-exit";
7
+ import { registerWorker } from "../dev-registry";
7
8
  import useInspector from "../inspect";
8
9
  import { logger } from "../logger";
9
10
  import { DEFAULT_MODULE_RULES } from "../module-collection";
@@ -23,7 +24,6 @@ interface LocalProps {
23
24
  compatibilityFlags: string[] | undefined;
24
25
  bindings: CfWorkerInit["bindings"];
25
26
  assetPaths: AssetPaths | undefined;
26
- isWorkersSite: boolean;
27
27
  port: number;
28
28
  ip: string;
29
29
  rules: Config["rules"];
@@ -58,7 +58,6 @@ function useLocalWorker({
58
58
  compatibilityFlags,
59
59
  bindings,
60
60
  assetPaths,
61
- isWorkersSite,
62
61
  port,
63
62
  inspectorPort,
64
63
  rules,
@@ -88,6 +87,15 @@ function useLocalWorker({
88
87
  // so that it's persisted in the temp dir across a dev session
89
88
  // even when we change source and reload
90
89
  null;
90
+
91
+ useEffect(() => {
92
+ if (bindings.services && bindings.services.length > 0) {
93
+ logger.warn(
94
+ "⎔ Support for service bindings in local mode is experimental and may change."
95
+ );
96
+ }
97
+ }, [bindings.services]);
98
+
91
99
  useEffect(() => {
92
100
  const abortController = new AbortController();
93
101
  async function startLocalWorker() {
@@ -100,12 +108,6 @@ function useLocalWorker({
100
108
  abortSignal: abortController.signal,
101
109
  });
102
110
 
103
- if (bindings.services && bindings.services.length > 0) {
104
- throw new Error(
105
- "⎔ Service bindings are not yet supported in local mode."
106
- );
107
- }
108
-
109
111
  // In local mode, we want to copy all referenced modules into
110
112
  // the output bundle directory before starting up
111
113
  for (const module of bundle.modules) {
@@ -273,8 +275,17 @@ function useLocalWorker({
273
275
  stdio: "pipe",
274
276
  }));
275
277
 
276
- child.on("message", (message) => {
278
+ child.on("message", async (message) => {
277
279
  if (message === "ready") {
280
+ // Let's register our presence in the dev registry
281
+ if (workerName) {
282
+ await registerWorker(workerName, {
283
+ protocol: localProtocol,
284
+ mode: "local",
285
+ port,
286
+ host: ip,
287
+ });
288
+ }
278
289
  onReady?.();
279
290
  }
280
291
  });
@@ -357,7 +368,6 @@ function useLocalWorker({
357
368
  localPersistencePath,
358
369
  liveReload,
359
370
  assetPaths,
360
- isWorkersSite,
361
371
  rules,
362
372
  bindings.wasm_modules,
363
373
  bindings.text_blobs,
@@ -55,6 +55,8 @@ export function Remote(props: {
55
55
  host: string | undefined;
56
56
  routes: Route[] | undefined;
57
57
  onReady?: (() => void) | undefined;
58
+ sourceMapPath: string | undefined;
59
+ sendMetrics: boolean | undefined;
58
60
  }) {
59
61
  const [accountId, setAccountId] = useState(props.accountId);
60
62
  const accountChoicesRef = useRef<Promise<ChooseAccountItem[]>>();
@@ -78,6 +80,7 @@ export function Remote(props: {
78
80
  host: props.host,
79
81
  routes: props.routes,
80
82
  onReady: props.onReady,
83
+ sendMetrics: props.sendMetrics,
81
84
  });
82
85
 
83
86
  usePreviewServer({
@@ -97,6 +100,7 @@ export function Remote(props: {
97
100
  : undefined,
98
101
  port: props.inspectorPort,
99
102
  logToTerminal: true,
103
+ sourceMapPath: props.sourceMapPath,
100
104
  });
101
105
 
102
106
  const errorHandler = useErrorHandler();
@@ -161,6 +165,7 @@ export function useWorker(props: {
161
165
  host: string | undefined;
162
166
  routes: Route[] | undefined;
163
167
  onReady: (() => void) | undefined;
168
+ sendMetrics: boolean | undefined;
164
169
  }): CfPreviewToken | undefined {
165
170
  const {
166
171
  name,
@@ -202,6 +207,7 @@ export function useWorker(props: {
202
207
  zone: props.zone,
203
208
  host: props.host,
204
209
  routes: props.routes,
210
+ sendMetrics: props.sendMetrics,
205
211
  };
206
212
 
207
213
  setSession(
@@ -230,6 +236,7 @@ export function useWorker(props: {
230
236
  props.legacyEnv,
231
237
  props.routes,
232
238
  props.zone,
239
+ props.sendMetrics,
233
240
  ]);
234
241
 
235
242
  // This effect uses the session to upload the worker and create a preview
@@ -320,17 +327,40 @@ export function useWorker(props: {
320
327
  zone: props.zone,
321
328
  host: props.host,
322
329
  routes: props.routes,
330
+ sendMetrics: props.sendMetrics,
323
331
  };
324
332
 
325
- setToken(
326
- await createWorkerPreview(
327
- init,
328
- workerAccount,
329
- workerCtx,
330
- session,
331
- abortController.signal
332
- )
333
+ const workerPreviewToken = await createWorkerPreview(
334
+ init,
335
+ workerAccount,
336
+ workerCtx,
337
+ session,
338
+ abortController.signal
333
339
  );
340
+
341
+ setToken(workerPreviewToken);
342
+
343
+ // TODO: Once we get service bindings working in the
344
+ // edge preview server, we can define remote dev service bindings
345
+ // and you can uncomment this code.
346
+ // https://github.com/cloudflare/wrangler2/issues/1182
347
+
348
+ /*
349
+ if (name) {
350
+ await registerWorker(name, {
351
+ mode: "remote",
352
+ // upstream protocol is always https (https://github.com/cloudflare/wrangler2/issues/583)
353
+ protocol: "https",
354
+ port: undefined,
355
+ host: workerPreviewToken.host,
356
+ headers: {
357
+ "cf-workers-preview-token": workerPreviewToken.value,
358
+ host: workerPreviewToken.host,
359
+ },
360
+ });
361
+ }
362
+ */
363
+
334
364
  onReady?.();
335
365
  }
336
366
  start().catch((err) => {
@@ -377,6 +407,7 @@ export function useWorker(props: {
377
407
  props.routes,
378
408
  session,
379
409
  onReady,
410
+ props.sendMetrics,
380
411
  ]);
381
412
  return token;
382
413
  }
@@ -5,6 +5,7 @@ import { useState, useEffect } from "react";
5
5
  import { bundleWorker } from "../bundle";
6
6
  import { logger } from "../logger";
7
7
  import type { Config } from "../config";
8
+ import type { WorkerRegistry } from "../dev-registry";
8
9
  import type { Entry } from "../entry";
9
10
  import type { CfModule } from "../worker";
10
11
  import type { WatchMode } from "esbuild";
@@ -15,6 +16,7 @@ export type EsbuildBundle = {
15
16
  entry: Entry;
16
17
  type: "esm" | "commonjs";
17
18
  modules: CfModule[];
19
+ sourceMapPath: string | undefined;
18
20
  };
19
21
 
20
22
  export function useEsbuild({
@@ -23,24 +25,32 @@ export function useEsbuild({
23
25
  jsxFactory,
24
26
  jsxFragment,
25
27
  rules,
28
+ assets,
26
29
  serveAssetsFromWorker,
27
30
  tsconfig,
28
31
  minify,
29
32
  nodeCompat,
30
33
  define,
31
34
  noBundle,
35
+ workerDefinitions,
36
+ services,
37
+ firstPartyWorkerDevFacade,
32
38
  }: {
33
39
  entry: Entry;
34
40
  destination: string | undefined;
35
41
  jsxFactory: string | undefined;
36
42
  jsxFragment: string | undefined;
37
43
  rules: Config["rules"];
44
+ assets: Config["assets"];
38
45
  define: Config["define"];
46
+ services: Config["services"];
39
47
  serveAssetsFromWorker: boolean;
40
48
  tsconfig: string | undefined;
41
49
  minify: boolean | undefined;
42
50
  nodeCompat: boolean | undefined;
43
51
  noBundle: boolean;
52
+ workerDefinitions: WorkerRegistry;
53
+ firstPartyWorkerDevFacade: boolean | undefined;
44
54
  }): EsbuildBundle | undefined {
45
55
  const [bundle, setBundle] = useState<EsbuildBundle>();
46
56
  const { exit } = useApp();
@@ -76,12 +86,14 @@ export function useEsbuild({
76
86
  bundleType,
77
87
  modules,
78
88
  stop,
89
+ sourceMapPath,
79
90
  }: Awaited<ReturnType<typeof bundleWorker>> = noBundle
80
91
  ? {
81
92
  modules: [],
82
93
  resolvedEntryPointPath: entry.file,
83
94
  bundleType: entry.format === "modules" ? "esm" : "commonjs",
84
95
  stop: undefined,
96
+ sourceMapPath: undefined,
85
97
  }
86
98
  : await bundleWorker(entry, destination, {
87
99
  serveAssetsFromWorker,
@@ -94,6 +106,14 @@ export function useEsbuild({
94
106
  nodeCompat,
95
107
  define,
96
108
  checkFetch: true,
109
+ assets: assets && {
110
+ ...assets,
111
+ // disable the cache in dev
112
+ bypassCache: true,
113
+ },
114
+ workerDefinitions,
115
+ services,
116
+ firstPartyWorkerDevFacade,
97
117
  });
98
118
 
99
119
  // Capture the `stop()` method to use as the `useEffect()` destructor.
@@ -119,6 +139,7 @@ export function useEsbuild({
119
139
  path: resolvedEntryPointPath,
120
140
  type: bundleType,
121
141
  modules,
142
+ sourceMapPath,
122
143
  });
123
144
  }
124
145
 
@@ -145,6 +166,10 @@ export function useEsbuild({
145
166
  minify,
146
167
  nodeCompat,
147
168
  define,
169
+ assets,
170
+ services,
171
+ workerDefinitions,
172
+ firstPartyWorkerDevFacade,
148
173
  ]);
149
174
  return bundle;
150
175
  }