wrangler 2.1.0 → 2.1.3

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/dev.tsx CHANGED
@@ -24,7 +24,6 @@ import {
24
24
  isLegacyEnv,
25
25
  DEFAULT_INSPECTOR_PORT,
26
26
  } from "./index";
27
-
28
27
  import type { Config } from "./config";
29
28
  import type { Route } from "./config/environment";
30
29
  import type { EnablePagesAssetsServiceBindingOptions } from "./miniflare-cli";
@@ -66,6 +65,8 @@ interface DevArgs {
66
65
  define?: string[];
67
66
  "node-compat"?: boolean;
68
67
  "experimental-enable-local-persistence"?: boolean;
68
+ persist?: boolean;
69
+ "persist-to"?: string;
69
70
  "live-reload"?: boolean;
70
71
  onReady?: (ip: string, port: number) => void;
71
72
  logLevel?: "none" | "error" | "log" | "warn" | "debug";
@@ -239,8 +240,22 @@ export function devOptions(yargs: Argv): Argv<DevArgs> {
239
240
  type: "boolean",
240
241
  })
241
242
  .option("experimental-enable-local-persistence", {
242
- describe: "Enable persistence for this session (only for local mode)",
243
+ describe:
244
+ "Enable persistence for local mode (deprecated, use --persist)",
243
245
  type: "boolean",
246
+ deprecated: true,
247
+ hidden: true,
248
+ })
249
+ .option("persist", {
250
+ describe:
251
+ "Enable persistence for local mode, using default path: .wrangler/state",
252
+ type: "boolean",
253
+ })
254
+ .option("persist-to", {
255
+ describe:
256
+ "Specify directory to use for local persistence (implies --persist)",
257
+ type: "string",
258
+ requiresArg: true,
244
259
  })
245
260
  .option("live-reload", {
246
261
  // TODO: Add back in once we have remote `--live-reload`
@@ -342,6 +357,7 @@ export async function startDev(args: StartDevOptions) {
342
357
  getLocalPort,
343
358
  getInspectorPort,
344
359
  cliDefines,
360
+ localPersistencePath,
345
361
  } = await validateDevServerSettings(args, config);
346
362
 
347
363
  await metrics.sendMetricsEvent(
@@ -382,9 +398,7 @@ export async function startDev(args: StartDevOptions) {
382
398
  upstreamProtocol={upstreamProtocol}
383
399
  localProtocol={args.localProtocol || configParam.dev.local_protocol}
384
400
  localUpstream={args["local-upstream"] || host}
385
- enableLocalPersistence={
386
- args.experimentalEnableLocalPersistence || false
387
- }
401
+ localPersistencePath={localPersistencePath}
388
402
  liveReload={args.liveReload || false}
389
403
  accountId={configParam.account_id || getAccountFromCache()?.id}
390
404
  assetPaths={assetPaths}
@@ -457,6 +471,7 @@ export async function startApiDev(args: StartDevOptions) {
457
471
  getLocalPort,
458
472
  getInspectorPort,
459
473
  cliDefines,
474
+ localPersistencePath,
460
475
  } = await validateDevServerSettings(args, config);
461
476
 
462
477
  await metrics.sendMetricsEvent(
@@ -493,7 +508,7 @@ export async function startApiDev(args: StartDevOptions) {
493
508
  upstreamProtocol: upstreamProtocol,
494
509
  localProtocol: args.localProtocol || configParam.dev.local_protocol,
495
510
  localUpstream: args["local-upstream"] || host,
496
- enableLocalPersistence: args.experimentalEnableLocalPersistence || false,
511
+ localPersistencePath: localPersistencePath,
497
512
  liveReload: args.liveReload || false,
498
513
  accountId: configParam.account_id || getAccountFromCache()?.id,
499
514
  assetPaths: assetPaths,
@@ -670,6 +685,23 @@ async function validateDevServerSettings(
670
685
  );
671
686
  }
672
687
 
688
+ if (args.experimentalEnableLocalPersistence) {
689
+ logger.warn(
690
+ `--experimental-enable-local-persistence is deprecated.\n` +
691
+ `Move any existing data to .wrangler/state and use --persist, or\n` +
692
+ `use --persist-to=./wrangler-local-state to keep using the old path.`
693
+ );
694
+ }
695
+
696
+ const localPersistencePath = args.persistTo
697
+ ? // If path specified, always treat it as relative to cwd()
698
+ path.resolve(process.cwd(), args.persistTo)
699
+ : args.persist
700
+ ? // If just flagged on, treat it as relative to wrangler.toml,
701
+ // if one can be found, otherwise cwd()
702
+ path.resolve(config.configPath || process.cwd(), ".wrangler/state")
703
+ : null;
704
+
673
705
  const cliDefines =
674
706
  args.define?.reduce<Record<string, string>>((collectDefines, d) => {
675
707
  const [key, ...value] = d.split(":");
@@ -687,6 +719,7 @@ async function validateDevServerSettings(
687
719
  host,
688
720
  routes,
689
721
  cliDefines,
722
+ localPersistencePath,
690
723
  };
691
724
  }
692
725
 
package/src/dialogs.tsx CHANGED
@@ -4,7 +4,11 @@ import SelectInput from "ink-select-input";
4
4
  import TextInput from "ink-text-input";
5
5
  import * as React from "react";
6
6
  import { useState } from "react";
7
+
8
+ import { CI } from "./is-ci";
9
+ import isInteractive from "./is-interactive";
7
10
  import { logger } from "./logger";
11
+
8
12
  type ConfirmProps = {
9
13
  text: string;
10
14
  onConfirm: (answer: boolean) => void;
@@ -135,13 +139,16 @@ export function select(
135
139
  }
136
140
 
137
141
  export async function fromDashMessagePrompt(
138
- deploySource: string
142
+ deploySource: "dash" | "wrangler" | "api"
139
143
  ): Promise<boolean | void> {
140
144
  if (deploySource === "dash") {
141
145
  logger.warn(
142
146
  `You are about to publish a Workers Service that was last published via the Cloudflare Dashboard.
143
147
  Edits that have been made via the dashboard will be overridden by your local code and config.`
144
148
  );
149
+
150
+ if (!isInteractive() || CI.isCI()) return true;
151
+
145
152
  return await confirm("Would you like to continue?");
146
153
  }
147
154
  }
package/src/index.tsx CHANGED
@@ -1163,6 +1163,96 @@ function createCLIParser(argv: string[]) {
1163
1163
  }
1164
1164
  );
1165
1165
 
1166
+ wrangler.command(
1167
+ "secret:bulk <json>",
1168
+ "🗄️ Bulk upload secrets for a Worker",
1169
+ (yargs) => {
1170
+ return yargs
1171
+ .positional("json", {
1172
+ describe: `The JSON file of key-value pairs to upload, in form {"key": value, ...}`,
1173
+ type: "string",
1174
+ demandOption: "true",
1175
+ })
1176
+ .option("name", {
1177
+ describe: "Name of the Worker",
1178
+ type: "string",
1179
+ requiresArg: true,
1180
+ })
1181
+ .option("env", {
1182
+ type: "string",
1183
+ requiresArg: true,
1184
+ describe:
1185
+ "Binds the secret to the Worker of the specific environment.",
1186
+ alias: "e",
1187
+ });
1188
+ },
1189
+ async (secretBulkArgs) => {
1190
+ await printWranglerBanner();
1191
+ const config = readConfig(
1192
+ secretBulkArgs.config as ConfigPath,
1193
+ secretBulkArgs
1194
+ );
1195
+
1196
+ const scriptName = getLegacyScriptName(secretBulkArgs, config);
1197
+ if (!scriptName) {
1198
+ throw new Error(
1199
+ "Required Worker name missing. Please specify the Worker name in wrangler.toml, or pass it as an argument with `--name <worker-name>`"
1200
+ );
1201
+ }
1202
+
1203
+ const accountId = await requireAuth(config);
1204
+
1205
+ logger.log(
1206
+ `🌀 Creating the secrets for the Worker "${scriptName}" ${
1207
+ secretBulkArgs.env && !isLegacyEnv(config)
1208
+ ? `(${secretBulkArgs.env})`
1209
+ : ""
1210
+ }`
1211
+ );
1212
+ const jsonFilePath = path.resolve(secretBulkArgs.json);
1213
+ const content = parseJSON<Record<string, string>>(
1214
+ readFileSync(jsonFilePath),
1215
+ jsonFilePath
1216
+ );
1217
+ for (const key in content) {
1218
+ if (typeof content[key] !== "string") {
1219
+ throw new Error(
1220
+ `The value for ${key} in ${jsonFilePath} is not a string.`
1221
+ );
1222
+ }
1223
+ }
1224
+
1225
+ const url =
1226
+ !secretBulkArgs.env || isLegacyEnv(config)
1227
+ ? `/accounts/${accountId}/workers/scripts/${scriptName}/secrets`
1228
+ : `/accounts/${accountId}/workers/services/${scriptName}/environments/${secretBulkArgs.env}/secrets`;
1229
+ // Until we have a bulk route for secrets, we need to make a request for each key/value pair
1230
+ await Promise.allSettled(
1231
+ Object.entries(content).map(async ([key, value]) => {
1232
+ return fetchResult(url, {
1233
+ method: "PUT",
1234
+ headers: { "Content-Type": "application/json" },
1235
+ body: JSON.stringify({
1236
+ name: key,
1237
+ text: value,
1238
+ type: "secret_text",
1239
+ }),
1240
+ })
1241
+ .then(() => {
1242
+ logger.log(`✨ Successfully created secret for key: ${key}`);
1243
+ })
1244
+ .catch((e) => {
1245
+ logger.error(
1246
+ `🚨 Error uploading secret for key: ${key}:
1247
+ ${e.message}`
1248
+ );
1249
+ });
1250
+ })
1251
+ );
1252
+ return logger.log("✨ Finished processing secrets JSON file");
1253
+ }
1254
+ );
1255
+
1166
1256
  // kv
1167
1257
  // :namespace
1168
1258
  wrangler.command(
@@ -100,7 +100,7 @@ async function generateAssetsFetch(
100
100
  let metadata = createMetadataObject({
101
101
  redirects,
102
102
  headers,
103
- logger: log.warn,
103
+ logger: log.warn.bind(log),
104
104
  });
105
105
 
106
106
  watch([headersFile, redirectsFile], { persistent: true }).on(
package/src/pages/dev.tsx CHANGED
@@ -116,9 +116,22 @@ export function Options(yargs: Argv) {
116
116
  choices: ["http", "https"] as const,
117
117
  },
118
118
  "experimental-enable-local-persistence": {
119
+ describe:
120
+ "Enable persistence for local mode (deprecated, use --persist)",
119
121
  type: "boolean",
120
- default: false,
121
- describe: "Enable persistence for this session (only for local mode)",
122
+ deprecated: true,
123
+ hidden: true,
124
+ },
125
+ persist: {
126
+ describe:
127
+ "Enable persistence for local mode, using default path: .wrangler/state",
128
+ type: "boolean",
129
+ },
130
+ "persist-to": {
131
+ describe:
132
+ "Specify directory to use for local persistence (implies --persist)",
133
+ type: "string",
134
+ requiresArg: true,
122
135
  },
123
136
  "node-compat": {
124
137
  describe: "Enable node.js compatibility",
@@ -151,7 +164,9 @@ export const Handler = async ({
151
164
  r2: r2s = [],
152
165
  "live-reload": liveReload,
153
166
  "local-protocol": localProtocol,
154
- "experimental-enable-local-persistence": experimentalEnableLocalPersistence,
167
+ experimentalEnableLocalPersistence,
168
+ persist,
169
+ persistTo,
155
170
  "node-compat": nodeCompat,
156
171
  config: config,
157
172
  _: [_pages, _dev, ...remaining],
@@ -189,6 +204,14 @@ export const Handler = async ({
189
204
  directory = resolve(directory);
190
205
  }
191
206
 
207
+ if (experimentalEnableLocalPersistence) {
208
+ logger.warn(
209
+ `--experimental-enable-local-persistence is deprecated.\n` +
210
+ `Move any existing data to .wrangler/state and use --persist, or\n` +
211
+ `use --persist-to=./wrangler-local-state to keep using the old path.`
212
+ );
213
+ }
214
+
192
215
  let scriptReadyResolve: () => void;
193
216
  const scriptReadyPromise = new Promise<void>(
194
217
  (promiseResolve) => (scriptReadyResolve = promiseResolve)
@@ -358,7 +381,8 @@ export const Handler = async ({
358
381
  directory,
359
382
  },
360
383
  forceLocal: true,
361
- experimentalEnableLocalPersistence,
384
+ persist,
385
+ persistTo,
362
386
  showInteractiveDevSession: undefined,
363
387
  inspect: true,
364
388
  logLevel: "error",
package/src/publish.ts CHANGED
@@ -220,21 +220,29 @@ export default async function publish(props: Props): Promise<void> {
220
220
  // TODO: warn if git/hg has uncommitted changes
221
221
  const { config, accountId, name } = props;
222
222
  if (accountId && name) {
223
- const serviceMetaData = await fetchResult(
224
- `/accounts/${accountId}/workers/services/${name}`
225
- );
226
- const { default_environment } = serviceMetaData as {
227
- default_environment: {
228
- script: { last_deployed_from: "dash" | "wrangler" | "api" };
223
+ try {
224
+ const serviceMetaData = await fetchResult(
225
+ `/accounts/${accountId}/workers/services/${name}`
226
+ );
227
+ const { default_environment } = serviceMetaData as {
228
+ default_environment: {
229
+ script: { last_deployed_from: "dash" | "wrangler" | "api" };
230
+ };
229
231
  };
230
- };
231
232
 
232
- if (
233
- (await fromDashMessagePrompt(
234
- default_environment.script.last_deployed_from
235
- )) === false
236
- )
237
- return;
233
+ if (
234
+ (await fromDashMessagePrompt(
235
+ default_environment.script.last_deployed_from
236
+ )) === false
237
+ )
238
+ return;
239
+ } catch (e) {
240
+ // code: 10090, message: workers.api.error.service_not_found
241
+ // is thrown from the above fetchResult on the first publish of a Worker
242
+ if ((e as { code?: number }).code !== 10090) {
243
+ logger.error(e);
244
+ }
245
+ }
238
246
  }
239
247
 
240
248
  if (!(props.compatibilityDate || config.compatibility_date)) {
@@ -52,7 +52,8 @@ declare interface DevOptions {
52
52
  nodeCompat?: boolean;
53
53
  compatibilityDate?: string;
54
54
  compatibilityFlags?: string[];
55
- experimentalEnableLocalPersistence?: boolean;
55
+ persist?: boolean;
56
+ persistTo?: string;
56
57
  liveReload?: boolean;
57
58
  watch?: boolean;
58
59
  vars?: {