wrangler 2.0.28 → 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.
@@ -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,7 +364,7 @@ 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
 
@@ -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",
@@ -1,5 +1,9 @@
1
1
  import { toUrlPath } from "../../paths";
2
- import { MAX_FUNCTIONS_ROUTES_RULES, ROUTES_SPEC_VERSION } from "../constants";
2
+ import {
3
+ MAX_FUNCTIONS_ROUTES_RULES,
4
+ ROUTES_SPEC_VERSION,
5
+ ROUTES_SPEC_DESCRIPTION,
6
+ } from "../constants";
3
7
  import {
4
8
  compareRoutes,
5
9
  convertRoutesToGlobPatterns,
@@ -164,6 +168,7 @@ describe("route-paths-to-glob-patterns", () => {
164
168
  ])
165
169
  ).toEqual({
166
170
  version: ROUTES_SPEC_VERSION,
171
+ description: ROUTES_SPEC_DESCRIPTION,
167
172
  include: ["/middleware/*", "/foo/*", "/api/foo/bar"],
168
173
  exclude: [],
169
174
  });
@@ -176,6 +181,7 @@ describe("route-paths-to-glob-patterns", () => {
176
181
  }
177
182
  expect(convertRoutesToRoutesJSONSpec(routes)).toEqual({
178
183
  version: ROUTES_SPEC_VERSION,
184
+ description: ROUTES_SPEC_DESCRIPTION,
179
185
  include: ["/*"],
180
186
  exclude: [],
181
187
  });
@@ -197,6 +203,7 @@ describe("route-paths-to-glob-patterns", () => {
197
203
  expect(
198
204
  optimizeRoutesJSONSpec({
199
205
  version: ROUTES_SPEC_VERSION,
206
+ description: ROUTES_SPEC_DESCRIPTION,
200
207
  exclude: [],
201
208
  include: [
202
209
  "/api/foo/bar",
@@ -208,6 +215,7 @@ describe("route-paths-to-glob-patterns", () => {
208
215
  })
209
216
  ).toEqual({
210
217
  version: ROUTES_SPEC_VERSION,
218
+ description: ROUTES_SPEC_DESCRIPTION,
211
219
  include: ["/middleware/*", "/foo/*", "/api/foo/bar"],
212
220
  exclude: [],
213
221
  });
@@ -221,11 +229,13 @@ describe("route-paths-to-glob-patterns", () => {
221
229
  expect(
222
230
  optimizeRoutesJSONSpec({
223
231
  version: ROUTES_SPEC_VERSION,
232
+ description: ROUTES_SPEC_DESCRIPTION,
224
233
  include,
225
234
  exclude: [],
226
235
  })
227
236
  ).toEqual({
228
237
  version: ROUTES_SPEC_VERSION,
238
+ description: ROUTES_SPEC_DESCRIPTION,
229
239
  include: ["/*"],
230
240
  exclude: [],
231
241
  });
@@ -239,6 +249,7 @@ describe("route-paths-to-glob-patterns", () => {
239
249
  expect(
240
250
  optimizeRoutesJSONSpec({
241
251
  version: ROUTES_SPEC_VERSION,
252
+ description: ROUTES_SPEC_DESCRIPTION,
242
253
  include,
243
254
  exclude: [],
244
255
  }).include.length
@@ -1,12 +1,17 @@
1
1
  import { join as pathJoin } from "node:path";
2
2
  import { toUrlPath } from "../../paths";
3
- import { MAX_FUNCTIONS_ROUTES_RULES, ROUTES_SPEC_VERSION } from "../constants";
3
+ import {
4
+ MAX_FUNCTIONS_ROUTES_RULES,
5
+ ROUTES_SPEC_DESCRIPTION,
6
+ ROUTES_SPEC_VERSION,
7
+ } from "../constants";
4
8
  import { consolidateRoutes } from "./routes-consolidation";
5
9
  import type { RouteConfig } from "./routes";
6
10
 
7
11
  /** Interface for _routes.json */
8
12
  interface RoutesJSONSpec {
9
13
  version: typeof ROUTES_SPEC_VERSION;
14
+ description?: string;
10
15
  include: string[];
11
16
  exclude: string[];
12
17
  }
@@ -53,6 +58,7 @@ export function convertRoutesToRoutesJSONSpec(
53
58
  const include = convertRoutesToGlobPatterns(reversedRoutes);
54
59
  return optimizeRoutesJSONSpec({
55
60
  version: ROUTES_SPEC_VERSION,
61
+ description: ROUTES_SPEC_DESCRIPTION,
56
62
  include,
57
63
  exclude: [],
58
64
  });
@@ -15,12 +15,9 @@ import { logger } from "../logger";
15
15
  import * as metrics from "../metrics";
16
16
  import { requireAuth } from "../user";
17
17
  import { buildFunctions } from "./build";
18
- import { PAGES_CONFIG_CACHE_FILENAME } from "./constants";
18
+ import { PAGES_CONFIG_CACHE_FILENAME, ROUTES_SPEC_VERSION } from "./constants";
19
19
  import { FunctionsNoRoutesError, getFunctionsNoRoutesWarning } from "./errors";
20
- import {
21
- isRoutesJSONSpec,
22
- optimizeRoutesJSONSpec,
23
- } from "./functions/routes-transformation";
20
+ import { isRoutesJSONSpec } from "./functions/routes-transformation";
24
21
  import { listProjects } from "./projects";
25
22
  import { upload } from "./upload";
26
23
  import { pagesBetaWarning } from "./utils";
@@ -254,7 +251,10 @@ export const Handler = async ({
254
251
 
255
252
  let builtFunctions: string | undefined = undefined;
256
253
  const functionsDirectory = join(cwd(), "functions");
257
- const routesOutputPath = join(tmpdir(), `_routes-${Math.random()}.json`);
254
+ const routesOutputPath = !existsSync(join(directory, "_routes.json"))
255
+ ? join(tmpdir(), `_routes-${Math.random()}.json`)
256
+ : undefined;
257
+
258
258
  if (existsSync(functionsDirectory)) {
259
259
  const outfile = join(tmpdir(), `./functionsWorker-${Math.random()}.js`);
260
260
  try {
@@ -265,6 +265,7 @@ export const Handler = async ({
265
265
  buildOutputDirectory: dirname(outfile),
266
266
  routesOutputPath,
267
267
  });
268
+
268
269
  builtFunctions = readFileSync(outfile, "utf-8");
269
270
  } catch (e) {
270
271
  if (e instanceof FunctionsNoRoutesError) {
@@ -301,7 +302,8 @@ export const Handler = async ({
301
302
 
302
303
  let _headers: string | undefined,
303
304
  _redirects: string | undefined,
304
- _routes: string | undefined,
305
+ _routesGenerated: string | undefined,
306
+ _routesCustom: string | undefined,
305
307
  _workerJS: string | undefined;
306
308
 
307
309
  try {
@@ -312,6 +314,12 @@ export const Handler = async ({
312
314
  _redirects = readFileSync(join(directory, "_redirects"), "utf-8");
313
315
  } catch {}
314
316
 
317
+ try {
318
+ // Developers can specify a custom _routes.json file, for projects with Pages
319
+ // Functions or projects in Advanced Mode
320
+ _routesCustom = readFileSync(join(directory, "_routes.json"), "utf-8");
321
+ } catch {}
322
+
315
323
  try {
316
324
  _workerJS = readFileSync(join(directory, "_worker.js"), "utf-8");
317
325
  } catch {}
@@ -327,47 +335,65 @@ export const Handler = async ({
327
335
  }
328
336
 
329
337
  if (builtFunctions) {
338
+ // with Pages Functions
339
+ // https://developers.cloudflare.com/pages/platform/functions/
330
340
  formData.append("_worker.js", new File([builtFunctions], "_worker.js"));
331
341
  logger.log(`✨ Uploading Functions`);
332
- try {
333
- _routes = readFileSync(routesOutputPath, "utf-8");
334
- if (_routes) {
335
- formData.append("_routes.json", new File([_routes], "_routes.json"));
342
+
343
+ if (_routesCustom) {
344
+ // user provided a custom _routes.json file
345
+ try {
346
+ validateRoutesFile(_routesCustom, join(directory, "_routes.json"));
347
+
348
+ formData.append(
349
+ "_routes.json",
350
+ new File([_routesCustom], "_routes.json")
351
+ );
352
+ logger.log(`✨ Uploading _routes.json`);
353
+ logger.warn(
354
+ `_routes.json is an experimental feature and is subject to change. Please use with care.`
355
+ );
356
+ } catch (err) {
357
+ if (err instanceof FatalError) {
358
+ throw err;
359
+ }
336
360
  }
337
- } catch {}
361
+ } else if (routesOutputPath) {
362
+ // no custom _routes.json file found, so fallback to the generated one
363
+ try {
364
+ _routesGenerated = readFileSync(routesOutputPath, "utf-8");
365
+
366
+ if (_routesGenerated) {
367
+ formData.append(
368
+ "_routes.json",
369
+ new File([_routesGenerated], "_routes.json")
370
+ );
371
+ }
372
+ } catch {}
373
+ }
338
374
  } else if (_workerJS) {
339
375
  // Advanced Mode
340
376
  // https://developers.cloudflare.com/pages/platform/functions/#advanced-mode
341
377
  formData.append("_worker.js", new File([_workerJS], "_worker.js"));
342
378
  logger.log(`✨ Uploading _worker.js`);
343
379
 
344
- try {
345
- // In advanced mode, developers can specify a custom _routes.json
346
- // file. In which case, we need to run it through optimization
347
- // to potentially reduce the overall worker pipeline size
348
- const routesPath = join(directory, "_routes.json");
349
- const advancedModeRoutesString = readFileSync(routesPath, "utf-8");
350
- const advancedModeRoutes = JSON.parse(advancedModeRoutesString);
351
-
352
- if (!isRoutesJSONSpec(advancedModeRoutes)) {
353
- throw new FatalError(
354
- "Invalid _routes.json file found at:" + routesPath,
355
- 1
356
- );
357
- }
380
+ if (_routesCustom) {
381
+ // user provided a custom _routes.json file
382
+ try {
383
+ validateRoutesFile(_routesCustom, join(directory, "_routes.json"));
358
384
 
359
- _routes = JSON.stringify(optimizeRoutesJSONSpec(advancedModeRoutes));
360
- formData.append("_routes.json", new File([_routes], "_routes.json"));
361
- logger.log(`✨ Uploading _routes.json`);
362
-
363
- logger.warn(
364
- `🚨 _routes.json is an experimental feature and is subject to change. Don't use unless you really must!`
365
- );
366
- } catch (e) {
367
- // Ignore file not existing errors for _routes.json but forward the potential
368
- // FatalError from an invalid spec
369
- if (e instanceof FatalError) {
370
- throw e;
385
+ formData.append(
386
+ "_routes.json",
387
+ new File([_routesCustom], "_routes.json")
388
+ );
389
+ logger.log(`✨ Uploading _routes.json`);
390
+ logger.warn(
391
+ `_routes.json is an experimental feature and is subject to change. Please use with care.`
392
+ );
393
+ } catch (err) {
394
+ if (err instanceof FatalError) {
395
+ throw err;
396
+ }
371
397
  }
372
398
  }
373
399
  }
@@ -389,3 +415,21 @@ export const Handler = async ({
389
415
  );
390
416
  await metrics.sendMetricsEvent("create pages deployment");
391
417
  };
418
+
419
+ function validateRoutesFile(_routes: string, routesPath: string) {
420
+ const routes = JSON.parse(_routes);
421
+
422
+ if (!isRoutesJSONSpec(routes)) {
423
+ throw new FatalError(
424
+ `Invalid _routes.json file found at: ${routesPath}. Please make sure the JSON object has the following format:
425
+ {
426
+ version: ${ROUTES_SPEC_VERSION};
427
+ include: string[];
428
+ exclude: string[];
429
+ }
430
+ and that at least one include rule is provided.
431
+ `,
432
+ 1
433
+ );
434
+ }
435
+ }
package/src/paths.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { assert } from "console";
1
+ import { assert } from "node:console";
2
+ import { resolve } from "node:path";
2
3
 
3
4
  type DiscriminatedPath<Discriminator extends string> = string & {
4
5
  _discriminator: Discriminator;
@@ -24,3 +25,21 @@ export function toUrlPath(path: string): UrlPath {
24
25
  );
25
26
  return path.replace(/\\/g, "/") as UrlPath;
26
27
  }
28
+
29
+ /**
30
+ * The __RELATIVE_PACKAGE_PATH__ is defined either in the esbuild config (for production)
31
+ * or the jest.setup.ts (for unit testing).
32
+ */
33
+ declare const __RELATIVE_PACKAGE_PATH__: string;
34
+
35
+ /**
36
+ * Use this function (rather than node.js constants like `__dirname`) to specify
37
+ * paths that are relative to the base path of the Wrangler package.
38
+ *
39
+ * It is important to use this function because it reliably maps to the root of the package
40
+ * no matter whether the code has been bundled or not.
41
+ */
42
+ export function getBasePath(): string {
43
+ // eslint-disable-next-line no-restricted-globals
44
+ return resolve(__dirname, __RELATIVE_PACKAGE_PATH__);
45
+ }
package/src/worker.ts CHANGED
@@ -65,14 +65,14 @@ export interface CfModule {
65
65
  /**
66
66
  * A map of variable names to values.
67
67
  */
68
- interface CfVars {
68
+ export interface CfVars {
69
69
  [key: string]: unknown;
70
70
  }
71
71
 
72
72
  /**
73
73
  * A KV namespace.
74
74
  */
75
- interface CfKvNamespace {
75
+ export interface CfKvNamespace {
76
76
  binding: string;
77
77
  id: string;
78
78
  }
@@ -81,7 +81,7 @@ interface CfKvNamespace {
81
81
  * A binding to a wasm module (in service-worker format)
82
82
  */
83
83
 
84
- interface CfWasmModuleBindings {
84
+ export interface CfWasmModuleBindings {
85
85
  [key: string]: string;
86
86
  }
87
87
 
@@ -89,7 +89,7 @@ interface CfWasmModuleBindings {
89
89
  * A binding to a text blob (in service-worker format)
90
90
  */
91
91
 
92
- interface CfTextBlobBindings {
92
+ export interface CfTextBlobBindings {
93
93
  [key: string]: string;
94
94
  }
95
95
 
@@ -97,21 +97,21 @@ interface CfTextBlobBindings {
97
97
  * A binding to a data blob (in service-worker format)
98
98
  */
99
99
 
100
- interface CfDataBlobBindings {
100
+ export interface CfDataBlobBindings {
101
101
  [key: string]: string;
102
102
  }
103
103
 
104
104
  /**
105
105
  * A Durable Object.
106
106
  */
107
- interface CfDurableObject {
107
+ export interface CfDurableObject {
108
108
  name: string;
109
109
  class_name: string;
110
110
  script_name?: string;
111
111
  environment?: string;
112
112
  }
113
113
 
114
- interface CfR2Bucket {
114
+ export interface CfR2Bucket {
115
115
  binding: string;
116
116
  bucket_name: string;
117
117
  }
@@ -33,7 +33,13 @@ declare interface BodyMixin {
33
33
  readonly text: () => Promise<string>
34
34
  }
35
35
 
36
+ declare interface DevApiOptions {
37
+ testMode?: boolean;
38
+ disableExperimentalWarning?: boolean;
39
+ }
40
+
36
41
  declare interface DevOptions {
42
+ config?: string;
37
43
  env?: string;
38
44
  ip?: string;
39
45
  port?: number;
@@ -49,7 +55,7 @@ declare interface DevOptions {
49
55
  experimentalEnableLocalPersistence?: boolean;
50
56
  liveReload?: boolean;
51
57
  watch?: boolean;
52
- vars: {
58
+ vars?: {
53
59
  [key: string]: unknown;
54
60
  };
55
61
  kv?: {
@@ -328,11 +334,15 @@ declare interface SpecIterator<T, TReturn = any, TNext = undefined> {
328
334
  * unstable_dev starts a wrangler dev server, and returns a promise that resolves with utility functions to interact with it.
329
335
  * @param {string} script
330
336
  * @param {DevOptions} options
337
+ * @param {DevApiOptions} apiOptions
338
+ * @returns {Promise<UnstableDev>}
331
339
  */
332
- export declare function unstable_dev(script: string, options: DevOptions, disableExperimentalWarning?: boolean): Promise<{
333
- stop: () => void;
334
- fetch: (init?: RequestInit | undefined) => Promise<Response | undefined>;
340
+ export declare function unstable_dev(script: string, options?: DevOptions, apiOptions?: DevApiOptions): Promise<UnstableDev>;
341
+
342
+ declare interface UnstableDev {
343
+ stop: () => Promise<void>;
344
+ fetch: (init?: RequestInit) => Promise<Response | undefined>;
335
345
  waitUntilExit: () => Promise<void>;
336
- }>;
346
+ }
337
347
 
338
348
  export { }