wrangler 2.1.14 → 2.2.0

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 (75) hide show
  1. package/miniflare-dist/index.mjs +3 -1
  2. package/package.json +2 -1
  3. package/src/__tests__/access.test.ts +25 -0
  4. package/src/__tests__/api-dev.test.ts +1 -1
  5. package/src/__tests__/api-devregistry.test.js +2 -2
  6. package/src/__tests__/configuration.test.ts +119 -2
  7. package/src/__tests__/d1.test.ts +2 -0
  8. package/src/__tests__/deployments.test.ts +22 -22
  9. package/src/__tests__/dev.test.tsx +167 -15
  10. package/src/__tests__/helpers/msw/handlers/access.ts +13 -0
  11. package/src/__tests__/helpers/msw/handlers/deployments.ts +22 -43
  12. package/src/__tests__/helpers/msw/handlers/zones.ts +22 -0
  13. package/src/__tests__/helpers/msw/index.ts +4 -0
  14. package/src/__tests__/index.test.ts +42 -33
  15. package/src/__tests__/init.test.ts +88 -4
  16. package/src/__tests__/jest.setup.ts +11 -0
  17. package/src/__tests__/kv.test.ts +400 -400
  18. package/src/__tests__/pages.test.ts +140 -28
  19. package/src/__tests__/publish.test.ts +1161 -647
  20. package/src/__tests__/pubsub.test.ts +3 -0
  21. package/src/__tests__/queues.test.ts +371 -0
  22. package/src/__tests__/r2.test.ts +57 -52
  23. package/src/__tests__/worker-namespace.test.ts +15 -10
  24. package/src/bundle-reporter.tsx +41 -2
  25. package/src/bundle.ts +59 -30
  26. package/src/cli.ts +0 -1
  27. package/src/config/environment.ts +50 -0
  28. package/src/config/index.ts +41 -0
  29. package/src/config/validation.ts +173 -0
  30. package/src/create-worker-preview.ts +10 -3
  31. package/src/create-worker-upload-form.ts +12 -0
  32. package/src/d1/backups.tsx +11 -5
  33. package/src/d1/execute.tsx +52 -47
  34. package/src/d1/index.ts +2 -1
  35. package/src/delete.ts +7 -10
  36. package/src/deployments.ts +73 -0
  37. package/src/deprecated/index.ts +9 -24
  38. package/src/dev/dev-vars.ts +11 -8
  39. package/src/dev/dev.tsx +12 -0
  40. package/src/dev/local.tsx +26 -0
  41. package/src/dev/remote.tsx +2 -0
  42. package/src/dev/start-server.ts +7 -0
  43. package/src/dev/use-esbuild.ts +12 -5
  44. package/src/dev.tsx +12 -9
  45. package/src/dispatch-namespace.ts +4 -3
  46. package/src/index.tsx +61 -45
  47. package/src/init.ts +4 -4
  48. package/src/inspect.ts +21 -1
  49. package/src/is-interactive.ts +4 -0
  50. package/src/kv/index.ts +5 -54
  51. package/src/logger.ts +12 -0
  52. package/src/pages/constants.ts +2 -0
  53. package/src/pages/upload.tsx +42 -15
  54. package/src/proxy.ts +38 -6
  55. package/src/publish/index.ts +11 -8
  56. package/src/publish/publish.ts +151 -30
  57. package/src/pubsub/pubsub-commands.tsx +3 -2
  58. package/src/queues/cli/commands/consumer/add.ts +71 -0
  59. package/src/queues/cli/commands/consumer/index.ts +22 -0
  60. package/src/queues/cli/commands/consumer/remove.ts +38 -0
  61. package/src/queues/cli/commands/create.ts +25 -0
  62. package/src/queues/cli/commands/delete.ts +26 -0
  63. package/src/queues/cli/commands/index.ts +33 -0
  64. package/src/queues/cli/commands/list.ts +25 -0
  65. package/src/queues/client.ts +135 -0
  66. package/src/secret/index.ts +14 -39
  67. package/src/tail/index.ts +5 -8
  68. package/src/user/access.ts +69 -0
  69. package/src/worker.ts +7 -0
  70. package/src/yargs-types.ts +15 -2
  71. package/src/zones.ts +31 -5
  72. package/templates/pages-template-plugin.ts +4 -0
  73. package/templates/pages-template-worker.ts +21 -4
  74. package/wrangler-dist/cli.d.ts +42 -0
  75. package/wrangler-dist/cli.js +4559 -3228
package/src/dev.tsx CHANGED
@@ -31,6 +31,7 @@ import type { Config, Environment } from "./config";
31
31
  import type { Route } from "./config/environment";
32
32
  import type { EnablePagesAssetsServiceBindingOptions } from "./miniflare-cli";
33
33
  import type { CfWorkerInit } from "./worker";
34
+ import type { CommonYargsOptions } from "./yargs-types";
34
35
  import type { Argv, ArgumentsCamelCase } from "yargs";
35
36
 
36
37
  interface DevArgs {
@@ -79,7 +80,7 @@ interface DevArgs {
79
80
  "test-scheduled"?: boolean;
80
81
  }
81
82
 
82
- export function devOptions(yargs: Argv): Argv<DevArgs> {
83
+ export function devOptions(yargs: Argv<CommonYargsOptions>): Argv<DevArgs> {
83
84
  return (
84
85
  yargs
85
86
  .positional("script", {
@@ -111,12 +112,6 @@ export function devOptions(yargs: Argv): Argv<DevArgs> {
111
112
  hidden: true,
112
113
  deprecated: true,
113
114
  })
114
- .option("env", {
115
- describe: "Perform on a specific environment",
116
- type: "string",
117
- requiresArg: true,
118
- alias: "e",
119
- })
120
115
  .option("compatibility-date", {
121
116
  describe: "Date to use for compatibility checks",
122
117
  type: "string",
@@ -455,6 +450,7 @@ export async function startDev(args: StartDevOptions) {
455
450
  usageModel={configParam.usage_model}
456
451
  bindings={bindings}
457
452
  crons={configParam.triggers.crons}
453
+ queueConsumers={configParam.queues.consumers}
458
454
  logPrefix={args.logPrefix}
459
455
  onReady={args.onReady}
460
456
  inspect={args.inspect ?? true}
@@ -574,6 +570,7 @@ export async function startApiDev(args: StartDevOptions) {
574
570
  usageModel: configParam.usage_model,
575
571
  bindings: bindings,
576
572
  crons: configParam.triggers.crons,
573
+ queueConsumers: configParam.queues.consumers,
577
574
  logPrefix: args.logPrefix,
578
575
  onReady: args.onReady,
579
576
  inspect: args.inspect ?? true,
@@ -763,7 +760,7 @@ async function getBindingsAndAssetPaths(
763
760
  const cliVars = collectKeyValues(args.var);
764
761
 
765
762
  // now log all available bindings into the terminal
766
- const bindings = await getBindings(configParam, {
763
+ const bindings = await getBindings(configParam, args.env, {
767
764
  kv: args.kv,
768
765
  vars: { ...args.vars, ...cliVars },
769
766
  durableObjects: args.durableObjects,
@@ -792,6 +789,7 @@ async function getBindingsAndAssetPaths(
792
789
 
793
790
  async function getBindings(
794
791
  configParam: Config,
792
+ env: string | undefined,
795
793
  args: AdditionalDevProps
796
794
  ): Promise<CfWorkerInit["bindings"]> {
797
795
  const bindings = {
@@ -821,7 +819,7 @@ async function getBindings(
821
819
  ],
822
820
  // Use a copy of combinedVars since we're modifying it later
823
821
  vars: {
824
- ...getVarsForDev(configParam),
822
+ ...getVarsForDev(configParam, env),
825
823
  ...args.vars,
826
824
  },
827
825
  wasm_modules: configParam.wasm_modules,
@@ -833,6 +831,11 @@ async function getBindings(
833
831
  ...(args.durableObjects || []),
834
832
  ],
835
833
  },
834
+ queues: [
835
+ ...(configParam.queues.producers || []).map((queue) => {
836
+ return { binding: queue.binding, queue_name: queue.queue };
837
+ }),
838
+ ],
836
839
  r2_buckets: [
837
840
  ...(configParam.r2_buckets?.map(
838
841
  ({ binding, preview_bucket_name, bucket_name: _bucket_name }) => {
@@ -5,6 +5,7 @@ import * as metrics from "./metrics";
5
5
  import { requireAuth } from "./user";
6
6
  import { printWranglerBanner } from ".";
7
7
  import type { ConfigPath } from ".";
8
+ import type { CommonYargsOptions } from "./yargs-types";
8
9
  import type { Argv, CommandModule } from "yargs";
9
10
 
10
11
  type Namespace = {
@@ -105,9 +106,9 @@ async function renameWorkerNamespace(
105
106
  }
106
107
 
107
108
  export function workerNamespaceCommands(
108
- workerNamespaceYargs: Argv,
109
- subHelp: CommandModule
110
- ): Argv {
109
+ workerNamespaceYargs: Argv<CommonYargsOptions>,
110
+ subHelp: CommandModule<CommonYargsOptions, CommonYargsOptions>
111
+ ) {
111
112
  return workerNamespaceYargs
112
113
  .command(subHelp)
113
114
  .command("list", "List all dispatch namespaces", {}, async (args) => {
package/src/index.tsx CHANGED
@@ -5,10 +5,11 @@ import supportsColor from "supports-color";
5
5
  import { ProxyAgent, setGlobalDispatcher } from "undici";
6
6
  import makeCLI from "yargs";
7
7
  import { version as wranglerVersion } from "../package.json";
8
- import { fetchResult } from "./cfetch";
9
- import { readConfig } from "./config";
8
+ import { isBuildFailure } from "./bundle";
9
+ import { loadDotEnv, readConfig } from "./config";
10
10
  import { d1 } from "./d1";
11
11
  import { deleteHandler, deleteOptions } from "./delete";
12
+ import { deployments } from "./deployments";
12
13
  import {
13
14
  buildHandler,
14
15
  buildOptions,
@@ -27,12 +28,13 @@ import { devHandler, devOptions } from "./dev";
27
28
  import { workerNamespaceCommands } from "./dispatch-namespace";
28
29
  import { initHandler, initOptions } from "./init";
29
30
  import { kvNamespace, kvKey, kvBulk } from "./kv";
30
- import { logger } from "./logger";
31
+ import { logBuildFailure, logger } from "./logger";
31
32
  import * as metrics from "./metrics";
32
33
  import { pages } from "./pages";
33
34
  import { formatMessage, ParseError } from "./parse";
34
35
  import { publishOptions, publishHandler } from "./publish";
35
36
  import { pubSubCommands } from "./pubsub/pubsub-commands";
37
+ import { queues } from "./queues/cli/commands";
36
38
  import { r2 } from "./r2";
37
39
  import { secret, secretBulkHandler, secretBulkOptions } from "./secret";
38
40
  import { tailOptions, tailHandler } from "./tail";
@@ -47,10 +49,9 @@ import {
47
49
  } from "./user";
48
50
  import { whoami } from "./whoami";
49
51
 
50
- import type { DeploymentListRes } from "./__tests__/helpers/msw/handlers/deployments";
51
52
  import type { Config } from "./config";
52
- import type { ServiceMetadataRes } from "./init";
53
53
  import type { PartialConfigToDTS } from "./type-generation";
54
+ import type { CommonYargsOptions } from "./yargs-types";
54
55
  import type { ArgumentsCamelCase } from "yargs";
55
56
  import type Yargs from "yargs";
56
57
 
@@ -174,7 +175,9 @@ export function demandOneOfOption(...options: string[]) {
174
175
  export class CommandLineArgsError extends Error {}
175
176
 
176
177
  export function createCLIParser(argv: string[]) {
177
- const wrangler = makeCLI(argv)
178
+ // Type check result against CommonYargsOptions to make sure we've included
179
+ // all common options
180
+ const wrangler: Yargs.Argv<CommonYargsOptions> = makeCLI(argv)
178
181
  .strict()
179
182
  // We handle errors ourselves in a try-catch around `yargs.parse`.
180
183
  // If you want the "help info" to be displayed then throw an instance of `CommandLineArgsError`.
@@ -189,10 +192,41 @@ export function createCLIParser(argv: string[]) {
189
192
  throw error;
190
193
  })
191
194
  .scriptName("wrangler")
192
- .wrap(null);
195
+ .wrap(null)
196
+ // Define global options here, so they get included in the `Argv` type of
197
+ // the `wrangler` variable
198
+ .version(false)
199
+ .option("v", {
200
+ describe: "Show version number",
201
+ alias: "version",
202
+ type: "boolean",
203
+ })
204
+ .option("config", {
205
+ alias: "c",
206
+ describe: "Path to .toml configuration file",
207
+ type: "string",
208
+ requiresArg: true,
209
+ })
210
+ .option("env", {
211
+ alias: "e",
212
+ describe: "Environment to use for operations and .env files",
213
+ type: "string",
214
+ requiresArg: true,
215
+ })
216
+ .check((args) => {
217
+ // Grab locally specified env params from `.env` file
218
+ const loaded = loadDotEnv(".env", args.env);
219
+ for (const [key, value] of Object.entries(loaded?.parsed ?? {})) {
220
+ if (!(key in process.env)) process.env[key] = value;
221
+ }
222
+ return true;
223
+ });
224
+
225
+ wrangler.group(["config", "env", "help", "version"], "Flags:");
226
+ wrangler.help().alias("h", "help");
193
227
 
194
228
  // Default help command that supports the subcommands
195
- const subHelp: Yargs.CommandModule = {
229
+ const subHelp: Yargs.CommandModule<CommonYargsOptions, CommonYargsOptions> = {
196
230
  command: ["*"],
197
231
  handler: async (args) => {
198
232
  setImmediate(() =>
@@ -363,6 +397,11 @@ export function createCLIParser(argv: string[]) {
363
397
  return pages(pagesYargs.command(subHelp));
364
398
  });
365
399
 
400
+ // queues
401
+ wrangler.command("queues", "🆀 Configure Workers Queues", (queuesYargs) => {
402
+ return queues(queuesYargs.command(subHelp));
403
+ });
404
+
366
405
  // r2
367
406
  wrangler.command("r2", "📦 Interact with an R2 store", (r2Yargs) => {
368
407
  return r2(r2Yargs.command(subHelp));
@@ -511,15 +550,20 @@ export function createCLIParser(argv: string[]) {
511
550
  }
512
551
  );
513
552
 
553
+ //deployments
554
+ const deploymentsWarning =
555
+ "🚧`wrangler deployments` is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";
514
556
  wrangler.command(
515
557
  "deployments",
516
558
  false,
517
559
  // "🚢 Logs the 10 most recent deployments with 'Version ID', 'Version number','Author email', 'Created on' and 'Latest deploy'",
518
560
  (yargs) => {
519
- yargs.option("name", {
520
- describe: "The name of your worker",
521
- type: "string",
522
- });
561
+ yargs
562
+ .option("name", {
563
+ describe: "The name of your worker",
564
+ type: "string",
565
+ })
566
+ .epilogue(deploymentsWarning);
523
567
  },
524
568
  async (deploymentsYargs: ArgumentsCamelCase<{ name: string }>) => {
525
569
  await printWranglerBanner();
@@ -532,24 +576,9 @@ export function createCLIParser(argv: string[]) {
532
576
  { name: deploymentsYargs.name, env: undefined },
533
577
  config
534
578
  );
535
- const scriptMetadata = await fetchResult<ServiceMetadataRes>(
536
- `/accounts/${accountId}/workers/services/${scriptName}`
537
- );
538
-
539
- const scriptTag = scriptMetadata.default_environment.script.tag;
540
- const deployments = await fetchResult<DeploymentListRes>(
541
- `/accounts/${accountId}/workers/versions/by-script/${scriptTag}`
542
- );
543
579
 
544
- const versionMessages = deployments.versions.map(
545
- (versions, index) =>
546
- `\nVersion ID: ${versions.version_id}\nVersion number: ${
547
- versions.version_number
548
- }\nCreated on: ${versions.metadata.created_on}\nAuthor email: ${
549
- versions.metadata.author_email
550
- }\nLatest deploy: ${index === 0}\n`
551
- );
552
- logger.log(...versionMessages);
580
+ logger.log(`${deploymentsWarning}\n`);
581
+ await deployments(accountId, scriptName);
553
582
  }
554
583
  );
555
584
 
@@ -570,22 +599,6 @@ export function createCLIParser(argv: string[]) {
570
599
  }
571
600
  );
572
601
 
573
- wrangler.option("v", {
574
- describe: "Show version number",
575
- alias: "version",
576
- type: "boolean",
577
- });
578
-
579
- wrangler.option("config", {
580
- alias: "c",
581
- describe: "Path to .toml configuration file",
582
- type: "string",
583
- requiresArg: true,
584
- });
585
-
586
- wrangler.group(["config", "help", "version"], "Flags:");
587
- wrangler.help().alias("h", "help");
588
-
589
602
  wrangler.exitProcess(false);
590
603
 
591
604
  return wrangler;
@@ -634,6 +647,9 @@ export async function main(argv: string[]): Promise<void> {
634
647
  logger.error(
635
648
  `${thisTerminalIsUnsupported}\n${soWranglerWontWork}\n${tryRunningItIn}${oneOfThese}`
636
649
  );
650
+ } else if (isBuildFailure(e)) {
651
+ logBuildFailure(e);
652
+ logger.error(e.message);
637
653
  } else {
638
654
  logger.error(e instanceof Error ? e.message : e);
639
655
  logger.log(
package/src/init.ts CHANGED
@@ -22,9 +22,10 @@ import type { Route, SimpleRoute } from "./config/environment";
22
22
  import type { WorkerMetadata } from "./create-worker-upload-form";
23
23
  import type { ConfigPath } from "./index";
24
24
  import type { PackageManager } from "./package-manager";
25
+ import type { CommonYargsOptions } from "./yargs-types";
25
26
  import type { Argv, ArgumentsCamelCase } from "yargs";
26
27
 
27
- export async function initOptions(yargs: Argv) {
28
+ export async function initOptions(yargs: Argv<CommonYargsOptions>) {
28
29
  return yargs
29
30
  .positional("name", {
30
31
  describe: "The name of your worker",
@@ -417,7 +418,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
417
418
  );
418
419
  instructions.push(
419
420
  `\nTo start developing your Worker, run \`${
420
- isNamedWorker ? `cd ${args.name} && ` : ""
421
+ isNamedWorker ? `cd ${args.name || fromDashScriptName} && ` : ""
421
422
  }npm start\``
422
423
  );
423
424
  if (isAddingTestScripts) {
@@ -805,8 +806,7 @@ async function getWorkerConfig(
805
806
  {
806
807
  configObj.vars = {
807
808
  ...(configObj.vars ?? {}),
808
- name: binding.name,
809
- text: binding.text,
809
+ [binding.name]: binding.text,
810
810
  };
811
811
  }
812
812
  break;
package/src/inspect.ts CHANGED
@@ -11,6 +11,7 @@ import WebSocket, { WebSocketServer } from "ws";
11
11
  import { version } from "../package.json";
12
12
  import { logger } from "./logger";
13
13
  import { waitForPortToBeAvailable } from "./proxy";
14
+ import { getAccessToken } from "./user/access";
14
15
  import type Protocol from "devtools-protocol";
15
16
  import type { IncomingMessage, Server, ServerResponse } from "node:http";
16
17
  import type { MessageEvent } from "ws";
@@ -58,6 +59,8 @@ interface InspectorProps {
58
59
  * Sourcemap path, so that stacktraces can be interpretted
59
60
  */
60
61
  sourceMapPath?: string | undefined;
62
+
63
+ host?: string;
61
64
  }
62
65
 
63
66
  export default function useInspector(props: InspectorProps) {
@@ -204,14 +207,31 @@ export default function useInspector(props: InspectorProps) {
204
207
  /** A simple incrementing id to attach to messages we send to devtools */
205
208
  const messageCounterRef = useRef(1);
206
209
 
210
+ const cfAccessRef = useRef<string>();
211
+
212
+ useEffect(() => {
213
+ const run = async () => {
214
+ if (props.host && !cfAccessRef.current) {
215
+ const token = await getAccessToken(props.host);
216
+ cfAccessRef.current = token;
217
+ }
218
+ };
219
+ if (props.host) void run();
220
+ }, [props.host]);
221
+
207
222
  // This effect tracks the connection to the remote websocket
208
223
  // (stored in, no surprises here, `remoteWebSocket`)
209
224
  useEffect(() => {
210
225
  if (!props.inspectorUrl) {
211
226
  return;
212
227
  }
228
+
213
229
  // The actual websocket instance
214
- const ws = new WebSocket(props.inspectorUrl);
230
+ const ws = new WebSocket(props.inspectorUrl, {
231
+ headers: {
232
+ cookie: `CF_Authorization=${cfAccessRef.current}`,
233
+ },
234
+ });
215
235
  setRemoteWebSocket(ws);
216
236
 
217
237
  /**
@@ -4,6 +4,10 @@
4
4
  * or you're piping values from / to another process, etc
5
5
  */
6
6
  export default function isInteractive(): boolean {
7
+ if (process.env.CF_PAGES === "1") {
8
+ return false;
9
+ }
10
+
7
11
  try {
8
12
  return Boolean(process.stdin.isTTY && process.stdout.isTTY);
9
13
  } catch {
package/src/kv/index.ts CHANGED
@@ -26,12 +26,11 @@ import {
26
26
  unexpectedKVKeyValueProps,
27
27
  } from "./helpers";
28
28
  import type { ConfigPath } from "../index";
29
+ import type { CommonYargsOptions } from "../yargs-types";
29
30
  import type { KeyValue } from "./helpers";
30
- import type { BuilderCallback, Argv } from "yargs";
31
+ import type { Argv } from "yargs";
31
32
 
32
- export const kvNamespace: BuilderCallback<unknown, unknown> = (
33
- kvYargs: Argv
34
- ) => {
33
+ export const kvNamespace = (kvYargs: Argv<CommonYargsOptions>) => {
35
34
  return kvYargs
36
35
  .command(
37
36
  "create <namespace>",
@@ -43,12 +42,6 @@ export const kvNamespace: BuilderCallback<unknown, unknown> = (
43
42
  type: "string",
44
43
  demandOption: true,
45
44
  })
46
- .option("env", {
47
- type: "string",
48
- requiresArg: true,
49
- describe: "Perform on a specific environment",
50
- alias: "e",
51
- })
52
45
  .option("preview", {
53
46
  type: "boolean",
54
47
  describe: "Interact with a preview namespace",
@@ -133,12 +126,6 @@ export const kvNamespace: BuilderCallback<unknown, unknown> = (
133
126
  describe: "The id of the namespace to delete",
134
127
  })
135
128
  .check(demandOneOfOption("binding", "namespace-id"))
136
- .option("env", {
137
- type: "string",
138
- requiresArg: true,
139
- describe: "Perform on a specific environment",
140
- alias: "e",
141
- })
142
129
  .option("preview", {
143
130
  type: "boolean",
144
131
  describe: "Interact with a preview namespace",
@@ -187,7 +174,7 @@ export const kvNamespace: BuilderCallback<unknown, unknown> = (
187
174
  );
188
175
  };
189
176
 
190
- export const kvKey: BuilderCallback<unknown, unknown> = (kvYargs: Argv) => {
177
+ export const kvKey = (kvYargs: Argv<CommonYargsOptions>) => {
191
178
  return kvYargs
192
179
  .command(
193
180
  "put <key> [value]",
@@ -214,12 +201,6 @@ export const kvKey: BuilderCallback<unknown, unknown> = (kvYargs: Argv) => {
214
201
  describe: "The id of the namespace to write to",
215
202
  })
216
203
  .check(demandOneOfOption("binding", "namespace-id"))
217
- .option("env", {
218
- type: "string",
219
- requiresArg: true,
220
- describe: "Perform on a specific environment",
221
- alias: "e",
222
- })
223
204
  .option("preview", {
224
205
  type: "boolean",
225
206
  describe: "Interact with a preview namespace",
@@ -302,12 +283,6 @@ export const kvKey: BuilderCallback<unknown, unknown> = (kvYargs: Argv) => {
302
283
  describe: "The id of the namespace to list",
303
284
  })
304
285
  .check(demandOneOfOption("binding", "namespace-id"))
305
- .option("env", {
306
- type: "string",
307
- requiresArg: true,
308
- describe: "Perform on a specific environment",
309
- alias: "e",
310
- })
311
286
  .option("preview", {
312
287
  type: "boolean",
313
288
  // In the case of listing keys we will default to non-preview mode
@@ -359,12 +334,6 @@ export const kvKey: BuilderCallback<unknown, unknown> = (kvYargs: Argv) => {
359
334
  describe: "The id of the namespace to get from",
360
335
  })
361
336
  .check(demandOneOfOption("binding", "namespace-id"))
362
- .option("env", {
363
- type: "string",
364
- requiresArg: true,
365
- describe: "Perform on a specific environment",
366
- alias: "e",
367
- })
368
337
  .option("preview", {
369
338
  type: "boolean",
370
339
  describe: "Interact with a preview namespace",
@@ -422,12 +391,6 @@ export const kvKey: BuilderCallback<unknown, unknown> = (kvYargs: Argv) => {
422
391
  describe: "The id of the namespace to delete from",
423
392
  })
424
393
  .check(demandOneOfOption("binding", "namespace-id"))
425
- .option("env", {
426
- type: "string",
427
- requiresArg: true,
428
- describe: "Perform on a specific environment",
429
- alias: "e",
430
- })
431
394
  .option("preview", {
432
395
  type: "boolean",
433
396
  describe: "Interact with a preview namespace",
@@ -450,7 +413,7 @@ export const kvKey: BuilderCallback<unknown, unknown> = (kvYargs: Argv) => {
450
413
  );
451
414
  };
452
415
 
453
- export const kvBulk: BuilderCallback<unknown, unknown> = (kvYargs: Argv) => {
416
+ export const kvBulk = (kvYargs: Argv<CommonYargsOptions>) => {
454
417
  return kvYargs
455
418
  .command(
456
419
  "put <filename>",
@@ -473,12 +436,6 @@ export const kvBulk: BuilderCallback<unknown, unknown> = (kvYargs: Argv) => {
473
436
  describe: "The id of the namespace to insert values into",
474
437
  })
475
438
  .check(demandOneOfOption("binding", "namespace-id"))
476
- .option("env", {
477
- type: "string",
478
- requiresArg: true,
479
- describe: "Perform on a specific environment",
480
- alias: "e",
481
- })
482
439
  .option("preview", {
483
440
  type: "boolean",
484
441
  describe: "Interact with a preview namespace",
@@ -572,12 +529,6 @@ export const kvBulk: BuilderCallback<unknown, unknown> = (kvYargs: Argv) => {
572
529
  describe: "The id of the namespace to delete from",
573
530
  })
574
531
  .check(demandOneOfOption("binding", "namespace-id"))
575
- .option("env", {
576
- type: "string",
577
- requiresArg: true,
578
- describe: "Perform on a specific environment",
579
- alias: "e",
580
- })
581
532
  .option("preview", {
582
533
  type: "boolean",
583
534
  describe: "Interact with a preview namespace",
package/src/logger.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { format } from "node:util";
2
2
  import { formatMessagesSync } from "esbuild";
3
3
  import { getEnvironmentVariableFactory } from "./environment-variables";
4
+ import type { BuildFailure } from "esbuild";
4
5
 
5
6
  export const LOGGER_LEVELS = {
6
7
  none: -1,
@@ -78,3 +79,14 @@ class Logger {
78
79
  * to filter out logging messages.
79
80
  */
80
81
  export const logger = new Logger();
82
+
83
+ /**
84
+ * Logs all errors/warnings associated with an esbuild BuildFailure in the same
85
+ * style esbuild would.
86
+ */
87
+ export function logBuildFailure(failure: BuildFailure) {
88
+ let logs = formatMessagesSync(failure.errors, { kind: "error", color: true });
89
+ for (const log of logs) console.error(log);
90
+ logs = formatMessagesSync(failure.warnings, { kind: "warning", color: true });
91
+ for (const log of logs) console.warn(log);
92
+ }
@@ -1,5 +1,7 @@
1
1
  import { version as wranglerVersion } from "../../package.json";
2
2
 
3
+ export const MAX_ASSET_COUNT = 20_000;
4
+ export const MAX_ASSET_SIZE = 25 * 1024 * 1024;
3
5
  export const PAGES_CONFIG_CACHE_FILENAME = "pages.json";
4
6
  export const MAX_BUCKET_SIZE = 50 * 1024 * 1024;
5
7
  export const MAX_BUCKET_FILE_COUNT = 5000;
@@ -3,13 +3,17 @@ import { dirname, join, relative, resolve, sep } from "node:path";
3
3
  import { render, Text } from "ink";
4
4
  import Spinner from "ink-spinner";
5
5
  import { getType } from "mime";
6
+ import { Minimatch } from "minimatch";
6
7
  import PQueue from "p-queue";
7
8
  import prettyBytes from "pretty-bytes";
8
9
  import React from "react";
9
10
  import { fetchResult } from "../cfetch";
10
11
  import { FatalError } from "../errors";
12
+ import isInteractive from "../is-interactive";
11
13
  import { logger } from "../logger";
12
14
  import {
15
+ MAX_ASSET_COUNT,
16
+ MAX_ASSET_SIZE,
13
17
  BULK_UPLOAD_CONCURRENCY,
14
18
  MAX_BUCKET_FILE_COUNT,
15
19
  MAX_BUCKET_SIZE,
@@ -96,10 +100,11 @@ export const upload = async (
96
100
  "_redirects",
97
101
  "_headers",
98
102
  "_routes.json",
99
- ".DS_Store",
100
- "node_modules",
101
- ".git",
102
- ];
103
+ "functions",
104
+ "**/.DS_Store",
105
+ "**/node_modules",
106
+ "**/.git",
107
+ ].map((pattern) => new Minimatch(pattern));
103
108
 
104
109
  const directory = resolve(args.directory);
105
110
 
@@ -121,10 +126,13 @@ export const upload = async (
121
126
  await Promise.all(
122
127
  files.map(async (file) => {
123
128
  const filepath = join(dir, file);
129
+ const relativeFilepath = relative(startingDir, filepath);
124
130
  const filestat = await stat(filepath);
125
131
 
126
- if (IGNORE_LIST.includes(file)) {
127
- return;
132
+ for (const minimatch of IGNORE_LIST) {
133
+ if (minimatch.match(relativeFilepath)) {
134
+ return;
135
+ }
128
136
  }
129
137
 
130
138
  if (filestat.isSymbolicLink()) {
@@ -134,12 +142,12 @@ export const upload = async (
134
142
  if (filestat.isDirectory()) {
135
143
  fileMap = await walk(filepath, fileMap, startingDir);
136
144
  } else {
137
- const name = relative(startingDir, filepath).split(sep).join("/");
145
+ const name = relativeFilepath.split(sep).join("/");
138
146
 
139
- if (filestat.size > 25 * 1024 * 1024) {
147
+ if (filestat.size > MAX_ASSET_SIZE) {
140
148
  throw new FatalError(
141
149
  `Error: Pages only supports files up to ${prettyBytes(
142
- 25 * 1024 * 1024
150
+ MAX_ASSET_SIZE
143
151
  )} in size\n${name} is ${prettyBytes(filestat.size)} in size`,
144
152
  1
145
153
  );
@@ -161,9 +169,9 @@ export const upload = async (
161
169
 
162
170
  const fileMap = await walk(directory);
163
171
 
164
- if (fileMap.size > 20000) {
172
+ if (fileMap.size > MAX_ASSET_COUNT) {
165
173
  throw new FatalError(
166
- `Error: Pages only supports up to 20,000 files in a deployment. Ensure you have specified your build output directory correctly.`,
174
+ `Error: Pages only supports up to ${MAX_ASSET_COUNT.toLocaleString()} files in a deployment. Ensure you have specified your build output directory correctly.`,
167
175
  1
168
176
  );
169
177
  }
@@ -289,7 +297,7 @@ export const upload = async (
289
297
  setTimeout(resolvePromise, Math.pow(2, attempts++) * 1000)
290
298
  );
291
299
 
292
- if ((e as { code: number }).code === 8000013) {
300
+ if ((e as { code: number }).code === 8000013 || isJwtExpired(jwt)) {
293
301
  // Looks like the JWT expired, fetch another one
294
302
  jwt = await fetchJwt();
295
303
  }
@@ -309,7 +317,9 @@ export const upload = async (
309
317
  (error) => {
310
318
  return Promise.reject(
311
319
  new FatalError(
312
- "Failed to upload files. Please try again.",
320
+ `Failed to upload files. Please try again. Error: ${JSON.stringify(
321
+ error
322
+ )})`,
313
323
  error.code || 1
314
324
  )
315
325
  );
@@ -348,7 +358,7 @@ export const upload = async (
348
358
  } catch (e) {
349
359
  await new Promise((resolvePromise) => setTimeout(resolvePromise, 1000));
350
360
 
351
- if ((e as { code: number }).code === 8000013) {
361
+ if ((e as { code: number }).code === 8000013 || isJwtExpired(jwt)) {
352
362
  // Looks like the JWT expired, fetch another one
353
363
  jwt = await fetchJwt();
354
364
  }
@@ -382,6 +392,23 @@ export const upload = async (
382
392
  );
383
393
  };
384
394
 
395
+ // Decode and check that the current JWT has not expired
396
+ function isJwtExpired(token: string): boolean | undefined {
397
+ try {
398
+ const decodedJwt = JSON.parse(
399
+ Buffer.from(token.split(".")[1], "base64").toString()
400
+ );
401
+
402
+ const dateNow = new Date().getTime() / 1000;
403
+
404
+ return decodedJwt.exp <= dateNow;
405
+ } catch (e) {
406
+ if (e instanceof Error) {
407
+ throw new Error(`Invalid token: ${e.message}`);
408
+ }
409
+ }
410
+ }
411
+
385
412
  function formatTime(duration: number) {
386
413
  return `(${(duration / 1000).toFixed(2)} sec)`;
387
414
  }
@@ -390,7 +417,7 @@ function Progress({ done, total }: { done: number; total: number }) {
390
417
  return (
391
418
  <>
392
419
  <Text>
393
- <Spinner type="earth" />
420
+ {isInteractive() ? <Spinner type="earth" /> : null}
394
421
  {` Uploading... (${done}/${total})\n`}
395
422
  </Text>
396
423
  </>