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
package/src/dev/dev.tsx CHANGED
@@ -9,8 +9,8 @@ import onExit from "signal-exit";
9
9
  import tmp from "tmp-promise";
10
10
  import { fetch } from "undici";
11
11
  import { runCustomBuild } from "../entry";
12
+ import { logger } from "../logger";
12
13
  import openInBrowser from "../open-in-browser";
13
- import { reportError } from "../reporting";
14
14
  import { getAPIToken } from "../user";
15
15
  import { Local } from "./local";
16
16
  import { Remote } from "./remote";
@@ -28,24 +28,27 @@ export type DevProps = {
28
28
  ip: string;
29
29
  inspectorPort: number;
30
30
  rules: Config["rules"];
31
- accountId: undefined | string;
31
+ accountId: string | undefined;
32
32
  initialMode: "local" | "remote";
33
- jsxFactory: undefined | string;
34
- jsxFragment: undefined | string;
33
+ jsxFactory: string | undefined;
34
+ jsxFragment: string | undefined;
35
35
  tsconfig: string | undefined;
36
36
  upstreamProtocol: "https" | "http";
37
37
  localProtocol: "https" | "http";
38
38
  enableLocalPersistence: boolean;
39
39
  bindings: CfWorkerInit["bindings"];
40
- public: undefined | string;
41
- assetPaths: undefined | AssetPaths;
42
- compatibilityDate: undefined | string;
43
- compatibilityFlags: undefined | string[];
44
- usageModel: undefined | "bundled" | "unbound";
40
+ crons: Config["triggers"]["crons"];
41
+ public: string | undefined;
42
+ assetPaths: AssetPaths | undefined;
43
+ compatibilityDate: string;
44
+ compatibilityFlags: string[] | undefined;
45
+ usageModel: "bundled" | "unbound" | undefined;
46
+ minify: boolean | undefined;
47
+ nodeCompat: boolean | undefined;
45
48
  build: {
46
- command?: undefined | string;
47
- cwd?: undefined | string;
48
- watch_dir?: undefined | string;
49
+ command?: string | undefined;
50
+ cwd?: string | undefined;
51
+ watch_dir?: string | undefined;
49
52
  };
50
53
  env: string | undefined;
51
54
  legacyEnv: boolean;
@@ -65,7 +68,7 @@ export function DevImplementation(props: DevProps): JSX.Element {
65
68
 
66
69
  if (props.public && props.entry.format === "service-worker") {
67
70
  throw new Error(
68
- "You cannot use the service worker format with a `public` directory."
71
+ "You cannot use the service-worker format with a `public` directory."
69
72
  );
70
73
  }
71
74
 
@@ -77,7 +80,13 @@ export function DevImplementation(props: DevProps): JSX.Element {
77
80
 
78
81
  if (props.bindings.text_blobs && props.entry.format === "modules") {
79
82
  throw new Error(
80
- "You cannot configure [text_blobs] with an ES module worker. Instead, import the file directly in your code, and optionally configure `[build.upload.rules]` in your wrangler.toml"
83
+ "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"
84
+ );
85
+ }
86
+
87
+ if (props.bindings.data_blobs && props.entry.format === "modules") {
88
+ throw new Error(
89
+ "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"
81
90
  );
82
91
  }
83
92
 
@@ -90,6 +99,8 @@ export function DevImplementation(props: DevProps): JSX.Element {
90
99
  jsxFragment: props.jsxFragment,
91
100
  serveAssetsFromWorker: !!props.public,
92
101
  tsconfig: props.tsconfig,
102
+ minify: props.minify,
103
+ nodeCompat: props.nodeCompat,
93
104
  });
94
105
 
95
106
  // only load the UI if we're running in a supported environment
@@ -129,11 +140,16 @@ function InteractiveDevSession(props: InteractiveDevSessionProps) {
129
140
  <>
130
141
  <DevSession {...props} local={toggles.local} />
131
142
  <Box borderStyle="round" paddingLeft={1} paddingRight={1}>
132
- <Text>
133
- {`B to open a browser, D to open Devtools, L to ${
134
- toggles.local ? "turn off" : "turn on"
135
- } local mode, X to exit`}
136
- </Text>
143
+ <Text bold={true}>[b]</Text>
144
+ <Text> open a browser, </Text>
145
+ <Text bold={true}>[d]</Text>
146
+ <Text> open Devtools, </Text>
147
+ <Text bold={true}>[l]</Text>
148
+ <Text> {toggles.local ? "turn off" : "turn on"} local mode, </Text>
149
+ <Text bold={true}>[c]</Text>
150
+ <Text> clear console, </Text>
151
+ <Text bold={true}>[x]</Text>
152
+ <Text> to exit</Text>
137
153
  </Box>
138
154
  </>
139
155
  );
@@ -157,6 +173,7 @@ function DevSession(props: DevSessionProps) {
157
173
  rules={props.rules}
158
174
  inspectorPort={props.inspectorPort}
159
175
  enableLocalPersistence={props.enableLocalPersistence}
176
+ crons={props.crons}
160
177
  />
161
178
  ) : (
162
179
  <Remote
@@ -197,7 +214,9 @@ function useTmpDir(): string | undefined {
197
214
  setDirectory(dir);
198
215
  return;
199
216
  } catch (err) {
200
- console.error("failed to create tmp dir");
217
+ logger.error(
218
+ "Failed to create temporary directory to store built files."
219
+ );
201
220
  handleError(err);
202
221
  }
203
222
  return () => {
@@ -210,9 +229,9 @@ function useTmpDir(): string | undefined {
210
229
  function useCustomBuild(
211
230
  expectedEntry: Entry,
212
231
  build: {
213
- command?: undefined | string;
214
- cwd?: undefined | string;
215
- watch_dir?: undefined | string;
232
+ command?: string | undefined;
233
+ cwd?: string | undefined;
234
+ watch_dir?: string | undefined;
216
235
  }
217
236
  ): void {
218
237
  useEffect(() => {
@@ -224,9 +243,9 @@ function useCustomBuild(
224
243
  ignoreInitial: true,
225
244
  }).on("all", (_event, filePath) => {
226
245
  //TODO: we should buffer requests to the proxy until this completes
227
- console.log(`The file ${filePath} changed, restarting build...`);
246
+ logger.log(`The file ${filePath} changed, restarting build...`);
228
247
  runCustomBuild(expectedEntry.file, build).catch((err) => {
229
- console.error("Custom build failed:", err);
248
+ logger.error("Custom build failed:", err);
230
249
  });
231
250
  });
232
251
  }
@@ -273,12 +292,12 @@ function useTunnel(toggle: boolean) {
273
292
  try {
274
293
  await commandExists("cloudflared");
275
294
  } catch (e) {
276
- console.error(
295
+ logger.warn(
277
296
  "To share your worker on the Internet, please install `cloudflared` from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation"
278
297
  );
279
298
  return;
280
299
  }
281
- console.log("⎔ Starting a tunnel...");
300
+ logger.log("⎔ Starting a tunnel...");
282
301
  tunnel.current = spawn("cloudflared", [
283
302
  "tunnel",
284
303
  "--url",
@@ -289,30 +308,29 @@ function useTunnel(toggle: boolean) {
289
308
 
290
309
  tunnel.current.on("close", (code) => {
291
310
  if (code) {
292
- console.log(`Tunnel process exited with code ${code}`);
311
+ logger.log(`Tunnel process exited with code ${code}`);
293
312
  }
294
313
  });
295
314
 
296
315
  removeSignalExitListener.current = onExit((_code, _signal) => {
297
- console.log("⎔ Shutting down local tunnel.");
316
+ logger.log("⎔ Shutting down local tunnel.");
298
317
  tunnel.current?.kill();
299
318
  tunnel.current = undefined;
300
319
  });
301
320
 
302
321
  const hostName = await findTunnelHostname();
303
322
  await clipboardy.write(hostName);
304
- console.log(`⬣ Sharing at ${hostName}, copied to clipboard.`);
323
+ logger.log(`⬣ Sharing at ${hostName}, copied to clipboard.`);
305
324
  }
306
325
  }
307
326
 
308
327
  startTunnel().catch(async (err) => {
309
- console.error("tunnel:", err);
310
- await reportError(err);
328
+ logger.error("tunnel:", err);
311
329
  });
312
330
 
313
331
  return () => {
314
332
  if (tunnel.current) {
315
- console.log("⎔ Shutting down tunnel.");
333
+ logger.log("⎔ Shutting down tunnel.");
316
334
  tunnel.current?.kill();
317
335
  tunnel.current = undefined;
318
336
  removeSignalExitListener.current && removeSignalExitListener.current();
@@ -335,6 +353,7 @@ function useHotkeys(
335
353
  ) {
336
354
  // UGH, we should put port in context instead
337
355
  const [toggles, setToggles] = useState(initial);
356
+ const { exit } = useApp();
338
357
  useInput(
339
358
  async (
340
359
  input,
@@ -342,6 +361,13 @@ function useHotkeys(
342
361
  key
343
362
  ) => {
344
363
  switch (input.toLowerCase()) {
364
+ // clear console
365
+ case "c":
366
+ console.clear();
367
+ // This console.log causes Ink to re-render the `DevSession` component.
368
+ // Couldn't find a better way to tell it to do so...
369
+ console.log();
370
+ break;
345
371
  // open browser
346
372
  case "b": {
347
373
  await openInBrowser(`${localProtocol}://${ip}:${port}`);
@@ -365,7 +391,7 @@ function useHotkeys(
365
391
  // shut down
366
392
  case "q":
367
393
  case "x":
368
- process.exit(0);
394
+ exit();
369
395
  break;
370
396
  default:
371
397
  // nothing?
package/src/dev/local.tsx CHANGED
@@ -5,6 +5,7 @@ import path from "node:path";
5
5
  import { useState, useEffect, useRef } from "react";
6
6
  import onExit from "signal-exit";
7
7
  import useInspector from "../inspect";
8
+ import { logger } from "../logger";
8
9
  import { DEFAULT_MODULE_RULES } from "../module-collection";
9
10
  import { waitForPortToBeAvailable } from "../proxy";
10
11
  import type { Config } from "../config";
@@ -14,19 +15,20 @@ import type { EsbuildBundle } from "./use-esbuild";
14
15
  import type { MiniflareOptions } from "miniflare";
15
16
 
16
17
  interface LocalProps {
17
- name: undefined | string;
18
+ name: string | undefined;
18
19
  bundle: EsbuildBundle | undefined;
19
20
  format: CfScriptFormat | undefined;
20
- compatibilityDate: string | undefined;
21
- compatibilityFlags: undefined | string[];
21
+ compatibilityDate: string;
22
+ compatibilityFlags: string[] | undefined;
22
23
  bindings: CfWorkerInit["bindings"];
23
- assetPaths: undefined | AssetPaths;
24
- public: undefined | string;
24
+ assetPaths: AssetPaths | undefined;
25
+ public: string | undefined;
25
26
  port: number;
26
27
  ip: string;
27
28
  rules: Config["rules"];
28
29
  inspectorPort: number;
29
30
  enableLocalPersistence: boolean;
31
+ crons: Config["triggers"]["crons"];
30
32
  }
31
33
 
32
34
  export function Local(props: LocalProps) {
@@ -52,17 +54,33 @@ function useLocalWorker({
52
54
  rules,
53
55
  enableLocalPersistence,
54
56
  ip,
57
+ crons,
55
58
  }: LocalProps) {
56
59
  // TODO: pass vars via command line
57
60
  const local = useRef<ReturnType<typeof spawn>>();
58
61
  const removeSignalExitListener = useRef<() => void>();
59
62
  const [inspectorUrl, setInspectorUrl] = useState<string | undefined>();
63
+ // if we're using local persistence for data, we should use the cwd
64
+ // as an explicit path, or else it'll use the temp dir
65
+ // which disappears when dev ends
66
+ const localPersistencePath = enableLocalPersistence
67
+ ? // Maybe we could make the path configurable as well?
68
+ path.join(process.cwd(), "wrangler-local-state")
69
+ : // We otherwise choose null, but choose true later
70
+ // so that it's persisted in the temp dir across a dev session
71
+ // even when we change source and reload
72
+ null;
60
73
  useEffect(() => {
74
+ const abortController = new AbortController();
61
75
  async function startLocalWorker() {
62
76
  if (!bundle || !format) return;
63
77
 
64
78
  // port for the worker
65
- await waitForPortToBeAvailable(port, { retryPeriod: 200, timeout: 2000 });
79
+ await waitForPortToBeAvailable(port, {
80
+ retryPeriod: 200,
81
+ timeout: 2000,
82
+ abortSignal: abortController.signal,
83
+ });
66
84
 
67
85
  if (publicDirectory) {
68
86
  throw new Error(
@@ -81,7 +99,35 @@ function useLocalWorker({
81
99
 
82
100
  const scriptPath = realpathSync(bundle.path);
83
101
 
84
- const wasmBindings = { ...bindings.wasm_modules };
102
+ // the wasm_modules/text_blobs/data_blobs bindings are
103
+ // relative to process.cwd(), but the actual worker bundle
104
+ // is in the temp output directory; so we rewrite the paths to be absolute,
105
+ // letting miniflare resolve them correctly
106
+
107
+ // wasm
108
+ const wasmBindings: Record<string, string> = {};
109
+ for (const [name, filePath] of Object.entries(
110
+ bindings.wasm_modules || {}
111
+ )) {
112
+ wasmBindings[name] = path.join(process.cwd(), filePath);
113
+ }
114
+
115
+ // text
116
+ const textBlobBindings: Record<string, string> = {};
117
+ for (const [name, filePath] of Object.entries(
118
+ bindings.text_blobs || {}
119
+ )) {
120
+ textBlobBindings[name] = path.join(process.cwd(), filePath);
121
+ }
122
+
123
+ // data
124
+ const dataBlobBindings: Record<string, string> = {};
125
+ for (const [name, filePath] of Object.entries(
126
+ bindings.data_blobs || {}
127
+ )) {
128
+ dataBlobBindings[name] = path.join(process.cwd(), filePath);
129
+ }
130
+
85
131
  if (format === "service-worker") {
86
132
  for (const { type, name } of bundle.modules) {
87
133
  if (type === "compiled-wasm") {
@@ -91,6 +137,20 @@ function useLocalWorker({
91
137
  // characters with an underscore.
92
138
  const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
93
139
  wasmBindings[identifier] = name;
140
+ } else if (type === "text") {
141
+ // In service-worker format, text modules are referenced by global identifiers,
142
+ // so we convert it here.
143
+ // This identifier has to be a valid JS identifier, so we replace all non alphanumeric
144
+ // characters with an underscore.
145
+ const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
146
+ textBlobBindings[identifier] = name;
147
+ } else if (type === "buffer") {
148
+ // In service-worker format, data blobs are referenced by global identifiers,
149
+ // so we convert it here.
150
+ // This identifier has to be a valid JS identifier, so we replace all non alphanumeric
151
+ // characters with an underscore.
152
+ const identifier = name.replace(/[^a-zA-Z0-9_$]/g, "_");
153
+ dataBlobBindings[identifier] = name;
94
154
  }
95
155
  }
96
156
  }
@@ -111,14 +171,28 @@ function useLocalWorker({
111
171
  compatibilityDate,
112
172
  compatibilityFlags,
113
173
  kvNamespaces: bindings.kv_namespaces?.map((kv) => kv.binding),
114
- kvPersist: enableLocalPersistence,
115
174
  durableObjects: Object.fromEntries(
116
175
  (bindings.durable_objects?.bindings ?? []).map<[string, string]>(
117
176
  (value) => [value.name, value.class_name]
118
177
  )
119
178
  ),
120
- durableObjectsPersist: enableLocalPersistence,
121
- cachePersist: enableLocalPersistence,
179
+ ...(localPersistencePath
180
+ ? {
181
+ kvPersist: path.join(localPersistencePath, "kv"),
182
+ durableObjectsPersist: path.join(localPersistencePath, "do"),
183
+ cachePersist: path.join(localPersistencePath, "cache"),
184
+ }
185
+ : {
186
+ // We mark these as true, so that they'll
187
+ // persist in the temp directory.
188
+ // This means they'll persist across a dev session,
189
+ // even if we change source and reload,
190
+ // and be deleted when the dev session ends
191
+ durableObjectsPersist: true,
192
+ cachePersist: true,
193
+ kvPersist: true,
194
+ }),
195
+
122
196
  sitePath: assetPaths?.assetDirectory
123
197
  ? path.join(assetPaths.baseDirectory, assetPaths.assetDirectory)
124
198
  : undefined,
@@ -130,8 +204,11 @@ function useLocalWorker({
130
204
  : undefined,
131
205
  bindings: bindings.vars,
132
206
  wasmBindings,
207
+ textBlobBindings,
208
+ dataBlobBindings,
133
209
  sourceMap: true,
134
210
  logUnhandledRejections: true,
211
+ crons,
135
212
  };
136
213
 
137
214
  // The path to the Miniflare CLI assumes that this file is being run from
@@ -143,7 +220,7 @@ function useLocalWorker({
143
220
  );
144
221
  const optionsArg = JSON.stringify(options, null);
145
222
 
146
- console.log("⎔ Starting a local server...");
223
+ logger.log("⎔ Starting a local server...");
147
224
  local.current = spawn(
148
225
  "node",
149
226
  [
@@ -161,16 +238,15 @@ function useLocalWorker({
161
238
 
162
239
  local.current.on("close", (code) => {
163
240
  if (code) {
164
- console.log(`Miniflare process exited with code ${code}`);
241
+ logger.log(`Miniflare process exited with code ${code}`);
165
242
  }
166
243
  });
167
244
 
168
245
  local.current.stdout?.on("data", (data: Buffer) => {
169
- console.log(`${data.toString()}`);
246
+ process.stdout.write(data);
170
247
  });
171
248
 
172
249
  local.current.stderr?.on("data", (data: Buffer) => {
173
- console.error(`${data.toString()}`);
174
250
  const matches =
175
251
  /Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)/.exec(
176
252
  data.toString()
@@ -182,29 +258,30 @@ function useLocalWorker({
182
258
 
183
259
  local.current.on("exit", (code) => {
184
260
  if (code) {
185
- console.error(`Miniflare process exited with code ${code}`);
261
+ logger.error(`Miniflare process exited with code ${code}`);
186
262
  }
187
263
  });
188
264
 
189
265
  local.current.on("error", (error: Error) => {
190
- console.error(`Miniflare process failed to spawn`);
191
- console.error(error);
266
+ logger.error(`Miniflare process failed to spawn`);
267
+ logger.error(error);
192
268
  });
193
269
 
194
270
  removeSignalExitListener.current = onExit((_code, _signal) => {
195
- console.log("⎔ Shutting down local server.");
271
+ logger.log("⎔ Shutting down local server.");
196
272
  local.current?.kill();
197
273
  local.current = undefined;
198
274
  });
199
275
  }
200
276
 
201
277
  startLocalWorker().catch((err) => {
202
- console.error("local worker:", err);
278
+ logger.error("local worker:", err);
203
279
  });
204
280
 
205
281
  return () => {
282
+ abortController.abort();
206
283
  if (local.current) {
207
- console.log("⎔ Shutting down local server.");
284
+ logger.log("⎔ Shutting down local server.");
208
285
  local.current?.kill();
209
286
  local.current = undefined;
210
287
  removeSignalExitListener.current && removeSignalExitListener.current();
@@ -222,11 +299,14 @@ function useLocalWorker({
222
299
  bindings.vars,
223
300
  compatibilityDate,
224
301
  compatibilityFlags,
225
- enableLocalPersistence,
302
+ localPersistencePath,
226
303
  assetPaths,
227
304
  publicDirectory,
228
305
  rules,
229
306
  bindings.wasm_modules,
307
+ bindings.text_blobs,
308
+ bindings.data_blobs,
309
+ crons,
230
310
  ]);
231
311
  return { inspectorUrl };
232
312
  }
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import { useState, useEffect, useRef } from "react";
5
5
  import { createWorkerPreview } from "../create-worker-preview";
6
6
  import useInspector from "../inspect";
7
+ import { logger } from "../logger";
7
8
  import { usePreviewServer } from "../proxy";
8
9
  import { syncAssets } from "../sites";
9
10
  import type { CfPreviewToken } from "../create-worker-preview";
@@ -15,18 +16,18 @@ export function Remote(props: {
15
16
  name: string | undefined;
16
17
  bundle: EsbuildBundle | undefined;
17
18
  format: CfScriptFormat | undefined;
18
- public: undefined | string;
19
- assetPaths: undefined | AssetPaths;
19
+ public: string | undefined;
20
+ assetPaths: AssetPaths | undefined;
20
21
  port: number;
21
22
  ip: string;
22
23
  localProtocol: "https" | "http";
23
24
  inspectorPort: number;
24
- accountId: undefined | string;
25
- apiToken: undefined | string;
25
+ accountId: string | undefined;
26
+ apiToken: string | undefined;
26
27
  bindings: CfWorkerInit["bindings"];
27
- compatibilityDate: string | undefined;
28
- compatibilityFlags: undefined | string[];
29
- usageModel: undefined | "bundled" | "unbound";
28
+ compatibilityDate: string;
29
+ compatibilityFlags: string[] | undefined;
30
+ usageModel: "bundled" | "unbound" | undefined;
30
31
  env: string | undefined;
31
32
  legacyEnv: boolean | undefined;
32
33
  zone: { id: string; host: string } | undefined;
@@ -75,11 +76,11 @@ export function useWorker(props: {
75
76
  accountId: string;
76
77
  apiToken: string;
77
78
  bindings: CfWorkerInit["bindings"];
78
- assetPaths: undefined | AssetPaths;
79
+ assetPaths: AssetPaths | undefined;
79
80
  port: number;
80
81
  compatibilityDate: string | undefined;
81
82
  compatibilityFlags: string[] | undefined;
82
- usageModel: undefined | "bundled" | "unbound";
83
+ usageModel: "bundled" | "unbound" | undefined;
83
84
  env: string | undefined;
84
85
  legacyEnv: boolean | undefined;
85
86
  zone: { id: string; host: string } | undefined;
@@ -106,6 +107,7 @@ export function useWorker(props: {
106
107
  const startedRef = useRef(false);
107
108
 
108
109
  useEffect(() => {
110
+ const abortController = new AbortController();
109
111
  async function start() {
110
112
  setToken(undefined); // reset token in case we're re-running
111
113
 
@@ -114,7 +116,7 @@ export function useWorker(props: {
114
116
  if (!startedRef.current) {
115
117
  startedRef.current = true;
116
118
  } else {
117
- console.log("⎔ Detected changes, restarted server.");
119
+ logger.log("⎔ Detected changes, restarted server.");
118
120
  }
119
121
 
120
122
  const assets = await syncAssets(
@@ -125,7 +127,8 @@ export function useWorker(props: {
125
127
  // concept of service environments for kv namespaces yet).
126
128
  name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
127
129
  assetPaths,
128
- true
130
+ true,
131
+ false
129
132
  ); // TODO: cancellable?
130
133
 
131
134
  const content = await readFile(bundle.path, "utf-8");
@@ -173,15 +176,22 @@ export function useWorker(props: {
173
176
  accountId,
174
177
  apiToken,
175
178
  },
176
- { env: props.env, legacyEnv: props.legacyEnv, zone: props.zone }
179
+ { env: props.env, legacyEnv: props.legacyEnv, zone: props.zone },
180
+ abortController.signal
177
181
  )
178
182
  );
179
183
  }
180
184
  start().catch((err) => {
181
185
  // we want to log the error, but not end the process
182
186
  // since it could recover after the developer fixes whatever's wrong
183
- console.error("remote worker:", err);
187
+ if ((err as { code: string }).code !== "ABORT_ERR") {
188
+ logger.error("Error on remote worker:", err);
189
+ }
184
190
  });
191
+
192
+ return () => {
193
+ abortController.abort();
194
+ };
185
195
  }, [
186
196
  name,
187
197
  bundle,
@@ -1,6 +1,8 @@
1
1
  import assert from "node:assert";
2
+ import { useApp } from "ink";
2
3
  import { useState, useEffect } from "react";
3
4
  import { bundleWorker } from "../bundle";
5
+ import { logger } from "../logger";
4
6
  import type { Config } from "../config";
5
7
  import type { Entry } from "../entry";
6
8
  import type { CfModule } from "../worker";
@@ -24,23 +26,28 @@ export function useEsbuild({
24
26
  rules,
25
27
  serveAssetsFromWorker,
26
28
  tsconfig,
29
+ minify,
30
+ nodeCompat,
27
31
  }: {
28
32
  entry: Entry;
29
33
  destination: string | undefined;
30
- staticRoot: undefined | string;
34
+ staticRoot: string | undefined;
31
35
  jsxFactory: string | undefined;
32
36
  jsxFragment: string | undefined;
33
37
  rules: Config["rules"];
34
38
  serveAssetsFromWorker: boolean;
35
39
  tsconfig: string | undefined;
40
+ minify: boolean | undefined;
41
+ nodeCompat: boolean | undefined;
36
42
  }): EsbuildBundle | undefined {
37
43
  const [bundle, setBundle] = useState<EsbuildBundle>();
44
+ const { exit } = useApp();
38
45
  useEffect(() => {
39
46
  let stopWatching: (() => void) | undefined = undefined;
40
47
 
41
48
  const watchMode: WatchMode = {
42
49
  async onRebuild(error) {
43
- if (error) console.error("watch build failed:", error);
50
+ if (error) logger.error("Watch build failed:", error);
44
51
  else {
45
52
  // nothing really changes here, so let's increment the id
46
53
  // to change the return object's identity
@@ -67,6 +74,8 @@ export function useEsbuild({
67
74
  rules,
68
75
  watch: watchMode,
69
76
  tsconfig,
77
+ minify,
78
+ nodeCompat,
70
79
  });
71
80
 
72
81
  // Capture the `stop()` method to use as the `useEffect()` destructor.
@@ -82,9 +91,11 @@ export function useEsbuild({
82
91
  });
83
92
  }
84
93
 
85
- build().catch(() => {
86
- // esbuild already logs errors to stderr and we don't want to end the process
87
- // on build errors anyway so this is a no-op error handler
94
+ build().catch((err) => {
95
+ // If esbuild fails on first run, we want to quit the process
96
+ // since we can't recover from here
97
+ // related: https://github.com/evanw/esbuild/issues/1037
98
+ exit(err);
88
99
  });
89
100
 
90
101
  return () => {
@@ -99,6 +110,9 @@ export function useEsbuild({
99
110
  serveAssetsFromWorker,
100
111
  rules,
101
112
  tsconfig,
113
+ exit,
114
+ minify,
115
+ nodeCompat,
102
116
  ]);
103
117
  return bundle;
104
118
  }