wrangler 0.0.0-e22f9d7 → 0.0.0-e2b7352

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 (100) hide show
  1. package/README.md +3 -3
  2. package/bin/wrangler.js +12 -3
  3. package/miniflare-dist/index.mjs +79 -0
  4. package/miniflare-dist/index.mjs.map +7 -0
  5. package/package.json +33 -16
  6. package/pages/functions/buildWorker.ts +1 -1
  7. package/pages/functions/filepath-routing.test.ts +212 -28
  8. package/pages/functions/filepath-routing.ts +91 -131
  9. package/pages/functions/routes.ts +11 -18
  10. package/pages/functions/template-worker.ts +19 -33
  11. package/src/__tests__/configuration.test.ts +2820 -0
  12. package/src/__tests__/dev.test.tsx +535 -61
  13. package/src/__tests__/guess-worker-format.test.ts +68 -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} +52 -16
  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 +773 -77
  30. package/src/__tests__/jest.setup.ts +53 -6
  31. package/src/__tests__/kv.test.ts +725 -456
  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 +82 -0
  35. package/src/__tests__/parse.test.ts +322 -0
  36. package/src/__tests__/publish.test.ts +3509 -0
  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 +638 -0
  42. package/src/__tests__/whoami.test.tsx +100 -0
  43. package/src/bundle.ts +155 -0
  44. package/src/cfetch/index.ts +21 -19
  45. package/src/cfetch/internal.ts +92 -22
  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 +371 -0
  51. package/src/config/index.ts +62 -0
  52. package/src/config/validation-helpers.ts +554 -0
  53. package/src/config/validation.ts +1232 -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 +392 -0
  57. package/src/dev/local.tsx +232 -0
  58. package/src/dev/remote.tsx +203 -0
  59. package/src/dev/use-esbuild.ts +104 -0
  60. package/src/dialogs.tsx +2 -2
  61. package/src/entry.ts +240 -0
  62. package/src/environment-variables.ts +33 -0
  63. package/src/https-options.ts +122 -0
  64. package/src/index.tsx +1218 -803
  65. package/src/inspect.ts +103 -25
  66. package/src/intl-polyfill.d.ts +139 -0
  67. package/src/{kv.tsx → kv.ts} +104 -61
  68. package/src/miniflare-cli/README.md +30 -0
  69. package/src/miniflare-cli/enum-keys.ts +17 -0
  70. package/src/miniflare-cli/index.ts +37 -0
  71. package/src/module-collection.ts +229 -35
  72. package/src/open-in-browser.ts +25 -0
  73. package/src/package-manager.ts +126 -0
  74. package/src/pages.tsx +74 -44
  75. package/src/parse.ts +183 -0
  76. package/src/paths.ts +26 -0
  77. package/src/proxy.ts +177 -39
  78. package/src/publish.ts +286 -300
  79. package/src/r2.ts +50 -0
  80. package/src/reporting.ts +131 -0
  81. package/src/selfsigned.d.ts +29 -0
  82. package/src/sites.tsx +224 -62
  83. package/src/tail/filters.ts +279 -0
  84. package/src/tail/index.ts +299 -0
  85. package/src/tail/printing.ts +62 -0
  86. package/src/user.tsx +278 -178
  87. package/src/whoami.tsx +69 -0
  88. package/src/{api/worker.ts → worker.ts} +45 -37
  89. package/templates/new-worker.js +15 -0
  90. package/templates/new-worker.ts +15 -0
  91. package/{static-asset-facade.js → templates/static-asset-facade.js} +0 -0
  92. package/templates/tsconfig.json +105 -0
  93. package/wrangler-dist/cli.js +130377 -112126
  94. package/wrangler-dist/cli.js.map +3 -3
  95. package/src/__tests__/run-in-tmp.ts +0 -19
  96. package/src/__tests__/run-wrangler.ts +0 -32
  97. package/src/api/form_data.ts +0 -131
  98. package/src/config.ts +0 -487
  99. package/src/dev.tsx +0 -784
  100. package/src/tail.tsx +0 -73
package/src/pages.tsx CHANGED
@@ -1,32 +1,36 @@
1
1
  /* eslint-disable no-shadow */
2
2
 
3
- import assert from "assert";
4
- import type { BuilderCallback } from "yargs";
5
- import { join } from "path";
6
- import { tmpdir } from "os";
7
- import { existsSync, lstatSync, readFileSync, writeFileSync } from "fs";
8
- import { execSync, spawn } from "child_process";
9
- import { URL } from "url";
10
- import { getType } from "mime";
11
- import open from "open";
3
+ import { execSync, spawn } from "node:child_process";
4
+ import { existsSync, lstatSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { join } from "node:path";
7
+ import { URL } from "node:url";
12
8
  import { watch } from "chokidar";
13
- import type { BuildResult } from "esbuild";
9
+ import { getType } from "mime";
14
10
  import { buildWorker } from "../pages/functions/buildWorker";
15
- import type { Config } from "../pages/functions/routes";
16
- import { writeRoutesModule } from "../pages/functions/routes";
17
11
  import { generateConfigFromFileTree } from "../pages/functions/filepath-routing";
12
+ import { writeRoutesModule } from "../pages/functions/routes";
13
+ import openInBrowser from "./open-in-browser";
14
+ import { toUrlPath } from "./paths";
15
+ import type { Config } from "../pages/functions/routes";
16
+ import type { Headers, Request, fetch } from "@miniflare/core";
17
+ import type { BuildResult } from "esbuild";
18
+ import type { MiniflareOptions } from "miniflare";
19
+ import type { BuilderCallback } from "yargs";
18
20
 
19
21
  // Defer importing miniflare until we really need it. This takes ~0.5s
20
22
  // and also modifies some `stream/web` and `undici` prototypes, so we
21
23
  // don't want to do this if pages commands aren't being called.
22
- import type { Headers, Request, fetch } from "@miniflare/core";
23
- import type { MiniflareOptions } from "miniflare";
24
24
 
25
- const EXIT_CALLBACKS = [];
25
+ export const pagesBetaWarning =
26
+ "🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/wrangler2/issues/new/choose";
27
+
28
+ const EXIT_CALLBACKS: (() => void)[] = [];
26
29
  const EXIT = (message?: string, code?: number) => {
27
30
  if (message) console.log(message);
28
31
  if (code) process.exitCode = code;
29
32
  EXIT_CALLBACKS.forEach((callback) => callback());
33
+ RUNNING_BUILDERS.forEach((builder) => builder.stop?.());
30
34
  process.exit(code);
31
35
  };
32
36
 
@@ -122,7 +126,9 @@ async function spawnProxyProcess({
122
126
  },
123
127
  }
124
128
  );
125
- EXIT_CALLBACKS.push(() => proxy.kill());
129
+ EXIT_CALLBACKS.push(() => {
130
+ proxy.kill();
131
+ });
126
132
 
127
133
  proxy.stdout.on("data", (data) => {
128
134
  console.log(`[proxy]: ${data}`);
@@ -664,16 +670,17 @@ async function buildFunctions({
664
670
  );
665
671
 
666
672
  const routesModule = join(tmpdir(), "./functionsRoutes.mjs");
673
+ const baseURL = toUrlPath("/");
667
674
 
668
675
  const config: Config = await generateConfigFromFileTree({
669
676
  baseDir: functionsDirectory,
670
- baseURL: "/",
677
+ baseURL,
671
678
  });
672
679
 
673
680
  if (outputConfigPath) {
674
681
  writeFileSync(
675
682
  outputConfigPath,
676
- JSON.stringify({ ...config, baseURL: "/" }, null, 2)
683
+ JSON.stringify({ ...config, baseURL }, null, 2)
677
684
  );
678
685
  }
679
686
 
@@ -756,7 +763,8 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
756
763
  description: "Auto reload HTML pages when change is detected",
757
764
  },
758
765
  // TODO: Miniflare user options
759
- });
766
+ })
767
+ .epilogue(pagesBetaWarning);
760
768
  },
761
769
  async ({
762
770
  local,
@@ -768,8 +776,11 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
768
776
  kv: kvs = [],
769
777
  do: durableObjects = [],
770
778
  "live-reload": liveReload,
771
- "--": remaining = [],
779
+ _: [_pages, _dev, ...remaining],
772
780
  }) => {
781
+ // Beta message for `wrangler pages <commands>` usage
782
+ console.log(pagesBetaWarning);
783
+
773
784
  if (!local) {
774
785
  console.error("Only local mode is supported at the moment.");
775
786
  return;
@@ -792,8 +803,8 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
792
803
 
793
804
  let miniflareArgs: MiniflareOptions = {};
794
805
 
795
- let scriptReadyResolve;
796
- const scriptReadyPromise = new Promise(
806
+ let scriptReadyResolve: () => void;
807
+ const scriptReadyPromise = new Promise<void>(
797
808
  (resolve) => (scriptReadyResolve = resolve)
798
809
  );
799
810
 
@@ -802,13 +813,15 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
802
813
 
803
814
  console.log(`Compiling worker to "${scriptPath}"...`);
804
815
 
805
- await buildFunctions({
806
- scriptPath,
807
- functionsDirectory,
808
- sourcemap: true,
809
- watch: true,
810
- onEnd: () => scriptReadyResolve(),
811
- });
816
+ try {
817
+ await buildFunctions({
818
+ scriptPath,
819
+ functionsDirectory,
820
+ sourcemap: true,
821
+ watch: true,
822
+ onEnd: () => scriptReadyResolve(),
823
+ });
824
+ } catch {}
812
825
 
813
826
  watch([functionsDirectory], {
814
827
  persistent: true,
@@ -827,6 +840,9 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
827
840
  scriptPath,
828
841
  };
829
842
  } else {
843
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
844
+ scriptReadyResolve!();
845
+
830
846
  const scriptPath =
831
847
  directory !== undefined
832
848
  ? join(directory, singleWorkerScriptPath)
@@ -862,11 +878,13 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
862
878
  // internally just waits for that promise to resolve.
863
879
  await scriptReadyPromise;
864
880
 
865
- // Should only be called if no proxyPort, using `assert.fail()` here
866
- // means the type of `assetsFetch` is still `typeof fetch`
867
- const assetsFetch = proxyPort
868
- ? () => assert.fail()
869
- : await generateAssetsFetch(directory);
881
+ // `assetsFetch()` will only be called if there is `proxyPort` defined.
882
+ // We only define `proxyPort`, above, when there is no `directory` defined.
883
+ const assetsFetch =
884
+ directory !== undefined
885
+ ? await generateAssetsFetch(directory)
886
+ : invalidAssetsFetch;
887
+
870
888
  const miniflare = new Miniflare({
871
889
  port,
872
890
  watch: true,
@@ -887,17 +905,19 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
887
905
  // User bindings
888
906
  bindings: {
889
907
  ...Object.fromEntries(
890
- bindings.map((binding) => binding.toString().split("="))
908
+ bindings
909
+ .map((binding) => binding.toString().split("="))
910
+ .map(([key, ...values]) => [key, values.join("=")])
891
911
  ),
892
912
  },
893
913
 
894
914
  // env.ASSETS.fetch
895
915
  serviceBindings: {
896
- async ASSETS(request) {
916
+ async ASSETS(request: Request) {
897
917
  if (proxyPort) {
898
918
  try {
899
919
  const url = new URL(request.url);
900
- url.host = `127.0.0.1:${proxyPort}`;
920
+ url.host = `localhost:${proxyPort}`;
901
921
  return await fetch(url, request);
902
922
  } catch (thrown) {
903
923
  console.error(`Could not proxy request: ${thrown}`);
@@ -935,12 +955,10 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
935
955
  try {
936
956
  // `startServer` might throw if user code contains errors
937
957
  const server = await miniflare.startServer();
938
- console.log(`Serving at http://127.0.0.1:${port}/`);
958
+ console.log(`Serving at http://localhost:${port}/`);
939
959
 
940
960
  if (process.env.BROWSER !== "none") {
941
- const childProcess = await open(`http://127.0.0.1:${port}/`);
942
- // fail silently if the open command doesn't work (e.g. in GitHub Codespaces)
943
- childProcess.on("error", (_err) => {});
961
+ await openInBrowser(`http://localhost:${port}/`);
944
962
  }
945
963
 
946
964
  if (directory !== undefined && liveReload) {
@@ -957,12 +975,14 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
957
975
  miniflare.dispose().catch((err) => miniflare.log.error(err));
958
976
  });
959
977
  } catch (e) {
960
- miniflare.log.error(e);
978
+ miniflare.log.error(e as Error);
961
979
  EXIT("Could not start Miniflare.", 1);
962
980
  }
963
981
  }
964
982
  )
965
- .command("functions", "Cloudflare Pages Functions", (yargs) =>
983
+ .command("functions", false, (yargs) =>
984
+ // we hide this command from help output because
985
+ // it's not meant to be used directly right now
966
986
  yargs.command(
967
987
  "build [directory]",
968
988
  "Compile a folder of Cloudflare Pages Functions into a single Worker",
@@ -1006,7 +1026,8 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
1006
1026
  description:
1007
1027
  "Watch for changes to the functions and automatically rebuild the Worker script",
1008
1028
  },
1009
- }),
1029
+ })
1030
+ .epilogue(pagesBetaWarning),
1010
1031
  async ({
1011
1032
  directory,
1012
1033
  "script-path": scriptPath,
@@ -1016,6 +1037,9 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
1016
1037
  fallbackService,
1017
1038
  watch,
1018
1039
  }) => {
1040
+ // Beta message for `wrangler pages <commands>` usage
1041
+ console.log(pagesBetaWarning);
1042
+
1019
1043
  await buildFunctions({
1020
1044
  scriptPath,
1021
1045
  outputConfigPath,
@@ -1029,3 +1053,9 @@ export const pages: BuilderCallback<unknown, unknown> = (yargs) => {
1029
1053
  )
1030
1054
  );
1031
1055
  };
1056
+
1057
+ const invalidAssetsFetch: typeof fetch = () => {
1058
+ throw new Error(
1059
+ "Trying to fetch assets directly when there is no `directory` option specified, and not in `local` mode."
1060
+ );
1061
+ };
package/src/parse.ts ADDED
@@ -0,0 +1,183 @@
1
+ import * as fs from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import TOML from "@iarna/toml";
4
+ import { formatMessagesSync } from "esbuild";
5
+
6
+ export type Message = {
7
+ text: string;
8
+ location?: Location;
9
+ notes?: Message[];
10
+ kind?: "warning" | "error";
11
+ };
12
+
13
+ export type Location = File & {
14
+ line: number;
15
+ column: number;
16
+ length?: number;
17
+ lineText?: string;
18
+ suggestion?: string;
19
+ };
20
+
21
+ export type File = {
22
+ file?: string;
23
+ fileText?: string;
24
+ };
25
+
26
+ /**
27
+ * Formats a `Message` using esbuild's pretty-printing algorithm.
28
+ */
29
+ export function formatMessage(
30
+ { text, notes, location, kind = "error" }: Message,
31
+ color = true
32
+ ): string {
33
+ const input = { text, notes, location };
34
+ delete input.location?.fileText;
35
+ for (const note of notes ?? []) {
36
+ delete note.location?.fileText;
37
+ }
38
+ const lines = formatMessagesSync([input], {
39
+ color,
40
+ kind: kind,
41
+ terminalWidth: process.stderr.columns,
42
+ });
43
+ return lines.join("\n");
44
+ }
45
+
46
+ /**
47
+ * An error that's thrown when something fails to parse.
48
+ */
49
+ export class ParseError extends Error implements Message {
50
+ readonly text: string;
51
+ readonly notes: Message[];
52
+ readonly location?: Location;
53
+ readonly kind: "warning" | "error";
54
+
55
+ constructor({ text, notes, location, kind }: Message) {
56
+ super(text);
57
+ this.name = this.constructor.name;
58
+ this.text = text;
59
+ this.notes = notes ?? [];
60
+ this.location = location;
61
+ this.kind = kind ?? "error";
62
+ }
63
+ }
64
+
65
+ /* eslint-disable @typescript-eslint/no-explicit-any */
66
+
67
+ const TOML_ERROR_NAME = "TomlError";
68
+ const TOML_ERROR_SUFFIX = " at row ";
69
+
70
+ type TomlError = Error & {
71
+ line: number;
72
+ col: number;
73
+ };
74
+
75
+ /**
76
+ * A wrapper around `TOML.parse` that throws a `ParseError`.
77
+ */
78
+ export function parseTOML(input: string, file?: string): object {
79
+ try {
80
+ return TOML.parse(input);
81
+ } catch (err) {
82
+ const { name, message, line, col } = err as TomlError;
83
+ if (name !== TOML_ERROR_NAME) {
84
+ throw err;
85
+ }
86
+ const text = message.substring(0, message.lastIndexOf(TOML_ERROR_SUFFIX));
87
+ const lineText = input.split("\n")[line];
88
+ const location = {
89
+ lineText,
90
+ line: line + 1,
91
+ column: col - 1,
92
+ file,
93
+ fileText: input,
94
+ };
95
+ throw new ParseError({ text, location });
96
+ }
97
+ }
98
+
99
+ const JSON_ERROR_SUFFIX = " in JSON at position ";
100
+
101
+ /**
102
+ * A wrapper around `JSON.parse` that throws a `ParseError`.
103
+ */
104
+ export function parseJSON(input: string, file?: string): any {
105
+ try {
106
+ return JSON.parse(input);
107
+ } catch (err) {
108
+ const { message } = err as Error;
109
+ const index = message.lastIndexOf(JSON_ERROR_SUFFIX);
110
+ if (index < 0) {
111
+ throw err;
112
+ }
113
+ const text = message.substring(0, index);
114
+ const position = parseInt(
115
+ message.substring(index + JSON_ERROR_SUFFIX.length)
116
+ );
117
+ const location = indexLocation({ file, fileText: input }, position);
118
+ throw new ParseError({ text, location });
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Reads a file and parses it based on its type.
124
+ */
125
+ export function readFileSync(file: string): string {
126
+ try {
127
+ return fs.readFileSync(file, { encoding: "utf-8" });
128
+ } catch (err) {
129
+ const { message } = err as Error;
130
+ throw new ParseError({
131
+ text: `Could not read file: ${file}`,
132
+ notes: [
133
+ {
134
+ text: message.replace(file, resolve(file)),
135
+ },
136
+ ],
137
+ });
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Calculates the line and column location from an index.
143
+ */
144
+ export function indexLocation(file: File, index: number): Location {
145
+ let lineText,
146
+ line = 0,
147
+ column = 0,
148
+ cursor = 0;
149
+ const { fileText = "" } = file;
150
+ for (const row of fileText.split("\n")) {
151
+ line++;
152
+ cursor += row.length + 1;
153
+ if (cursor >= index) {
154
+ lineText = row;
155
+ column = row.length - (cursor - index);
156
+ break;
157
+ }
158
+ }
159
+ return { lineText, line, column, ...file };
160
+ }
161
+
162
+ /**
163
+ * Guesses the line and column location of a search query.
164
+ */
165
+ export function searchLocation(file: File, query: unknown): Location {
166
+ let lineText,
167
+ length,
168
+ line = 0,
169
+ column = 0;
170
+ const queryText = String(query);
171
+ const { fileText = "" } = file;
172
+ for (const content of fileText.split("\n")) {
173
+ line++;
174
+ const index = content.indexOf(queryText);
175
+ if (index >= 0) {
176
+ lineText = content;
177
+ column = index;
178
+ length = queryText.length;
179
+ break;
180
+ }
181
+ }
182
+ return { lineText, line, column, length, ...file };
183
+ }
package/src/paths.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { assert } from "console";
2
+
3
+ type DiscriminatedPath<Discriminator extends string> = string & {
4
+ _discriminator: Discriminator;
5
+ };
6
+
7
+ /**
8
+ * A branded string that expects to be URL compatible.
9
+ *
10
+ * Require this type when you want callers to ensure that they have converted file-path strings into URL-safe paths.
11
+ */
12
+ export type UrlPath = DiscriminatedPath<"UrlPath">;
13
+
14
+ /**
15
+ * Convert a file-path string to a URL-path string.
16
+ *
17
+ * Use this helper to convert a `string` to a `UrlPath` when it is not clear whether the string needs normalizing.
18
+ * Replaces all back-slashes with forward-slashes, and throws an error if the path contains a drive letter (e.g. `C:`).
19
+ */
20
+ export function toUrlPath(path: string): UrlPath {
21
+ assert(
22
+ !/^[a-z]:/i.test(path),
23
+ "Tried to convert a Windows file path with a drive to a URL path."
24
+ );
25
+ return path.replace(/\\/g, "/") as UrlPath;
26
+ }