wrangler 2.0.16 → 2.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.
package/src/index.tsx CHANGED
@@ -64,6 +64,7 @@ import { whoami } from "./whoami";
64
64
 
65
65
  import { workerNamespaceCommands } from "./worker-namespace";
66
66
  import type { Config } from "./config";
67
+ import type { KeyValue } from "./kv";
67
68
  import type { TailCLIFilters } from "./tail";
68
69
  import type { RawData } from "ws";
69
70
  import type { CommandModule } from "yargs";
@@ -382,7 +383,7 @@ function createCLIParser(argv: string[]) {
382
383
  hidden: true,
383
384
  })
384
385
  .option("no-bundle", {
385
- describe: "Skip internal build steps and directly publish script",
386
+ describe: "Skip internal build steps and directly publish Worker",
386
387
  type: "boolean",
387
388
  default: false,
388
389
  })
@@ -474,7 +475,7 @@ function createCLIParser(argv: string[]) {
474
475
  requiresArg: true,
475
476
  })
476
477
  .option("minify", {
477
- describe: "Minify the script",
478
+ describe: "Minify the Worker",
478
479
  type: "boolean",
479
480
  })
480
481
  .option("node-compat", {
@@ -643,7 +644,9 @@ function createCLIParser(argv: string[]) {
643
644
  const scriptName = getLegacyScriptName(args, config);
644
645
 
645
646
  if (!scriptName) {
646
- throw new Error("Missing script name");
647
+ throw new Error(
648
+ "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `wrangler tail <worker-name>`"
649
+ );
647
650
  }
648
651
 
649
652
  const accountId = await requireAuth(config);
@@ -820,7 +823,7 @@ function createCLIParser(argv: string[]) {
820
823
  // secret
821
824
  wrangler.command(
822
825
  "secret",
823
- "🤫 Generate a secret that can be referenced in the worker script",
826
+ "🤫 Generate a secret that can be referenced in a Worker",
824
827
  (secretYargs) => {
825
828
  return secretYargs
826
829
  .command(subHelp)
@@ -831,15 +834,15 @@ function createCLIParser(argv: string[]) {
831
834
  })
832
835
  .command(
833
836
  "put <key>",
834
- "Create or update a secret variable for a script",
837
+ "Create or update a secret variable for a Worker",
835
838
  (yargs) => {
836
839
  return yargs
837
840
  .positional("key", {
838
- describe: "The variable name to be accessible in the script",
841
+ describe: "The variable name to be accessible in the Worker",
839
842
  type: "string",
840
843
  })
841
844
  .option("name", {
842
- describe: "Name of the worker",
845
+ describe: "Name of the Worker",
843
846
  type: "string",
844
847
  requiresArg: true,
845
848
  })
@@ -857,7 +860,9 @@ function createCLIParser(argv: string[]) {
857
860
 
858
861
  const scriptName = getLegacyScriptName(args, config);
859
862
  if (!scriptName) {
860
- throw new Error("Missing script name");
863
+ throw new Error(
864
+ "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
865
+ );
861
866
  }
862
867
 
863
868
  const accountId = await requireAuth(config);
@@ -870,7 +875,7 @@ function createCLIParser(argv: string[]) {
870
875
  );
871
876
 
872
877
  logger.log(
873
- `🌀 Creating the secret for script ${scriptName} ${
878
+ `🌀 Creating the secret for the Worker "${scriptName}" ${
874
879
  args.env && !isLegacyEnv(config) ? `(${args.env})` : ""
875
880
  }`
876
881
  );
@@ -955,16 +960,16 @@ function createCLIParser(argv: string[]) {
955
960
  )
956
961
  .command(
957
962
  "delete <key>",
958
- "Delete a secret variable from a script",
963
+ "Delete a secret variable from a Worker",
959
964
  async (yargs) => {
960
965
  await printWranglerBanner();
961
966
  return yargs
962
967
  .positional("key", {
963
- describe: "The variable name to be accessible in the script",
968
+ describe: "The variable name to be accessible in the Worker",
964
969
  type: "string",
965
970
  })
966
971
  .option("name", {
967
- describe: "Name of the worker",
972
+ describe: "Name of the Worker",
968
973
  type: "string",
969
974
  requiresArg: true,
970
975
  })
@@ -981,22 +986,26 @@ function createCLIParser(argv: string[]) {
981
986
 
982
987
  const scriptName = getLegacyScriptName(args, config);
983
988
  if (!scriptName) {
984
- throw new Error("Missing script name");
989
+ throw new Error(
990
+ "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
991
+ );
985
992
  }
986
993
 
987
994
  const accountId = await requireAuth(config);
988
995
 
989
996
  if (
990
997
  await confirm(
991
- `Are you sure you want to permanently delete the variable ${
998
+ `Are you sure you want to permanently delete the secret ${
992
999
  args.key
993
- } on the script ${scriptName}${
1000
+ } on the Worker ${scriptName}${
994
1001
  args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
995
1002
  }?`
996
1003
  )
997
1004
  ) {
998
1005
  logger.log(
999
- `🌀 Deleting the secret ${args.key} on script ${scriptName}${
1006
+ `🌀 Deleting the secret ${
1007
+ args.key
1008
+ } on the Worker ${scriptName}${
1000
1009
  args.env && !isLegacyEnv(config) ? ` (${args.env})` : ""
1001
1010
  }`
1002
1011
  );
@@ -1013,11 +1022,11 @@ function createCLIParser(argv: string[]) {
1013
1022
  )
1014
1023
  .command(
1015
1024
  "list",
1016
- "List all secrets for a script",
1025
+ "List all secrets for a Worker",
1017
1026
  (yargs) => {
1018
1027
  return yargs
1019
1028
  .option("name", {
1020
- describe: "Name of the worker",
1029
+ describe: "Name of the Worker",
1021
1030
  type: "string",
1022
1031
  requiresArg: true,
1023
1032
  })
@@ -1034,7 +1043,9 @@ function createCLIParser(argv: string[]) {
1034
1043
 
1035
1044
  const scriptName = getLegacyScriptName(args, config);
1036
1045
  if (!scriptName) {
1037
- throw new Error("Missing script name");
1046
+ throw new Error(
1047
+ "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
1048
+ );
1038
1049
  }
1039
1050
 
1040
1051
  const accountId = await requireAuth(config);
@@ -1253,6 +1264,15 @@ function createCLIParser(argv: string[]) {
1253
1264
  describe:
1254
1265
  "Time since the UNIX epoch after which the entry expires",
1255
1266
  })
1267
+ .option("metadata", {
1268
+ type: "string",
1269
+ describe: "Arbitrary JSON that is associated with a key",
1270
+ coerce: (jsonStr: string): KeyValue["metadata"] => {
1271
+ try {
1272
+ return JSON.parse(jsonStr);
1273
+ } catch (_) {}
1274
+ },
1275
+ })
1256
1276
  .option("path", {
1257
1277
  type: "string",
1258
1278
  requiresArg: true,
@@ -1260,7 +1280,7 @@ function createCLIParser(argv: string[]) {
1260
1280
  })
1261
1281
  .check(demandOneOfOption("value", "path"));
1262
1282
  },
1263
- async ({ key, ttl, expiration, ...args }) => {
1283
+ async ({ key, ttl, expiration, metadata, ...args }) => {
1264
1284
  await printWranglerBanner();
1265
1285
  const config = readConfig(args.config as ConfigPath, args);
1266
1286
  const namespaceId = getKVNamespaceId(args, config);
@@ -1270,13 +1290,17 @@ function createCLIParser(argv: string[]) {
1270
1290
  : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1271
1291
  args.value!;
1272
1292
 
1293
+ const metadataLog = metadata
1294
+ ? ` with metadata "${JSON.stringify(metadata)}"`
1295
+ : "";
1296
+
1273
1297
  if (args.path) {
1274
1298
  logger.log(
1275
- `Writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}.`
1299
+ `Writing the contents of ${args.path} to the key "${key}" on namespace ${namespaceId}${metadataLog}.`
1276
1300
  );
1277
1301
  } else {
1278
1302
  logger.log(
1279
- `Writing the value "${value}" to key "${key}" on namespace ${namespaceId}.`
1303
+ `Writing the value "${value}" to key "${key}" on namespace ${namespaceId}${metadataLog}.`
1280
1304
  );
1281
1305
  }
1282
1306
 
@@ -1287,6 +1311,7 @@ function createCLIParser(argv: string[]) {
1287
1311
  value,
1288
1312
  expiration,
1289
1313
  expiration_ttl: ttl,
1314
+ metadata: metadata as KeyValue["metadata"],
1290
1315
  });
1291
1316
  }
1292
1317
  )
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
  }
@@ -41,6 +41,7 @@ async function main() {
41
41
  // Start Miniflare development server
42
42
  await mf.startServer();
43
43
  await mf.startScheduler();
44
+ process.send && process.send("ready");
44
45
  } catch (e) {
45
46
  mf.log.error(e as Error);
46
47
  process.exitCode = 1;
@@ -1,5 +1,6 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { env } from "node:process";
3
4
  import { execa, execaCommandSync } from "execa";
4
5
  import { logger } from "./logger";
5
6
 
@@ -19,7 +20,9 @@ export async function getPackageManager(cwd: string): Promise<PackageManager> {
19
20
  const hasYarnLock = existsSync(join(cwd, "yarn.lock"));
20
21
  const hasNpmLock = existsSync(join(cwd, "package-lock.json"));
21
22
  const hasPnpmLock = existsSync(join(cwd, "pnpm-lock.yaml"));
23
+ const userAgent = sniffUserAgent();
22
24
 
25
+ // check for lockfiles
23
26
  if (hasNpmLock) {
24
27
  if (hasNpm) {
25
28
  logger.log(
@@ -60,6 +63,19 @@ export async function getPackageManager(cwd: string): Promise<PackageManager> {
60
63
  }
61
64
  }
62
65
 
66
+ // check the user agent
67
+ if (userAgent === "npm" && hasNpm) {
68
+ logger.log("Using npm as package manager.");
69
+ return { ...NpmPackageManager, cwd };
70
+ } else if (userAgent === "pnpm" && hasPnpm) {
71
+ logger.log("Using pnpm as package manager.");
72
+ return { ...PnpmPackageManager, cwd };
73
+ } else if (userAgent === "yarn" && hasYarn) {
74
+ logger.log("Using yarn as package manager.");
75
+ return { ...YarnPackageManager, cwd };
76
+ }
77
+
78
+ // lastly, check what's installed
63
79
  if (hasNpm) {
64
80
  logger.log("Using npm as package manager.");
65
81
  return { ...NpmPackageManager, cwd };
@@ -172,3 +188,32 @@ function supportsNpm(): Promise<boolean> {
172
188
  function supportsPnpm(): Promise<boolean> {
173
189
  return supports("pnpm");
174
190
  }
191
+
192
+ /**
193
+ * The environment variable `npm_config_user_agent` can be used to
194
+ * guess the package manager that was used to execute wrangler.
195
+ * It's imperfect (just like regular user agent sniffing!)
196
+ * but the package managers we support all set this property:
197
+ *
198
+ * - [npm](https://github.com/npm/cli/blob/1415b4bdeeaabb6e0ba12b6b1b0cc56502bd64ab/lib/utils/config/definitions.js#L1945-L1979)
199
+ * - [pnpm](https://github.com/pnpm/pnpm/blob/cd4f9341e966eb8b411462b48ff0c0612e0a51a7/packages/plugin-commands-script-runners/src/makeEnv.ts#L14)
200
+ * - [yarn](https://yarnpkg.com/advanced/lifecycle-scripts#environment-variables)
201
+ */
202
+ function sniffUserAgent(): "npm" | "pnpm" | "yarn" | undefined {
203
+ const userAgent = env.npm_config_user_agent;
204
+ if (userAgent === undefined) {
205
+ return undefined;
206
+ }
207
+
208
+ if (userAgent.includes("yarn")) {
209
+ return "yarn";
210
+ }
211
+
212
+ if (userAgent.includes("pnpm")) {
213
+ return "pnpm";
214
+ }
215
+
216
+ if (userAgent.includes("npm")) {
217
+ return "npm";
218
+ }
219
+ }
package/src/user/user.tsx CHANGED
@@ -368,6 +368,11 @@ function getAuthTokens(config?: UserAuthConfig): AuthTokens | undefined {
368
368
  refreshToken: { value: refresh_token ?? "" },
369
369
  };
370
370
  } else if (api_token) {
371
+ logger.warn(
372
+ "It looks like you have used Wrangler 1's `config` command to login with an API token.\n" +
373
+ "This is no longer supported in the current version of Wrangler.\n" +
374
+ "If you wish to authenticate via an API token then please set the `CLOUDFLARE_API_TOKEN` environment variable."
375
+ );
371
376
  return { apiToken: api_token };
372
377
  }
373
378
  } catch {
@@ -396,12 +401,8 @@ export function reinitialiseAuthTokens(config?: UserAuthConfig): void {
396
401
  }
397
402
 
398
403
  export function getAPIToken(): ApiCredentials | undefined {
399
- if ("apiToken" in LocalState) {
400
- logger.warn(
401
- "It looks like you have used Wrangler 1's `config` command to login with an API token.\n" +
402
- "This is no longer supported in the current version of Wrangler.\n" +
403
- "If you wish to authenticate via an API token then please set the `CLOUDFLARE_API_TOKEN` environment variable."
404
- );
404
+ if (LocalState.apiToken) {
405
+ return { apiToken: LocalState.apiToken };
405
406
  }
406
407
 
407
408
  const localAPIToken = getAuthFromEnv();
@@ -410,11 +411,7 @@ export function getAPIToken(): ApiCredentials | undefined {
410
411
  const storedAccessToken = LocalState.accessToken?.value;
411
412
  if (storedAccessToken) return { apiToken: storedAccessToken };
412
413
 
413
- if (!process.stdout.isTTY) {
414
- throw new Error(
415
- "In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
416
- );
417
- }
414
+ return undefined;
418
415
  }
419
416
 
420
417
  interface AccessContext {
@@ -846,11 +843,12 @@ async function generatePKCECodes(): Promise<PKCECodes> {
846
843
  * and updates the user auth state with the new credentials.
847
844
  */
848
845
  export function writeAuthConfigFile(config: UserAuthConfig) {
849
- mkdirSync(path.join(os.homedir(), ".wrangler/config/"), {
846
+ const authConfigFilePath = path.join(os.homedir(), USER_AUTH_CONFIG_FILE);
847
+ mkdirSync(path.dirname(authConfigFilePath), {
850
848
  recursive: true,
851
849
  });
852
850
  writeFileSync(
853
- path.join(os.homedir(), USER_AUTH_CONFIG_FILE),
851
+ path.join(authConfigFilePath),
854
852
  TOML.stringify(config as TOML.JsonMap),
855
853
  { encoding: "utf-8" }
856
854
  );
@@ -859,9 +857,8 @@ export function writeAuthConfigFile(config: UserAuthConfig) {
859
857
  }
860
858
 
861
859
  export function readAuthConfigFile(): UserAuthConfig {
862
- const toml = parseTOML(
863
- readFileSync(path.join(os.homedir(), USER_AUTH_CONFIG_FILE))
864
- );
860
+ const authConfigFilePath = path.join(os.homedir(), USER_AUTH_CONFIG_FILE);
861
+ const toml = parseTOML(readFileSync(authConfigFilePath));
865
862
  return toml;
866
863
  }
867
864
 
@@ -1114,8 +1111,14 @@ export async function requireAuth(config: {
1114
1111
  }): Promise<string> {
1115
1112
  const loggedIn = await loginOrRefreshIfRequired();
1116
1113
  if (!loggedIn) {
1117
- // didn't login, let's just quit
1118
- throw new Error("Did not login, quitting...");
1114
+ if (!isInteractive()) {
1115
+ throw new Error(
1116
+ "In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
1117
+ );
1118
+ } else {
1119
+ // didn't login, let's just quit
1120
+ throw new Error("Did not login, quitting...");
1121
+ }
1119
1122
  }
1120
1123
  const accountId = config.account_id || (await getAccountId());
1121
1124
  if (!accountId) {
@@ -1,6 +1,10 @@
1
1
  // DO NOT IMPORT THIS DIRECTLY
2
2
  import worker from "__ENTRY_POINT__";
3
- import { getAssetFromKV } from "__KV_ASSET_HANDLER__";
3
+ import {
4
+ getAssetFromKV,
5
+ NotFoundError,
6
+ MethodNotAllowedError,
7
+ } from "__KV_ASSET_HANDLER__";
4
8
  import manifest from "__STATIC_CONTENT_MANIFEST";
5
9
  const ASSET_MANIFEST = JSON.parse(manifest);
6
10
 
@@ -33,11 +37,13 @@ export default {
33
37
 
34
38
  return response;
35
39
  } catch (e) {
40
+ if (e instanceof NotFoundError || e instanceof MethodNotAllowedError) {
41
+ // if a known error is thrown then serve from actual worker
42
+ return worker.fetch(request, env, ctx);
43
+ }
44
+ // otherwise it's a real error, so throw it
36
45
  console.error(e);
37
- // if an error is thrown then serve from actual worker
38
- return worker.fetch(request, env, ctx);
39
- // TODO: throw here if worker is not available
40
- // (which implies it may be a service-worker)
46
+ return new Response(e.message, { status: 500 });
41
47
  }
42
48
  },
43
49
  };
@@ -15,7 +15,7 @@
15
15
  "lib": [
16
16
  "es2021"
17
17
  ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
18
- // "jsx": "preserve", /* Specify what JSX code is generated. */
18
+ "jsx": "react" /* Specify what JSX code is generated. */,
19
19
  // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
20
20
  // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
21
21
  // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
@@ -42,7 +42,7 @@
42
42
 
43
43
  /* JavaScript Support */
44
44
  "allowJs": true /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */,
45
- "checkJs": true /* Enable error reporting in type-checked JavaScript files. */,
45
+ "checkJs": false /* Enable error reporting in type-checked JavaScript files. */,
46
46
  // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
47
47
 
48
48
  /* Emit */