wrangler 2.2.2 → 2.2.3

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.
@@ -0,0 +1,446 @@
1
+ import fs from "node:fs";
2
+ import path from "path";
3
+ import { Box, render, Text } from "ink";
4
+ import Table from "ink-table";
5
+ import React from "react";
6
+ import { withConfig } from "../config";
7
+ import { confirm } from "../dialogs";
8
+ import { logger } from "../logger";
9
+ import { requireAuth } from "../user";
10
+ import { createBackup } from "./backups";
11
+ import { DEFAULT_MIGRATION_PATH } from "./constants";
12
+ import { executeSql } from "./execute";
13
+ import { Database } from "./options";
14
+ import { d1BetaWarning, getDatabaseInfoFromConfig } from "./utils";
15
+ import type { ConfigFields, DevConfig, Environment } from "../config";
16
+ import type { ParseError } from "../parse";
17
+ import type { BaseSqlExecuteArgs, QueryResult } from "./execute";
18
+ import type { Migration } from "./types";
19
+ import type { Argv } from "yargs";
20
+
21
+ async function getMigrationsPath(
22
+ projectPath: string,
23
+ migrationsFolderPath: string,
24
+ createIfMissing: boolean
25
+ ): Promise<string> {
26
+ const dir = path.resolve(projectPath, migrationsFolderPath);
27
+ if (fs.existsSync(dir)) return dir;
28
+
29
+ const warning = `No migrations folder found.${
30
+ migrationsFolderPath === DEFAULT_MIGRATION_PATH
31
+ ? " Set `migrations_dir` in wrangler.toml to choose a different path."
32
+ : ""
33
+ }`;
34
+
35
+ if (createIfMissing && (await confirm(`${warning}\nOk to create ${dir}?`))) {
36
+ fs.mkdirSync(dir, { recursive: true });
37
+ return dir;
38
+ } else {
39
+ logger.warn(warning);
40
+ }
41
+
42
+ throw new Error(`No migrations present at ${dir}.`);
43
+ }
44
+
45
+ async function getUnappliedMigrations(
46
+ migrationsTableName: string,
47
+ migrationsPath: string,
48
+ local: undefined | boolean,
49
+ config: ConfigFields<DevConfig> & Environment,
50
+ name: string,
51
+ persistTo: undefined | string
52
+ ): Promise<Array<string>> {
53
+ const appliedMigrations = (
54
+ await listAppliedMigrations(
55
+ migrationsTableName,
56
+ local,
57
+ config,
58
+ name,
59
+ persistTo
60
+ )
61
+ ).map((migration) => {
62
+ return migration.name;
63
+ });
64
+ const projectMigrations = getMigrationNames(migrationsPath);
65
+
66
+ const unappliedMigrations: Array<string> = [];
67
+
68
+ for (const migration of projectMigrations) {
69
+ if (!appliedMigrations.includes(migration)) {
70
+ unappliedMigrations.push(migration);
71
+ }
72
+ }
73
+
74
+ return unappliedMigrations;
75
+ }
76
+
77
+ export function ListOptions(yargs: Argv): Argv<BaseSqlExecuteArgs> {
78
+ return Database(yargs);
79
+ }
80
+
81
+ export const ListHandler = withConfig<BaseSqlExecuteArgs>(
82
+ async ({ config, database, local, persistTo }): Promise<void> => {
83
+ await requireAuth({});
84
+ logger.log(d1BetaWarning);
85
+
86
+ const databaseInfo = await getDatabaseInfoFromConfig(config, database);
87
+ if (!databaseInfo) {
88
+ throw new Error(
89
+ `Can't find a DB with name/binding '${database}' in local config. Check info in wrangler.toml...`
90
+ );
91
+ }
92
+
93
+ if (!config.configPath) {
94
+ return;
95
+ }
96
+ const { migrationsTableName, migrationsFolderPath } = databaseInfo;
97
+
98
+ const migrationsPath = await getMigrationsPath(
99
+ path.dirname(config.configPath),
100
+ migrationsFolderPath,
101
+ false
102
+ );
103
+ await initMigrationsTable(
104
+ migrationsTableName,
105
+ local,
106
+ config,
107
+ database,
108
+ persistTo
109
+ );
110
+
111
+ const unappliedMigrations = (
112
+ await getUnappliedMigrations(
113
+ migrationsTableName,
114
+ migrationsPath,
115
+ local,
116
+ config,
117
+ database,
118
+ persistTo
119
+ )
120
+ ).map((migration) => {
121
+ return {
122
+ Name: migration,
123
+ };
124
+ });
125
+
126
+ if (unappliedMigrations.length === 0) {
127
+ render(<Text>✅ No migrations to apply!</Text>);
128
+ return;
129
+ }
130
+
131
+ render(
132
+ <Box flexDirection="column">
133
+ <Text>Migrations to be applied:</Text>
134
+ <Table data={unappliedMigrations} columns={["Name"]}></Table>
135
+ </Box>
136
+ );
137
+ }
138
+ );
139
+
140
+ export function ApplyOptions(yargs: Argv): Argv<BaseSqlExecuteArgs> {
141
+ return Database(yargs);
142
+ }
143
+
144
+ export const ApplyHandler = withConfig<BaseSqlExecuteArgs>(
145
+ async ({ config, database, local, persistTo }): Promise<void> => {
146
+ const accountId = await requireAuth({});
147
+ logger.log(d1BetaWarning);
148
+
149
+ const databaseInfo = await getDatabaseInfoFromConfig(config, database);
150
+ if (!databaseInfo) {
151
+ throw new Error(
152
+ `Can't find a DB with name/binding '${database}' in local config. Check info in wrangler.toml...`
153
+ );
154
+ }
155
+
156
+ if (!config.configPath) {
157
+ return;
158
+ }
159
+
160
+ const migrationsPath = await getMigrationsPath(
161
+ path.dirname(config.configPath),
162
+ databaseInfo.migrationsFolderPath,
163
+ false
164
+ );
165
+ await initMigrationsTable(
166
+ databaseInfo.migrationsTableName,
167
+ local,
168
+ config,
169
+ database,
170
+ persistTo
171
+ );
172
+
173
+ const unappliedMigrations = (
174
+ await getUnappliedMigrations(
175
+ databaseInfo.migrationsTableName,
176
+ migrationsPath,
177
+ local,
178
+ config,
179
+ database,
180
+ persistTo
181
+ )
182
+ )
183
+ .map((migration) => {
184
+ return {
185
+ Name: migration,
186
+ Status: "🕒️",
187
+ };
188
+ })
189
+ .sort((a, b) => {
190
+ const migrationNumberA = parseInt(a.Name.split("_")[0]);
191
+ const migrationNumberB = parseInt(b.Name.split("_")[0]);
192
+ if (migrationNumberA < migrationNumberB) {
193
+ return -1;
194
+ }
195
+ if (migrationNumberA > migrationNumberB) {
196
+ return 1;
197
+ }
198
+
199
+ // numbers must be equal
200
+ return 0;
201
+ });
202
+
203
+ if (unappliedMigrations.length === 0) {
204
+ render(<Text>✅ No migrations to apply!</Text>);
205
+ return;
206
+ }
207
+
208
+ const isInteractive = process.stdout.isTTY;
209
+ if (isInteractive) {
210
+ const ok = await confirm(
211
+ `About to apply ${unappliedMigrations.length} migration(s)\n` +
212
+ "Your database may not be available to serve requests during the migration, continue?",
213
+ <Box flexDirection="column">
214
+ <Text>Migrations to be applied:</Text>
215
+ <Table data={unappliedMigrations} columns={["Name"]}></Table>
216
+ </Box>
217
+ );
218
+ if (!ok) return;
219
+ }
220
+
221
+ render(<Text>🕒 Creating backup...</Text>);
222
+ await createBackup(accountId, databaseInfo.uuid);
223
+
224
+ for (const migration of unappliedMigrations) {
225
+ let query = fs.readFileSync(
226
+ `${migrationsPath}/${migration.Name}`,
227
+ "utf8"
228
+ );
229
+ query += `
230
+ INSERT INTO ${databaseInfo.migrationsTableName} (name)
231
+ values ('${migration.Name}');
232
+ `;
233
+
234
+ let success = true;
235
+ let errorNotes: Array<string> = [];
236
+ try {
237
+ const response = await executeSql(
238
+ local,
239
+ config,
240
+ database,
241
+ undefined,
242
+ persistTo,
243
+ undefined,
244
+ query
245
+ );
246
+
247
+ if (response === null) {
248
+ // TODO: return error
249
+ return;
250
+ }
251
+
252
+ for (const result of response) {
253
+ // When executing more than 1 statement, response turns into an array of QueryResult
254
+ if (Array.isArray(result)) {
255
+ for (const subResult of result) {
256
+ if (!subResult.success) {
257
+ success = false;
258
+ }
259
+ }
260
+ } else {
261
+ if (!result.success) {
262
+ success = false;
263
+ }
264
+ }
265
+ }
266
+ } catch (e) {
267
+ const err = e as ParseError;
268
+
269
+ success = false;
270
+ errorNotes = err.notes.map((msg) => msg.text);
271
+ }
272
+
273
+ migration.Status = success ? "✅" : "❌";
274
+
275
+ render(
276
+ <Box flexDirection="column">
277
+ <Table
278
+ data={unappliedMigrations}
279
+ columns={["Name", "Status"]}
280
+ ></Table>
281
+ {errorNotes.length > 0 && (
282
+ <Box flexDirection="column">
283
+ <Text>&nbsp;</Text>
284
+ <Text>
285
+ ❌ Migration {migration.Name} failed with following Errors
286
+ </Text>
287
+ <Table
288
+ data={errorNotes.map((err) => {
289
+ return { Error: err };
290
+ })}
291
+ ></Table>
292
+ </Box>
293
+ )}
294
+ </Box>
295
+ );
296
+
297
+ if (errorNotes.length > 0) return;
298
+ }
299
+ }
300
+ );
301
+
302
+ export const listAppliedMigrations = async (
303
+ migrationsTableName: string,
304
+ local: undefined | boolean,
305
+ config: ConfigFields<DevConfig> & Environment,
306
+ name: string,
307
+ persistTo: undefined | string
308
+ ): Promise<Migration[]> => {
309
+ const Query = `SELECT *
310
+ FROM ${migrationsTableName}
311
+ ORDER BY id`;
312
+
313
+ const response: QueryResult[] | null = await executeSql(
314
+ local,
315
+ config,
316
+ name,
317
+ undefined,
318
+ persistTo,
319
+ undefined,
320
+ Query
321
+ );
322
+
323
+ if (!response || response[0].results.length === 0) return [];
324
+
325
+ return response[0].results as Migration[];
326
+ };
327
+
328
+ const initMigrationsTable = async (
329
+ migrationsTableName: string,
330
+ local: undefined | boolean,
331
+ config: ConfigFields<DevConfig> & Environment,
332
+ name: string,
333
+ persistTo: undefined | string
334
+ ) => {
335
+ return executeSql(
336
+ local,
337
+ config,
338
+ name,
339
+ undefined,
340
+ persistTo,
341
+ undefined,
342
+ `
343
+ CREATE TABLE IF NOT EXISTS ${migrationsTableName}
344
+ (
345
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
346
+ name TEXT UNIQUE,
347
+ applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
348
+ );
349
+ `
350
+ );
351
+ };
352
+
353
+ function getMigrationNames(migrationsPath: string): Array<string> {
354
+ const migrations = [];
355
+
356
+ const dir = fs.opendirSync(migrationsPath);
357
+
358
+ let dirent;
359
+ while ((dirent = dir.readSync()) !== null) {
360
+ if (dirent.name.endsWith(".sql")) migrations.push(dirent.name);
361
+ }
362
+
363
+ dir.closeSync();
364
+
365
+ return migrations;
366
+ }
367
+
368
+ function getNextMigrationNumber(migrationsPath: string): number {
369
+ let highestMigrationNumber = -1;
370
+
371
+ for (const migration in getMigrationNames(migrationsPath)) {
372
+ const migrationNumber = parseInt(migration.split("_")[0]);
373
+
374
+ if (migrationNumber > highestMigrationNumber) {
375
+ highestMigrationNumber = migrationNumber;
376
+ }
377
+ }
378
+
379
+ return highestMigrationNumber + 1;
380
+ }
381
+
382
+ function pad(num: number, size: number): string {
383
+ let newNum = num.toString();
384
+ while (newNum.length < size) newNum = "0" + newNum;
385
+ return newNum;
386
+ }
387
+
388
+ type MigrationsCreateArgs = {
389
+ config?: string;
390
+ database: string;
391
+ message: string;
392
+ };
393
+
394
+ export function CreateOptions(yargs: Argv): Argv<MigrationsCreateArgs> {
395
+ return Database(yargs).positional("message", {
396
+ describe: "The Migration message",
397
+ type: "string",
398
+ demandOption: true,
399
+ });
400
+ }
401
+
402
+ export const CreateHandler = withConfig<MigrationsCreateArgs>(
403
+ async ({ config, database, message }): Promise<void> => {
404
+ await requireAuth({});
405
+ logger.log(d1BetaWarning);
406
+
407
+ const databaseInfo = await getDatabaseInfoFromConfig(config, database);
408
+ if (!databaseInfo) {
409
+ throw new Error(
410
+ `Can't find a DB with name/binding '${database}' in local config. Check info in wrangler.toml...`
411
+ );
412
+ }
413
+
414
+ if (!config.configPath) {
415
+ return;
416
+ }
417
+
418
+ const migrationsPath = await getMigrationsPath(
419
+ path.dirname(config.configPath),
420
+ databaseInfo.migrationsFolderPath,
421
+ true
422
+ );
423
+ const nextMigrationNumber = pad(getNextMigrationNumber(migrationsPath), 4);
424
+ const migrationName = message.replaceAll(" ", "_");
425
+
426
+ const newMigrationName = `${nextMigrationNumber}_${migrationName}.sql`;
427
+
428
+ fs.writeFileSync(
429
+ `${migrationsPath}/${newMigrationName}`,
430
+ `-- Migration number: ${nextMigrationNumber} \t ${new Date().toISOString()}\n`
431
+ );
432
+
433
+ render(
434
+ <Box flexDirection="column">
435
+ <Text>
436
+ ✅ Successfully created Migration &apos;{newMigrationName}&apos;!
437
+ </Text>
438
+ <Text>&nbsp;</Text>
439
+ <Text>The migration is available for editing here</Text>
440
+ <Text>
441
+ {migrationsPath}/{newMigrationName}
442
+ </Text>
443
+ </Box>
444
+ );
445
+ }
446
+ );
package/src/d1/options.ts CHANGED
@@ -10,3 +10,13 @@ export function Name(yargs: Argv) {
10
10
  })
11
11
  .epilogue(d1BetaWarning);
12
12
  }
13
+
14
+ export function Database(yargs: Argv) {
15
+ return yargs
16
+ .positional("database", {
17
+ describe: "The name or binding of the DB",
18
+ type: "string",
19
+ demandOption: true,
20
+ })
21
+ .epilogue(d1BetaWarning);
22
+ }
package/src/d1/types.tsx CHANGED
@@ -1,6 +1,10 @@
1
1
  export type Database = {
2
2
  uuid: string;
3
3
  name: string;
4
+ binding: string;
5
+ internal_env?: string;
6
+ migrationsTableName: string;
7
+ migrationsFolderPath: string;
4
8
  };
5
9
 
6
10
  export type Backup = {
@@ -12,3 +16,9 @@ export type Backup = {
12
16
  file_size: number;
13
17
  size?: string;
14
18
  };
19
+
20
+ export type Migration = {
21
+ id: string;
22
+ name: string;
23
+ applied_at: string;
24
+ };
package/src/d1/utils.ts CHANGED
@@ -1,8 +1,12 @@
1
+ import { DEFAULT_MIGRATION_PATH, DEFAULT_MIGRATION_TABLE } from "./constants";
1
2
  import { listDatabases } from "./list";
2
3
  import type { Config } from "../config";
3
4
  import type { Database } from "./types";
4
5
 
5
- export function getDatabaseInfoFromConfig(config: Config, name: string) {
6
+ export function getDatabaseInfoFromConfig(
7
+ config: Config,
8
+ name: string
9
+ ): Database | null {
6
10
  for (const d1Database of config.d1_databases) {
7
11
  if (
8
12
  d1Database.database_id &&
@@ -12,6 +16,11 @@ export function getDatabaseInfoFromConfig(config: Config, name: string) {
12
16
  uuid: d1Database.database_id,
13
17
  binding: d1Database.binding,
14
18
  name: d1Database.database_name,
19
+ migrationsTableName:
20
+ d1Database.migrations_table || DEFAULT_MIGRATION_TABLE,
21
+ migrationsFolderPath:
22
+ d1Database.migrations_dir || DEFAULT_MIGRATION_PATH,
23
+ internal_env: d1Database.database_internal_env,
15
24
  };
16
25
  }
17
26
  }
package/src/dev/local.tsx CHANGED
@@ -4,6 +4,7 @@ import { realpathSync } from "node:fs";
4
4
  import { readFile, writeFile } from "node:fs/promises";
5
5
  import path from "node:path";
6
6
  import chalk from "chalk";
7
+ import getPort from "get-port";
7
8
  import { npxImport } from "npx-import";
8
9
  import { useState, useEffect, useRef } from "react";
9
10
  import onExit from "signal-exit";
@@ -109,6 +110,7 @@ function useLocalWorker({
109
110
  const local = useRef<ChildProcess>();
110
111
  const experimentalLocalRef = useRef<Miniflare3Type>();
111
112
  const removeSignalExitListener = useRef<() => void>();
113
+ const removeExperimentalLocalSignalExitListener = useRef<() => void>();
112
114
  const [inspectorUrl, setInspectorUrl] = useState<string | undefined>();
113
115
 
114
116
  useEffect(() => {
@@ -174,7 +176,7 @@ function useLocalWorker({
174
176
  bundle,
175
177
  });
176
178
 
177
- const { forkOptions, miniflareCLIPath } = setupMiniflareOptions({
179
+ const { forkOptions, miniflareCLIPath, options } = setupMiniflareOptions({
178
180
  workerName,
179
181
  port,
180
182
  scriptPath,
@@ -207,16 +209,27 @@ function useLocalWorker({
207
209
  });
208
210
 
209
211
  if (experimentalLocal) {
210
- // TODO: refactor setupMiniflareOptions so we don't need to parse here
211
- const mf2Options: MiniflareOptions = JSON.parse(forkOptions[0]);
212
- const mf = await setupExperimentalLocal(mf2Options, format, bundle);
213
- await mf.ready;
214
- experimentalLocalRef.current = mf;
215
- removeSignalExitListener.current = onExit(() => {
216
- logger.log("⎔ Shutting down experimental local server.");
217
- mf.dispose();
218
- experimentalLocalRef.current = undefined;
219
- });
212
+ const mf3Options = await transformLocalOptions(options, format, bundle);
213
+ const current = experimentalLocalRef.current;
214
+ if (current === undefined) {
215
+ // If we don't have an active Miniflare instance, create a new one
216
+ const Miniflare = await getMiniflare3Constructor();
217
+ if (abortController.signal.aborted) return;
218
+ const mf = new Miniflare(mf3Options);
219
+ experimentalLocalRef.current = mf;
220
+ removeExperimentalLocalSignalExitListener.current = onExit(() => {
221
+ logger.log("⎔ Shutting down experimental local server.");
222
+ mf.dispose();
223
+ experimentalLocalRef.current = undefined;
224
+ });
225
+ await mf.ready;
226
+ } else {
227
+ // Otherwise, reuse the existing instance with its loopback server
228
+ // and just update the options
229
+ if (abortController.signal.aborted) return;
230
+ logger.log("⎔ Reloading experimental local server.");
231
+ await current.setOptions(mf3Options);
232
+ }
220
233
  return;
221
234
  }
222
235
 
@@ -317,13 +330,6 @@ function useLocalWorker({
317
330
  local.current?.kill();
318
331
  local.current = undefined;
319
332
  }
320
- if (experimentalLocalRef.current) {
321
- logger.log("⎔ Shutting down experimental local server.");
322
- // Initialisation errors are also thrown asynchronously by dispose().
323
- // The catch() above should've caught them though.
324
- experimentalLocalRef.current?.dispose().catch(() => {});
325
- experimentalLocalRef.current = undefined;
326
- }
327
333
  removeSignalExitListener.current?.();
328
334
  removeSignalExitListener.current = undefined;
329
335
  };
@@ -362,6 +368,26 @@ function useLocalWorker({
362
368
  enablePagesAssetsServiceBinding,
363
369
  experimentalLocal,
364
370
  ]);
371
+
372
+ // Rather than disposing the Miniflare instance on every reload, only dispose
373
+ // it if local mode is disabled and the `Local` component is unmounted. This
374
+ // allows us to use the more efficient `Miniflare#setOptions` on reload which
375
+ // retains internal state (e.g. the Miniflare loopback server).
376
+ useEffect(
377
+ () => () => {
378
+ if (experimentalLocalRef.current) {
379
+ logger.log("⎔ Shutting down experimental local server.");
380
+ // Initialisation errors are also thrown asynchronously by dispose().
381
+ // The catch() above should've caught them though.
382
+ experimentalLocalRef.current?.dispose().catch(() => {});
383
+ experimentalLocalRef.current = undefined;
384
+ }
385
+ removeExperimentalLocalSignalExitListener.current?.();
386
+ removeExperimentalLocalSignalExitListener.current = undefined;
387
+ },
388
+ []
389
+ );
390
+
365
391
  return { inspectorUrl };
366
392
  }
367
393
 
@@ -512,9 +538,10 @@ export function setupMiniflareOptions({
512
538
  }: SetupMiniflareOptionsProps): {
513
539
  miniflareCLIPath: string;
514
540
  forkOptions: string[];
541
+ options: MiniflareOptions;
515
542
  } {
516
543
  // It's now getting _really_ messy now with Pages ASSETS binding outside and the external Durable Objects inside.
517
- const options = {
544
+ const options: MiniflareOptions = {
518
545
  name: workerName,
519
546
  port,
520
547
  scriptPath,
@@ -633,7 +660,7 @@ export function setupMiniflareOptions({
633
660
  if (enablePagesAssetsServiceBinding) {
634
661
  forkOptions.push(JSON.stringify(enablePagesAssetsServiceBinding));
635
662
  }
636
- return { miniflareCLIPath, forkOptions };
663
+ return { miniflareCLIPath, forkOptions, options };
637
664
  }
638
665
 
639
666
  export function setupNodeOptions({
@@ -656,19 +683,15 @@ export function setupNodeOptions({
656
683
  return nodeOptions;
657
684
  }
658
685
 
659
- // Caching of the `npx-import`ed `@miniflare/tre` package
660
- // eslint-disable-next-line @typescript-eslint/consistent-type-imports
661
- let Miniflare: typeof import("@miniflare/tre").Miniflare;
662
-
663
686
  function arrayToObject(values: string[] = []): Record<string, string> {
664
687
  return Object.fromEntries(values.map((value) => [value, value]));
665
688
  }
666
689
 
667
- export async function setupExperimentalLocal(
690
+ export async function transformLocalOptions(
668
691
  mf2Options: MiniflareOptions,
669
692
  format: CfScriptFormat,
670
693
  bundle: EsbuildBundle
671
- ): Promise<Miniflare3Type> {
694
+ ): Promise<Miniflare3Options> {
672
695
  const options: Miniflare3Options = {
673
696
  ...mf2Options,
674
697
  // Miniflare 3 distinguishes between binding name and namespace/bucket IDs.
@@ -676,6 +699,8 @@ export async function setupExperimentalLocal(
676
699
  // TODO: use defined KV preview ID if any
677
700
  kvNamespaces: arrayToObject(mf2Options.kvNamespaces),
678
701
  r2Buckets: arrayToObject(mf2Options.r2Buckets),
702
+ inspectorPort: await getPort({ port: 9229 }),
703
+ verbose: true,
679
704
  };
680
705
 
681
706
  if (format === "modules") {
@@ -704,14 +729,18 @@ export async function setupExperimentalLocal(
704
729
  ];
705
730
  }
706
731
 
707
- logger.log("⎔ Starting an experimental local server...");
732
+ return options;
733
+ }
708
734
 
735
+ // Caching of the `npx-import`ed `@miniflare/tre` package
736
+ // eslint-disable-next-line @typescript-eslint/consistent-type-imports
737
+ let Miniflare: typeof import("@miniflare/tre").Miniflare;
738
+ export async function getMiniflare3Constructor(): Promise<typeof Miniflare> {
709
739
  if (Miniflare === undefined) {
710
740
  ({ Miniflare } = await npxImport<
711
741
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
712
742
  typeof import("@miniflare/tre")
713
- >("@miniflare/tre@next"));
743
+ >("@miniflare/tre@3.0.0-next.5"));
714
744
  }
715
-
716
- return new Miniflare(options);
745
+ return Miniflare;
717
746
  }