wrangler 2.0.8 → 2.0.12

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 (67) hide show
  1. package/kv-asset-handler.js +1 -0
  2. package/package.json +3 -1
  3. package/src/__tests__/configuration.test.ts +255 -142
  4. package/src/__tests__/dev.test.tsx +88 -58
  5. package/src/__tests__/index.test.ts +2 -1
  6. package/src/__tests__/init.test.ts +3 -0
  7. package/src/__tests__/kv.test.ts +23 -2
  8. package/src/__tests__/pages.test.ts +98 -1
  9. package/src/__tests__/publish.test.ts +514 -162
  10. package/src/__tests__/whoami.test.tsx +34 -0
  11. package/src/bundle.ts +9 -5
  12. package/src/cfetch/internal.ts +6 -9
  13. package/src/config/config.ts +1 -1
  14. package/src/config/environment.ts +1 -1
  15. package/src/config/validation-helpers.ts +10 -1
  16. package/src/config/validation.ts +22 -13
  17. package/src/create-worker-preview.ts +15 -15
  18. package/src/dev/dev.tsx +32 -56
  19. package/src/dev/local.tsx +10 -7
  20. package/src/dev/remote.tsx +30 -17
  21. package/src/dev/use-esbuild.ts +1 -4
  22. package/src/index.tsx +239 -244
  23. package/src/kv.ts +1 -1
  24. package/src/pages.tsx +295 -229
  25. package/src/parse.ts +21 -1
  26. package/src/proxy.ts +19 -6
  27. package/src/publish.ts +154 -16
  28. package/src/sites.tsx +49 -18
  29. package/src/user.tsx +12 -1
  30. package/src/whoami.tsx +3 -2
  31. package/src/worker.ts +2 -1
  32. package/src/zones.ts +73 -0
  33. package/templates/static-asset-facade.js +1 -5
  34. package/wrangler-dist/cli.js +73693 -73458
  35. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  36. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  37. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  38. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  39. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  40. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  41. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  42. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  43. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  44. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  45. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  46. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  47. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  48. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  49. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  50. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  51. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  52. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  53. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  54. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  55. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  56. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  57. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  58. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  59. package/vendor/wrangler-mime/LICENSE +0 -21
  60. package/vendor/wrangler-mime/Mime.js +0 -97
  61. package/vendor/wrangler-mime/README.md +0 -187
  62. package/vendor/wrangler-mime/cli.js +0 -46
  63. package/vendor/wrangler-mime/index.js +0 -4
  64. package/vendor/wrangler-mime/lite.js +0 -4
  65. package/vendor/wrangler-mime/package.json +0 -52
  66. package/vendor/wrangler-mime/types/other.js +0 -1
  67. package/vendor/wrangler-mime/types/standard.js +0 -1
@@ -9,6 +9,8 @@ import { runInTempDir } from "./helpers/run-in-tmp";
9
9
  import type { UserInfo } from "../whoami";
10
10
 
11
11
  describe("getUserInfo()", () => {
12
+ const ENV_COPY = process.env;
13
+
12
14
  runInTempDir({ homedir: "./home" });
13
15
  const std = mockConsoleMethods();
14
16
  const { setIsTTY } = useMockIsTTY();
@@ -17,6 +19,10 @@ describe("getUserInfo()", () => {
17
19
  setIsTTY(true);
18
20
  });
19
21
 
22
+ afterEach(() => {
23
+ process.env = ENV_COPY;
24
+ });
25
+
20
26
  it("should return undefined if there is no config file", async () => {
21
27
  const userInfo = await getUserInfo();
22
28
  expect(userInfo).toBeUndefined();
@@ -28,6 +34,34 @@ describe("getUserInfo()", () => {
28
34
  expect(userInfo).toBeUndefined();
29
35
  });
30
36
 
37
+ it("should say it's using an API token when one is set", async () => {
38
+ process.env = {
39
+ CLOUDFLARE_API_TOKEN: "123456789",
40
+ };
41
+ setMockResponse("/user", () => {
42
+ return { email: "user@example.com" };
43
+ });
44
+ setMockResponse("/accounts", () => {
45
+ return [
46
+ { name: "Account One", id: "account-1" },
47
+ { name: "Account Two", id: "account-2" },
48
+ { name: "Account Three", id: "account-3" },
49
+ ];
50
+ });
51
+
52
+ const userInfo = await getUserInfo();
53
+ expect(userInfo).toEqual({
54
+ authType: "API",
55
+ apiToken: "123456789",
56
+ email: "user@example.com",
57
+ accounts: [
58
+ { name: "Account One", id: "account-1" },
59
+ { name: "Account Two", id: "account-2" },
60
+ { name: "Account Three", id: "account-3" },
61
+ ],
62
+ });
63
+ });
64
+
31
65
  it("should return the user's email and accounts if authenticated via config token", async () => {
32
66
  writeAuthConfigFile({ oauth_token: "some-oauth-token" });
33
67
 
package/src/bundle.ts CHANGED
@@ -154,9 +154,7 @@ export async function bundleWorker(
154
154
  };
155
155
  }
156
156
 
157
- type EntryPoint =
158
- | { stdin: esbuild.StdinOptions; nodePaths: string[] }
159
- | { entryPoints: string[] };
157
+ type EntryPoint = { stdin: esbuild.StdinOptions } | { entryPoints: string[] };
160
158
 
161
159
  /**
162
160
  * Create an object that describes the entry point for esbuild.
@@ -177,11 +175,17 @@ function getEntryPoint(
177
175
  path.join(__dirname, "../templates/static-asset-facade.js"),
178
176
  "utf8"
179
177
  )
180
- .replace("__ENTRY_POINT__", entryFile),
178
+ // on windows, escape backslashes in the path (`\`)
179
+ .replace("__ENTRY_POINT__", entryFile.replaceAll("\\", "\\\\"))
180
+ .replace(
181
+ "__KV_ASSET_HANDLER__",
182
+ path
183
+ .join(__dirname, "../kv-asset-handler.js")
184
+ .replaceAll("\\", "\\\\")
185
+ ),
181
186
  sourcefile: "static-asset-facade.js",
182
187
  resolveDir: path.dirname(entryFile),
183
188
  },
184
- nodePaths: [path.join(__dirname, "../vendor")],
185
189
  };
186
190
  } else {
187
191
  return { entryPoints: [entryFile] };
@@ -1,8 +1,9 @@
1
+ import assert from "node:assert";
1
2
  import { fetch, Headers } from "undici";
2
3
  import { version as wranglerVersion } from "../../package.json";
3
4
  import { getEnvironmentVariableFactory } from "../environment-variables";
4
5
  import { ParseError, parseJSON } from "../parse";
5
- import { getAPIToken, loginOrRefreshIfRequired } from "../user";
6
+ import { loginOrRefreshIfRequired, requireApiToken } from "../user";
6
7
  import type { URLSearchParams } from "node:url";
7
8
  import type { RequestInit, HeadersInit } from "undici";
8
9
 
@@ -30,6 +31,10 @@ export async function fetchInternal<ResponseType>(
30
31
  queryParams?: URLSearchParams,
31
32
  abortSignal?: AbortSignal
32
33
  ): Promise<ResponseType> {
34
+ assert(
35
+ resource.startsWith("/"),
36
+ `CF API fetch - resource path must start with a "/" but got "${resource}"`
37
+ );
33
38
  await requireLoggedIn();
34
39
  const apiToken = requireApiToken();
35
40
  const headers = cloneHeaders(init.headers);
@@ -90,14 +95,6 @@ async function requireLoggedIn(): Promise<void> {
90
95
  }
91
96
  }
92
97
 
93
- function requireApiToken(): string {
94
- const authToken = getAPIToken();
95
- if (!authToken) {
96
- throw new Error("No API token found.");
97
- }
98
- return authToken;
99
- }
100
-
101
98
  function addAuthorizationHeaderIfUnspecified(
102
99
  headers: Record<string, string>,
103
100
  apiToken: string
@@ -184,7 +184,7 @@ export interface DevConfig {
184
184
  /**
185
185
  * Host to forward requests to, defaults to the host of the first route of project
186
186
  */
187
- host: Config["route"];
187
+ host: string | undefined;
188
188
  }
189
189
 
190
190
  export type RawDevConfig = Partial<DevConfig>;
@@ -175,7 +175,7 @@ interface EnvironmentInheritable {
175
175
  /** The directory in which the command is executed. */
176
176
  cwd?: string;
177
177
  /** The directory to watch for changes while using wrangler dev, defaults to the current working directory */
178
- watch_dir?: string;
178
+ watch_dir?: string | string[];
179
179
  /**
180
180
  * Deprecated field previously used to configure the build and upload of the script.
181
181
  * @deprecated
@@ -277,12 +277,21 @@ export const isObjectWith =
277
277
  !properties.every((prop) => prop in value))
278
278
  ) {
279
279
  diagnostics.errors.push(
280
- `Expected "${field}" to be of type object, containing properties ${properties}, but got ${JSON.stringify(
280
+ `Expected "${field}" to be of type object, containing only properties ${properties}, but got ${JSON.stringify(
281
281
  value
282
282
  )}.`
283
283
  );
284
284
  return false;
285
285
  }
286
+ // it's an object with the field as desired,
287
+ // but let's also check for unexpected fields
288
+ if (value !== undefined) {
289
+ const restFields = Object.keys(value).filter(
290
+ (key) => !properties.includes(key)
291
+ );
292
+ validateAdditionalProperties(diagnostics, field, restFields, []);
293
+ }
294
+
286
295
  return true;
287
296
  };
288
297
 
@@ -227,19 +227,23 @@ function normalizeAndValidateBuild(
227
227
  rawBuild: Config["build"],
228
228
  configPath: string | undefined
229
229
  ): Config["build"] & { deprecatedUpload: DeprecatedUpload } {
230
- const { command, cwd, watch_dir, upload, ...rest } = rawBuild;
230
+ const { command, cwd, watch_dir = "./src", upload, ...rest } = rawBuild;
231
231
  const deprecatedUpload: DeprecatedUpload = { ...upload };
232
232
  validateAdditionalProperties(diagnostics, "build", Object.keys(rest), []);
233
233
 
234
234
  validateOptionalProperty(diagnostics, "build", "command", command, "string");
235
235
  validateOptionalProperty(diagnostics, "build", "cwd", cwd, "string");
236
- validateOptionalProperty(
237
- diagnostics,
238
- "build",
239
- "watch_dir",
240
- watch_dir,
241
- "string"
242
- );
236
+ if (Array.isArray(watch_dir)) {
237
+ validateTypedArray(diagnostics, "build.watch_dir", watch_dir, "string");
238
+ } else {
239
+ validateOptionalProperty(
240
+ diagnostics,
241
+ "build",
242
+ "watch_dir",
243
+ watch_dir,
244
+ "string"
245
+ );
246
+ }
243
247
 
244
248
  deprecated(
245
249
  diagnostics,
@@ -288,13 +292,18 @@ function normalizeAndValidateBuild(
288
292
  // - `configPath` will always be defined since `build` can only
289
293
  // be configured in `wrangler.toml`, but who knows, that may
290
294
  // change in the future, so we do a check anyway
291
- command
292
- ? configPath
293
- ? path.relative(
295
+ command && configPath
296
+ ? Array.isArray(watch_dir)
297
+ ? watch_dir.map((dir) =>
298
+ path.relative(
299
+ process.cwd(),
300
+ path.join(path.dirname(configPath), `${dir}`)
301
+ )
302
+ )
303
+ : path.relative(
294
304
  process.cwd(),
295
- path.join(path.dirname(configPath), watch_dir || "./src")
305
+ path.join(path.dirname(configPath), `${watch_dir}`)
296
306
  )
297
- : watch_dir || "./src"
298
307
  : watch_dir,
299
308
  cwd,
300
309
  deprecatedUpload,
@@ -63,7 +63,7 @@ async function sessionToken(
63
63
  ): Promise<CfPreviewToken> {
64
64
  const { accountId } = account;
65
65
  const initUrl = ctx.zone
66
- ? `/zones/${ctx.zone.id}/workers/edge-preview`
66
+ ? `/zones/${ctx.zone}/workers/edge-preview`
67
67
  : `/accounts/${accountId}/workers/subdomain/edge-preview`;
68
68
 
69
69
  const { exchange_url } = await fetchResult<{ exchange_url: string }>(
@@ -111,7 +111,7 @@ async function createPreviewToken(
111
111
  );
112
112
 
113
113
  const { accountId } = account;
114
- const scriptId = ctx.zone ? randomId() : worker.name || host.split(".")[0];
114
+ const scriptId = worker.name || (ctx.zone ? randomId() : host.split(".")[0]);
115
115
  const url =
116
116
  ctx.env && !ctx.legacyEnv
117
117
  ? `/accounts/${accountId}/workers/services/${scriptId}/environments/${ctx.env}/edge-preview`
@@ -139,19 +139,19 @@ async function createPreviewToken(
139
139
 
140
140
  return {
141
141
  value: preview_token,
142
- host: ctx.zone
143
- ? ctx.zone.host
144
- : worker.name
145
- ? `${
146
- worker.name
147
- // TODO: this should also probably have the env prefix
148
- // but it doesn't appear to work yet, instead giving us the
149
- // "There is nothing here yet" screen
150
- // ctx.env && !ctx.legacyEnv
151
- // ? `${ctx.env}.${worker.name}`
152
- // : worker.name
153
- }.${host.split(".").slice(1).join(".")}`
154
- : host,
142
+ host:
143
+ ctx.host ??
144
+ (worker.name
145
+ ? `${
146
+ worker.name
147
+ // TODO: this should also probably have the env prefix
148
+ // but it doesn't appear to work yet, instead giving us the
149
+ // "There is nothing here yet" screen
150
+ // ctx.env && !ctx.legacyEnv
151
+ // ? `${ctx.env}.${worker.name}`
152
+ // : worker.name
153
+ }.${host.split(".").slice(1).join(".")}`
154
+ : host),
155
155
 
156
156
  inspectorUrl,
157
157
  prewarmUrl,
package/src/dev/dev.tsx CHANGED
@@ -13,7 +13,6 @@ import { runCustomBuild } from "../entry";
13
13
  import { openInspector } from "../inspect";
14
14
  import { logger } from "../logger";
15
15
  import openInBrowser from "../open-in-browser";
16
- import { getAPIToken } from "../user";
17
16
  import { Local } from "./local";
18
17
  import { Remote } from "./remote";
19
18
  import { useEsbuild } from "./use-esbuild";
@@ -21,7 +20,6 @@ import type { Config } from "../config";
21
20
  import type { Entry } from "../entry";
22
21
  import type { AssetPaths } from "../sites";
23
22
  import type { CfWorkerInit } from "../worker";
24
- import type { EsbuildBundle } from "./use-esbuild";
25
23
 
26
24
  export type DevProps = {
27
25
  name?: string;
@@ -47,27 +45,14 @@ export type DevProps = {
47
45
  usageModel: "bundled" | "unbound" | undefined;
48
46
  minify: boolean | undefined;
49
47
  nodeCompat: boolean | undefined;
50
- build: {
51
- command?: string | undefined;
52
- cwd?: string | undefined;
53
- watch_dir?: string | undefined;
54
- };
48
+ build: Config["build"];
55
49
  env: string | undefined;
56
50
  legacyEnv: boolean;
57
- zone:
58
- | {
59
- id: string;
60
- host: string;
61
- }
62
- | undefined;
51
+ zone: string | undefined;
52
+ host: string | undefined;
63
53
  };
64
54
 
65
55
  export function DevImplementation(props: DevProps): JSX.Element {
66
- const apiToken = props.initialMode === "remote" ? getAPIToken() : undefined;
67
- const directory = useTmpDir();
68
-
69
- useCustomBuild(props.entry, props.build);
70
-
71
56
  if (props.public && props.entry.format === "service-worker") {
72
57
  throw new Error(
73
58
  "You cannot use the service-worker format with a `public` directory."
@@ -92,39 +77,16 @@ export function DevImplementation(props: DevProps): JSX.Element {
92
77
  );
93
78
  }
94
79
 
95
- const bundle = useEsbuild({
96
- entry: props.entry,
97
- destination: directory,
98
- staticRoot: props.public,
99
- jsxFactory: props.jsxFactory,
100
- rules: props.rules,
101
- jsxFragment: props.jsxFragment,
102
- serveAssetsFromWorker: !!props.public,
103
- tsconfig: props.tsconfig,
104
- minify: props.minify,
105
- nodeCompat: props.nodeCompat,
106
- });
107
-
108
80
  // only load the UI if we're running in a supported environment
109
81
  const { isRawModeSupported } = useStdin();
110
82
  return isRawModeSupported ? (
111
- <InteractiveDevSession {...props} bundle={bundle} apiToken={apiToken} />
83
+ <InteractiveDevSession {...props} />
112
84
  ) : (
113
- <DevSession
114
- {...props}
115
- bundle={bundle}
116
- apiToken={apiToken}
117
- local={props.initialMode === "local"}
118
- />
85
+ <DevSession {...props} local={props.initialMode === "local"} />
119
86
  );
120
87
  }
121
88
 
122
- type InteractiveDevSessionProps = DevProps & {
123
- apiToken: string | undefined;
124
- bundle: EsbuildBundle | undefined;
125
- };
126
-
127
- function InteractiveDevSession(props: InteractiveDevSessionProps) {
89
+ function InteractiveDevSession(props: DevProps) {
128
90
  const toggles = useHotkeys(
129
91
  {
130
92
  local: props.initialMode === "local",
@@ -157,15 +119,36 @@ function InteractiveDevSession(props: InteractiveDevSessionProps) {
157
119
  );
158
120
  }
159
121
 
160
- type DevSessionProps = InteractiveDevSessionProps & { local: boolean };
122
+ type DevSessionProps = DevProps & {
123
+ local: boolean;
124
+ };
161
125
 
162
126
  function DevSession(props: DevSessionProps) {
127
+ useCustomBuild(props.entry, props.build);
128
+
129
+ const directory = useTmpDir();
130
+
131
+ const bundle = useEsbuild({
132
+ entry: props.entry,
133
+ destination: directory,
134
+ staticRoot: props.public,
135
+ jsxFactory: props.jsxFactory,
136
+ rules: props.rules,
137
+ jsxFragment: props.jsxFragment,
138
+ // In dev for remote mode, we serve --experimental-assets from the local proxy before we send the request to the worker.
139
+ serveAssetsFromWorker: !!props.public && !!props.local,
140
+ tsconfig: props.tsconfig,
141
+ minify: props.minify,
142
+ nodeCompat: props.nodeCompat,
143
+ });
144
+
163
145
  return props.local ? (
164
146
  <Local
165
147
  name={props.name}
166
- bundle={props.bundle}
148
+ bundle={bundle}
167
149
  format={props.entry.format}
168
150
  compatibilityDate={props.compatibilityDate}
151
+ localProtocol={props.localProtocol}
169
152
  compatibilityFlags={props.compatibilityFlags}
170
153
  bindings={props.bindings}
171
154
  assetPaths={props.assetPaths}
@@ -180,10 +163,9 @@ function DevSession(props: DevSessionProps) {
180
163
  ) : (
181
164
  <Remote
182
165
  name={props.name}
183
- bundle={props.bundle}
166
+ bundle={bundle}
184
167
  format={props.entry.format}
185
168
  accountId={props.accountId}
186
- apiToken={props.apiToken}
187
169
  bindings={props.bindings}
188
170
  assetPaths={props.assetPaths}
189
171
  public={props.public}
@@ -197,6 +179,7 @@ function DevSession(props: DevSessionProps) {
197
179
  env={props.env}
198
180
  legacyEnv={props.legacyEnv}
199
181
  zone={props.zone}
182
+ host={props.host}
200
183
  />
201
184
  );
202
185
  }
@@ -228,14 +211,7 @@ function useTmpDir(): string | undefined {
228
211
  return directory?.name;
229
212
  }
230
213
 
231
- function useCustomBuild(
232
- expectedEntry: Entry,
233
- build: {
234
- command?: string | undefined;
235
- cwd?: string | undefined;
236
- watch_dir?: string | undefined;
237
- }
238
- ): void {
214
+ function useCustomBuild(expectedEntry: Entry, build: Config["build"]): void {
239
215
  useEffect(() => {
240
216
  if (!build.command) return;
241
217
  let watcher: ReturnType<typeof watch> | undefined;
package/src/dev/local.tsx CHANGED
@@ -21,6 +21,7 @@ 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
26
  public: string | undefined;
26
27
  port: number;
@@ -54,6 +55,7 @@ function useLocalWorker({
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,11 +84,6 @@ function useLocalWorker({
82
84
  abortSignal: abortController.signal,
83
85
  });
84
86
 
85
- if (publicDirectory) {
86
- throw new Error(
87
- '⎔ A "public" folder is not yet supported in local mode.'
88
- );
89
- }
90
87
  if (bindings.services && bindings.services.length > 0) {
91
88
  throw new Error(
92
89
  "⎔ Service bindings are not yet supported in local mode."
@@ -164,6 +161,7 @@ function useLocalWorker({
164
161
  name: workerName,
165
162
  port,
166
163
  scriptPath,
164
+ https: localProtocol === "https",
167
165
  host: ip,
168
166
  modules: format === "modules",
169
167
  modulesRules: (rules || [])
@@ -251,12 +249,16 @@ function useLocalWorker({
251
249
  process.stdout.write(data);
252
250
  });
253
251
 
252
+ // parse the node inspector url (which may be received in chunks) from stderr
253
+ let stderrData = "";
254
254
  local.current.stderr?.on("data", (data: Buffer) => {
255
+ stderrData += data.toString();
255
256
  const matches =
256
- /Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)/.exec(
257
- data.toString()
257
+ /Debugger listening on (ws:\/\/127\.0\.0\.1:\d+\/[A-Za-z0-9-]+)[\r|\n]/.exec(
258
+ stderrData
258
259
  );
259
260
  if (matches) {
261
+ local.current?.stderr?.removeAllListeners("data");
260
262
  setInspectorUrl(matches[1]);
261
263
  }
262
264
  });
@@ -298,6 +300,7 @@ function useLocalWorker({
298
300
  workerName,
299
301
  format,
300
302
  port,
303
+ localProtocol,
301
304
  ip,
302
305
  bindings.durable_objects?.bindings,
303
306
  bindings.kv_namespaces,
@@ -1,4 +1,3 @@
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";
@@ -7,6 +6,7 @@ import useInspector from "../inspect";
7
6
  import { logger } from "../logger";
8
7
  import { usePreviewServer } from "../proxy";
9
8
  import { syncAssets } from "../sites";
9
+ import { requireApiToken, requireAuth } from "../user";
10
10
  import type { CfPreviewToken } from "../create-worker-preview";
11
11
  import type { AssetPaths } from "../sites";
12
12
  import type { CfModule, CfWorkerInit, CfScriptFormat } from "../worker";
@@ -23,26 +23,24 @@ export function Remote(props: {
23
23
  localProtocol: "https" | "http";
24
24
  inspectorPort: number;
25
25
  accountId: string | undefined;
26
- apiToken: string | undefined;
27
26
  bindings: CfWorkerInit["bindings"];
28
27
  compatibilityDate: string;
29
28
  compatibilityFlags: string[] | undefined;
30
29
  usageModel: "bundled" | "unbound" | undefined;
31
30
  env: string | undefined;
32
31
  legacyEnv: boolean | undefined;
33
- zone: { id: string; host: string } | undefined;
32
+ zone: string | undefined;
33
+ host: string | undefined;
34
34
  }) {
35
- assert(props.accountId, "accountId is required");
36
- assert(props.apiToken, "apiToken is required");
37
35
  const previewToken = useWorker({
38
36
  name: props.name,
39
37
  bundle: props.bundle,
40
38
  format: props.format,
41
39
  modules: props.bundle ? props.bundle.modules : [],
42
40
  accountId: props.accountId,
43
- apiToken: props.apiToken,
44
41
  bindings: props.bindings,
45
42
  assetPaths: props.assetPaths,
43
+ public: props.public,
46
44
  port: props.port,
47
45
  compatibilityDate: props.compatibilityDate,
48
46
  compatibilityFlags: props.compatibilityFlags,
@@ -50,6 +48,7 @@ export function Remote(props: {
50
48
  env: props.env,
51
49
  legacyEnv: props.legacyEnv,
52
50
  zone: props.zone,
51
+ host: props.host,
53
52
  });
54
53
 
55
54
  usePreviewServer({
@@ -73,17 +72,18 @@ export function useWorker(props: {
73
72
  bundle: EsbuildBundle | undefined;
74
73
  format: CfScriptFormat | undefined;
75
74
  modules: CfModule[];
76
- accountId: string;
77
- apiToken: string;
75
+ accountId: string | undefined;
78
76
  bindings: CfWorkerInit["bindings"];
79
77
  assetPaths: AssetPaths | undefined;
78
+ public: string | undefined;
80
79
  port: number;
81
80
  compatibilityDate: string | undefined;
82
81
  compatibilityFlags: string[] | undefined;
83
82
  usageModel: "bundled" | "unbound" | undefined;
84
83
  env: string | undefined;
85
84
  legacyEnv: boolean | undefined;
86
- zone: { id: string; host: string } | undefined;
85
+ zone: string | undefined;
86
+ host: string | undefined;
87
87
  }): CfPreviewToken | undefined {
88
88
  const {
89
89
  name,
@@ -91,7 +91,6 @@ export function useWorker(props: {
91
91
  format,
92
92
  modules,
93
93
  accountId,
94
- apiToken,
95
94
  bindings,
96
95
  assetPaths,
97
96
  compatibilityDate,
@@ -105,6 +104,9 @@ export function useWorker(props: {
105
104
  // something's "happened" in our system; We make a ref and
106
105
  // mark it once we log our initial message. Refs are vars!
107
106
  const startedRef = useRef(false);
107
+ // This ref holds the actual accountId being used, the `accountId` prop could be undefined
108
+ // as it is only what is retrieved from the wrangler.toml config.
109
+ const accountIdRef = useRef(accountId);
108
110
 
109
111
  useEffect(() => {
110
112
  const abortController = new AbortController();
@@ -119,14 +121,19 @@ export function useWorker(props: {
119
121
  logger.log("⎔ Detected changes, restarted server.");
120
122
  }
121
123
 
124
+ // Ensure we have an account id, even if it means logging in here.
125
+ accountIdRef.current = await requireAuth({
126
+ account_id: accountIdRef.current,
127
+ });
128
+
122
129
  const assets = await syncAssets(
123
- accountId,
130
+ accountIdRef.current,
124
131
  // When we're using the newer service environments, we wouldn't
125
132
  // have added the env name on to the script name. However, we must
126
133
  // include it in the kv namespace name regardless (since there's no
127
134
  // concept of service environments for kv namespaces yet).
128
135
  name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
129
- assetPaths,
136
+ props.public ? undefined : assetPaths,
130
137
  true,
131
138
  false
132
139
  ); // TODO: cancellable?
@@ -173,10 +180,15 @@ export function useWorker(props: {
173
180
  await createWorkerPreview(
174
181
  init,
175
182
  {
176
- accountId,
177
- apiToken,
183
+ accountId: accountIdRef.current,
184
+ apiToken: requireApiToken(),
185
+ },
186
+ {
187
+ env: props.env,
188
+ legacyEnv: props.legacyEnv,
189
+ zone: props.zone,
190
+ host: props.host,
178
191
  },
179
- { env: props.env, legacyEnv: props.legacyEnv, zone: props.zone },
180
192
  abortController.signal
181
193
  )
182
194
  );
@@ -193,7 +205,7 @@ export function useWorker(props: {
193
205
  "Error: You need to register a workers.dev subdomain before running the dev command in remote mode";
194
206
  const solutionMessage =
195
207
  "You can either enable local mode by pressing l, or register a workers.dev subdomain here:";
196
- const onboardingLink = `https://dash.cloudflare.com/${accountId}/workers/onboarding`;
208
+ const onboardingLink = `https://dash.cloudflare.com/${accountIdRef.current}/workers/onboarding`;
197
209
  logger.error(
198
210
  `${errorMessage}\n${solutionMessage}\n${onboardingLink}`
199
211
  );
@@ -211,9 +223,9 @@ export function useWorker(props: {
211
223
  bundle,
212
224
  format,
213
225
  accountId,
214
- apiToken,
215
226
  port,
216
227
  assetPaths,
228
+ props.public,
217
229
  compatibilityDate,
218
230
  compatibilityFlags,
219
231
  usageModel,
@@ -222,6 +234,7 @@ export function useWorker(props: {
222
234
  props.env,
223
235
  props.legacyEnv,
224
236
  props.zone,
237
+ props.host,
225
238
  ]);
226
239
  return token;
227
240
  }
@@ -14,7 +14,6 @@ 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({
@@ -67,8 +66,7 @@ export function useEsbuild({
67
66
 
68
67
  const { resolvedEntryPointPath, bundleType, modules, stop } =
69
68
  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,
69
+ serveAssetsFromWorker,
72
70
  jsxFactory,
73
71
  jsxFragment,
74
72
  rules,
@@ -87,7 +85,6 @@ export function useEsbuild({
87
85
  path: resolvedEntryPointPath,
88
86
  type: bundleType,
89
87
  modules,
90
- serveAssetsFromWorker,
91
88
  });
92
89
  }
93
90