wrangler 2.0.16 → 2.0.19

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 (51) hide show
  1. package/bin/wrangler.js +42 -8
  2. package/miniflare-dist/index.mjs +1 -0
  3. package/package.json +65 -63
  4. package/src/__tests__/configuration.test.ts +11 -7
  5. package/src/__tests__/helpers/run-in-tmp.ts +22 -23
  6. package/src/__tests__/https-options.test.ts +51 -18
  7. package/src/__tests__/index.test.ts +13 -13
  8. package/src/__tests__/jest.setup.ts +13 -0
  9. package/src/__tests__/kv.test.ts +33 -2
  10. package/src/__tests__/metrics.test.ts +415 -0
  11. package/src/__tests__/pages.test.ts +15 -8
  12. package/src/__tests__/publish.test.ts +49 -23
  13. package/src/__tests__/secret.test.ts +84 -78
  14. package/src/__tests__/tail.test.ts +8 -0
  15. package/src/__tests__/test-old-node-version.js +31 -0
  16. package/src/__tests__/user.test.ts +7 -7
  17. package/src/__tests__/whoami.test.tsx +11 -1
  18. package/src/api/dev.ts +4 -1
  19. package/src/cli.ts +1 -1
  20. package/src/config/config.ts +8 -0
  21. package/src/config/validation.ts +9 -0
  22. package/src/config-cache.ts +2 -1
  23. package/src/dev/local.tsx +31 -25
  24. package/src/dev/remote.tsx +2 -2
  25. package/src/dev.tsx +6 -0
  26. package/src/entry.ts +1 -1
  27. package/src/{__tests__/helpers/faye-websocket.d.ts → faye-websocket.d.ts} +0 -0
  28. package/src/global-wrangler-config-path.ts +26 -0
  29. package/src/https-options.ts +8 -4
  30. package/src/index.tsx +120 -22
  31. package/src/kv.ts +23 -1
  32. package/src/metrics/index.ts +4 -0
  33. package/src/metrics/metrics-config.ts +222 -0
  34. package/src/metrics/metrics-dispatcher.ts +93 -0
  35. package/src/metrics/send-event.ts +80 -0
  36. package/src/miniflare-cli/index.ts +1 -0
  37. package/src/package-manager.ts +45 -0
  38. package/src/pages/build.tsx +2 -0
  39. package/src/pages/deployments.tsx +2 -0
  40. package/src/pages/dev.tsx +4 -0
  41. package/src/pages/projects.tsx +3 -0
  42. package/src/pages/publish.tsx +3 -0
  43. package/src/pages/upload.tsx +26 -14
  44. package/src/publish.ts +28 -15
  45. package/src/pubsub/pubsub-commands.tsx +43 -0
  46. package/src/user/user.tsx +29 -20
  47. package/src/worker-namespace.ts +16 -0
  48. package/templates/static-asset-facade.js +11 -5
  49. package/templates/tsconfig.json +2 -2
  50. package/wrangler-dist/cli.d.ts +298 -0
  51. package/wrangler-dist/cli.js +2589 -1659
package/src/dev.tsx CHANGED
@@ -10,6 +10,7 @@ import { getVarsForDev } from "./dev/dev-vars";
10
10
 
11
11
  import { getEntry } from "./entry";
12
12
  import { logger } from "./logger";
13
+ import * as metrics from "./metrics";
13
14
  import { getAssetPaths, getSiteAssetPaths } from "./sites";
14
15
  import { getAccountFromCache } from "./user";
15
16
  import { getZoneIdFromHost, getZoneForRoute, getHostFromRoute } from "./zones";
@@ -264,6 +265,11 @@ export async function startDev(args: ArgumentsCamelCase<DevArgs>) {
264
265
  ((args.script &&
265
266
  findWranglerToml(path.dirname(args.script))) as ConfigPath);
266
267
  let config = readConfig(configPath, args);
268
+ metrics.sendMetricsEvent(
269
+ "run dev",
270
+ { local: args.local },
271
+ { sendMetrics: config.send_metrics, offline: args.local }
272
+ );
267
273
 
268
274
  if (config.configPath) {
269
275
  watcher = watch(config.configPath, {
package/src/entry.ts CHANGED
@@ -39,7 +39,7 @@ export async function getEntry(
39
39
  ? path.resolve(config.site?.["entry-point"])
40
40
  : // site.entry-point could be a directory
41
41
  path.resolve(config.site?.["entry-point"], "index.js");
42
- } else if (args.assets) {
42
+ } else if (args.assets || config.assets) {
43
43
  file = path.resolve(__dirname, "../templates/no-op-worker.js");
44
44
  } else {
45
45
  throw new Error(
@@ -0,0 +1,26 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import xdgAppPaths from "xdg-app-paths";
5
+
6
+ function isDirectory(configPath: string) {
7
+ try {
8
+ return fs.statSync(configPath).isDirectory();
9
+ } catch (error) {
10
+ // ignore error
11
+ return false;
12
+ }
13
+ }
14
+
15
+ export function getGlobalWranglerConfigPath() {
16
+ //TODO: We should implement a custom path --global-config and/or the WRANGLER_HOME type environment variable
17
+ const configDir = xdgAppPaths(".wrangler").config(); // New XDG compliant config path
18
+ const legacyConfigDir = path.join(os.homedir(), ".wrangler"); // Legacy config in user's home directory
19
+
20
+ // Check for the .wrangler directory in root if it is not there then use the XDG compliant path.
21
+ if (isDirectory(legacyConfigDir)) {
22
+ return legacyConfigDir;
23
+ } else {
24
+ return configDir;
25
+ }
26
+ }
@@ -1,7 +1,8 @@
1
1
  import * as fs from "node:fs";
2
- import { homedir, networkInterfaces } from "node:os";
2
+ import os from "node:os";
3
3
  import * as path from "node:path";
4
4
  import { promisify } from "node:util";
5
+ import { getGlobalWranglerConfigPath } from "./global-wrangler-config-path";
5
6
  import { logger } from "./logger";
6
7
  import type { Attributes, Options } from "selfsigned";
7
8
 
@@ -17,7 +18,7 @@ const ONE_DAY_IN_MS = 86400000;
17
18
  * The certificates are self-signed and generated locally, and cached in the `CERT_ROOT` directory.
18
19
  */
19
20
  export async function getHttpsOptions() {
20
- const certDirectory = path.join(homedir(), ".wrangler/local-cert");
21
+ const certDirectory = path.join(getGlobalWranglerConfigPath(), "local-cert");
21
22
  const keyPath = path.join(certDirectory, "key.pem");
22
23
  const certPath = path.join(certDirectory, "cert.pem");
23
24
 
@@ -37,7 +38,10 @@ export async function getHttpsOptions() {
37
38
  } catch (e) {
38
39
  const message = e instanceof Error ? e.message : `${e}`;
39
40
  logger.warn(
40
- `Unable to cache generated self-signed certificate in ${certDirectory}.\n${message}`
41
+ `Unable to cache generated self-signed certificate in ${path.relative(
42
+ process.cwd(),
43
+ certDirectory
44
+ )}.\n${message}`
41
45
  );
42
46
  }
43
47
  return { key, cert };
@@ -114,7 +118,7 @@ async function generateCertificate() {
114
118
  */
115
119
  function getAccessibleHosts(ipv4 = false): string[] {
116
120
  const hosts: string[] = [];
117
- Object.values(networkInterfaces()).forEach((net) =>
121
+ Object.values(os.networkInterfaces()).forEach((net) =>
118
122
  net?.forEach(({ family, address }) => {
119
123
  if (!ipv4 || family === "IPv4") hosts.push(address);
120
124
  })
package/src/index.tsx CHANGED
@@ -33,6 +33,7 @@ import {
33
33
  deleteKVKeyValue,
34
34
  } from "./kv";
35
35
  import { logger } from "./logger";
36
+ import * as metrics from "./metrics";
36
37
  import { pages } from "./pages";
37
38
  import {
38
39
  formatMessage,
@@ -64,6 +65,7 @@ import { whoami } from "./whoami";
64
65
 
65
66
  import { workerNamespaceCommands } from "./worker-namespace";
66
67
  import type { Config } from "./config";
68
+ import type { KeyValue } from "./kv";
67
69
  import type { TailCLIFilters } from "./tail";
68
70
  import type { RawData } from "ws";
69
71
  import type { CommandModule } from "yargs";
@@ -382,7 +384,7 @@ function createCLIParser(argv: string[]) {
382
384
  hidden: true,
383
385
  })
384
386
  .option("no-bundle", {
385
- describe: "Skip internal build steps and directly publish script",
387
+ describe: "Skip internal build steps and directly publish Worker",
386
388
  type: "boolean",
387
389
  default: false,
388
390
  })
@@ -474,7 +476,7 @@ function createCLIParser(argv: string[]) {
474
476
  requiresArg: true,
475
477
  })
476
478
  .option("minify", {
477
- describe: "Minify the script",
479
+ describe: "Minify the Worker",
478
480
  type: "boolean",
479
481
  })
480
482
  .option("node-compat", {
@@ -499,6 +501,9 @@ function createCLIParser(argv: string[]) {
499
501
  (args.config as ConfigPath) ||
500
502
  (args.script && findWranglerToml(path.dirname(args.script)));
501
503
  const config = readConfig(configPath, args);
504
+ metrics.sendMetricsEvent("deploy worker script", {
505
+ sendMetrics: config.send_metrics,
506
+ });
502
507
  const entry = await getEntry(args, config, "publish");
503
508
 
504
509
  if (args.public) {
@@ -639,11 +644,16 @@ function createCLIParser(argv: string[]) {
639
644
  await printWranglerBanner();
640
645
  }
641
646
  const config = readConfig(args.config as ConfigPath, args);
647
+ metrics.sendMetricsEvent("begin log stream", {
648
+ sendMetrics: config.send_metrics,
649
+ });
642
650
 
643
651
  const scriptName = getLegacyScriptName(args, config);
644
652
 
645
653
  if (!scriptName) {
646
- throw new Error("Missing script name");
654
+ throw new Error(
655
+ "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `wrangler tail <worker-name>`"
656
+ );
647
657
  }
648
658
 
649
659
  const accountId = await requireAuth(config);
@@ -682,6 +692,9 @@ function createCLIParser(argv: string[]) {
682
692
  onExit(async () => {
683
693
  tail.terminate();
684
694
  await deleteTail();
695
+ metrics.sendMetricsEvent("end log stream", {
696
+ sendMetrics: config.send_metrics,
697
+ });
685
698
  });
686
699
 
687
700
  const printLog: (data: RawData) => void =
@@ -698,6 +711,9 @@ function createCLIParser(argv: string[]) {
698
711
  await setTimeout(100);
699
712
  break;
700
713
  case tail.CLOSED:
714
+ metrics.sendMetricsEvent("end log stream", {
715
+ sendMetrics: config.send_metrics,
716
+ });
701
717
  throw new Error(
702
718
  `Connection to ${scriptDisplayName} closed unexpectedly.`
703
719
  );
@@ -711,6 +727,9 @@ function createCLIParser(argv: string[]) {
711
727
  tail.on("close", async () => {
712
728
  tail.terminate();
713
729
  await deleteTail();
730
+ metrics.sendMetricsEvent("end log stream", {
731
+ sendMetrics: config.send_metrics,
732
+ });
714
733
  });
715
734
  }
716
735
  );
@@ -820,7 +839,7 @@ function createCLIParser(argv: string[]) {
820
839
  // secret
821
840
  wrangler.command(
822
841
  "secret",
823
- "🤫 Generate a secret that can be referenced in the worker script",
842
+ "🤫 Generate a secret that can be referenced in a Worker",
824
843
  (secretYargs) => {
825
844
  return secretYargs
826
845
  .command(subHelp)
@@ -831,15 +850,15 @@ function createCLIParser(argv: string[]) {
831
850
  })
832
851
  .command(
833
852
  "put <key>",
834
- "Create or update a secret variable for a script",
853
+ "Create or update a secret variable for a Worker",
835
854
  (yargs) => {
836
855
  return yargs
837
856
  .positional("key", {
838
- describe: "The variable name to be accessible in the script",
857
+ describe: "The variable name to be accessible in the Worker",
839
858
  type: "string",
840
859
  })
841
860
  .option("name", {
842
- describe: "Name of the worker",
861
+ describe: "Name of the Worker",
843
862
  type: "string",
844
863
  requiresArg: true,
845
864
  })
@@ -857,7 +876,9 @@ function createCLIParser(argv: string[]) {
857
876
 
858
877
  const scriptName = getLegacyScriptName(args, config);
859
878
  if (!scriptName) {
860
- throw new Error("Missing script name");
879
+ throw new Error(
880
+ "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
881
+ );
861
882
  }
862
883
 
863
884
  const accountId = await requireAuth(config);
@@ -870,7 +891,7 @@ function createCLIParser(argv: string[]) {
870
891
  );
871
892
 
872
893
  logger.log(
873
- `🌀 Creating the secret for script ${scriptName} ${
894
+ `🌀 Creating the secret for the Worker "${scriptName}" ${
874
895
  args.env && !isLegacyEnv(config) ? `(${args.env})` : ""
875
896
  }`
876
897
  );
@@ -939,6 +960,9 @@ function createCLIParser(argv: string[]) {
939
960
 
940
961
  try {
941
962
  await submitSecret();
963
+ metrics.sendMetricsEvent("create encrypted variable", {
964
+ sendMetrics: config.send_metrics,
965
+ });
942
966
  } catch (e) {
943
967
  if (isMissingWorkerError(e)) {
944
968
  // create a draft worker and try again
@@ -955,16 +979,16 @@ function createCLIParser(argv: string[]) {
955
979
  )
956
980
  .command(
957
981
  "delete <key>",
958
- "Delete a secret variable from a script",
982
+ "Delete a secret variable from a Worker",
959
983
  async (yargs) => {
960
984
  await printWranglerBanner();
961
985
  return yargs
962
986
  .positional("key", {
963
- describe: "The variable name to be accessible in the script",
987
+ describe: "The variable name to be accessible in the Worker",
964
988
  type: "string",
965
989
  })
966
990
  .option("name", {
967
- describe: "Name of the worker",
991
+ describe: "Name of the Worker",
968
992
  type: "string",
969
993
  requiresArg: true,
970
994
  })
@@ -981,22 +1005,26 @@ function createCLIParser(argv: string[]) {
981
1005
 
982
1006
  const scriptName = getLegacyScriptName(args, config);
983
1007
  if (!scriptName) {
984
- throw new Error("Missing script name");
1008
+ throw new Error(
1009
+ "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
1010
+ );
985
1011
  }
986
1012
 
987
1013
  const accountId = await requireAuth(config);
988
1014
 
989
1015
  if (
990
1016
  await confirm(
991
- `Are you sure you want to permanently delete the variable ${
1017
+ `Are you sure you want to permanently delete the secret ${
992
1018
  args.key
993
- } on the script ${scriptName}${
1019
+ } on the Worker ${scriptName}${
994
1020
  args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
995
1021
  }?`
996
1022
  )
997
1023
  ) {
998
1024
  logger.log(
999
- `🌀 Deleting the secret ${args.key} on script ${scriptName}${
1025
+ `🌀 Deleting the secret ${
1026
+ args.key
1027
+ } on the Worker ${scriptName}${
1000
1028
  args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1001
1029
  }`
1002
1030
  );
@@ -1007,17 +1035,20 @@ function createCLIParser(argv: string[]) {
1007
1035
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1008
1036
 
1009
1037
  await fetchResult(`${url}/${args.key}`, { method: "DELETE" });
1038
+ metrics.sendMetricsEvent("delete encrypted variable", {
1039
+ sendMetrics: config.send_metrics,
1040
+ });
1010
1041
  logger.log(`✨ Success! Deleted secret ${args.key}`);
1011
1042
  }
1012
1043
  }
1013
1044
  )
1014
1045
  .command(
1015
1046
  "list",
1016
- "List all secrets for a script",
1047
+ "List all secrets for a Worker",
1017
1048
  (yargs) => {
1018
1049
  return yargs
1019
1050
  .option("name", {
1020
- describe: "Name of the worker",
1051
+ describe: "Name of the Worker",
1021
1052
  type: "string",
1022
1053
  requiresArg: true,
1023
1054
  })
@@ -1034,7 +1065,9 @@ function createCLIParser(argv: string[]) {
1034
1065
 
1035
1066
  const scriptName = getLegacyScriptName(args, config);
1036
1067
  if (!scriptName) {
1037
- throw new Error("Missing script name");
1068
+ throw new Error(
1069
+ "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
1070
+ );
1038
1071
  }
1039
1072
 
1040
1073
  const accountId = await requireAuth(config);
@@ -1045,6 +1078,9 @@ function createCLIParser(argv: string[]) {
1045
1078
  : `/accounts/${accountId}/workers/services/${scriptName}/environments/${args.env}/secrets`;
1046
1079
 
1047
1080
  logger.log(JSON.stringify(await fetchResult(url), null, " "));
1081
+ metrics.sendMetricsEvent("list encrypted variables", {
1082
+ sendMetrics: config.send_metrics,
1083
+ });
1048
1084
  }
1049
1085
  );
1050
1086
  }
@@ -1106,6 +1142,9 @@ function createCLIParser(argv: string[]) {
1106
1142
 
1107
1143
  logger.log(`🌀 Creating namespace with title "${title}"`);
1108
1144
  const namespaceId = await createKVNamespace(accountId, title);
1145
+ metrics.sendMetricsEvent("create kv namespace", {
1146
+ sendMetrics: config.send_metrics,
1147
+ });
1109
1148
 
1110
1149
  logger.log("✨ Success!");
1111
1150
  const envString = args.env ? ` under [env.${args.env}]` : "";
@@ -1134,6 +1173,9 @@ function createCLIParser(argv: string[]) {
1134
1173
  logger.log(
1135
1174
  JSON.stringify(await listKVNamespaces(accountId), null, " ")
1136
1175
  );
1176
+ metrics.sendMetricsEvent("list kv namespaces", {
1177
+ sendMetrics: config.send_metrics,
1178
+ });
1137
1179
  }
1138
1180
  )
1139
1181
  .command(
@@ -1179,6 +1221,9 @@ function createCLIParser(argv: string[]) {
1179
1221
  const accountId = await requireAuth(config);
1180
1222
 
1181
1223
  await deleteKVNamespace(accountId, id);
1224
+ metrics.sendMetricsEvent("delete kv namespace", {
1225
+ sendMetrics: config.send_metrics,
1226
+ });
1182
1227
 
1183
1228
  // TODO: recommend they remove it from wrangler.toml
1184
1229
 
@@ -1253,6 +1298,15 @@ function createCLIParser(argv: string[]) {
1253
1298
  describe:
1254
1299
  "Time since the UNIX epoch after which the entry expires",
1255
1300
  })
1301
+ .option("metadata", {
1302
+ type: "string",
1303
+ describe: "Arbitrary JSON that is associated with a key",
1304
+ coerce: (jsonStr: string): KeyValue["metadata"] => {
1305
+ try {
1306
+ return JSON.parse(jsonStr);
1307
+ } catch (_) {}
1308
+ },
1309
+ })
1256
1310
  .option("path", {
1257
1311
  type: "string",
1258
1312
  requiresArg: true,
@@ -1260,7 +1314,7 @@ function createCLIParser(argv: string[]) {
1260
1314
  })
1261
1315
  .check(demandOneOfOption("value", "path"));
1262
1316
  },
1263
- async ({ key, ttl, expiration, ...args }) => {
1317
+ async ({ key, ttl, expiration, metadata, ...args }) => {
1264
1318
  await printWranglerBanner();
1265
1319
  const config = readConfig(args.config as ConfigPath, args);
1266
1320
  const namespaceId = getKVNamespaceId(args, config);
@@ -1270,13 +1324,17 @@ function createCLIParser(argv: string[]) {
1270
1324
  : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1271
1325
  args.value!;
1272
1326
 
1327
+ const metadataLog = metadata
1328
+ ? ` with metadata "${JSON.stringify(metadata)}"`
1329
+ : "";
1330
+
1273
1331
  if (args.path) {
1274
1332
  logger.log(
1275
- `Writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}.`
1333
+ `Writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}${metadataLog}.`
1276
1334
  );
1277
1335
  } else {
1278
1336
  logger.log(
1279
- `Writing the value "${value}" to key "${key}" on namespace ${namespaceId}.`
1337
+ `Writing the value "${value}" to key "${key}" on namespace ${namespaceId}${metadataLog}.`
1280
1338
  );
1281
1339
  }
1282
1340
 
@@ -1287,6 +1345,10 @@ function createCLIParser(argv: string[]) {
1287
1345
  value,
1288
1346
  expiration,
1289
1347
  expiration_ttl: ttl,
1348
+ metadata: metadata as KeyValue["metadata"],
1349
+ });
1350
+ metrics.sendMetricsEvent("write kv key-value", {
1351
+ sendMetrics: config.send_metrics,
1290
1352
  });
1291
1353
  }
1292
1354
  )
@@ -1337,6 +1399,9 @@ function createCLIParser(argv: string[]) {
1337
1399
  prefix
1338
1400
  );
1339
1401
  logger.log(JSON.stringify(results, undefined, 2));
1402
+ metrics.sendMetricsEvent("list kv keys", {
1403
+ sendMetrics: config.send_metrics,
1404
+ });
1340
1405
  }
1341
1406
  )
1342
1407
  .command(
@@ -1397,6 +1462,9 @@ function createCLIParser(argv: string[]) {
1397
1462
  } else {
1398
1463
  process.stdout.write(bufferKVValue);
1399
1464
  }
1465
+ metrics.sendMetricsEvent("read kv value", {
1466
+ sendMetrics: config.send_metrics,
1467
+ });
1400
1468
  }
1401
1469
  )
1402
1470
  .command(
@@ -1443,6 +1511,9 @@ function createCLIParser(argv: string[]) {
1443
1511
  const accountId = await requireAuth(config);
1444
1512
 
1445
1513
  await deleteKVKeyValue(accountId, namespaceId, key);
1514
+ metrics.sendMetricsEvent("delete kv key-value", {
1515
+ sendMetrics: config.send_metrics,
1516
+ });
1446
1517
  }
1447
1518
  );
1448
1519
  }
@@ -1547,6 +1618,9 @@ function createCLIParser(argv: string[]) {
1547
1618
 
1548
1619
  const accountId = await requireAuth(config);
1549
1620
  await putKVBulkKeyValue(accountId, namespaceId, content);
1621
+ metrics.sendMetricsEvent("write kv key-values (bulk)", {
1622
+ sendMetrics: config.send_metrics,
1623
+ });
1550
1624
 
1551
1625
  logger.log("Success!");
1552
1626
  }
@@ -1637,6 +1711,9 @@ function createCLIParser(argv: string[]) {
1637
1711
  const accountId = await requireAuth(config);
1638
1712
 
1639
1713
  await deleteKVBulkKeyValue(accountId, namespaceId, content);
1714
+ metrics.sendMetricsEvent("delete kv key-values (bulk)", {
1715
+ sendMetrics: config.send_metrics,
1716
+ });
1640
1717
 
1641
1718
  logger.log("Success!");
1642
1719
  }
@@ -1676,6 +1753,9 @@ function createCLIParser(argv: string[]) {
1676
1753
  logger.log(`Creating bucket ${args.name}.`);
1677
1754
  await createR2Bucket(accountId, args.name);
1678
1755
  logger.log(`Created bucket ${args.name}.`);
1756
+ metrics.sendMetricsEvent("create r2 bucket", {
1757
+ sendMetrics: config.send_metrics,
1758
+ });
1679
1759
  }
1680
1760
  );
1681
1761
 
@@ -1685,6 +1765,9 @@ function createCLIParser(argv: string[]) {
1685
1765
  const accountId = await requireAuth(config);
1686
1766
 
1687
1767
  logger.log(JSON.stringify(await listR2Buckets(accountId), null, 2));
1768
+ metrics.sendMetricsEvent("list r2 buckets", {
1769
+ sendMetrics: config.send_metrics,
1770
+ });
1688
1771
  });
1689
1772
 
1690
1773
  r2BucketYargs.command(
@@ -1707,6 +1790,9 @@ function createCLIParser(argv: string[]) {
1707
1790
  logger.log(`Deleting bucket ${args.name}.`);
1708
1791
  await deleteR2Bucket(accountId, args.name);
1709
1792
  logger.log(`Deleted bucket ${args.name}.`);
1793
+ metrics.sendMetricsEvent("delete r2 bucket", {
1794
+ sendMetrics: config.send_metrics,
1795
+ });
1710
1796
  }
1711
1797
  );
1712
1798
  return r2BucketYargs;
@@ -1775,6 +1861,10 @@ function createCLIParser(argv: string[]) {
1775
1861
  return;
1776
1862
  }
1777
1863
  await login();
1864
+ const config = readConfig(args.config as ConfigPath, args);
1865
+ metrics.sendMetricsEvent("login user", {
1866
+ sendMetrics: config.send_metrics,
1867
+ });
1778
1868
 
1779
1869
  // TODO: would be nice if it optionally saved login
1780
1870
  // credentials inside node_modules/.cache or something
@@ -1791,6 +1881,10 @@ function createCLIParser(argv: string[]) {
1791
1881
  async () => {
1792
1882
  await printWranglerBanner();
1793
1883
  await logout();
1884
+ const config = readConfig(undefined, {});
1885
+ metrics.sendMetricsEvent("logout user", {
1886
+ sendMetrics: config.send_metrics,
1887
+ });
1794
1888
  }
1795
1889
  );
1796
1890
 
@@ -1802,6 +1896,10 @@ function createCLIParser(argv: string[]) {
1802
1896
  async () => {
1803
1897
  await printWranglerBanner();
1804
1898
  await whoami();
1899
+ const config = readConfig(undefined, {});
1900
+ metrics.sendMetricsEvent("view accounts", {
1901
+ sendMetrics: config.send_metrics,
1902
+ });
1805
1903
  }
1806
1904
  );
1807
1905
 
package/src/kv.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { URLSearchParams } from "node:url";
2
+ import { FormData } from "undici";
2
3
  import { fetchListResult, fetchResult, fetchKVGetValue } from "./cfetch";
3
4
  import { logger } from "./logger";
4
5
  import type { Config } from "./config";
@@ -186,6 +187,19 @@ export function unexpectedKVKeyValueProps(keyValue: KeyValue): string[] {
186
187
  return props.filter((prop) => !KeyValueKeys.has(prop));
187
188
  }
188
189
 
190
+ /**
191
+ * Turn object with fields into FormData
192
+ */
193
+ function asFormData(fields: Record<string, unknown>): FormData {
194
+ const formData = new FormData();
195
+
196
+ for (const [name, value] of Object.entries(fields)) {
197
+ formData.append(name, value);
198
+ }
199
+
200
+ return formData;
201
+ }
202
+
189
203
  export async function putKVKeyValue(
190
204
  accountId: string,
191
205
  namespaceId: string,
@@ -205,7 +219,15 @@ export async function putKVKeyValue(
205
219
  `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${encodeURIComponent(
206
220
  keyValue.key
207
221
  )}`,
208
- { method: "PUT", body: keyValue.value },
222
+ {
223
+ method: "PUT",
224
+ body: keyValue.metadata
225
+ ? asFormData({
226
+ value: keyValue.value,
227
+ metadata: JSON.stringify(keyValue.metadata),
228
+ })
229
+ : keyValue.value,
230
+ },
209
231
  searchParams
210
232
  );
211
233
  }
@@ -0,0 +1,4 @@
1
+ export { getMetricsDispatcher } from "./metrics-dispatcher";
2
+ export type { Properties } from "./metrics-dispatcher";
3
+ export { getMetricsConfig } from "./metrics-config";
4
+ export * from "./send-event";