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
package/src/dev/dev.tsx CHANGED
@@ -1,4 +1,5 @@
1
1
  import { spawn } from "node:child_process";
2
+ import * as path from "node:path";
2
3
  import { watch } from "chokidar";
3
4
  import clipboardy from "clipboardy";
4
5
  import commandExists from "command-exists";
@@ -9,9 +10,9 @@ import onExit from "signal-exit";
9
10
  import tmp from "tmp-promise";
10
11
  import { fetch } from "undici";
11
12
  import { runCustomBuild } from "../entry";
13
+ import { openInspector } from "../inspect";
12
14
  import { logger } from "../logger";
13
15
  import openInBrowser from "../open-in-browser";
14
- import { getAPIToken } from "../user";
15
16
  import { Local } from "./local";
16
17
  import { Remote } from "./remote";
17
18
  import { useEsbuild } from "./use-esbuild";
@@ -19,7 +20,6 @@ import type { Config } from "../config";
19
20
  import type { Entry } from "../entry";
20
21
  import type { AssetPaths } from "../sites";
21
22
  import type { CfWorkerInit } from "../worker";
22
- import type { EsbuildBundle } from "./use-esbuild";
23
23
 
24
24
  export type DevProps = {
25
25
  name?: string;
@@ -38,37 +38,28 @@ export type DevProps = {
38
38
  enableLocalPersistence: boolean;
39
39
  bindings: CfWorkerInit["bindings"];
40
40
  crons: Config["triggers"]["crons"];
41
- public: string | undefined;
41
+ isWorkersSite: boolean;
42
42
  assetPaths: AssetPaths | undefined;
43
43
  compatibilityDate: string;
44
44
  compatibilityFlags: string[] | undefined;
45
45
  usageModel: "bundled" | "unbound" | undefined;
46
46
  minify: boolean | undefined;
47
47
  nodeCompat: boolean | undefined;
48
- build: {
49
- command?: string | undefined;
50
- cwd?: string | undefined;
51
- watch_dir?: string | undefined;
52
- };
48
+ build: Config["build"];
53
49
  env: string | undefined;
54
50
  legacyEnv: boolean;
55
- zone:
56
- | {
57
- id: string;
58
- host: string;
59
- }
60
- | undefined;
51
+ zone: string | undefined;
52
+ host: string | undefined;
61
53
  };
62
54
 
63
55
  export function DevImplementation(props: DevProps): JSX.Element {
64
- const apiToken = props.initialMode === "remote" ? getAPIToken() : undefined;
65
- const directory = useTmpDir();
66
-
67
- useCustomBuild(props.entry, props.build);
68
-
69
- if (props.public && props.entry.format === "service-worker") {
56
+ if (
57
+ !props.isWorkersSite &&
58
+ props.assetPaths &&
59
+ props.entry.format === "service-worker"
60
+ ) {
70
61
  throw new Error(
71
- "You cannot use the service-worker format with a `public` directory."
62
+ "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/"
72
63
  );
73
64
  }
74
65
 
@@ -90,39 +81,16 @@ export function DevImplementation(props: DevProps): JSX.Element {
90
81
  );
91
82
  }
92
83
 
93
- const bundle = useEsbuild({
94
- entry: props.entry,
95
- destination: directory,
96
- staticRoot: props.public,
97
- jsxFactory: props.jsxFactory,
98
- rules: props.rules,
99
- jsxFragment: props.jsxFragment,
100
- serveAssetsFromWorker: !!props.public,
101
- tsconfig: props.tsconfig,
102
- minify: props.minify,
103
- nodeCompat: props.nodeCompat,
104
- });
105
-
106
84
  // only load the UI if we're running in a supported environment
107
85
  const { isRawModeSupported } = useStdin();
108
86
  return isRawModeSupported ? (
109
- <InteractiveDevSession {...props} bundle={bundle} apiToken={apiToken} />
87
+ <InteractiveDevSession {...props} />
110
88
  ) : (
111
- <DevSession
112
- {...props}
113
- bundle={bundle}
114
- apiToken={apiToken}
115
- local={props.initialMode === "local"}
116
- />
89
+ <DevSession {...props} local={props.initialMode === "local"} />
117
90
  );
118
91
  }
119
92
 
120
- type InteractiveDevSessionProps = DevProps & {
121
- apiToken: string | undefined;
122
- bundle: EsbuildBundle | undefined;
123
- };
124
-
125
- function InteractiveDevSession(props: InteractiveDevSessionProps) {
93
+ function InteractiveDevSession(props: DevProps) {
126
94
  const toggles = useHotkeys(
127
95
  {
128
96
  local: props.initialMode === "local",
@@ -155,19 +123,40 @@ function InteractiveDevSession(props: InteractiveDevSessionProps) {
155
123
  );
156
124
  }
157
125
 
158
- type DevSessionProps = InteractiveDevSessionProps & { local: boolean };
126
+ type DevSessionProps = DevProps & {
127
+ local: boolean;
128
+ };
159
129
 
160
130
  function DevSession(props: DevSessionProps) {
131
+ useCustomBuild(props.entry, props.build);
132
+
133
+ const directory = useTmpDir();
134
+
135
+ const bundle = useEsbuild({
136
+ entry: props.entry,
137
+ destination: directory,
138
+ jsxFactory: props.jsxFactory,
139
+ rules: props.rules,
140
+ jsxFragment: props.jsxFragment,
141
+ serveAssetsFromWorker: Boolean(
142
+ props.assetPaths && !props.isWorkersSite && props.local
143
+ ),
144
+ tsconfig: props.tsconfig,
145
+ minify: props.minify,
146
+ nodeCompat: props.nodeCompat,
147
+ });
148
+
161
149
  return props.local ? (
162
150
  <Local
163
151
  name={props.name}
164
- bundle={props.bundle}
152
+ bundle={bundle}
165
153
  format={props.entry.format}
166
154
  compatibilityDate={props.compatibilityDate}
155
+ localProtocol={props.localProtocol}
167
156
  compatibilityFlags={props.compatibilityFlags}
168
157
  bindings={props.bindings}
169
158
  assetPaths={props.assetPaths}
170
- public={props.public}
159
+ isWorkersSite={props.isWorkersSite}
171
160
  port={props.port}
172
161
  ip={props.ip}
173
162
  rules={props.rules}
@@ -178,13 +167,12 @@ function DevSession(props: DevSessionProps) {
178
167
  ) : (
179
168
  <Remote
180
169
  name={props.name}
181
- bundle={props.bundle}
170
+ bundle={bundle}
182
171
  format={props.entry.format}
183
172
  accountId={props.accountId}
184
- apiToken={props.apiToken}
185
173
  bindings={props.bindings}
186
174
  assetPaths={props.assetPaths}
187
- public={props.public}
175
+ isWorkersSite={props.isWorkersSite}
188
176
  port={props.port}
189
177
  ip={props.ip}
190
178
  localProtocol={props.localProtocol}
@@ -195,6 +183,7 @@ function DevSession(props: DevSessionProps) {
195
183
  env={props.env}
196
184
  legacyEnv={props.legacyEnv}
197
185
  zone={props.zone}
186
+ host={props.host}
198
187
  />
199
188
  );
200
189
  }
@@ -226,14 +215,7 @@ function useTmpDir(): string | undefined {
226
215
  return directory?.name;
227
216
  }
228
217
 
229
- function useCustomBuild(
230
- expectedEntry: Entry,
231
- build: {
232
- command?: string | undefined;
233
- cwd?: string | undefined;
234
- watch_dir?: string | undefined;
235
- }
236
- ): void {
218
+ function useCustomBuild(expectedEntry: Entry, build: Config["build"]): void {
237
219
  useEffect(() => {
238
220
  if (!build.command) return;
239
221
  let watcher: ReturnType<typeof watch> | undefined;
@@ -242,9 +224,11 @@ function useCustomBuild(
242
224
  persistent: true,
243
225
  ignoreInitial: true,
244
226
  }).on("all", (_event, filePath) => {
227
+ const relativeFile =
228
+ path.relative(expectedEntry.directory, expectedEntry.file) || ".";
245
229
  //TODO: we should buffer requests to the proxy until this completes
246
230
  logger.log(`The file ${filePath} changed, restarting build...`);
247
- runCustomBuild(expectedEntry.file, build).catch((err) => {
231
+ runCustomBuild(expectedEntry.file, relativeFile, build).catch((err) => {
248
232
  logger.error("Custom build failed:", err);
249
233
  });
250
234
  });
@@ -253,7 +237,7 @@ function useCustomBuild(
253
237
  return () => {
254
238
  watcher?.close();
255
239
  };
256
- }, [build, expectedEntry.file]);
240
+ }, [build, expectedEntry]);
257
241
  }
258
242
 
259
243
  function sleep(period: number) {
@@ -375,10 +359,7 @@ function useHotkeys(
375
359
  }
376
360
  // toggle inspector
377
361
  case "d": {
378
- await openInBrowser(
379
- `https://built-devtools.pages.dev/js_app?experiments=true&v8only=true&ws=localhost:${inspectorPort}/ws`,
380
- { forceChromium: true }
381
- );
362
+ await openInspector(inspectorPort);
382
363
  break;
383
364
  }
384
365
  // toggle local
package/src/dev/local.tsx CHANGED
@@ -21,8 +21,9 @@ interface LocalProps {
21
21
  compatibilityDate: string;
22
22
  compatibilityFlags: string[] | undefined;
23
23
  bindings: CfWorkerInit["bindings"];
24
+ localProtocol: "http" | "https";
24
25
  assetPaths: AssetPaths | undefined;
25
- public: string | undefined;
26
+ isWorkersSite: boolean;
26
27
  port: number;
27
28
  ip: string;
28
29
  rules: Config["rules"];
@@ -49,11 +50,12 @@ function useLocalWorker({
49
50
  compatibilityFlags,
50
51
  bindings,
51
52
  assetPaths,
52
- public: publicDirectory,
53
+ isWorkersSite,
53
54
  port,
54
55
  rules,
55
56
  enableLocalPersistence,
56
57
  ip,
58
+ localProtocol,
57
59
  crons,
58
60
  }: LocalProps) {
59
61
  // TODO: pass vars via command line
@@ -82,9 +84,9 @@ function useLocalWorker({
82
84
  abortSignal: abortController.signal,
83
85
  });
84
86
 
85
- if (publicDirectory) {
87
+ if (bindings.services && bindings.services.length > 0) {
86
88
  throw new Error(
87
- '⎔ A "public" folder is not yet supported in local mode.'
89
+ "⎔ Service bindings are not yet supported in local mode."
88
90
  );
89
91
  }
90
92
 
@@ -159,6 +161,7 @@ function useLocalWorker({
159
161
  name: workerName,
160
162
  port,
161
163
  scriptPath,
164
+ https: localProtocol === "https",
162
165
  host: ip,
163
166
  modules: format === "modules",
164
167
  modulesRules: (rules || [])
@@ -246,12 +249,16 @@ function useLocalWorker({
246
249
  process.stdout.write(data);
247
250
  });
248
251
 
252
+ // parse the node inspector url (which may be received in chunks) from stderr
253
+ let stderrData = "";
249
254
  local.current.stderr?.on("data", (data: Buffer) => {
255
+ stderrData += data.toString();
250
256
  const matches =
251
- /Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)/.exec(
252
- data.toString()
257
+ /Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)[\r|\n]/.exec(
258
+ stderrData
253
259
  );
254
260
  if (matches) {
261
+ local.current?.stderr?.removeAllListeners("data");
255
262
  setInspectorUrl(matches[1]);
256
263
  }
257
264
  });
@@ -293,15 +300,17 @@ function useLocalWorker({
293
300
  workerName,
294
301
  format,
295
302
  port,
303
+ localProtocol,
296
304
  ip,
297
305
  bindings.durable_objects?.bindings,
298
306
  bindings.kv_namespaces,
299
307
  bindings.vars,
308
+ bindings.services,
300
309
  compatibilityDate,
301
310
  compatibilityFlags,
302
311
  localPersistencePath,
303
312
  assetPaths,
304
- publicDirectory,
313
+ isWorkersSite,
305
314
  rules,
306
315
  bindings.wasm_modules,
307
316
  bindings.text_blobs,
@@ -1,12 +1,13 @@
1
- import assert from "node:assert";
2
1
  import { readFile } from "node:fs/promises";
3
2
  import path from "node:path";
4
3
  import { useState, useEffect, useRef } from "react";
4
+ import { printBundleSize } from "../bundle-reporter";
5
5
  import { createWorkerPreview } from "../create-worker-preview";
6
6
  import useInspector from "../inspect";
7
7
  import { logger } from "../logger";
8
8
  import { usePreviewServer } from "../proxy";
9
9
  import { syncAssets } from "../sites";
10
+ import { requireApiToken, requireAuth } from "../user";
10
11
  import type { CfPreviewToken } from "../create-worker-preview";
11
12
  import type { AssetPaths } from "../sites";
12
13
  import type { CfModule, CfWorkerInit, CfScriptFormat } from "../worker";
@@ -16,33 +17,31 @@ export function Remote(props: {
16
17
  name: string | undefined;
17
18
  bundle: EsbuildBundle | undefined;
18
19
  format: CfScriptFormat | undefined;
19
- public: string | undefined;
20
+ isWorkersSite: boolean;
20
21
  assetPaths: AssetPaths | undefined;
21
22
  port: number;
22
23
  ip: string;
23
24
  localProtocol: "https" | "http";
24
25
  inspectorPort: number;
25
26
  accountId: string | undefined;
26
- apiToken: string | undefined;
27
27
  bindings: CfWorkerInit["bindings"];
28
28
  compatibilityDate: string;
29
29
  compatibilityFlags: string[] | undefined;
30
30
  usageModel: "bundled" | "unbound" | undefined;
31
31
  env: string | undefined;
32
32
  legacyEnv: boolean | undefined;
33
- zone: { id: string; host: string } | undefined;
33
+ zone: string | undefined;
34
+ host: string | undefined;
34
35
  }) {
35
- assert(props.accountId, "accountId is required");
36
- assert(props.apiToken, "apiToken is required");
37
36
  const previewToken = useWorker({
38
37
  name: props.name,
39
38
  bundle: props.bundle,
40
39
  format: props.format,
41
40
  modules: props.bundle ? props.bundle.modules : [],
42
41
  accountId: props.accountId,
43
- apiToken: props.apiToken,
44
42
  bindings: props.bindings,
45
43
  assetPaths: props.assetPaths,
44
+ isWorkersSite: props.isWorkersSite,
46
45
  port: props.port,
47
46
  compatibilityDate: props.compatibilityDate,
48
47
  compatibilityFlags: props.compatibilityFlags,
@@ -50,11 +49,14 @@ export function Remote(props: {
50
49
  env: props.env,
51
50
  legacyEnv: props.legacyEnv,
52
51
  zone: props.zone,
52
+ host: props.host,
53
53
  });
54
54
 
55
55
  usePreviewServer({
56
56
  previewToken,
57
- publicRoot: props.public,
57
+ assetDirectory: props.isWorkersSite
58
+ ? undefined
59
+ : props.assetPaths?.assetDirectory,
58
60
  localProtocol: props.localProtocol,
59
61
  localPort: props.port,
60
62
  ip: props.ip,
@@ -73,17 +75,18 @@ export function useWorker(props: {
73
75
  bundle: EsbuildBundle | undefined;
74
76
  format: CfScriptFormat | undefined;
75
77
  modules: CfModule[];
76
- accountId: string;
77
- apiToken: string;
78
+ accountId: string | undefined;
78
79
  bindings: CfWorkerInit["bindings"];
79
80
  assetPaths: AssetPaths | undefined;
81
+ isWorkersSite: boolean;
80
82
  port: number;
81
83
  compatibilityDate: string | undefined;
82
84
  compatibilityFlags: string[] | undefined;
83
85
  usageModel: "bundled" | "unbound" | undefined;
84
86
  env: string | undefined;
85
87
  legacyEnv: boolean | undefined;
86
- zone: { id: string; host: string } | undefined;
88
+ zone: string | undefined;
89
+ host: string | undefined;
87
90
  }): CfPreviewToken | undefined {
88
91
  const {
89
92
  name,
@@ -91,7 +94,6 @@ export function useWorker(props: {
91
94
  format,
92
95
  modules,
93
96
  accountId,
94
- apiToken,
95
97
  bindings,
96
98
  assetPaths,
97
99
  compatibilityDate,
@@ -105,6 +107,9 @@ export function useWorker(props: {
105
107
  // something's "happened" in our system; We make a ref and
106
108
  // mark it once we log our initial message. Refs are vars!
107
109
  const startedRef = useRef(false);
110
+ // This ref holds the actual accountId being used, the `accountId` prop could be undefined
111
+ // as it is only what is retrieved from the wrangler.toml config.
112
+ const accountIdRef = useRef(accountId);
108
113
 
109
114
  useEffect(() => {
110
115
  const abortController = new AbortController();
@@ -119,20 +124,31 @@ export function useWorker(props: {
119
124
  logger.log("⎔ Detected changes, restarted server.");
120
125
  }
121
126
 
127
+ // Ensure we have an account id, even if it means logging in here.
128
+ accountIdRef.current = await requireAuth({
129
+ account_id: accountIdRef.current,
130
+ });
131
+
132
+ const content = await readFile(bundle.path, "utf-8");
133
+
134
+ // TODO: For Dev we could show the reporter message in the interactive box.
135
+ void printBundleSize(
136
+ { name: path.basename(bundle.path), content: content },
137
+ modules
138
+ );
139
+
122
140
  const assets = await syncAssets(
123
- accountId,
141
+ accountIdRef.current,
124
142
  // When we're using the newer service environments, we wouldn't
125
143
  // have added the env name on to the script name. However, we must
126
144
  // include it in the kv namespace name regardless (since there's no
127
145
  // concept of service environments for kv namespaces yet).
128
146
  name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
129
- assetPaths,
147
+ props.isWorkersSite ? assetPaths : undefined,
130
148
  true,
131
149
  false
132
150
  ); // TODO: cancellable?
133
151
 
134
- const content = await readFile(bundle.path, "utf-8");
135
-
136
152
  const init: CfWorkerInit = {
137
153
  name,
138
154
  main: {
@@ -173,10 +189,15 @@ export function useWorker(props: {
173
189
  await createWorkerPreview(
174
190
  init,
175
191
  {
176
- accountId,
177
- apiToken,
192
+ accountId: accountIdRef.current,
193
+ apiToken: requireApiToken(),
194
+ },
195
+ {
196
+ env: props.env,
197
+ legacyEnv: props.legacyEnv,
198
+ zone: props.zone,
199
+ host: props.host,
178
200
  },
179
- { env: props.env, legacyEnv: props.legacyEnv, zone: props.zone },
180
201
  abortController.signal
181
202
  )
182
203
  );
@@ -185,7 +206,21 @@ export function useWorker(props: {
185
206
  // we want to log the error, but not end the process
186
207
  // since it could recover after the developer fixes whatever's wrong
187
208
  if ((err as { code: string }).code !== "ABORT_ERR") {
188
- logger.error("Error on remote worker:", err);
209
+ // instead of logging the raw API error to the user,
210
+ // give them friendly instructions
211
+ // for error 10063 (workers.dev subdomain required)
212
+ if (err.code === 10063) {
213
+ const errorMessage =
214
+ "Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
215
+ const solutionMessage =
216
+ "You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
217
+ const onboardingLink = `https://dash.cloudflare.com/${accountIdRef.current}/workers/onboarding`;
218
+ logger.error(
219
+ `${errorMessage}\n${solutionMessage}\n${onboardingLink}`
220
+ );
221
+ } else {
222
+ logger.error("Error on remote worker:", err);
223
+ }
189
224
  }
190
225
  });
191
226
 
@@ -197,9 +232,9 @@ export function useWorker(props: {
197
232
  bundle,
198
233
  format,
199
234
  accountId,
200
- apiToken,
201
235
  port,
202
236
  assetPaths,
237
+ props.isWorkersSite,
203
238
  compatibilityDate,
204
239
  compatibilityFlags,
205
240
  usageModel,
@@ -208,6 +243,7 @@ export function useWorker(props: {
208
243
  props.env,
209
244
  props.legacyEnv,
210
245
  props.zone,
246
+ props.host,
211
247
  ]);
212
248
  return token;
213
249
  }
@@ -14,13 +14,11 @@ export type EsbuildBundle = {
14
14
  entry: Entry;
15
15
  type: "esm" | "commonjs";
16
16
  modules: CfModule[];
17
- serveAssetsFromWorker: boolean;
18
17
  };
19
18
 
20
19
  export function useEsbuild({
21
20
  entry,
22
21
  destination,
23
- staticRoot,
24
22
  jsxFactory,
25
23
  jsxFragment,
26
24
  rules,
@@ -31,7 +29,6 @@ export function useEsbuild({
31
29
  }: {
32
30
  entry: Entry;
33
31
  destination: string | undefined;
34
- staticRoot: string | undefined;
35
32
  jsxFactory: string | undefined;
36
33
  jsxFragment: string | undefined;
37
34
  rules: Config["rules"];
@@ -67,8 +64,7 @@ export function useEsbuild({
67
64
 
68
65
  const { resolvedEntryPointPath, bundleType, modules, stop } =
69
66
  await bundleWorker(entry, destination, {
70
- // In dev, we serve assets from the local proxy before we send the request to the worker.
71
- serveAssetsFromWorker: false,
67
+ serveAssetsFromWorker,
72
68
  jsxFactory,
73
69
  jsxFragment,
74
70
  rules,
@@ -87,7 +83,6 @@ export function useEsbuild({
87
83
  path: resolvedEntryPointPath,
88
84
  type: bundleType,
89
85
  modules,
90
- serveAssetsFromWorker,
91
86
  });
92
87
  }
93
88
 
@@ -104,7 +99,6 @@ export function useEsbuild({
104
99
  }, [
105
100
  entry,
106
101
  destination,
107
- staticRoot,
108
102
  jsxFactory,
109
103
  jsxFragment,
110
104
  serveAssetsFromWorker,
package/src/dialogs.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import { Box, Text, useInput, render } from "ink";
3
+ import SelectInput from "ink-select-input";
3
4
  import TextInput from "ink-text-input";
4
5
  import * as React from "react";
5
6
  import { useState } from "react";
@@ -85,3 +86,50 @@ export async function prompt(
85
86
  );
86
87
  });
87
88
  }
89
+
90
+ type SelectOption = {
91
+ value: string;
92
+ label: string;
93
+ };
94
+
95
+ type SelectProps = {
96
+ text: string;
97
+ options: SelectOption[];
98
+ initialIndex: number;
99
+ onSelect: (value: string) => void;
100
+ };
101
+
102
+ function Select(props: SelectProps) {
103
+ return (
104
+ <Box flexDirection="column">
105
+ <Text>{props.text}</Text>
106
+ <SelectInput
107
+ initialIndex={props.initialIndex}
108
+ items={props.options}
109
+ onSelect={async (selected) => {
110
+ props.onSelect(selected.value);
111
+ }}
112
+ />
113
+ </Box>
114
+ );
115
+ }
116
+
117
+ export function select(
118
+ text: string,
119
+ options: SelectOption[],
120
+ initialIndex: number
121
+ ): Promise<string> {
122
+ return new Promise((resolve) => {
123
+ const { unmount } = render(
124
+ <Select
125
+ text={text}
126
+ options={options}
127
+ initialIndex={initialIndex}
128
+ onSelect={(option: string) => {
129
+ unmount();
130
+ resolve(option);
131
+ }}
132
+ />
133
+ );
134
+ });
135
+ }