wrangler 0.0.0-de73fa2 → 0.0.0-e0b9366

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 (94) hide show
  1. package/bin/wrangler.js +2 -2
  2. package/package.json +33 -15
  3. package/pages/functions/buildWorker.ts +1 -1
  4. package/pages/functions/filepath-routing.test.ts +212 -28
  5. package/pages/functions/filepath-routing.ts +91 -131
  6. package/pages/functions/routes.ts +11 -18
  7. package/pages/functions/template-worker.ts +19 -33
  8. package/src/__tests__/configuration.test.ts +2551 -0
  9. package/src/__tests__/dev.test.tsx +393 -59
  10. package/src/__tests__/dev2.test.tsx +152 -0
  11. package/src/__tests__/guess-worker-format.test.ts +58 -0
  12. package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
  13. package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
  14. package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
  15. package/src/__tests__/helpers/mock-account-id.ts +48 -0
  16. package/src/__tests__/helpers/mock-bin.ts +36 -0
  17. package/src/__tests__/{mock-cfetch.ts → helpers/mock-cfetch.ts} +52 -16
  18. package/src/__tests__/helpers/mock-console.ts +70 -0
  19. package/src/__tests__/{mock-dialogs.ts → helpers/mock-dialogs.ts} +1 -1
  20. package/src/__tests__/helpers/mock-kv.ts +33 -0
  21. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  22. package/src/__tests__/helpers/mock-web-socket.ts +29 -0
  23. package/src/__tests__/helpers/run-in-tmp.ts +32 -0
  24. package/src/__tests__/helpers/run-wrangler.ts +16 -0
  25. package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
  26. package/src/__tests__/https-options.test.ts +127 -0
  27. package/src/__tests__/index.test.ts +779 -75
  28. package/src/__tests__/jest.setup.ts +53 -6
  29. package/src/__tests__/kv.test.ts +713 -442
  30. package/src/__tests__/logout.test.ts +52 -0
  31. package/src/__tests__/package-manager.test.ts +206 -0
  32. package/src/__tests__/pages.test.ts +44 -0
  33. package/src/__tests__/parse.test.ts +322 -0
  34. package/src/__tests__/publish.test.ts +2611 -0
  35. package/src/__tests__/r2.test.ts +198 -0
  36. package/src/__tests__/route.test.ts +45 -0
  37. package/src/__tests__/secret.test.ts +425 -0
  38. package/src/__tests__/sentry.test.ts +146 -0
  39. package/src/__tests__/tail.test.ts +564 -0
  40. package/src/__tests__/whoami.test.tsx +100 -0
  41. package/src/bundle.ts +142 -0
  42. package/src/cfetch/index.ts +21 -19
  43. package/src/cfetch/internal.ts +92 -22
  44. package/src/cli.ts +10 -0
  45. package/src/config/README.md +107 -0
  46. package/src/config/config.ts +279 -0
  47. package/src/config/diagnostics.ts +80 -0
  48. package/src/config/environment.ts +305 -0
  49. package/src/config/index.ts +52 -0
  50. package/src/config/validation-helpers.ts +468 -0
  51. package/src/config/validation.ts +1037 -0
  52. package/src/{api/preview.ts → create-worker-preview.ts} +61 -23
  53. package/src/create-worker-upload-form.ts +217 -0
  54. package/src/dev/dev.tsx +357 -0
  55. package/src/dev/local.tsx +241 -0
  56. package/src/dev/remote.tsx +201 -0
  57. package/src/dev/use-esbuild.ts +100 -0
  58. package/src/dialogs.tsx +2 -2
  59. package/src/entry.ts +159 -0
  60. package/src/environment-variables.ts +33 -0
  61. package/src/https-options.ts +122 -0
  62. package/src/index.tsx +1181 -791
  63. package/src/inspect.ts +103 -25
  64. package/src/{kv.tsx → kv.ts} +119 -56
  65. package/src/module-collection.ts +215 -26
  66. package/src/open-in-browser.ts +25 -0
  67. package/src/package-manager.ts +126 -0
  68. package/src/pages.tsx +49 -35
  69. package/src/parse.ts +183 -0
  70. package/src/paths.ts +26 -0
  71. package/src/proxy.ts +175 -39
  72. package/src/publish.ts +291 -301
  73. package/src/r2.ts +50 -0
  74. package/src/reporting.ts +131 -0
  75. package/src/selfsigned.d.ts +29 -0
  76. package/src/sites.tsx +217 -62
  77. package/src/tail/filters.ts +279 -0
  78. package/src/tail/index.ts +299 -0
  79. package/src/tail/printing.ts +62 -0
  80. package/src/user.tsx +268 -177
  81. package/src/whoami.tsx +69 -0
  82. package/src/{api/worker.ts → worker.ts} +45 -37
  83. package/templates/new-worker.js +15 -0
  84. package/templates/new-worker.ts +15 -0
  85. package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
  86. package/templates/tsconfig.json +105 -0
  87. package/wrangler-dist/cli.js +130804 -112757
  88. package/wrangler-dist/cli.js.map +3 -3
  89. package/src/__tests__/run-in-tmp.ts +0 -19
  90. package/src/__tests__/run-wrangler.ts +0 -32
  91. package/src/api/form_data.ts +0 -131
  92. package/src/config.ts +0 -487
  93. package/src/dev.tsx +0 -786
  94. package/src/tail.tsx +0 -73
package/src/inspect.ts CHANGED
@@ -1,12 +1,14 @@
1
- import assert from "assert";
2
- import type { MessageEvent } from "ws";
3
- import WebSocket, { WebSocketServer } from "ws";
4
- import type { IncomingMessage, Server, ServerResponse } from "http";
5
- import { createServer } from "http";
1
+ import assert from "node:assert";
2
+ import { createServer } from "node:http";
3
+ import { URL } from "node:url";
4
+
6
5
  import { useEffect, useRef, useState } from "react";
6
+ import WebSocket, { WebSocketServer } from "ws";
7
7
  import { version } from "../package.json";
8
-
8
+ import { waitForPortToBeAvailable } from "./proxy";
9
9
  import type Protocol from "devtools-protocol";
10
+ import type { IncomingMessage, Server, ServerResponse } from "node:http";
11
+ import type { MessageEvent } from "ws";
10
12
 
11
13
  /**
12
14
  * `useInspector` is a hook for debugging Workers applications
@@ -152,7 +154,14 @@ export default function useInspector(props: InspectorProps) {
152
154
  * of the component lifecycle. Convenient.
153
155
  */
154
156
  useEffect(() => {
155
- server.listen(props.port);
157
+ async function startInspectorProxy() {
158
+ await waitForPortToBeAvailable(props.port, {
159
+ retryPeriod: 200,
160
+ timeout: 2000,
161
+ });
162
+ server.listen(props.port);
163
+ }
164
+ startInspectorProxy().catch((err) => console.error(err));
156
165
  return () => {
157
166
  server.close();
158
167
  // Also disconnect any open websockets/devtools connections
@@ -315,9 +324,14 @@ export default function useInspector(props: InspectorProps) {
315
324
  };
316
325
  }, [
317
326
  props.inspectorUrl,
318
- retryRemoteWebSocketConnectionSigil,
319
327
  props.logToTerminal,
320
328
  wsServer,
329
+ // We use a state value as a sigil to trigger a retry of the
330
+ // remote websocket connection. It's not used inside the effect,
331
+ // so react-hooks/exhaustive-deps doesn't complain if it's not
332
+ // included in the dependency array. But its presence is critical,
333
+ // so do NOT remove it from the dependency list.
334
+ retryRemoteWebSocketConnectionSigil,
321
335
  ]);
322
336
 
323
337
  /**
@@ -361,7 +375,10 @@ export default function useInspector(props: InspectorProps) {
361
375
  );
362
376
  remoteWebSocket.send(event.data);
363
377
  } catch (e) {
364
- if (e.message !== "WebSocket is not open: readyState 0 (CONNECTING)") {
378
+ if (
379
+ (e as Error).message !==
380
+ "WebSocket is not open: readyState 0 (CONNECTING)"
381
+ ) {
365
382
  /**
366
383
  * ^ this just means we haven't opened a websocket yet
367
384
  * usually happens until there's at least one request
@@ -432,6 +449,33 @@ function randomId(): string {
432
449
  * we're just doing a little bit of the work of the devtools console,
433
450
  * directly in the terminal.
434
451
  */
452
+
453
+ export const mapConsoleAPIMessageTypeToConsoleMethod: {
454
+ [key in Protocol.Runtime.ConsoleAPICalledEvent["type"]]: Exclude<
455
+ keyof Console,
456
+ "Console"
457
+ >;
458
+ } = {
459
+ log: "log",
460
+ debug: "debug",
461
+ info: "info",
462
+ warning: "warn",
463
+ error: "error",
464
+ dir: "dir",
465
+ dirxml: "dirxml",
466
+ table: "table",
467
+ trace: "trace",
468
+ clear: "clear",
469
+ count: "count",
470
+ assert: "assert",
471
+ profile: "profile",
472
+ profileEnd: "profileEnd",
473
+ timeEnd: "timeEnd",
474
+ startGroup: "group",
475
+ startGroupCollapsed: "groupCollapsed",
476
+ endGroup: "groupEnd",
477
+ };
478
+
435
479
  function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
436
480
  const args: string[] = [];
437
481
  for (const ro of evt.args) {
@@ -449,7 +493,11 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
449
493
  break;
450
494
  case "object":
451
495
  if (!ro.preview) {
452
- args.push(ro.description ?? "<no-description>");
496
+ args.push(
497
+ ro.subtype === "null"
498
+ ? "null"
499
+ : ro.description ?? "<no-description>"
500
+ );
453
501
  } else {
454
502
  args.push(ro.preview.description ?? "<no-description>");
455
503
 
@@ -467,11 +515,14 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
467
515
  );
468
516
 
469
517
  break;
518
+ case "weakmap":
470
519
  case "map":
471
520
  args.push(
472
521
  "{\n" +
473
- ro.preview.entries
474
- .map(({ key, value }) => {
522
+ // Maps always have entries
523
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
524
+ ro.preview
525
+ .entries!.map(({ key, value }) => {
475
526
  return ` ${key?.description ?? "<unknown>"} => ${
476
527
  value.description
477
528
  }`;
@@ -482,38 +533,47 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
482
533
  );
483
534
 
484
535
  break;
536
+ case "weakset":
485
537
  case "set":
486
538
  args.push(
487
539
  "{ " +
488
- ro.preview.entries
489
- .map(({ value }) => {
540
+ // Sets always have entries
541
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
542
+ ro.preview
543
+ .entries!.map(({ value }) => {
490
544
  return `${value.description}`;
491
545
  })
492
546
  .join(", ") +
493
547
  (ro.preview.overflow ? ", ..." : "") +
494
548
  " }"
495
549
  );
496
-
497
- break;
498
- case "null":
499
- args.push("null");
500
550
  break;
501
- case "node":
502
551
  case "regexp":
552
+ break;
503
553
  case "date":
504
- case "weakmap":
505
- case "weakset":
506
- case "iterator":
554
+ break;
507
555
  case "generator":
508
- case "error":
509
- case "proxy":
556
+ args.push(ro.preview.properties[0].value || "");
557
+ break;
510
558
  case "promise":
559
+ if (ro.preview.properties[0].value === "pending") {
560
+ args.push(`{<${ro.preview.properties[0].value}>}`);
561
+ } else {
562
+ args.push(
563
+ `{<${ro.preview.properties[0].value}>: ${ro.preview.properties[1].value}}`
564
+ );
565
+ }
566
+ break;
567
+ case "node":
568
+ case "iterator":
569
+ case "proxy":
511
570
  case "typedarray":
512
571
  case "arraybuffer":
513
572
  case "dataview":
514
573
  case "webassemblymemory":
515
574
  case "wasmvalue":
516
575
  break;
576
+ case "error":
517
577
  default:
518
578
  // just a pojo
519
579
  args.push(
@@ -535,5 +595,23 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
535
595
  }
536
596
  }
537
597
 
538
- console[evt.type](...args);
598
+ const method = mapConsoleAPIMessageTypeToConsoleMethod[evt.type];
599
+
600
+ if (method in console) {
601
+ switch (method) {
602
+ case "dir":
603
+ console.dir(args);
604
+ break;
605
+ case "table":
606
+ console.table(args);
607
+ break;
608
+ default:
609
+ // eslint-disable-next-line prefer-spread
610
+ console[method].apply(console, args);
611
+ break;
612
+ }
613
+ } else {
614
+ console.warn(`Unsupported console method: ${method}`);
615
+ console.log("console event:", evt);
616
+ }
539
617
  }
@@ -1,13 +1,18 @@
1
1
  import { URLSearchParams } from "node:url";
2
- import type { Config } from "./config";
3
- import { fetchListResult, fetchResult } from "./cfetch";
2
+ import { fetchListResult, fetchResult, fetchKVGetValue } from "./cfetch";
3
+ import type { Config, Environment } from "./config";
4
+
5
+ /** The largest number of kv items we can pass to the API in a single request. */
6
+ const API_MAX = 10000;
7
+ // The const below are halved from the API's true capacity to help avoid
8
+ // hammering it with large requests.
9
+ const BATCH_KEY_MAX = API_MAX / 2;
4
10
 
5
11
  type KvArgs = {
6
12
  binding?: string;
7
13
  "namespace-id"?: string;
8
14
  env?: string;
9
15
  preview?: boolean;
10
- config?: Config;
11
16
  };
12
17
 
13
18
  /**
@@ -91,67 +96,130 @@ export async function listNamespaceKeys(
91
96
  );
92
97
  }
93
98
 
99
+ /**
100
+ * Information about a key-value pair, including its "metadata" fields.
101
+ */
102
+ export interface KeyValue {
103
+ key: string;
104
+ value: string;
105
+ expiration?: number;
106
+ expiration_ttl?: number;
107
+ metadata?: object;
108
+ base64?: boolean;
109
+ }
110
+
111
+ const KeyValueKeys = new Set([
112
+ "key",
113
+ "value",
114
+ "expiration",
115
+ "expiration_ttl",
116
+ "metadata",
117
+ "base64",
118
+ ]);
119
+
120
+ /**
121
+ * Is the given object a valid `KeyValue` type?
122
+ */
123
+ export function isKeyValue(keyValue: object): keyValue is KeyValue {
124
+ const props = Object.keys(keyValue);
125
+ if (!props.includes("key") || !props.includes("value")) {
126
+ return false;
127
+ }
128
+ return true;
129
+ }
130
+
131
+ /**
132
+ * Get all the properties on the `keyValue` that are not expected.
133
+ */
134
+ export function unexpectedKeyValueProps(keyValue: KeyValue): string[] {
135
+ const props = Object.keys(keyValue);
136
+ return props.filter((prop) => !KeyValueKeys.has(prop));
137
+ }
138
+
94
139
  export async function putKeyValue(
95
140
  accountId: string,
96
141
  namespaceId: string,
97
- key: string,
98
- value: string,
99
- args?: { expiration?: number; expiration_ttl?: number }
142
+ keyValue: KeyValue
100
143
  ) {
101
144
  let searchParams: URLSearchParams | undefined;
102
- if (args) {
145
+ if (keyValue.expiration || keyValue.expiration_ttl) {
103
146
  searchParams = new URLSearchParams();
104
- if (args.expiration) {
105
- searchParams.set("expiration", `${args.expiration}`);
147
+ if (keyValue.expiration) {
148
+ searchParams.set("expiration", `${keyValue.expiration}`);
106
149
  }
107
- if (args.expiration_ttl) {
108
- searchParams.set("expiration_ttl", `${args.expiration_ttl}`);
150
+ if (keyValue.expiration_ttl) {
151
+ searchParams.set("expiration_ttl", `${keyValue.expiration_ttl}`);
109
152
  }
110
153
  }
111
154
  return await fetchResult(
112
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${key}`,
113
- { method: "PUT", body: value },
155
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${keyValue.key}`,
156
+ { method: "PUT", body: keyValue.value },
114
157
  searchParams
115
158
  );
116
159
  }
117
160
 
161
+ export async function getKeyValue(
162
+ accountId: string,
163
+ namespaceId: string,
164
+ key: string
165
+ ): Promise<string> {
166
+ return await fetchKVGetValue(accountId, namespaceId, key);
167
+ }
168
+
118
169
  export async function putBulkKeyValue(
119
170
  accountId: string,
120
171
  namespaceId: string,
121
- keyvalueStr: string
172
+ keyValues: KeyValue[],
173
+ progressCallback: (index: number, total: number) => void
122
174
  ) {
123
- return await fetchResult(
124
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
125
- {
126
- method: "PUT",
127
- body: keyvalueStr,
128
- headers: { "Content-Type": "application/json" },
175
+ for (let index = 0; index < keyValues.length; index += BATCH_KEY_MAX) {
176
+ if (progressCallback && keyValues.length > BATCH_KEY_MAX) {
177
+ progressCallback(index, keyValues.length);
129
178
  }
130
- );
179
+
180
+ await fetchResult(
181
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
182
+ {
183
+ method: "PUT",
184
+ body: JSON.stringify(keyValues.slice(index, index + BATCH_KEY_MAX)),
185
+ headers: { "Content-Type": "application/json" },
186
+ }
187
+ );
188
+ }
189
+ if (progressCallback && keyValues.length > BATCH_KEY_MAX) {
190
+ progressCallback(keyValues.length, keyValues.length);
191
+ }
131
192
  }
132
193
 
133
194
  export async function deleteBulkKeyValue(
134
195
  accountId: string,
135
196
  namespaceId: string,
136
- keyStr: string
197
+ keys: string[],
198
+ progressCallback: (index: number, total: number) => void
137
199
  ) {
138
- return await fetchResult(
139
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
140
- {
141
- method: "DELETE",
142
- body: keyStr,
143
- headers: { "Content-Type": "application/json" },
200
+ for (let index = 0; index < keys.length; index += BATCH_KEY_MAX) {
201
+ if (progressCallback && keys.length > BATCH_KEY_MAX) {
202
+ progressCallback(index, keys.length);
144
203
  }
145
- );
204
+
205
+ await fetchResult(
206
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
207
+ {
208
+ method: "DELETE",
209
+ body: JSON.stringify(keys.slice(index, index + BATCH_KEY_MAX)),
210
+ headers: { "Content-Type": "application/json" },
211
+ }
212
+ );
213
+ }
214
+ if (progressCallback && keys.length > BATCH_KEY_MAX) {
215
+ progressCallback(keys.length, keys.length);
216
+ }
146
217
  }
147
218
 
148
- export function getNamespaceId({
149
- preview,
150
- binding,
151
- config,
152
- "namespace-id": namespaceId,
153
- env,
154
- }: KvArgs): string {
219
+ export function getNamespaceId(
220
+ { preview, binding, "namespace-id": namespaceId, env }: KvArgs,
221
+ configOrEnv: Config | Environment | undefined
222
+ ): string {
155
223
  // nice
156
224
  if (namespaceId) {
157
225
  return namespaceId;
@@ -159,13 +227,13 @@ export function getNamespaceId({
159
227
 
160
228
  // begin pre-flight checks
161
229
 
162
- // `--binding` is only valid if there's a wrangler configuration file.
163
- if (binding && !config) {
230
+ // `--binding` is only valid if there's a wrangler configuration.
231
+ if (binding && !configOrEnv) {
164
232
  throw new Error("--binding specified, but no config file was found.");
165
233
  }
166
234
 
167
235
  // there's no config. abort here
168
- if (!config) {
236
+ if (!configOrEnv) {
169
237
  throw new Error(
170
238
  "Failed to find a config file.\n" +
171
239
  "Either use --namespace-id to upload directly or create a configuration file with a binding."
@@ -174,36 +242,31 @@ export function getNamespaceId({
174
242
 
175
243
  // they want to use an environment, actually
176
244
  if (env) {
177
- if (!config.env || !config.env[env]) {
245
+ if (!("env" in configOrEnv) || !configOrEnv.env[env]) {
178
246
  throw new Error(
179
247
  `Failed to find environment "${env}" in configuration file!`
180
248
  );
181
249
  }
182
-
183
- // TODO: either a bespoke arg type for this function to avoid `undefined`s or an `EnvOrConfig` type
184
- return getNamespaceId({
185
- binding,
186
- "namespace-id": namespaceId,
187
- env: undefined,
188
- preview,
189
- config: {
190
- env: undefined,
191
- build: undefined,
192
- name: undefined,
193
- account_id: undefined,
194
- ...config.env[env],
250
+ return getNamespaceId(
251
+ {
252
+ binding,
253
+ "namespace-id": namespaceId,
254
+ preview,
195
255
  },
196
- });
256
+ configOrEnv.env[env]
257
+ );
197
258
  }
198
259
 
199
260
  // there's no KV namespaces
200
- if (!config.kv_namespaces || config.kv_namespaces.length === 0) {
261
+ if (!configOrEnv.kv_namespaces || configOrEnv.kv_namespaces.length === 0) {
201
262
  throw new Error(
202
263
  "No KV Namespaces configured! Either use --namespace-id to upload directly or add a KV namespace to your wrangler config file."
203
264
  );
204
265
  }
205
266
 
206
- const namespace = config.kv_namespaces.find((ns) => ns.binding === binding);
267
+ const namespace = configOrEnv.kv_namespaces.find(
268
+ (ns) => ns.binding === binding
269
+ );
207
270
 
208
271
  // we couldn't find a namespace with that binding
209
272
  if (!namespace) {