wrangler 2.0.22 → 2.0.25

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 (75) hide show
  1. package/README.md +20 -2
  2. package/bin/wrangler.js +1 -1
  3. package/miniflare-dist/index.mjs +643 -7
  4. package/package.json +17 -5
  5. package/src/__tests__/configuration.test.ts +89 -17
  6. package/src/__tests__/dev.test.tsx +121 -8
  7. package/src/__tests__/generate.test.ts +93 -0
  8. package/src/__tests__/helpers/mock-cfetch.ts +54 -2
  9. package/src/__tests__/index.test.ts +10 -27
  10. package/src/__tests__/jest.setup.ts +31 -1
  11. package/src/__tests__/kv.test.ts +82 -61
  12. package/src/__tests__/metrics.test.ts +5 -0
  13. package/src/__tests__/publish.test.ts +573 -254
  14. package/src/__tests__/r2.test.ts +173 -71
  15. package/src/__tests__/tail.test.ts +93 -39
  16. package/src/__tests__/user.test.ts +1 -0
  17. package/src/__tests__/validate-dev-props.test.ts +56 -0
  18. package/src/__tests__/version.test.ts +35 -0
  19. package/src/__tests__/whoami.test.tsx +60 -1
  20. package/src/api/dev.ts +49 -9
  21. package/src/bundle.ts +298 -37
  22. package/src/cfetch/internal.ts +34 -2
  23. package/src/config/config.ts +15 -3
  24. package/src/config/environment.ts +40 -8
  25. package/src/config/index.ts +13 -0
  26. package/src/config/validation.ts +111 -9
  27. package/src/create-worker-preview.ts +3 -1
  28. package/src/create-worker-upload-form.ts +25 -0
  29. package/src/dev/dev.tsx +145 -31
  30. package/src/dev/local.tsx +116 -24
  31. package/src/dev/remote.tsx +39 -12
  32. package/src/dev/use-esbuild.ts +28 -0
  33. package/src/dev/validate-dev-props.ts +31 -0
  34. package/src/dev-registry.tsx +160 -0
  35. package/src/dev.tsx +148 -67
  36. package/src/generate.ts +112 -14
  37. package/src/index.tsx +252 -7
  38. package/src/inspect.ts +90 -5
  39. package/src/metrics/index.ts +1 -0
  40. package/src/metrics/metrics-dispatcher.ts +1 -0
  41. package/src/metrics/metrics-usage-headers.ts +24 -0
  42. package/src/metrics/send-event.ts +2 -2
  43. package/src/miniflare-cli/assets.ts +546 -0
  44. package/src/miniflare-cli/index.ts +157 -6
  45. package/src/module-collection.ts +3 -3
  46. package/src/pages/build.tsx +36 -28
  47. package/src/pages/constants.ts +4 -0
  48. package/src/pages/deployments.tsx +10 -10
  49. package/src/pages/dev.tsx +155 -651
  50. package/src/pages/functions/buildPlugin.ts +4 -0
  51. package/src/pages/functions/buildWorker.ts +4 -0
  52. package/src/pages/functions/routes-consolidation.test.ts +66 -0
  53. package/src/pages/functions/routes-consolidation.ts +29 -0
  54. package/src/pages/functions/routes-transformation.test.ts +271 -0
  55. package/src/pages/functions/routes-transformation.ts +125 -0
  56. package/src/pages/projects.tsx +9 -3
  57. package/src/pages/publish.tsx +57 -15
  58. package/src/pages/types.ts +9 -0
  59. package/src/pages/upload.tsx +38 -21
  60. package/src/publish.ts +139 -112
  61. package/src/r2.ts +81 -0
  62. package/src/tail/index.ts +15 -2
  63. package/src/tail/printing.ts +41 -3
  64. package/src/user/choose-account.tsx +20 -11
  65. package/src/user/user.tsx +20 -2
  66. package/src/whoami.tsx +79 -1
  67. package/src/worker.ts +12 -0
  68. package/templates/first-party-worker-module-facade.ts +18 -0
  69. package/templates/format-dev-errors.ts +32 -0
  70. package/templates/pages-shim.ts +9 -0
  71. package/templates/{static-asset-facade.js → serve-static-assets.ts} +21 -7
  72. package/templates/service-bindings-module-facade.js +51 -0
  73. package/templates/service-bindings-sw-facade.js +39 -0
  74. package/wrangler-dist/cli.d.ts +38 -3
  75. package/wrangler-dist/cli.js +45244 -25199
@@ -1,8 +1,28 @@
1
- import { Log, LogLevel, Miniflare } from "miniflare";
1
+ import { fetch } from "@miniflare/core";
2
+ import {
3
+ DurableObjectNamespace,
4
+ DurableObjectStub,
5
+ } from "@miniflare/durable-objects";
6
+ import {
7
+ Log,
8
+ LogLevel,
9
+ Miniflare,
10
+ Response as MiniflareResponse,
11
+ Request as MiniflareRequest,
12
+ } from "miniflare";
2
13
  import yargs from "yargs";
3
14
  import { hideBin } from "yargs/helpers";
15
+ import { FatalError } from "../errors";
16
+ import generateASSETSBinding from "./assets";
4
17
  import { enumKeys } from "./enum-keys";
5
18
  import { getRequestContextCheckOptions } from "./request-context";
19
+ import type { Options } from "./assets";
20
+ import type { AddressInfo } from "net";
21
+
22
+ export interface EnablePagesAssetsServiceBindingOptions {
23
+ proxyPort?: number;
24
+ directory?: string;
25
+ }
6
26
 
7
27
  // miniflare defines this but importing it throws:
8
28
  // Dynamic require of "path" is not supported
@@ -29,24 +49,155 @@ async function main() {
29
49
  ...requestContextCheckOptions,
30
50
  };
31
51
  //miniflare's logLevel 0 still logs routes, so lets override the logger
32
- config.log = config.disableLogs ? new NoOpLog() : new Log(logLevel);
52
+ config.log = config.disableLogs
53
+ ? new NoOpLog()
54
+ : new Log(logLevel, config.logOptions);
33
55
 
34
56
  if (logLevel > LogLevel.INFO) {
35
57
  console.log("OPTIONS:\n", JSON.stringify(config, null, 2));
36
58
  }
37
59
 
38
- const mf = new Miniflare(config);
60
+ config.bindings = {
61
+ ...config.bindings,
62
+ ...Object.fromEntries(
63
+ Object.entries(
64
+ config.externalDurableObjects as Record<
65
+ string,
66
+ { name: string; host: string; port: number }
67
+ >
68
+ ).map(([binding, { name, host, port }]) => {
69
+ const factory = () => {
70
+ throw new FatalError(
71
+ "An external Durable Object instance's state has somehow been attempted to be accessed.",
72
+ 1
73
+ );
74
+ };
75
+ const namespace = new DurableObjectNamespace(name as string, factory);
76
+ namespace.get = (id) => {
77
+ const stub = new DurableObjectStub(factory, id);
78
+ stub.fetch = (...reqArgs) => {
79
+ const url = `http://${host}${port ? `:${port}` : ""}`;
80
+ const request = new MiniflareRequest(
81
+ url,
82
+ new MiniflareRequest(...reqArgs)
83
+ );
84
+ request.headers.set("x-miniflare-durable-object-name", name);
85
+ request.headers.set("x-miniflare-durable-object-id", id.toString());
86
+
87
+ return fetch(request);
88
+ };
89
+ return stub;
90
+ };
91
+ return [binding, namespace];
92
+ })
93
+ ),
94
+ };
95
+
96
+ let mf: Miniflare | undefined;
97
+ let durableObjectsMf: Miniflare | undefined = undefined;
98
+ let durableObjectsMfPort: number | undefined = undefined;
39
99
 
40
100
  try {
101
+ if (args._[1]) {
102
+ const opts: EnablePagesAssetsServiceBindingOptions = JSON.parse(
103
+ args._[1] as string
104
+ );
105
+
106
+ if (isNaN(opts.proxyPort || NaN) && !opts.directory) {
107
+ throw new Error(
108
+ "MiniflareCLIOptions: built in service bindings set to true, but no port or directory provided"
109
+ );
110
+ }
111
+
112
+ const options: Options = {
113
+ log: config.log,
114
+ proxyPort: opts.proxyPort,
115
+ directory: opts.directory,
116
+ };
117
+
118
+ config.serviceBindings = {
119
+ ...config.serviceBindings,
120
+ ASSETS: await generateASSETSBinding(options),
121
+ };
122
+ }
123
+ mf = new Miniflare(config);
41
124
  // Start Miniflare development server
42
125
  await mf.startServer();
43
126
  await mf.startScheduler();
44
- process.send && process.send("ready");
127
+
128
+ const internalDurableObjectClassNames = Object.values(
129
+ config.durableObjects as Record<string, string>
130
+ );
131
+
132
+ if (internalDurableObjectClassNames.length > 0) {
133
+ durableObjectsMf = new Miniflare({
134
+ host: config.host,
135
+ port: 0,
136
+ script: `
137
+ export default {
138
+ fetch(request, env) {
139
+ return env.DO.fetch(request)
140
+ }
141
+ }`,
142
+ serviceBindings: {
143
+ DO: async (request: MiniflareRequest) => {
144
+ request = new MiniflareRequest(request);
145
+
146
+ const name = request.headers.get("x-miniflare-durable-object-name");
147
+ const idString = request.headers.get(
148
+ "x-miniflare-durable-object-id"
149
+ );
150
+ request.headers.delete("x-miniflare-durable-object-name");
151
+ request.headers.delete("x-miniflare-durable-object-id");
152
+
153
+ if (!name || !idString) {
154
+ return new MiniflareResponse(
155
+ "[durable-object-proxy-err] Missing `x-miniflare-durable-object-name` or `x-miniflare-durable-object-id` headers.",
156
+ { status: 400 }
157
+ );
158
+ }
159
+
160
+ const namespace = await mf?.getDurableObjectNamespace(name);
161
+ const id = namespace?.idFromString(idString);
162
+
163
+ if (!id) {
164
+ return new MiniflareResponse(
165
+ "[durable-object-proxy-err] Could not generate an ID. Possibly due to a mismatched DO name and ID?",
166
+ { status: 500 }
167
+ );
168
+ }
169
+
170
+ const stub = namespace?.get(id);
171
+
172
+ if (!stub) {
173
+ return new MiniflareResponse(
174
+ "[durable-object-proxy-err] Could not generate a stub. Possibly due to a mismatched DO name and ID?",
175
+ { status: 500 }
176
+ );
177
+ }
178
+
179
+ return stub.fetch(request);
180
+ },
181
+ },
182
+ modules: true,
183
+ });
184
+ const server = await durableObjectsMf.startServer();
185
+ durableObjectsMfPort = (server.address() as AddressInfo).port;
186
+ }
187
+
188
+ process.send &&
189
+ process.send(
190
+ JSON.stringify({
191
+ ready: true,
192
+ durableObjectsPort: durableObjectsMfPort,
193
+ })
194
+ );
45
195
  } catch (e) {
46
- mf.log.error(e as Error);
196
+ mf?.log.error(e as Error);
47
197
  process.exitCode = 1;
48
198
  // Unmount any mounted workers
49
- await mf.dispose();
199
+ await mf?.dispose();
200
+ await durableObjectsMf?.dispose();
50
201
  }
51
202
  }
52
203
 
@@ -123,9 +123,9 @@ export default function createModuleCollector(props: {
123
123
  {
124
124
  filter: new RegExp(
125
125
  "^(" +
126
- [...props.wrangler1xlegacyModuleReferences.fileNames].join(
127
- "|"
128
- ) +
126
+ [...props.wrangler1xlegacyModuleReferences.fileNames]
127
+ .map((name) => name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
128
+ .join("|") +
129
129
  ")$"
130
130
  ),
131
131
  },
@@ -9,24 +9,15 @@ import { buildPlugin } from "./functions/buildPlugin";
9
9
  import { buildWorker } from "./functions/buildWorker";
10
10
  import { generateConfigFromFileTree } from "./functions/filepath-routing";
11
11
  import { writeRoutesModule } from "./functions/routes";
12
+ import { convertRoutesToRoutesJSONSpec } from "./functions/routes-transformation";
12
13
  import { pagesBetaWarning, RUNNING_BUILDERS } from "./utils";
13
14
  import type { Config } from "./functions/routes";
14
- import type { ArgumentsCamelCase, Argv } from "yargs";
15
+ import type { YargsOptionsToInterface } from "./types";
16
+ import type { Argv } from "yargs";
15
17
 
16
- type PagesBuildArgs = {
17
- directory: string;
18
- outfile: string;
19
- "output-config-path"?: string;
20
- minify: boolean;
21
- sourcemap: boolean;
22
- "fallback-service": string;
23
- watch: boolean;
24
- plugin: boolean;
25
- "build-output-directory"?: string;
26
- "node-compat": boolean;
27
- };
18
+ type PagesBuildArgs = YargsOptionsToInterface<typeof Options>;
28
19
 
29
- export function Options(yargs: Argv): Argv<PagesBuildArgs> {
20
+ export function Options(yargs: Argv) {
30
21
  return yargs
31
22
  .positional("directory", {
32
23
  type: "string",
@@ -43,6 +34,10 @@ export function Options(yargs: Argv): Argv<PagesBuildArgs> {
43
34
  type: "string",
44
35
  description: "The location for the output config file",
45
36
  },
37
+ "output-routes-path": {
38
+ type: "string",
39
+ description: "The location for the output _routes.json file",
40
+ },
46
41
  minify: {
47
42
  type: "boolean",
48
43
  default: false,
@@ -87,15 +82,16 @@ export function Options(yargs: Argv): Argv<PagesBuildArgs> {
87
82
  export const Handler = async ({
88
83
  directory,
89
84
  outfile,
90
- "output-config-path": outputConfigPath,
85
+ outputConfigPath,
86
+ outputRoutesPath: routesOutputPath,
91
87
  minify,
92
88
  sourcemap,
93
89
  fallbackService,
94
90
  watch,
95
91
  plugin,
96
- "build-output-directory": buildOutputDirectory,
97
- "node-compat": nodeCompat,
98
- }: ArgumentsCamelCase<PagesBuildArgs>) => {
92
+ buildOutputDirectory,
93
+ nodeCompat,
94
+ }: PagesBuildArgs) => {
99
95
  if (!isInPagesCI) {
100
96
  // Beta message for `wrangler pages <commands>` usage
101
97
  logger.log(pagesBetaWarning);
@@ -120,6 +116,7 @@ export const Handler = async ({
120
116
  plugin,
121
117
  buildOutputDirectory,
122
118
  nodeCompat,
119
+ routesOutputPath,
123
120
  });
124
121
  await metrics.sendMetricsEvent("build pages functions");
125
122
  };
@@ -135,19 +132,25 @@ export async function buildFunctions({
135
132
  onEnd,
136
133
  plugin = false,
137
134
  buildOutputDirectory,
135
+ routesOutputPath,
138
136
  nodeCompat,
139
- }: {
140
- outfile: string;
141
- outputConfigPath?: string;
137
+ }: Partial<
138
+ Pick<
139
+ PagesBuildArgs,
140
+ | "outputConfigPath"
141
+ | "minify"
142
+ | "sourcemap"
143
+ | "fallbackService"
144
+ | "watch"
145
+ | "plugin"
146
+ | "buildOutputDirectory"
147
+ | "nodeCompat"
148
+ >
149
+ > & {
142
150
  functionsDirectory: string;
143
- minify?: boolean;
144
- sourcemap?: boolean;
145
- fallbackService?: string;
146
- watch?: boolean;
147
151
  onEnd?: () => void;
148
- plugin?: boolean;
149
- buildOutputDirectory?: string;
150
- nodeCompat?: boolean;
152
+ outfile: Required<PagesBuildArgs>["outfile"];
153
+ routesOutputPath?: PagesBuildArgs["outputRoutesPath"];
151
154
  }) {
152
155
  RUNNING_BUILDERS.forEach(
153
156
  (runningBuilder) => runningBuilder.stop && runningBuilder.stop()
@@ -161,6 +164,11 @@ export async function buildFunctions({
161
164
  baseURL,
162
165
  });
163
166
 
167
+ if (config.routes && routesOutputPath) {
168
+ const routesJSON = convertRoutesToRoutesJSONSpec(config.routes);
169
+ writeFileSync(routesOutputPath, JSON.stringify(routesJSON, null, 2));
170
+ }
171
+
164
172
  if (outputConfigPath) {
165
173
  writeFileSync(
166
174
  outputConfigPath,
@@ -3,5 +3,9 @@ export const MAX_BUCKET_SIZE = 50 * 1024 * 1024;
3
3
  export const MAX_BUCKET_FILE_COUNT = 5000;
4
4
  export const BULK_UPLOAD_CONCURRENCY = 3;
5
5
  export const MAX_UPLOAD_ATTEMPTS = 5;
6
+ export const MAX_CHECK_MISSING_ATTEMPTS = 5;
6
7
  export const SECONDS_TO_WAIT_FOR_PROXY = 5;
7
8
  export const isInPagesCI = !!process.env.CF_PAGES;
9
+ /** The max number of rules in _routes.json */
10
+ export const MAX_FUNCTIONS_ROUTES_RULES = 100;
11
+ export const ROUTES_SPEC_VERSION = 1;
@@ -11,14 +11,16 @@ import { requireAuth } from "../user";
11
11
  import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
12
12
  import { listProjects } from "./projects";
13
13
  import { pagesBetaWarning } from "./utils";
14
- import type { Deployment, PagesConfigCache } from "./types";
15
- import type { ArgumentsCamelCase, Argv } from "yargs";
14
+ import type {
15
+ Deployment,
16
+ PagesConfigCache,
17
+ YargsOptionsToInterface,
18
+ } from "./types";
19
+ import type { Argv } from "yargs";
16
20
 
17
- type ListArgs = {
18
- "project-name"?: string;
19
- };
21
+ type ListArgs = YargsOptionsToInterface<typeof ListOptions>;
20
22
 
21
- export function ListOptions(yargs: Argv): Argv<ListArgs> {
23
+ export function ListOptions(yargs: Argv) {
22
24
  return yargs
23
25
  .options({
24
26
  "project-name": {
@@ -30,9 +32,7 @@ export function ListOptions(yargs: Argv): Argv<ListArgs> {
30
32
  .epilogue(pagesBetaWarning);
31
33
  }
32
34
 
33
- export async function ListHandler({
34
- projectName,
35
- }: ArgumentsCamelCase<ListArgs>) {
35
+ export async function ListHandler({ projectName }: ListArgs) {
36
36
  const config = getConfigCache<PagesConfigCache>(PAGES_CONFIG_CACHE_FILENAME);
37
37
  const accountId = await requireAuth(config);
38
38
 
@@ -102,5 +102,5 @@ export async function ListHandler({
102
102
  patchConsole: false,
103
103
  });
104
104
  unmount();
105
- await metrics.sendMetricsEvent("list pages projects deployments");
105
+ await metrics.sendMetricsEvent("list pages deployments");
106
106
  }