wrangler 0.0.13 → 0.0.17

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/bin/wrangler.js +2 -2
  2. package/package.json +20 -11
  3. package/pages/functions/buildWorker.ts +1 -1
  4. package/pages/functions/filepath-routing.test.ts +112 -28
  5. package/pages/functions/filepath-routing.ts +44 -51
  6. package/pages/functions/routes.ts +11 -18
  7. package/pages/functions/template-worker.ts +3 -9
  8. package/src/__tests__/dev.test.tsx +42 -5
  9. package/src/__tests__/guess-worker-format.test.ts +66 -0
  10. package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
  11. package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
  12. package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
  13. package/src/__tests__/helpers/mock-account-id.ts +30 -0
  14. package/src/__tests__/helpers/mock-bin.ts +36 -0
  15. package/src/__tests__/{mock-cfetch.ts → helpers/mock-cfetch.ts} +43 -9
  16. package/src/__tests__/helpers/mock-console.ts +62 -0
  17. package/src/__tests__/{mock-dialogs.ts → helpers/mock-dialogs.ts} +1 -1
  18. package/src/__tests__/helpers/mock-kv.ts +40 -0
  19. package/src/__tests__/helpers/mock-user.ts +27 -0
  20. package/src/__tests__/helpers/mock-web-socket.ts +37 -0
  21. package/src/__tests__/{run-in-tmp.ts → helpers/run-in-tmp.ts} +1 -1
  22. package/src/__tests__/helpers/run-wrangler.ts +16 -0
  23. package/src/__tests__/helpers/write-wrangler-toml.ts +20 -0
  24. package/src/__tests__/index.test.ts +418 -71
  25. package/src/__tests__/jest.setup.ts +30 -2
  26. package/src/__tests__/kv.test.ts +147 -252
  27. package/src/__tests__/logout.test.ts +50 -0
  28. package/src/__tests__/package-manager.test.ts +206 -0
  29. package/src/__tests__/publish.test.ts +1136 -291
  30. package/src/__tests__/r2.test.ts +206 -0
  31. package/src/__tests__/secret.test.ts +210 -0
  32. package/src/__tests__/sentry.test.ts +146 -0
  33. package/src/__tests__/tail.test.ts +246 -0
  34. package/src/__tests__/whoami.test.tsx +6 -47
  35. package/src/api/form_data.ts +75 -25
  36. package/src/api/preview.ts +2 -2
  37. package/src/api/worker.ts +34 -15
  38. package/src/bundle.ts +127 -0
  39. package/src/cfetch/index.ts +7 -15
  40. package/src/cfetch/internal.ts +41 -6
  41. package/src/cli.ts +10 -0
  42. package/src/config.ts +125 -95
  43. package/src/dev.tsx +300 -193
  44. package/src/dialogs.tsx +2 -2
  45. package/src/guess-worker-format.ts +68 -0
  46. package/src/index.tsx +578 -192
  47. package/src/inspect.ts +29 -10
  48. package/src/kv.tsx +23 -17
  49. package/src/module-collection.ts +32 -12
  50. package/src/open-in-browser.ts +13 -0
  51. package/src/package-manager.ts +120 -0
  52. package/src/pages.tsx +28 -23
  53. package/src/paths.ts +26 -0
  54. package/src/proxy.ts +88 -14
  55. package/src/publish.ts +260 -297
  56. package/src/r2.ts +50 -0
  57. package/src/reporting.ts +115 -0
  58. package/src/sites.tsx +28 -27
  59. package/src/tail.tsx +178 -9
  60. package/src/user.tsx +58 -44
  61. package/templates/new-worker.js +15 -0
  62. package/templates/new-worker.ts +15 -0
  63. package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
  64. package/wrangler-dist/cli.js +124315 -104677
  65. package/wrangler-dist/cli.js.map +3 -3
  66. package/src/__tests__/mock-console.ts +0 -34
  67. package/src/__tests__/run-wrangler.ts +0 -8
@@ -1,6 +1,6 @@
1
- import type { RequestInit } from "node-fetch";
2
- import { URLSearchParams } from "url";
1
+ import { URLSearchParams } from "node:url";
3
2
  import { fetchInternal } from "./internal";
3
+ import type { RequestInit } from "undici";
4
4
 
5
5
  // Check out https://api.cloudflare.com/ for API docs.
6
6
 
@@ -18,16 +18,7 @@ export interface FetchResult<ResponseType = unknown> {
18
18
  result_info?: unknown;
19
19
  }
20
20
 
21
- /**
22
- * Make a fetch request for a raw JSON value.
23
- */
24
- export async function fetchRaw<ResponseType>(
25
- resource: string,
26
- init: RequestInit = {},
27
- queryParams?: URLSearchParams
28
- ): Promise<ResponseType> {
29
- return fetchInternal<ResponseType>(resource, init, queryParams);
30
- }
21
+ export { fetchKVGetValue } from "./internal";
31
22
 
32
23
  /**
33
24
  * Make a fetch request, and extract the `result` from the JSON response.
@@ -92,9 +83,9 @@ function throwFetchError(
92
83
  ): never {
93
84
  response.messages.forEach((message) => console.warn(message));
94
85
  const errors = response.errors
95
- .map((error) => `${error.code}: ${error.message}`)
86
+ .map((error) => `- ${error.code}: ${error.message}`)
96
87
  .join("\n");
97
- const error = new Error(`Failed to fetch ${resource} - \n${errors}`);
88
+ const error = new Error(`Failed to fetch ${resource}:\n${errors}`);
98
89
  // add the first error code directly to this error
99
90
  // so consumers can use it for specific behaviour
100
91
  const code = response.errors[0]?.code;
@@ -106,5 +97,6 @@ function throwFetchError(
106
97
  }
107
98
 
108
99
  function hasCursor(result_info: unknown): result_info is { cursor: string } {
109
- return (result_info as { cursor: string } | undefined)?.cursor !== undefined;
100
+ const cursor = (result_info as { cursor: string } | undefined)?.cursor;
101
+ return cursor !== undefined && cursor !== null && cursor !== "";
110
102
  }
@@ -1,6 +1,7 @@
1
- import fetch from "node-fetch";
2
- import type { RequestInit, HeadersInit } from "node-fetch";
1
+ import { fetch } from "undici";
3
2
  import { getAPIToken, loginOrRefreshIfRequired } from "../user";
3
+ import type { URLSearchParams } from "node:url";
4
+ import type { RequestInit, HeadersInit } from "undici";
4
5
 
5
6
  export const CF_API_BASE_URL =
6
7
  process.env.CF_API_BASE_URL || "https://api.cloudflare.com/client/v4";
@@ -30,7 +31,6 @@ export async function fetchInternal<ResponseType>(
30
31
  ...init,
31
32
  headers,
32
33
  });
33
-
34
34
  const jsonText = await response.text();
35
35
  try {
36
36
  const json = JSON.parse(jsonText);
@@ -42,7 +42,9 @@ export async function fetchInternal<ResponseType>(
42
42
  }
43
43
  }
44
44
 
45
- function cloneHeaders(headers: HeadersInit | undefined): HeadersInit {
45
+ function cloneHeaders(
46
+ headers: HeadersInit | undefined
47
+ ): Record<string, string> {
46
48
  return { ...headers };
47
49
  }
48
50
 
@@ -61,11 +63,44 @@ function requireApiToken(): string {
61
63
  return apiToken;
62
64
  }
63
65
 
64
- function addAuthorizationHeader(headers: HeadersInit, apiToken: string): void {
65
- if (headers["Authorization"]) {
66
+ function addAuthorizationHeader(
67
+ headers: Record<string, string>,
68
+ apiToken: string
69
+ ): void {
70
+ if ("Authorization" in headers) {
66
71
  throw new Error(
67
72
  "The request already specifies an authorisation header - cannot add a new one."
68
73
  );
69
74
  }
70
75
  headers["Authorization"] = `Bearer ${apiToken}`;
71
76
  }
77
+
78
+ /**
79
+ * The implementation for fetching a kv value from the cloudflare API.
80
+ * We special-case this one call, because it's the only API call that
81
+ * doesn't return json. We inline the implementation and try not to share
82
+ * any code with the other calls. We should push back on any new APIs that
83
+ * try to introduce non-"standard" response structures.
84
+ */
85
+
86
+ export async function fetchKVGetValue(
87
+ accountId: string,
88
+ namespaceId: string,
89
+ key: string
90
+ ): Promise<string> {
91
+ await requireLoggedIn();
92
+ const apiToken = requireApiToken();
93
+ const headers = { Authorization: `Bearer ${apiToken}` };
94
+ const resource = `${CF_API_BASE_URL}/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${key}`;
95
+ const response = await fetch(resource, {
96
+ method: "GET",
97
+ headers,
98
+ });
99
+ if (response.ok) {
100
+ return await response.text();
101
+ } else {
102
+ throw new Error(
103
+ `Failed to fetch ${resource} - ${response.status}: ${response.statusText});`
104
+ );
105
+ }
106
+ }
package/src/cli.ts CHANGED
@@ -1,6 +1,16 @@
1
+ import process from "process";
1
2
  import { hideBin } from "yargs/helpers";
3
+ import { reportError, initReporting } from "./reporting";
2
4
  import { main } from ".";
3
5
 
6
+ try {
7
+ initReporting();
8
+ } catch (error) {}
9
+
10
+ process.on("uncaughtExceptionMonitor", async (err, origin) => {
11
+ await reportError(err, origin);
12
+ });
13
+
4
14
  main(hideBin(process.argv)).catch(() => {
5
15
  // The logging of any error that was thrown from `main()` is handled in the `yargs.fail()` handler.
6
16
  // Here we just want to ensure that the process exits with a non-zero code.
package/src/config.ts CHANGED
@@ -39,7 +39,7 @@ export type Config = {
39
39
  * @inherited
40
40
  * @todo this needs to be implemented!
41
41
  */
42
- entry?: string;
42
+ main?: string;
43
43
 
44
44
  /**
45
45
  * This is the ID of the account associated with your zone.
@@ -154,15 +154,14 @@ export type Config = {
154
154
 
155
155
  /**
156
156
  * A map of environment variables to set when deploying your worker.
157
- * Of note, they can only be strings. Which is unfortunate, really.
158
- * (TODO: verify that they can only be strings?)
157
+ *
159
158
  * NB: these are not inherited, and HAVE to be duplicated across all environments.
160
159
  *
161
160
  * @default `{}`
162
161
  * @optional
163
162
  * @inherited false
164
163
  */
165
- vars?: { [key: string]: string };
164
+ vars?: { [key: string]: unknown };
166
165
 
167
166
  /**
168
167
  * A list of durable objects that your worker should be bound to.
@@ -204,12 +203,22 @@ export type Config = {
204
203
  preview_id?: string;
205
204
  }[];
206
205
 
206
+ r2_buckets?: {
207
+ /** The binding name used to refer to the R2 bucket in the worker. */
208
+ binding: string;
209
+ /** The name of this R2 bucket at the edge. */
210
+ bucket_name: string;
211
+ /** The preview name of this R2 bucket at the edge. */
212
+ preview_bucket_name?: string;
213
+ }[];
214
+
207
215
  /**
208
216
  * A list of services that your worker should be bound to.
209
217
  * NB: these are not inherited, and HAVE to be duplicated across all environments.
210
218
  *
211
219
  * @default `[]`
212
220
  * @optional
221
+ * @deprecated DO NOT USE. We'd added this to test the new service binding system, but the proper way to test experimental features is to use `unsafe.bindings` configuration.
213
222
  * @inherited false
214
223
  */
215
224
  experimental_services?: {
@@ -221,11 +230,43 @@ export type Config = {
221
230
  environment: string;
222
231
  }[];
223
232
 
233
+ /**
234
+ * A list of wasm modules that your worker should be bound to. This is
235
+ * the "legacy" way of binding to a wasm module. ES module workers should
236
+ * do proper module imports.
237
+ * NB: these ARE NOT inherited, and SHOULD NOT be duplicated across all environments.
238
+ */
239
+ wasm_modules?: {
240
+ [key: string]: string;
241
+ };
242
+
243
+ /**
244
+ * "Unsafe" tables for features that aren't directly supported by wrangler.
245
+ * NB: these are not inherited, and HAVE to be duplicated across all environments.
246
+ *
247
+ * @default `[]`
248
+ * @optional
249
+ * @inherited false
250
+ */
251
+ unsafe?: {
252
+ /**
253
+ * A set of bindings that should be put into a Worker's upload metadata without changes. These
254
+ * can be used to implement bindings for features that haven't released and aren't supported
255
+ * directly by wrangler or miniflare.
256
+ */
257
+ bindings?: {
258
+ name: string;
259
+ type: string;
260
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
261
+ [key: string]: any;
262
+ }[];
263
+ };
264
+
224
265
  /**
225
266
  * A list of migrations that should be uploaded with your Worker.
226
267
  * These define changes in your Durable Object declarations.
227
268
  * More details at https://developers.cloudflare.com/workers/learning/using-durable-objects#configuring-durable-object-classes-with-migrations
228
- * NB: these ARE inherited, and SHOULD NOT be duplicated across all environments.
269
+ * NB: these ARE NOT inherited, and SHOULD NOT be duplicated across all environments.
229
270
  *
230
271
  * @default `[]`
231
272
  * @optional
@@ -249,7 +290,7 @@ export type Config = {
249
290
  * The definition of a Worker Site, a feature that lets you upload
250
291
  * static assets with your Worker.
251
292
  * More details at https://developers.cloudflare.com/workers/platform/sites
252
- * NB: This IS inherited, and SHOULD NOT be duplicated across all environments.
293
+ * NB: This IS NOT inherited, and SHOULD NOT be duplicated across all environments.
253
294
  *
254
295
  * @default `undefined`
255
296
  * @optional
@@ -268,7 +309,7 @@ export type Config = {
268
309
  /**
269
310
  * The location of your Worker script.
270
311
  *
271
- * @deprecated DO NOT use this (it's a holdover from wrangler 1.x). Either use the top level `entry` field, or pass the path to your entry file as a command line argument.
312
+ * @deprecated DO NOT use this (it's a holdover from wrangler 1.x). Either use the top level `main` field, or pass the path to your entry file as a command line argument.
272
313
  * @breaking
273
314
  */
274
315
  "entry-point"?: string;
@@ -377,75 +418,57 @@ export type Config = {
377
418
  * Much of the rest of this configuration isn't necessary anymore
378
419
  * in wrangler2. We infer the format automatically, and we can pass
379
420
  * the path to the script either in the CLI (or, @todo, as the top level
380
- * `entry` property).
381
- */ (
382
- | {
383
- upload?: {
384
- /**
385
- * The format of the Worker script, must be "service-worker".
386
- *
387
- * @deprecated We infer the format automatically now.
388
- */
389
- format?: "service-worker";
390
-
391
- /**
392
- * The path to the Worker script. This should be replaced
393
- * by the top level `entry' property.
394
- *
395
- * @deprecated This will be replaced by the top level `entry' property.
396
- */
397
- main: string;
398
- };
399
- }
400
- | {
401
- /**
402
- * When we use the module format, we only really
403
- * need to specify the entry point. The format is deduced
404
- * automatically in wrangler2.
405
- */
406
- upload?: {
407
- /**
408
- * The format of the Worker script, must be "modules".
409
- *
410
- * @deprecated We infer the format automatically now.
411
- */
412
- format?: "modules";
413
-
414
- /**
415
- * The directory you wish to upload your modules from,
416
- * defaults to the dist relative to the project root directory.
417
- *
418
- * @deprecated
419
- * @breaking
420
- */
421
- dir?: string;
422
-
423
- /**
424
- * The path to the Worker script. This should be replaced
425
- * by the top level `entry' property.
426
- *
427
- * @deprecated This will be replaced by the top level `entry' property.
428
- */
429
- main?: string;
430
-
431
- /**
432
- * An ordered list of rules that define which modules to import,
433
- * and what type to import them as. You will need to specify rules
434
- * to use Text, Data, and CompiledWasm modules, or when you wish to
435
- * have a .js file be treated as an ESModule instead of CommonJS.
436
- *
437
- * @deprecated These are now inferred automatically for major file types, but you can still specify them manually.
438
- * @todo this needs to be implemented!
439
- * @breaking
440
- */
441
- rules?: {
442
- type: "ESModule" | "CommonJS" | "Text" | "Data" | "CompiledWasm";
443
- globs: string[];
444
- fallthrough?: boolean;
445
- };
446
- };
447
- }
448
- );
421
+ * `main` property).
422
+ */ {
423
+ /**
424
+ * We only really need to specify the entry point.
425
+ * The format is deduced automatically in wrangler2.
426
+ *
427
+ * @deprecated Instead use top level main
428
+ */
429
+ upload?: {
430
+ /**
431
+ * The format of the Worker script.
432
+ *
433
+ * @deprecated We infer the format automatically now.
434
+ */
435
+ format?: "modules" | "service-worker";
436
+
437
+ /**
438
+ * The directory you wish to upload your worker from,
439
+ * relative to the wrangler.toml file.
440
+ *
441
+ * Defaults to the directory containing the wrangler.toml file.
442
+ *
443
+ * @deprecated
444
+ * @breaking In wrangler 1, this defaults to ./dist, whereas in wrangler 2 it defaults to ./
445
+ */
446
+ dir?: string;
447
+
448
+ /**
449
+ * The path to the Worker script, relative to `upload.dir`.
450
+ *
451
+ * @deprecated This will be replaced by a command line argument.
452
+ */
453
+ main?: string;
454
+
455
+ /**
456
+ * An ordered list of rules that define which modules to import,
457
+ * and what type to import them as. You will need to specify rules
458
+ * to use Text, Data, and CompiledWasm modules, or when you wish to
459
+ * have a .js file be treated as an ESModule instead of CommonJS.
460
+ *
461
+ * @deprecated These are now inferred automatically for major file types, but you can still specify them manually.
462
+ * @todo this needs to be implemented!
463
+ * @breaking
464
+ */
465
+ rules?: {
466
+ type: "ESModule" | "CommonJS" | "Text" | "Data" | "CompiledWasm";
467
+ globs: string[];
468
+ fallthrough?: boolean;
469
+ };
470
+ };
471
+ };
449
472
 
450
473
  /**
451
474
  * The `env` section defines overrides for the configuration for
@@ -456,7 +479,7 @@ export type Config = {
456
479
  env?: {
457
480
  [envName: string]:
458
481
  | undefined
459
- | Omit<Config, "env" | "migrations" | "site" | "dev">;
482
+ | Omit<Config, "env" | "wasm_modules" | "migrations" | "site" | "dev">;
460
483
  };
461
484
  };
462
485
 
@@ -512,7 +535,13 @@ export function normaliseAndValidateEnvironmentsConfig(config: Config) {
512
535
  );
513
536
 
514
537
  // Fall back on "inherited fields" from the config, if not specified in the environment.
515
- const inheritedFields = [
538
+
539
+ type InheritedField = keyof Omit<
540
+ Config,
541
+ "env" | "migrations" | "wasm_modules" | "site" | "dev"
542
+ >;
543
+
544
+ const inheritedFields: InheritedField[] = [
516
545
  "name",
517
546
  "account_id",
518
547
  "workers_dev",
@@ -523,20 +552,21 @@ export function normaliseAndValidateEnvironmentsConfig(config: Config) {
523
552
  "route",
524
553
  "jsx_factory",
525
554
  "jsx_fragment",
526
- "site",
527
555
  "triggers",
528
556
  "usage_model",
529
557
  ];
558
+
530
559
  for (const inheritedField of inheritedFields) {
531
560
  if (config[inheritedField] !== undefined) {
532
561
  if (environment[inheritedField] === undefined) {
533
- environment[inheritedField] = config[inheritedField]; // TODO: - shallow or deep copy?
562
+ (environment[inheritedField] as typeof environment[InheritedField]) =
563
+ config[inheritedField]; // TODO: - shallow or deep copy?
534
564
  }
535
565
  }
536
566
  }
537
567
 
538
568
  // Warn if there is a "required" field in the top level config that has not been specified specified in the environment.
539
- // These required fields are `vars`, `durable_objects`, `kv_namespaces` and `experimental_services`.
569
+ // These required fields are `vars`, `durable_objects`, `kv_namespaces`, and "r2_buckets".
540
570
  // Each of them has different characteristics that need to be checked.
541
571
 
542
572
  // `vars` is just an object
@@ -612,26 +642,26 @@ export function normaliseAndValidateEnvironmentsConfig(config: Config) {
612
642
  }
613
643
  }
614
644
 
615
- // `experimental_services` contains an array of namespace bindings
616
- if (config.experimental_services !== undefined) {
617
- if (environment.experimental_services === undefined) {
645
+ // `r2_buckets` contains an array of bucket bindings
646
+ if (config.r2_buckets !== undefined) {
647
+ if (environment.r2_buckets === undefined) {
618
648
  console.warn(
619
- `In your configuration, "experimental_services" exists at the top level, but not on "env.${envKey}".\n` +
620
- `This is not what you probably want, since "experimental_services" is not inherited by environments.\n` +
621
- `Please add "experimental_services" to "env.${envKey}".`
649
+ `In your configuration, "r2_buckets" exists at the top level, but not on "env.${envKey}".\n` +
650
+ `This is not what you probably want, since "r2_buckets" is not inherited by environments.\n` +
651
+ `Please add "r2_buckets" to "env.${envKey}".`
622
652
  );
623
653
  } else {
624
- const envBindingNames = new Set(
625
- environment.experimental_services.map((service) => service.name)
654
+ const envBindings = new Set(
655
+ environment.r2_buckets.map((r2Bucket) => r2Bucket.binding)
626
656
  );
627
- for (const bindingName of config.experimental_services.map(
628
- (service) => service.name
657
+ for (const bindingName of config.r2_buckets.map(
658
+ (r2Bucket) => r2Bucket.binding
629
659
  )) {
630
- if (!envBindingNames.has(bindingName)) {
660
+ if (!envBindings.has(bindingName)) {
631
661
  console.warn(
632
- `In your configuration, there is a experimental_services with binding name "${bindingName}" at the top level, but not on "env.${envKey}".\n` +
633
- `This is not what you probably want, since "experimental_services" is not inherited by environments.\n` +
634
- `Please add a service for "${bindingName}" to "env.${envKey}.experimental_services".`
662
+ `In your configuration, there is a r2_buckets with binding "${bindingName}" at the top level, but not on "env.${envKey}".\n` +
663
+ `This is not what you probably want, since "r2_buckets" is not inherited by environments.\n` +
664
+ `Please add a binding for "${bindingName}" to "env.${envKey}.r2_buckets".`
635
665
  );
636
666
  }
637
667
  }