wrangler 0.0.0-e5d3690 → 0.0.0-e88a54e

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 (98) hide show
  1. package/bin/wrangler.js +2 -2
  2. package/miniflare-dist/index.mjs +79 -0
  3. package/miniflare-dist/index.mjs.map +7 -0
  4. package/package.json +32 -16
  5. package/pages/functions/buildWorker.ts +1 -1
  6. package/pages/functions/filepath-routing.test.ts +212 -28
  7. package/pages/functions/filepath-routing.ts +83 -130
  8. package/pages/functions/routes.ts +11 -18
  9. package/pages/functions/template-worker.ts +3 -9
  10. package/src/__tests__/configuration.test.ts +2796 -0
  11. package/src/__tests__/dev.test.tsx +488 -59
  12. package/src/__tests__/dev2.test.tsx +154 -0
  13. package/src/__tests__/guess-worker-format.test.ts +58 -0
  14. package/src/__tests__/{clipboardy-mock.js → helpers/clipboardy-mock.js} +0 -0
  15. package/src/__tests__/helpers/cmd-shim.d.ts +11 -0
  16. package/src/__tests__/helpers/faye-websocket.d.ts +6 -0
  17. package/src/__tests__/helpers/mock-account-id.ts +48 -0
  18. package/src/__tests__/helpers/mock-bin.ts +36 -0
  19. package/src/__tests__/{mock-cfetch.ts → helpers/mock-cfetch.ts} +48 -14
  20. package/src/__tests__/helpers/mock-console.ts +70 -0
  21. package/src/__tests__/{mock-dialogs.ts → helpers/mock-dialogs.ts} +1 -1
  22. package/src/__tests__/helpers/mock-kv.ts +33 -0
  23. package/src/__tests__/helpers/mock-stdin.ts +103 -0
  24. package/src/__tests__/helpers/mock-web-socket.ts +29 -0
  25. package/src/__tests__/helpers/run-in-tmp.ts +32 -0
  26. package/src/__tests__/helpers/run-wrangler.ts +16 -0
  27. package/src/__tests__/helpers/write-wrangler-toml.ts +17 -0
  28. package/src/__tests__/https-options.test.ts +127 -0
  29. package/src/__tests__/index.test.ts +777 -75
  30. package/src/__tests__/jest.setup.ts +53 -6
  31. package/src/__tests__/kv.test.ts +674 -491
  32. package/src/__tests__/logout.test.ts +52 -0
  33. package/src/__tests__/package-manager.test.ts +206 -0
  34. package/src/__tests__/pages.test.ts +44 -0
  35. package/src/__tests__/parse.test.ts +322 -0
  36. package/src/__tests__/publish.test.ts +2327 -295
  37. package/src/__tests__/r2.test.ts +198 -0
  38. package/src/__tests__/route.test.ts +45 -0
  39. package/src/__tests__/secret.test.ts +425 -0
  40. package/src/__tests__/sentry.test.ts +146 -0
  41. package/src/__tests__/tail.test.ts +564 -0
  42. package/src/__tests__/whoami.test.tsx +26 -56
  43. package/src/bundle.ts +142 -0
  44. package/src/cfetch/index.ts +13 -19
  45. package/src/cfetch/internal.ts +91 -23
  46. package/src/cli.ts +10 -0
  47. package/src/config/README.md +107 -0
  48. package/src/config/config.ts +218 -0
  49. package/src/config/diagnostics.ts +80 -0
  50. package/src/config/environment.ts +366 -0
  51. package/src/config/index.ts +62 -0
  52. package/src/config/validation-helpers.ts +486 -0
  53. package/src/config/validation.ts +1090 -0
  54. package/src/{api/preview.ts → create-worker-preview.ts} +61 -23
  55. package/src/create-worker-upload-form.ts +217 -0
  56. package/src/dev/dev.tsx +364 -0
  57. package/src/dev/local.tsx +232 -0
  58. package/src/dev/remote.tsx +203 -0
  59. package/src/dev/use-esbuild.ts +100 -0
  60. package/src/dialogs.tsx +2 -2
  61. package/src/entry.ts +208 -0
  62. package/src/environment-variables.ts +33 -0
  63. package/src/https-options.ts +122 -0
  64. package/src/index.tsx +1175 -777
  65. package/src/inspect.ts +95 -21
  66. package/src/{kv.tsx → kv.ts} +104 -61
  67. package/src/miniflare-cli/README.md +30 -0
  68. package/src/miniflare-cli/enum-keys.ts +17 -0
  69. package/src/miniflare-cli/index.ts +37 -0
  70. package/src/module-collection.ts +229 -35
  71. package/src/open-in-browser.ts +25 -0
  72. package/src/package-manager.ts +126 -0
  73. package/src/pages.tsx +39 -32
  74. package/src/parse.ts +183 -0
  75. package/src/paths.ts +26 -0
  76. package/src/proxy.ts +177 -39
  77. package/src/publish.ts +274 -309
  78. package/src/r2.ts +50 -0
  79. package/src/reporting.ts +131 -0
  80. package/src/selfsigned.d.ts +29 -0
  81. package/src/sites.tsx +95 -58
  82. package/src/tail/filters.ts +279 -0
  83. package/src/tail/index.ts +299 -0
  84. package/src/tail/printing.ts +62 -0
  85. package/src/user.tsx +233 -134
  86. package/src/{api/worker.ts → worker.ts} +45 -37
  87. package/templates/new-worker.js +15 -0
  88. package/templates/new-worker.ts +15 -0
  89. package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
  90. package/templates/tsconfig.json +105 -0
  91. package/wrangler-dist/cli.js +130352 -112740
  92. package/wrangler-dist/cli.js.map +3 -3
  93. package/src/__tests__/run-in-tmp.ts +0 -19
  94. package/src/__tests__/run-wrangler.ts +0 -32
  95. package/src/api/form_data.ts +0 -131
  96. package/src/config.ts +0 -646
  97. package/src/dev.tsx +0 -779
  98. 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,6 +515,7 @@ 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" +
@@ -484,6 +533,7 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
484
533
  );
485
534
 
486
535
  break;
536
+ case "weakset":
487
537
  case "set":
488
538
  args.push(
489
539
  "{ " +
@@ -497,27 +547,33 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
497
547
  (ro.preview.overflow ? ", ..." : "") +
498
548
  " }"
499
549
  );
500
-
501
550
  break;
502
- case "null":
503
- args.push("null");
504
- break;
505
- case "node":
506
551
  case "regexp":
552
+ break;
507
553
  case "date":
508
- case "weakmap":
509
- case "weakset":
510
- case "iterator":
554
+ break;
511
555
  case "generator":
512
- case "error":
513
- case "proxy":
556
+ args.push(ro.preview.properties[0].value || "");
557
+ break;
514
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":
515
570
  case "typedarray":
516
571
  case "arraybuffer":
517
572
  case "dataview":
518
573
  case "webassemblymemory":
519
574
  case "wasmvalue":
520
575
  break;
576
+ case "error":
521
577
  default:
522
578
  // just a pojo
523
579
  args.push(
@@ -539,5 +595,23 @@ function logConsoleMessage(evt: Protocol.Runtime.ConsoleAPICalledEvent): void {
539
595
  }
540
596
  }
541
597
 
542
- 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
+ }
543
617
  }
@@ -1,13 +1,17 @@
1
1
  import { URLSearchParams } from "node:url";
2
+ import { fetchListResult, fetchResult, fetchKVGetValue } from "./cfetch";
2
3
  import type { Config } from "./config";
3
- import { fetchListResult, fetchResult } from "./cfetch";
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
- env?: string;
9
14
  preview?: boolean;
10
- config?: Config;
11
15
  };
12
16
 
13
17
  /**
@@ -91,67 +95,130 @@ export async function listNamespaceKeys(
91
95
  );
92
96
  }
93
97
 
98
+ /**
99
+ * Information about a key-value pair, including its "metadata" fields.
100
+ */
101
+ export interface KeyValue {
102
+ key: string;
103
+ value: string;
104
+ expiration?: number;
105
+ expiration_ttl?: number;
106
+ metadata?: object;
107
+ base64?: boolean;
108
+ }
109
+
110
+ const KeyValueKeys = new Set([
111
+ "key",
112
+ "value",
113
+ "expiration",
114
+ "expiration_ttl",
115
+ "metadata",
116
+ "base64",
117
+ ]);
118
+
119
+ /**
120
+ * Is the given object a valid `KeyValue` type?
121
+ */
122
+ export function isKeyValue(keyValue: object): keyValue is KeyValue {
123
+ const props = Object.keys(keyValue);
124
+ if (!props.includes("key") || !props.includes("value")) {
125
+ return false;
126
+ }
127
+ return true;
128
+ }
129
+
130
+ /**
131
+ * Get all the properties on the `keyValue` that are not expected.
132
+ */
133
+ export function unexpectedKeyValueProps(keyValue: KeyValue): string[] {
134
+ const props = Object.keys(keyValue);
135
+ return props.filter((prop) => !KeyValueKeys.has(prop));
136
+ }
137
+
94
138
  export async function putKeyValue(
95
139
  accountId: string,
96
140
  namespaceId: string,
97
- key: string,
98
- value: string,
99
- args?: { expiration?: number; expiration_ttl?: number }
141
+ keyValue: KeyValue
100
142
  ) {
101
143
  let searchParams: URLSearchParams | undefined;
102
- if (args) {
144
+ if (keyValue.expiration || keyValue.expiration_ttl) {
103
145
  searchParams = new URLSearchParams();
104
- if (args.expiration) {
105
- searchParams.set("expiration", `${args.expiration}`);
146
+ if (keyValue.expiration) {
147
+ searchParams.set("expiration", `${keyValue.expiration}`);
106
148
  }
107
- if (args.expiration_ttl) {
108
- searchParams.set("expiration_ttl", `${args.expiration_ttl}`);
149
+ if (keyValue.expiration_ttl) {
150
+ searchParams.set("expiration_ttl", `${keyValue.expiration_ttl}`);
109
151
  }
110
152
  }
111
153
  return await fetchResult(
112
- `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${key}`,
113
- { method: "PUT", body: value },
154
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/values/${keyValue.key}`,
155
+ { method: "PUT", body: keyValue.value },
114
156
  searchParams
115
157
  );
116
158
  }
117
159
 
160
+ export async function getKeyValue(
161
+ accountId: string,
162
+ namespaceId: string,
163
+ key: string
164
+ ): Promise<string> {
165
+ return await fetchKVGetValue(accountId, namespaceId, key);
166
+ }
167
+
118
168
  export async function putBulkKeyValue(
119
169
  accountId: string,
120
170
  namespaceId: string,
121
- keyvalueStr: string
171
+ keyValues: KeyValue[],
172
+ progressCallback: (index: number, total: number) => void
122
173
  ) {
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" },
174
+ for (let index = 0; index < keyValues.length; index += BATCH_KEY_MAX) {
175
+ if (progressCallback && keyValues.length > BATCH_KEY_MAX) {
176
+ progressCallback(index, keyValues.length);
129
177
  }
130
- );
178
+
179
+ await fetchResult(
180
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
181
+ {
182
+ method: "PUT",
183
+ body: JSON.stringify(keyValues.slice(index, index + BATCH_KEY_MAX)),
184
+ headers: { "Content-Type": "application/json" },
185
+ }
186
+ );
187
+ }
188
+ if (progressCallback && keyValues.length > BATCH_KEY_MAX) {
189
+ progressCallback(keyValues.length, keyValues.length);
190
+ }
131
191
  }
132
192
 
133
193
  export async function deleteBulkKeyValue(
134
194
  accountId: string,
135
195
  namespaceId: string,
136
- keyStr: string
196
+ keys: string[],
197
+ progressCallback: (index: number, total: number) => void
137
198
  ) {
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" },
199
+ for (let index = 0; index < keys.length; index += BATCH_KEY_MAX) {
200
+ if (progressCallback && keys.length > BATCH_KEY_MAX) {
201
+ progressCallback(index, keys.length);
144
202
  }
145
- );
203
+
204
+ await fetchResult(
205
+ `/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
206
+ {
207
+ method: "DELETE",
208
+ body: JSON.stringify(keys.slice(index, index + BATCH_KEY_MAX)),
209
+ headers: { "Content-Type": "application/json" },
210
+ }
211
+ );
212
+ }
213
+ if (progressCallback && keys.length > BATCH_KEY_MAX) {
214
+ progressCallback(keys.length, keys.length);
215
+ }
146
216
  }
147
217
 
148
- export function getNamespaceId({
149
- preview,
150
- binding,
151
- config,
152
- "namespace-id": namespaceId,
153
- env,
154
- }: KvArgs): string {
218
+ export function getNamespaceId(
219
+ { preview, binding, "namespace-id": namespaceId }: KvArgs,
220
+ config: Config
221
+ ): string {
155
222
  // nice
156
223
  if (namespaceId) {
157
224
  return namespaceId;
@@ -159,7 +226,7 @@ export function getNamespaceId({
159
226
 
160
227
  // begin pre-flight checks
161
228
 
162
- // `--binding` is only valid if there's a wrangler configuration file.
229
+ // `--binding` is only valid if there's a wrangler configuration.
163
230
  if (binding && !config) {
164
231
  throw new Error("--binding specified, but no config file was found.");
165
232
  }
@@ -172,30 +239,6 @@ export function getNamespaceId({
172
239
  );
173
240
  }
174
241
 
175
- // they want to use an environment, actually
176
- if (env) {
177
- if (!config.env || !config.env[env]) {
178
- throw new Error(
179
- `Failed to find environment "${env}" in configuration file!`
180
- );
181
- }
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],
195
- },
196
- });
197
- }
198
-
199
242
  // there's no KV namespaces
200
243
  if (!config.kv_namespaces || config.kv_namespaces.length === 0) {
201
244
  throw new Error(
@@ -0,0 +1,30 @@
1
+ # Custom Miniflare CLI
2
+
3
+ This directory contains a simple wrapper around the programmatic Miniflare API,
4
+ which Wrangler spawns when running `wrangler dev` in local mode.
5
+
6
+ ## Building
7
+
8
+ This CLI is built at the same time as Wrangler by running
9
+
10
+ ```
11
+ npm run -w wrangler build
12
+ ```
13
+
14
+ The output of the build is `miniflare-dist/index.mjs`.
15
+
16
+ ## Running
17
+
18
+ The CLI expects a single command line argument which is the Miniflare options formatted as a string of JSON.
19
+
20
+ ```bash
21
+ node --no-warnings ./packages/wrangler/miniflare-dist/index.mjs '{"watch": true, "script": ""}' --log VERBOSE
22
+ ```
23
+
24
+ The `--log` argument is optional and takes one of Miniflare's LogLevels: "NONE", "ERROR", "WARN", "INFO", "DEBUG", "VERBOSE".
25
+ It defaults to `INFO`.
26
+
27
+ ## Debugging
28
+
29
+ Simply place a breakpoint in the code and run the above command in the VS Code "JavaScript Debug Terminal".
30
+ The code will stop at the breakpoint as expected.
@@ -0,0 +1,17 @@
1
+ export type EnumKeys<Enum> = Exclude<keyof Enum, number>;
2
+
3
+ export const enumObject = <Enum extends Record<string, number | string>>(
4
+ e: Enum
5
+ ) => {
6
+ const copy = { ...e } as { [K in EnumKeys<Enum>]: Enum[K] };
7
+ Object.values(e).forEach(
8
+ (value) => typeof value === "number" && delete copy[value]
9
+ );
10
+ return copy;
11
+ };
12
+
13
+ export const enumKeys = <Enum extends Record<string, number | string>>(
14
+ e: Enum
15
+ ) => {
16
+ return Object.keys(enumObject(e)) as EnumKeys<Enum>[];
17
+ };
@@ -0,0 +1,37 @@
1
+ import { Log, LogLevel, Miniflare } from "miniflare";
2
+ import yargs from "yargs";
3
+ import { hideBin } from "yargs/helpers";
4
+ import { enumKeys } from "./enum-keys";
5
+
6
+ async function main() {
7
+ const args = await yargs(hideBin(process.argv))
8
+ .help(false)
9
+ .version(false)
10
+ .option("log", {
11
+ choices: enumKeys(LogLevel),
12
+ }).argv;
13
+
14
+ const logLevel = LogLevel[args.log ?? "INFO"];
15
+ const config = {
16
+ ...JSON.parse((args._[0] as string) ?? "{}"),
17
+ log: new Log(logLevel),
18
+ };
19
+
20
+ if (logLevel > LogLevel.INFO) {
21
+ console.log("OPTIONS:\n", JSON.stringify(config, null, 2));
22
+ }
23
+
24
+ const mf = new Miniflare(config);
25
+
26
+ try {
27
+ // Start Miniflare development server
28
+ await mf.startServer();
29
+ } catch (e) {
30
+ mf.log.error(e as Error);
31
+ process.exitCode = 1;
32
+ // Unmount any mounted workers
33
+ await mf.dispose();
34
+ }
35
+ }
36
+
37
+ await main();