wrangler 2.0.26 → 2.0.29

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 (62) hide show
  1. package/bin/wrangler.js +1 -1
  2. package/miniflare-dist/index.mjs +54 -46
  3. package/package.json +6 -4
  4. package/src/__tests__/api-dev.test.ts +19 -0
  5. package/src/__tests__/configuration.test.ts +33 -29
  6. package/src/__tests__/dev.test.tsx +8 -6
  7. package/src/__tests__/generate.test.ts +2 -4
  8. package/src/__tests__/helpers/hello-world-worker.js +5 -0
  9. package/src/__tests__/helpers/mock-get-zone-from-host.ts +8 -0
  10. package/src/__tests__/helpers/mock-known-routes.ts +7 -0
  11. package/src/__tests__/index.test.ts +30 -30
  12. package/src/__tests__/jest.setup.ts +17 -0
  13. package/src/__tests__/metrics.test.ts +1 -1
  14. package/src/__tests__/pages.test.ts +829 -103
  15. package/src/__tests__/paths.test.ts +17 -0
  16. package/src/__tests__/publish.test.ts +59 -18
  17. package/src/__tests__/r2.test.ts +4 -3
  18. package/src/__tests__/tail.test.ts +34 -0
  19. package/src/__tests__/test-old-node-version.js +3 -3
  20. package/src/__tests__/user.test.ts +11 -0
  21. package/src/__tests__/worker-namespace.test.ts +37 -35
  22. package/src/api/dev.ts +74 -28
  23. package/src/bundle.ts +14 -11
  24. package/src/cfetch/internal.ts +81 -3
  25. package/src/cli.ts +1 -1
  26. package/src/config/environment.ts +1 -1
  27. package/src/config/index.ts +4 -4
  28. package/src/config/validation-helpers.ts +19 -6
  29. package/src/config/validation.ts +11 -5
  30. package/src/config-cache.ts +2 -1
  31. package/src/create-worker-upload-form.ts +29 -26
  32. package/src/dev/local.tsx +317 -169
  33. package/src/dev/remote.tsx +10 -1
  34. package/src/dev/start-server.ts +412 -0
  35. package/src/dev.tsx +341 -157
  36. package/src/{worker-namespace.ts → dispatch-namespace.ts} +18 -18
  37. package/src/entry.ts +2 -1
  38. package/src/generate.ts +1 -1
  39. package/src/index.tsx +54 -8
  40. package/src/init.ts +5 -5
  41. package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
  42. package/src/metrics/metrics-config.ts +1 -1
  43. package/src/metrics/send-event.ts +6 -5
  44. package/src/miniflare-cli/assets.ts +4 -65
  45. package/src/miniflare-cli/index.ts +36 -32
  46. package/src/pages/constants.ts +3 -0
  47. package/src/pages/dev.tsx +10 -15
  48. package/src/pages/functions/buildPlugin.ts +2 -1
  49. package/src/pages/functions/buildWorker.ts +2 -1
  50. package/src/pages/functions/routes-transformation.test.ts +12 -1
  51. package/src/pages/functions/routes-transformation.ts +7 -1
  52. package/src/pages/publish.tsx +82 -38
  53. package/src/paths.ts +20 -1
  54. package/src/proxy.ts +10 -0
  55. package/src/publish.ts +19 -4
  56. package/src/r2.ts +4 -4
  57. package/src/user/user.tsx +6 -4
  58. package/src/whoami.tsx +5 -5
  59. package/src/worker.ts +10 -9
  60. package/src/zones.ts +91 -0
  61. package/wrangler-dist/cli.d.ts +22 -8
  62. package/wrangler-dist/cli.js +7757 -2315
@@ -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/entry.ts CHANGED
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import * as esbuild from "esbuild";
5
5
  import { execaCommand } from "execa";
6
6
  import { logger } from "./logger";
7
+ import { getBasePath } from "./paths";
7
8
  import type { Config } from "./config";
8
9
  import type { CfScriptFormat } from "./worker";
9
10
  import type { Metafile } from "esbuild";
@@ -40,7 +41,7 @@ export async function getEntry(
40
41
  : // site.entry-point could be a directory
41
42
  path.resolve(config.site?.["entry-point"], "index.js");
42
43
  } else if (args.assets || config.assets) {
43
- file = path.resolve(__dirname, "../templates/no-op-worker.js");
44
+ file = path.resolve(getBasePath(), "templates/no-op-worker.js");
44
45
  } else {
45
46
  throw new Error(
46
47
  `Missing entry-point: The entry-point should be specified via the command line (e.g. \`wrangler ${command} path/to/script\`) or the \`main\` config field.`
package/src/generate.ts CHANGED
@@ -88,7 +88,7 @@ export async function generateHandler({
88
88
  `Creating a worker in ${path.basename(creationDirectory)} from ${template}`
89
89
  );
90
90
 
91
- await createCloudflare(creationDirectory, template, {
91
+ await createCloudflare(path.basename(creationDirectory), template, {
92
92
  init: true, // initialize a git repository
93
93
  debug: logger.loggerLevel === "debug",
94
94
  force: false, // do not overwrite an existing directory
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
  }
package/src/init.ts CHANGED
@@ -12,10 +12,10 @@ import { initializeGit, isGitInstalled, isInsideGitRepo } from "./git-client";
12
12
  import { logger } from "./logger";
13
13
  import { getPackageManager } from "./package-manager";
14
14
  import { parsePackageJSON, parseTOML, readFileSync } from "./parse";
15
+ import { getBasePath } from "./paths";
15
16
  import { requireAuth } from "./user";
16
17
  import { CommandLineArgsError, printWranglerBanner } from "./index";
17
18
  import type { ConfigPath } from "./index";
18
-
19
19
  import type { Argv, ArgumentsCamelCase } from "yargs";
20
20
 
21
21
  export async function initOptions(yargs: Argv) {
@@ -162,7 +162,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
162
162
  await initializeGit(creationDirectory);
163
163
  await writeFile(
164
164
  path.join(creationDirectory, ".gitignore"),
165
- readFileSync(path.join(__dirname, "../templates/gitignore"))
165
+ readFileSync(path.join(getBasePath(), "templates/gitignore"))
166
166
  );
167
167
  logger.log(
168
168
  args.name && args.name !== "."
@@ -254,7 +254,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
254
254
  isTypescriptProject = true;
255
255
  await writeFile(
256
256
  path.join(creationDirectory, "./tsconfig.json"),
257
- readFileSync(path.join(__dirname, "../templates/tsconfig.json"))
257
+ readFileSync(path.join(getBasePath(), "templates/tsconfig.json"))
258
258
  );
259
259
  devDepsToInstall.push("@cloudflare/workers-types");
260
260
  devDepsToInstall.push("typescript");
@@ -495,7 +495,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
495
495
  });
496
496
  await writeFile(
497
497
  path.join(creationDirectory, "./src/index.ts"),
498
- readFileSync(path.join(__dirname, `../templates/${template}`))
498
+ readFileSync(path.join(getBasePath(), `templates/${template}`))
499
499
  );
500
500
 
501
501
  logger.log(
@@ -561,7 +561,7 @@ export async function initHandler(args: ArgumentsCamelCase<InitArgs>) {
561
561
  });
562
562
  await writeFile(
563
563
  path.join(creationDirectory, "./src/index.js"),
564
- readFileSync(path.join(__dirname, `../templates/${template}`))
564
+ readFileSync(path.join(getBasePath(), `templates/${template}`))
565
565
  );
566
566
 
567
567
  logger.log(
File without changes
@@ -6,10 +6,10 @@ import { fetchResult } from "../cfetch";
6
6
  import { getConfigCache, saveToConfigCache } from "../config-cache";
7
7
  import { confirm } from "../dialogs";
8
8
  import { getEnvironmentVariableFactory } from "../environment-variables";
9
+ import { CI } from "../is-ci";
9
10
  import isInteractive from "../is-interactive";
10
11
  import { logger } from "../logger";
11
12
  import { getAPIToken } from "../user";
12
- import { CI } from "./is-ci";
13
13
 
14
14
  /**
15
15
  * The date that the metrics being gathered was last updated in a way that would require
@@ -40,17 +40,18 @@ 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"
51
51
  | "list pages deployments"
52
52
  | "build pages functions"
53
53
  | "run dev"
54
+ | "run dev (api)"
54
55
  | "run pages dev";
55
56
 
56
57
  /**
@@ -1,5 +1,9 @@
1
1
  import { existsSync, lstatSync, readFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import {
4
+ generateRulesMatcher,
5
+ replacer,
6
+ } from "@cloudflare/pages-shared/src/asset-server/rulesEngine";
3
7
  import { fetch as miniflareFetch } from "@miniflare/core";
4
8
  import { watch } from "chokidar";
5
9
  import { getType } from "mime";
@@ -54,71 +58,6 @@ export default async function generateASSETSBinding(options: Options) {
54
58
  };
55
59
  }
56
60
 
57
- function escapeRegex(str: string) {
58
- return str.replace(/[-/\\^$*+?.()|[]{}]/g, "\\$&");
59
- }
60
-
61
- type Replacements = Record<string, string>;
62
-
63
- function replacer(str: string, replacements: Replacements) {
64
- for (const [replacement, value] of Object.entries(replacements)) {
65
- str = str.replace(`:${replacement}`, value);
66
- }
67
- return str;
68
- }
69
-
70
- function generateRulesMatcher<T>(
71
- rules?: Record<string, T>,
72
- replacerFn: (match: T, replacements: Replacements) => T = (match) => match
73
- ) {
74
- // TODO: How can you test cross-host rules?
75
- if (!rules) return () => [];
76
-
77
- const compiledRules = Object.entries(rules)
78
- .map(([rule, match]) => {
79
- const crossHost = rule.startsWith("https://");
80
-
81
- rule = rule.split("*").map(escapeRegex).join("(?<splat>.*)");
82
-
83
- const host_matches = rule.matchAll(
84
- /(?<=^https:\\\/\\\/[^/]*?):([^\\]+)(?=\\)/g
85
- );
86
- for (const hostMatch of host_matches) {
87
- rule = rule.split(hostMatch[0]).join(`(?<${hostMatch[1]}>[^/.]+)`);
88
- }
89
-
90
- const path_matches = rule.matchAll(/:(\w+)/g);
91
- for (const pathMatch of path_matches) {
92
- rule = rule.split(pathMatch[0]).join(`(?<${pathMatch[1]}>[^/]+)`);
93
- }
94
-
95
- rule = "^" + rule + "$";
96
-
97
- try {
98
- const regExp = new RegExp(rule);
99
- return [{ crossHost, regExp }, match];
100
- } catch {}
101
- })
102
- .filter((value) => value !== undefined) as [
103
- { crossHost: boolean; regExp: RegExp },
104
- T
105
- ][];
106
-
107
- return ({ request }: { request: MiniflareRequest }) => {
108
- const { pathname, host } = new URL(request.url);
109
-
110
- return compiledRules
111
- .map(([{ crossHost, regExp }, match]) => {
112
- const test = crossHost ? `https://${host}${pathname}` : pathname;
113
- const result = regExp.exec(test);
114
- if (result) {
115
- return replacerFn(match, result.groups || {});
116
- }
117
- })
118
- .filter((value) => value !== undefined) as T[];
119
- };
120
- }
121
-
122
61
  function generateHeadersMatcher(headersFile: string) {
123
62
  if (existsSync(headersFile)) {
124
63
  const contents = readFileSync(headersFile).toString();
@@ -59,41 +59,45 @@ async function main() {
59
59
 
60
60
  config.bindings = {
61
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 requestFromArgs = new MiniflareRequest(...reqArgs);
80
- const url = new URL(requestFromArgs.url);
81
- url.host = host;
82
- if (port !== undefined) url.port = port.toString();
83
- const request = new MiniflareRequest(
84
- url.toString(),
85
- requestFromArgs
62
+ ...(config.externalDurableObjects &&
63
+ Object.fromEntries(
64
+ Object.entries(
65
+ config.externalDurableObjects as Record<
66
+ string,
67
+ { name: string; host: string; port: number }
68
+ >
69
+ ).map(([binding, { name, host, port }]) => {
70
+ const factory = () => {
71
+ throw new FatalError(
72
+ "An external Durable Object instance's state has somehow been attempted to be accessed.",
73
+ 1
86
74
  );
87
- request.headers.set("x-miniflare-durable-object-name", name);
88
- request.headers.set("x-miniflare-durable-object-id", id.toString());
75
+ };
76
+ const namespace = new DurableObjectNamespace(name as string, factory);
77
+ namespace.get = (id) => {
78
+ const stub = new DurableObjectStub(factory, id);
79
+ stub.fetch = (...reqArgs) => {
80
+ const requestFromArgs = new MiniflareRequest(...reqArgs);
81
+ const url = new URL(requestFromArgs.url);
82
+ url.host = host;
83
+ if (port !== undefined) url.port = port.toString();
84
+ const request = new MiniflareRequest(
85
+ url.toString(),
86
+ requestFromArgs
87
+ );
88
+ request.headers.set("x-miniflare-durable-object-name", name);
89
+ request.headers.set(
90
+ "x-miniflare-durable-object-id",
91
+ id.toString()
92
+ );
89
93
 
90
- return fetch(request);
94
+ return fetch(request);
95
+ };
96
+ return stub;
91
97
  };
92
- return stub;
93
- };
94
- return [binding, namespace];
95
- })
96
- ),
98
+ return [binding, namespace];
99
+ })
100
+ )),
97
101
  };
98
102
 
99
103
  let mf: Miniflare | undefined;
@@ -1,3 +1,5 @@
1
+ import { version as wranglerVersion } from "../../package.json";
2
+
1
3
  export const PAGES_CONFIG_CACHE_FILENAME = "pages.json";
2
4
  export const MAX_BUCKET_SIZE = 50 * 1024 * 1024;
3
5
  export const MAX_BUCKET_FILE_COUNT = 5000;
@@ -10,3 +12,4 @@ export const isInPagesCI = !!process.env.CF_PAGES;
10
12
  export const MAX_FUNCTIONS_ROUTES_RULES = 100;
11
13
  export const MAX_FUNCTIONS_ROUTES_RULE_LENGTH = 100;
12
14
  export const ROUTES_SPEC_VERSION = 1;
15
+ export const ROUTES_SPEC_DESCRIPTION = `Generated by wrangler@${wranglerVersion}`;
package/src/pages/dev.tsx CHANGED
@@ -8,6 +8,7 @@ import { unstable_dev } from "../api";
8
8
  import { FatalError } from "../errors";
9
9
  import { logger } from "../logger";
10
10
  import * as metrics from "../metrics";
11
+ import { getBasePath } from "../paths";
11
12
  import { buildFunctions } from "./build";
12
13
  import { SECONDS_TO_WAIT_FOR_PROXY } from "./constants";
13
14
  import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
@@ -274,7 +275,7 @@ export const Handler = async ({
274
275
 
275
276
  if (!existsSync(scriptPath)) {
276
277
  logger.log("No functions. Shimming...");
277
- scriptPath = resolve(__dirname, "../templates/pages-shim.ts");
278
+ scriptPath = resolve(getBasePath(), "templates/pages-shim.ts");
278
279
  } else {
279
280
  const runBuild = async () => {
280
281
  try {
@@ -363,28 +364,20 @@ export const Handler = async ({
363
364
  logLevel: "error",
364
365
  logPrefix: "pages",
365
366
  },
366
- true
367
+ { testMode: false, disableExperimentalWarning: true }
367
368
  );
368
369
  await metrics.sendMetricsEvent("run pages dev");
369
370
 
371
+ CLEANUP_CALLBACKS.push(stop);
372
+
370
373
  waitUntilExit().then(() => {
371
374
  CLEANUP();
372
- stop();
373
375
  process.exit(0);
374
376
  });
375
377
 
376
- process.on("exit", () => {
377
- CLEANUP();
378
- stop();
379
- });
380
- process.on("SIGINT", () => {
381
- CLEANUP();
382
- stop();
383
- });
384
- process.on("SIGTERM", () => {
385
- CLEANUP();
386
- stop();
387
- });
378
+ process.on("exit", CLEANUP);
379
+ process.on("SIGINT", CLEANUP);
380
+ process.on("SIGTERM", CLEANUP);
388
381
  };
389
382
 
390
383
  function isWindows() {
@@ -491,6 +484,8 @@ async function spawnProxyProcess({
491
484
 
492
485
  proxy.on("close", (code) => {
493
486
  logger.error(`Proxy exited with status ${code}.`);
487
+ CLEANUP();
488
+ process.exitCode = code ?? 0;
494
489
  });
495
490
 
496
491
  // Wait for proxy process to start...
@@ -3,6 +3,7 @@ import { dirname, relative, resolve } from "node:path";
3
3
  import NodeGlobalsPolyfills from "@esbuild-plugins/node-globals-polyfill";
4
4
  import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
5
5
  import { build } from "esbuild";
6
+ import { getBasePath } from "../../paths";
6
7
 
7
8
  type Options = {
8
9
  routesModule: string;
@@ -24,7 +25,7 @@ export function buildPlugin({
24
25
  onEnd = () => {},
25
26
  }: Options) {
26
27
  return build({
27
- entryPoints: [resolve(__dirname, "../templates/pages-template-plugin.ts")],
28
+ entryPoints: [resolve(getBasePath(), "templates/pages-template-plugin.ts")],
28
29
  inject: [routesModule],
29
30
  bundle: true,
30
31
  format: "esm",
@@ -4,6 +4,7 @@ import NodeGlobalsPolyfills from "@esbuild-plugins/node-globals-polyfill";
4
4
  import NodeModulesPolyfills from "@esbuild-plugins/node-modules-polyfill";
5
5
  import { build } from "esbuild";
6
6
  import { nanoid } from "nanoid";
7
+ import { getBasePath } from "../../paths";
7
8
 
8
9
  type Options = {
9
10
  routesModule: string;
@@ -29,7 +30,7 @@ export function buildWorker({
29
30
  nodeCompat,
30
31
  }: Options) {
31
32
  return build({
32
- entryPoints: [resolve(__dirname, "../templates/pages-template-worker.ts")],
33
+ entryPoints: [resolve(getBasePath(), "templates/pages-template-worker.ts")],
33
34
  inject: [routesModule],
34
35
  bundle: true,
35
36
  format: "esm",