wrangler 2.0.9 → 2.0.14

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 (61) hide show
  1. package/kv-asset-handler.js +1 -0
  2. package/package.json +4 -2
  3. package/src/__tests__/configuration.test.ts +255 -142
  4. package/src/__tests__/dev.test.tsx +27 -0
  5. package/src/__tests__/index.test.ts +2 -1
  6. package/src/__tests__/jest.setup.ts +30 -0
  7. package/src/__tests__/publish.test.ts +393 -160
  8. package/src/__tests__/user.test.ts +1 -0
  9. package/src/bundle.ts +9 -5
  10. package/src/config/environment.ts +1 -1
  11. package/src/config/validation-helpers.ts +10 -1
  12. package/src/config/validation.ts +22 -13
  13. package/src/dev/dev.tsx +29 -45
  14. package/src/dev/local.tsx +10 -7
  15. package/src/dev/remote.tsx +4 -1
  16. package/src/dev/use-esbuild.ts +1 -4
  17. package/src/generate-auth-url.ts +33 -0
  18. package/src/generate-random-state.ts +16 -0
  19. package/src/index.tsx +234 -179
  20. package/src/open-in-browser.ts +1 -3
  21. package/src/pages.tsx +295 -240
  22. package/src/parse.ts +2 -1
  23. package/src/proxy.ts +19 -6
  24. package/src/publish.ts +6 -1
  25. package/src/sites.tsx +49 -18
  26. package/src/user.tsx +12 -24
  27. package/templates/static-asset-facade.js +2 -6
  28. package/wrangler-dist/cli.js +73627 -73462
  29. package/vendor/@cloudflare/kv-asset-handler/CHANGELOG.md +0 -332
  30. package/vendor/@cloudflare/kv-asset-handler/LICENSE_APACHE +0 -176
  31. package/vendor/@cloudflare/kv-asset-handler/LICENSE_MIT +0 -25
  32. package/vendor/@cloudflare/kv-asset-handler/README.md +0 -245
  33. package/vendor/@cloudflare/kv-asset-handler/dist/index.d.ts +0 -32
  34. package/vendor/@cloudflare/kv-asset-handler/dist/index.js +0 -354
  35. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.d.ts +0 -13
  36. package/vendor/@cloudflare/kv-asset-handler/dist/mocks.js +0 -148
  37. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.d.ts +0 -1
  38. package/vendor/@cloudflare/kv-asset-handler/dist/test/getAssetFromKV.js +0 -436
  39. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.d.ts +0 -1
  40. package/vendor/@cloudflare/kv-asset-handler/dist/test/mapRequestToAsset.js +0 -40
  41. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.d.ts +0 -1
  42. package/vendor/@cloudflare/kv-asset-handler/dist/test/serveSinglePageApp.js +0 -42
  43. package/vendor/@cloudflare/kv-asset-handler/dist/types.d.ts +0 -26
  44. package/vendor/@cloudflare/kv-asset-handler/dist/types.js +0 -31
  45. package/vendor/@cloudflare/kv-asset-handler/package.json +0 -52
  46. package/vendor/@cloudflare/kv-asset-handler/src/index.ts +0 -296
  47. package/vendor/@cloudflare/kv-asset-handler/src/mocks.ts +0 -136
  48. package/vendor/@cloudflare/kv-asset-handler/src/test/getAssetFromKV.ts +0 -464
  49. package/vendor/@cloudflare/kv-asset-handler/src/test/mapRequestToAsset.ts +0 -33
  50. package/vendor/@cloudflare/kv-asset-handler/src/test/serveSinglePageApp.ts +0 -42
  51. package/vendor/@cloudflare/kv-asset-handler/src/types.ts +0 -39
  52. package/vendor/wrangler-mime/CHANGELOG.md +0 -289
  53. package/vendor/wrangler-mime/LICENSE +0 -21
  54. package/vendor/wrangler-mime/Mime.js +0 -97
  55. package/vendor/wrangler-mime/README.md +0 -187
  56. package/vendor/wrangler-mime/cli.js +0 -46
  57. package/vendor/wrangler-mime/index.js +0 -4
  58. package/vendor/wrangler-mime/lite.js +0 -4
  59. package/vendor/wrangler-mime/package.json +0 -52
  60. package/vendor/wrangler-mime/types/other.js +0 -1
  61. package/vendor/wrangler-mime/types/standard.js +0 -1
@@ -46,6 +46,7 @@ describe("User", () => {
46
46
 
47
47
  expect(std.out).toMatchInlineSnapshot(`
48
48
  "Attempting to login via OAuth...
49
+ Opening a link in your default browser: https://dash.cloudflare.com/oauth2/auth?response_type=code&client_id=54d11594-84e4-41aa-b438-e81b8fa78ee7&redirect_uri=http%3A%2F%2Flocalhost%3A8976%2Foauth%2Fcallback&scope=account%3Aread%20user%3Aread%20workers%3Awrite%20workers_kv%3Awrite%20workers_routes%3Awrite%20workers_scripts%3Awrite%20workers_tail%3Aread%20pages%3Awrite%20zone%3Aread%20offline_access&state=MOCK_STATE_PARAM&code_challenge=MOCK_CODE_CHALLENGE&code_challenge_method=S256
49
50
  Successfully logged in."
50
51
  `);
51
52
 
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] };
@@ -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,
package/src/dev/dev.tsx CHANGED
@@ -20,7 +20,6 @@ import type { Config } from "../config";
20
20
  import type { Entry } from "../entry";
21
21
  import type { AssetPaths } from "../sites";
22
22
  import type { CfWorkerInit } from "../worker";
23
- import type { EsbuildBundle } from "./use-esbuild";
24
23
 
25
24
  export type DevProps = {
26
25
  name?: string;
@@ -46,11 +45,7 @@ export type DevProps = {
46
45
  usageModel: "bundled" | "unbound" | undefined;
47
46
  minify: boolean | undefined;
48
47
  nodeCompat: boolean | undefined;
49
- build: {
50
- command?: string | undefined;
51
- cwd?: string | undefined;
52
- watch_dir?: string | undefined;
53
- };
48
+ build: Config["build"];
54
49
  env: string | undefined;
55
50
  legacyEnv: boolean;
56
51
  zone: string | undefined;
@@ -58,10 +53,6 @@ export type DevProps = {
58
53
  };
59
54
 
60
55
  export function DevImplementation(props: DevProps): JSX.Element {
61
- const directory = useTmpDir();
62
-
63
- useCustomBuild(props.entry, props.build);
64
-
65
56
  if (props.public && props.entry.format === "service-worker") {
66
57
  throw new Error(
67
58
  "You cannot use the service-worker format with a `public` directory."
@@ -86,37 +77,16 @@ export function DevImplementation(props: DevProps): JSX.Element {
86
77
  );
87
78
  }
88
79
 
89
- const bundle = useEsbuild({
90
- entry: props.entry,
91
- destination: directory,
92
- staticRoot: props.public,
93
- jsxFactory: props.jsxFactory,
94
- rules: props.rules,
95
- jsxFragment: props.jsxFragment,
96
- serveAssetsFromWorker: !!props.public,
97
- tsconfig: props.tsconfig,
98
- minify: props.minify,
99
- nodeCompat: props.nodeCompat,
100
- });
101
-
102
80
  // only load the UI if we're running in a supported environment
103
81
  const { isRawModeSupported } = useStdin();
104
82
  return isRawModeSupported ? (
105
- <InteractiveDevSession {...props} bundle={bundle} />
83
+ <InteractiveDevSession {...props} />
106
84
  ) : (
107
- <DevSession
108
- {...props}
109
- bundle={bundle}
110
- local={props.initialMode === "local"}
111
- />
85
+ <DevSession {...props} local={props.initialMode === "local"} />
112
86
  );
113
87
  }
114
88
 
115
- type InteractiveDevSessionProps = DevProps & {
116
- bundle: EsbuildBundle | undefined;
117
- };
118
-
119
- function InteractiveDevSession(props: InteractiveDevSessionProps) {
89
+ function InteractiveDevSession(props: DevProps) {
120
90
  const toggles = useHotkeys(
121
91
  {
122
92
  local: props.initialMode === "local",
@@ -149,15 +119,36 @@ function InteractiveDevSession(props: InteractiveDevSessionProps) {
149
119
  );
150
120
  }
151
121
 
152
- type DevSessionProps = InteractiveDevSessionProps & { local: boolean };
122
+ type DevSessionProps = DevProps & {
123
+ local: boolean;
124
+ };
153
125
 
154
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
+
155
145
  return props.local ? (
156
146
  <Local
157
147
  name={props.name}
158
- bundle={props.bundle}
148
+ bundle={bundle}
159
149
  format={props.entry.format}
160
150
  compatibilityDate={props.compatibilityDate}
151
+ localProtocol={props.localProtocol}
161
152
  compatibilityFlags={props.compatibilityFlags}
162
153
  bindings={props.bindings}
163
154
  assetPaths={props.assetPaths}
@@ -172,7 +163,7 @@ function DevSession(props: DevSessionProps) {
172
163
  ) : (
173
164
  <Remote
174
165
  name={props.name}
175
- bundle={props.bundle}
166
+ bundle={bundle}
176
167
  format={props.entry.format}
177
168
  accountId={props.accountId}
178
169
  bindings={props.bindings}
@@ -220,14 +211,7 @@ function useTmpDir(): string | undefined {
220
211
  return directory?.name;
221
212
  }
222
213
 
223
- function useCustomBuild(
224
- expectedEntry: Entry,
225
- build: {
226
- command?: string | undefined;
227
- cwd?: string | undefined;
228
- watch_dir?: string | undefined;
229
- }
230
- ): void {
214
+ function useCustomBuild(expectedEntry: Entry, build: Config["build"]): void {
231
215
  useEffect(() => {
232
216
  if (!build.command) return;
233
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,
@@ -40,6 +40,7 @@ export function Remote(props: {
40
40
  accountId: props.accountId,
41
41
  bindings: props.bindings,
42
42
  assetPaths: props.assetPaths,
43
+ public: props.public,
43
44
  port: props.port,
44
45
  compatibilityDate: props.compatibilityDate,
45
46
  compatibilityFlags: props.compatibilityFlags,
@@ -74,6 +75,7 @@ export function useWorker(props: {
74
75
  accountId: string | undefined;
75
76
  bindings: CfWorkerInit["bindings"];
76
77
  assetPaths: AssetPaths | undefined;
78
+ public: string | undefined;
77
79
  port: number;
78
80
  compatibilityDate: string | undefined;
79
81
  compatibilityFlags: string[] | undefined;
@@ -131,7 +133,7 @@ export function useWorker(props: {
131
133
  // include it in the kv namespace name regardless (since there's no
132
134
  // concept of service environments for kv namespaces yet).
133
135
  name + (!props.legacyEnv && props.env ? `-${props.env}` : ""),
134
- assetPaths,
136
+ props.public ? undefined : assetPaths,
135
137
  true,
136
138
  false
137
139
  ); // TODO: cancellable?
@@ -223,6 +225,7 @@ export function useWorker(props: {
223
225
  accountId,
224
226
  port,
225
227
  assetPaths,
228
+ props.public,
226
229
  compatibilityDate,
227
230
  compatibilityFlags,
228
231
  usageModel,
@@ -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
 
@@ -0,0 +1,33 @@
1
+ interface GenerateAuthUrlProps {
2
+ authUrl: string;
3
+ clientId: string;
4
+ callbackUrl: string;
5
+ scopes: string[];
6
+ stateQueryParam: string;
7
+ codeChallenge: string;
8
+ }
9
+
10
+ /**
11
+ * generateAuthUrl was extracted from getAuthURL in user.tsx
12
+ * to make it possible to mock the generated URL
13
+ */
14
+ export const generateAuthUrl = ({
15
+ authUrl,
16
+ clientId,
17
+ callbackUrl,
18
+ scopes,
19
+ stateQueryParam,
20
+ codeChallenge,
21
+ }: GenerateAuthUrlProps) => {
22
+ return (
23
+ authUrl +
24
+ `?response_type=code&` +
25
+ `client_id=${encodeURIComponent(clientId)}&` +
26
+ `redirect_uri=${encodeURIComponent(callbackUrl)}&` +
27
+ // we add offline_access manually for every request
28
+ `scope=${encodeURIComponent([...scopes, "offline_access"].join(" "))}&` +
29
+ `state=${stateQueryParam}&` +
30
+ `code_challenge=${encodeURIComponent(codeChallenge)}&` +
31
+ `code_challenge_method=S256`
32
+ );
33
+ };
@@ -0,0 +1,16 @@
1
+ import { webcrypto as crypto } from "node:crypto";
2
+ import { PKCE_CHARSET } from "./user";
3
+
4
+ /**
5
+ * Generates random state to be passed for anti-csrf.
6
+ * extracted from user.tsx to make it possible to
7
+ * mock the generated URL
8
+ */
9
+ export function generateRandomState(lengthOfState: number): string {
10
+ const output = new Uint32Array(lengthOfState);
11
+ // @ts-expect-error crypto's types aren't there yet
12
+ crypto.getRandomValues(output);
13
+ return Array.from(output)
14
+ .map((num: number) => PKCE_CHARSET[num % PKCE_CHARSET.length])
15
+ .join("");
16
+ }