wrangler 2.0.27 → 2.0.28

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.
@@ -34,7 +34,7 @@ async function createWorkerNamespace(accountId: string, name: string) {
34
34
  }
35
35
  );
36
36
  logger.log(
37
- `Created Worker namespace "${name}" with ID "${namespace.namespace_id}"`
37
+ `Created dispatch namespace "${name}" with ID "${namespace.namespace_id}"`
38
38
  );
39
39
  }
40
40
 
@@ -46,7 +46,7 @@ async function deleteWorkerNamespace(accountId: string, name: string) {
46
46
  `/accounts/${accountId}/workers/dispatch/namespaces/${name}`,
47
47
  { method: "DELETE" }
48
48
  );
49
- logger.log(`Deleted Worker namespace "${name}"`);
49
+ logger.log(`Deleted dispatch namespace "${name}"`);
50
50
  }
51
51
 
52
52
  /**
@@ -101,7 +101,7 @@ async function renameWorkerNamespace(
101
101
  body: JSON.stringify({ name: newName }),
102
102
  }
103
103
  );
104
- logger.log(`Renamed Worker namespace "${oldName}" to "${newName}"`);
104
+ logger.log(`Renamed dispatch namespace "${oldName}" to "${newName}"`);
105
105
  }
106
106
 
107
107
  export function workerNamespaceCommands(
@@ -110,20 +110,20 @@ export function workerNamespaceCommands(
110
110
  ): Argv {
111
111
  return workerNamespaceYargs
112
112
  .command(subHelp)
113
- .command("list", "List all Worker namespaces", {}, async (args) => {
113
+ .command("list", "List all dispatch namespaces", {}, async (args) => {
114
114
  const config = readConfig(args.config as ConfigPath, args);
115
115
  const accountId = await requireAuth(config);
116
116
  await listWorkerNamespaces(accountId);
117
- await metrics.sendMetricsEvent("list worker namespaces", {
117
+ await metrics.sendMetricsEvent("list dispatch namespaces", {
118
118
  sendMetrics: config.send_metrics,
119
119
  });
120
120
  })
121
121
  .command(
122
122
  "get <name>",
123
- "Get information about a Worker namespace",
123
+ "Get information about a dispatch namespace",
124
124
  (yargs) => {
125
125
  return yargs.positional("name", {
126
- describe: "Name of the Worker namespace",
126
+ describe: "Name of the dispatch namespace",
127
127
  type: "string",
128
128
  demandOption: true,
129
129
  });
@@ -132,17 +132,17 @@ export function workerNamespaceCommands(
132
132
  const config = readConfig(args.config as ConfigPath, args);
133
133
  const accountId = await requireAuth(config);
134
134
  await getWorkerNamespaceInfo(accountId, args.name);
135
- await metrics.sendMetricsEvent("view worker namespace", {
135
+ await metrics.sendMetricsEvent("view dispatch namespace", {
136
136
  sendMetrics: config.send_metrics,
137
137
  });
138
138
  }
139
139
  )
140
140
  .command(
141
141
  "create <name>",
142
- "Create a Worker namespace",
142
+ "Create a dispatch namespace",
143
143
  (yargs) => {
144
144
  return yargs.positional("name", {
145
- describe: "Name of the Worker namespace",
145
+ describe: "Name of the dispatch namespace",
146
146
  type: "string",
147
147
  demandOption: true,
148
148
  });
@@ -152,17 +152,17 @@ export function workerNamespaceCommands(
152
152
  const config = readConfig(args.config as ConfigPath, args);
153
153
  const accountId = await requireAuth(config);
154
154
  await createWorkerNamespace(accountId, args.name);
155
- await metrics.sendMetricsEvent("create worker namespace", {
155
+ await metrics.sendMetricsEvent("create dispatch namespace", {
156
156
  sendMetrics: config.send_metrics,
157
157
  });
158
158
  }
159
159
  )
160
160
  .command(
161
161
  "delete <name>",
162
- "Delete a Worker namespace",
162
+ "Delete a dispatch namespace",
163
163
  (yargs) => {
164
164
  return yargs.positional("name", {
165
- describe: "Name of the Worker namespace",
165
+ describe: "Name of the dispatch namespace",
166
166
  type: "string",
167
167
  demandOption: true,
168
168
  });
@@ -172,23 +172,23 @@ export function workerNamespaceCommands(
172
172
  const config = readConfig(args.config as ConfigPath, args);
173
173
  const accountId = await requireAuth(config);
174
174
  await deleteWorkerNamespace(accountId, args.name);
175
- await metrics.sendMetricsEvent("delete worker namespace", {
175
+ await metrics.sendMetricsEvent("delete dispatch namespace", {
176
176
  sendMetrics: config.send_metrics,
177
177
  });
178
178
  }
179
179
  )
180
180
  .command(
181
181
  "rename <old-name> <new-name>",
182
- "Rename a Worker namespace",
182
+ "Rename a dispatch namespace",
183
183
  (yargs) => {
184
184
  return yargs
185
185
  .positional("old-name", {
186
- describe: "Name of the Worker namespace",
186
+ describe: "Name of the dispatch namespace",
187
187
  type: "string",
188
188
  demandOption: true,
189
189
  })
190
190
  .positional("new-name", {
191
- describe: "New name of the Worker namespace",
191
+ describe: "New name of the dispatch namespace",
192
192
  type: "string",
193
193
  demandOption: true,
194
194
  });
@@ -198,7 +198,7 @@ export function workerNamespaceCommands(
198
198
  const config = readConfig(args.config as ConfigPath, args);
199
199
  const accountId = await requireAuth(config);
200
200
  await renameWorkerNamespace(accountId, args.oldName, args.newName);
201
- await metrics.sendMetricsEvent("rename worker namespace", {
201
+ await metrics.sendMetricsEvent("rename dispatch namespace", {
202
202
  sendMetrics: config.send_metrics,
203
203
  });
204
204
  }
package/src/index.tsx CHANGED
@@ -15,6 +15,7 @@ import { findWranglerToml, readConfig } from "./config";
15
15
  import { createWorkerUploadForm } from "./create-worker-upload-form";
16
16
  import { devHandler, devOptions } from "./dev";
17
17
  import { confirm, prompt } from "./dialogs";
18
+ import { workerNamespaceCommands } from "./dispatch-namespace";
18
19
  import { getEntry } from "./entry";
19
20
  import { DeprecationError } from "./errors";
20
21
  import { generateHandler, generateOptions } from "./generate";
@@ -73,7 +74,7 @@ import {
73
74
  } from "./user";
74
75
  import { whoami } from "./whoami";
75
76
 
76
- import { workerNamespaceCommands } from "./worker-namespace";
77
+ import { getWorkerForZone } from "./zones";
77
78
  import type { Config } from "./config";
78
79
  import type { KeyValue } from "./kv";
79
80
  import type { TailCLIFilters } from "./tail";
@@ -469,6 +470,19 @@ function createCLIParser(argv: string[]) {
469
470
  requiresArg: true,
470
471
  array: true,
471
472
  })
473
+ .option("var", {
474
+ describe:
475
+ "A key-value pair to be injected into the script as a variable",
476
+ type: "string",
477
+ requiresArg: true,
478
+ array: true,
479
+ })
480
+ .option("define", {
481
+ describe: "A key-value pair to be substituted in the script",
482
+ type: "string",
483
+ requiresArg: true,
484
+ array: true,
485
+ })
472
486
  .option("triggers", {
473
487
  describe: "cron schedules to attach",
474
488
  alias: ["schedule", "schedules"],
@@ -562,6 +576,20 @@ function createCLIParser(argv: string[]) {
562
576
  );
563
577
  }
564
578
 
579
+ const cliVars =
580
+ args.var?.reduce<Record<string, string>>((collectVars, v) => {
581
+ const [key, ...value] = v.split(":");
582
+ collectVars[key] = value.join("");
583
+ return collectVars;
584
+ }, {}) || {};
585
+
586
+ const cliDefines =
587
+ args.define?.reduce<Record<string, string>>((collectDefines, d) => {
588
+ const [key, ...value] = d.split(":");
589
+ collectDefines[key] = value.join("");
590
+ return collectDefines;
591
+ }, {}) || {};
592
+
565
593
  const accountId = args.dryRun ? undefined : await requireAuth(config);
566
594
 
567
595
  const assetPaths =
@@ -585,6 +613,8 @@ function createCLIParser(argv: string[]) {
585
613
  ? new Date().toISOString().substring(0, 10)
586
614
  : args["compatibility-date"],
587
615
  compatibilityFlags: args["compatibility-flags"],
616
+ vars: cliVars,
617
+ defines: cliDefines,
588
618
  triggers: args.triggers,
589
619
  jsxFactory: args["jsx-factory"],
590
620
  jsxFragment: args["jsx-fragment"],
@@ -604,12 +634,12 @@ function createCLIParser(argv: string[]) {
604
634
 
605
635
  // tail
606
636
  wrangler.command(
607
- "tail [name]",
637
+ "tail [worker]",
608
638
  "🦚 Starts a log tailing session for a published Worker.",
609
639
  (yargs) => {
610
640
  return yargs
611
- .positional("name", {
612
- describe: "Name of the worker",
641
+ .positional("worker", {
642
+ describe: "Name or route of the worker to tail",
613
643
  type: "string",
614
644
  })
615
645
  .option("format", {
@@ -677,7 +707,22 @@ function createCLIParser(argv: string[]) {
677
707
  sendMetrics: config.send_metrics,
678
708
  });
679
709
 
680
- const scriptName = getLegacyScriptName(args, config);
710
+ let scriptName;
711
+
712
+ // Worker names can't contain "." (and most routes should), so use that as a discriminator
713
+ if (args.worker?.includes(".")) {
714
+ scriptName = await getWorkerForZone(args.worker);
715
+ if (args.format === "pretty") {
716
+ logger.log(
717
+ `Connecting to worker ${scriptName} at route ${args.worker}`
718
+ );
719
+ }
720
+ } else {
721
+ scriptName = getLegacyScriptName(
722
+ { name: args.worker, ...args },
723
+ config
724
+ );
725
+ }
681
726
 
682
727
  if (!scriptName) {
683
728
  throw new Error(
@@ -966,7 +1011,7 @@ function createCLIParser(argv: string[]) {
966
1011
  wasm_modules: {},
967
1012
  text_blobs: {},
968
1013
  data_blobs: {},
969
- worker_namespaces: [],
1014
+ dispatch_namespaces: [],
970
1015
  logfwdr: { schema: undefined, bindings: [] },
971
1016
  unsafe: [],
972
1017
  },
@@ -975,6 +1020,7 @@ function createCLIParser(argv: string[]) {
975
1020
  compatibility_date: undefined,
976
1021
  compatibility_flags: undefined,
977
1022
  usage_model: undefined,
1023
+ keep_bindings: false, // this doesn't matter since it's a new script anyway
978
1024
  }),
979
1025
  }
980
1026
  );
@@ -2022,8 +2068,8 @@ function createCLIParser(argv: string[]) {
2022
2068
  });
2023
2069
 
2024
2070
  wrangler.command(
2025
- "worker-namespace",
2026
- "📦 Interact with a worker namespace",
2071
+ "dispatch-namespace",
2072
+ "📦 Interact with a dispatch namespace",
2027
2073
  (workerNamespaceYargs) => {
2028
2074
  return workerNamespaceCommands(workerNamespaceYargs, subHelp);
2029
2075
  }
@@ -40,11 +40,11 @@ export type EventNames =
40
40
  | "unrevoke pubsub broker credentials"
41
41
  | "list pubsub broker revoked credentials"
42
42
  | "list pubsub broker public-keys"
43
- | "list worker namespaces"
44
- | "view worker namespace"
45
- | "create worker namespace"
46
- | "delete worker namespace"
47
- | "rename worker namespace"
43
+ | "list dispatch namespaces"
44
+ | "view dispatch namespace"
45
+ | "create dispatch namespace"
46
+ | "delete dispatch namespace"
47
+ | "rename dispatch namespace"
48
48
  | "create pages project"
49
49
  | "list pages projects"
50
50
  | "create pages deployment"
package/src/publish.ts CHANGED
@@ -36,6 +36,8 @@ type Props = {
36
36
  compatibilityDate: string | undefined;
37
37
  compatibilityFlags: string[] | undefined;
38
38
  assetPaths: AssetPaths | undefined;
39
+ vars: Record<string, string> | undefined;
40
+ defines: Record<string, string> | undefined;
39
41
  triggers: string[] | undefined;
40
42
  routes: string[] | undefined;
41
43
  legacyEnv: boolean | undefined;
@@ -382,7 +384,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
382
384
  tsconfig: props.tsconfig ?? config.tsconfig,
383
385
  minify,
384
386
  nodeCompat,
385
- define: config.define,
387
+ define: { ...config.define, ...props.defines },
386
388
  checkFetch: false,
387
389
  assets: config.assets && {
388
390
  ...config.assets,
@@ -430,7 +432,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
430
432
  ? { binding: "__STATIC_CONTENT", id: assets.namespace }
431
433
  : []
432
434
  ),
433
- vars: config.vars,
435
+ vars: { ...config.vars, ...props.vars },
434
436
  wasm_modules: config.wasm_modules,
435
437
  text_blobs: {
436
438
  ...config.text_blobs,
@@ -443,7 +445,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
443
445
  durable_objects: config.durable_objects,
444
446
  r2_buckets: config.r2_buckets,
445
447
  services: config.services,
446
- worker_namespaces: config.worker_namespaces,
448
+ dispatch_namespaces: config.dispatch_namespaces,
447
449
  logfwdr: config.logfwdr,
448
450
  unsafe: config.unsafe?.bindings,
449
451
  };
@@ -470,6 +472,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
470
472
  compatibility_flags:
471
473
  props.compatibilityFlags ?? config.compatibility_flags,
472
474
  usage_model: config.usage_model,
475
+ keep_bindings: true,
473
476
  };
474
477
 
475
478
  void printBundleSize(
@@ -482,7 +485,19 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
482
485
  kv_namespaces: config.kv_namespaces,
483
486
  text_blobs: config.text_blobs,
484
487
  };
485
- printBindings(withoutStaticAssets);
488
+
489
+ // mask anything that was overridden in cli args
490
+ // so that we don't log potential secrets into the terminal
491
+ const maskedVars = { ...withoutStaticAssets.vars };
492
+ for (const key of Object.keys(maskedVars)) {
493
+ if (maskedVars[key] !== config.vars[key]) {
494
+ // This means it was overridden in cli args
495
+ // so let's mask it
496
+ maskedVars[key] = "(hidden)";
497
+ }
498
+ }
499
+
500
+ printBindings({ ...withoutStaticAssets, vars: maskedVars });
486
501
 
487
502
  if (!props.dryRun) {
488
503
  // Upload the script so it has time to propagate.
package/src/worker.ts CHANGED
@@ -122,7 +122,7 @@ interface CfService {
122
122
  environment?: string;
123
123
  }
124
124
 
125
- interface CfWorkerNamespace {
125
+ interface CfDispatchNamespace {
126
126
  binding: string;
127
127
  namespace: string;
128
128
  }
@@ -183,7 +183,7 @@ export interface CfWorkerInit {
183
183
  durable_objects: { bindings: CfDurableObject[] } | undefined;
184
184
  r2_buckets: CfR2Bucket[] | undefined;
185
185
  services: CfService[] | undefined;
186
- worker_namespaces: CfWorkerNamespace[] | undefined;
186
+ dispatch_namespaces: CfDispatchNamespace[] | undefined;
187
187
  logfwdr: CfLogfwdr | undefined;
188
188
  unsafe: CfUnsafeBinding[] | undefined;
189
189
  };
@@ -191,6 +191,7 @@ export interface CfWorkerInit {
191
191
  compatibility_date: string | undefined;
192
192
  compatibility_flags: string[] | undefined;
193
193
  usage_model: "bundled" | "unbound" | undefined;
194
+ keep_bindings: boolean | undefined;
194
195
  }
195
196
 
196
197
  export interface CfWorkerContext {
package/src/zones.ts CHANGED
@@ -74,3 +74,94 @@ export async function getZoneIdFromHost(host: string): Promise<string> {
74
74
 
75
75
  throw new Error(`Could not find zone for ${host}`);
76
76
  }
77
+
78
+ /**
79
+ * An object holding information about an assigned worker route, returned from the API
80
+ */
81
+ interface WorkerRoute {
82
+ id: string;
83
+ pattern: string;
84
+ script: string;
85
+ }
86
+
87
+ /**
88
+ * Given a zone within the user's account, return a list of all assigned worker routes
89
+ */
90
+ export async function getRoutesForZone(zone: string): Promise<WorkerRoute[]> {
91
+ const routes = await fetchListResult<WorkerRoute>(
92
+ `/zones/${zone}/workers/routes`
93
+ );
94
+ return routes;
95
+ }
96
+
97
+ /**
98
+ * Given two strings, return the levenshtein distance between them as a simple text match heuristic
99
+ */
100
+ function distanceBetween(a: string, b: string, cache = new Map()): number {
101
+ if (cache.has(`${a}|${b}`)) {
102
+ return cache.get(`${a}|${b}`);
103
+ }
104
+ let result;
105
+ if (b == "") {
106
+ result = a.length;
107
+ } else if (a == "") {
108
+ result = b.length;
109
+ } else if (a[0] === b[0]) {
110
+ result = distanceBetween(a.slice(1), b.slice(1), cache);
111
+ } else {
112
+ result =
113
+ 1 +
114
+ Math.min(
115
+ distanceBetween(a.slice(1), b, cache),
116
+ distanceBetween(a, b.slice(1), cache),
117
+ distanceBetween(a.slice(1), b.slice(1), cache)
118
+ );
119
+ }
120
+ cache.set(`${a}|${b}`, result);
121
+ return result;
122
+ }
123
+
124
+ /**
125
+ * Given an invalid route, sort the valid routes by closeness to the invalid route (levenstein distance)
126
+ */
127
+ export function findClosestRoute(
128
+ providedRoute: string,
129
+ assignedRoutes: WorkerRoute[]
130
+ ): WorkerRoute[] {
131
+ return assignedRoutes.sort((a, b) => {
132
+ const distanceA = distanceBetween(providedRoute, a.pattern);
133
+ const distanceB = distanceBetween(providedRoute, b.pattern);
134
+ return distanceA - distanceB;
135
+ });
136
+ }
137
+
138
+ /**
139
+ * Given a route (must be assigned and within the correct zone), return the name of the worker assigned to it
140
+ */
141
+ export async function getWorkerForZone(worker: string) {
142
+ const zone = await getZoneForRoute(worker);
143
+ if (!zone) {
144
+ throw new Error(
145
+ `The route '${worker}' is not part of one of your zones. Either add this zone from the Cloudflare dashboard, or try using a route within one of your existing zones.`
146
+ );
147
+ }
148
+ const routes = await getRoutesForZone(zone.id);
149
+
150
+ const scriptName = routes.find((route) => route.pattern === worker)?.script;
151
+
152
+ if (!scriptName) {
153
+ const closestRoute = findClosestRoute(worker, routes)?.[0];
154
+
155
+ if (!closestRoute) {
156
+ throw new Error(
157
+ `The route '${worker}' has no workers assigned. You can assign a worker to it from wrangler.toml or the Cloudflare dashboard`
158
+ );
159
+ } else {
160
+ throw new Error(
161
+ `The route '${worker}' has no workers assigned. Did you mean to tail the route '${closestRoute.pattern}'?`
162
+ );
163
+ }
164
+ }
165
+
166
+ return scriptName;
167
+ }
@@ -1,12 +1,16 @@
1
1
  /// <reference types="node" />
2
2
 
3
3
  import { Blob as Blob_2 } from 'buffer';
4
- import { BlobOptions } from 'buffer';
5
4
  import Dispatcher = require('./dispatcher');
6
5
  import { ReadableStream } from 'stream/web';
7
6
  import { URL as URL_2 } from 'url';
8
7
  import { URLSearchParams as URLSearchParams_2 } from 'url';
9
8
 
9
+ declare interface BlobPropertyBag {
10
+ type?: string
11
+ endings?: 'native' | 'transparent'
12
+ }
13
+
10
14
  declare type BodyInit =
11
15
  | ArrayBuffer
12
16
  | AsyncIterable<Uint8Array>
@@ -87,7 +91,7 @@ declare class File extends Blob_2 {
87
91
  * @param fileName The name of the file.
88
92
  * @param options An options object containing optional attributes for the file.
89
93
  */
90
- constructor(fileBits: ReadonlyArray<string | NodeJS.ArrayBufferView | Blob_2>, fileName: string, options?: FileOptions)
94
+ constructor(fileBits: ReadonlyArray<string | NodeJS.ArrayBufferView | Blob_2>, fileName: string, options?: FilePropertyBag)
91
95
 
92
96
  /**
93
97
  * Name of the file referenced by the File object.
@@ -102,7 +106,7 @@ declare class File extends Blob_2 {
102
106
  readonly [Symbol.toStringTag]: string
103
107
  }
104
108
 
105
- declare interface FileOptions extends BlobOptions {
109
+ declare interface FilePropertyBag extends BlobPropertyBag {
106
110
  /**
107
111
  * The last modified date of the file as the number of milliseconds since the Unix epoch (January 1, 1970 at midnight). Files without a known last modified date return the current date.
108
112
  */