wrangler 2.4.2 → 2.4.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.
- package/package.json +1 -1
- package/src/__tests__/d1.test.ts +8 -2
- package/src/__tests__/queues.test.ts +64 -1
- package/src/bundle.ts +113 -98
- package/src/config/validation.ts +1 -1
- package/src/d1/migrations/apply.tsx +183 -0
- package/src/d1/migrations/create.tsx +77 -0
- package/src/d1/migrations/helpers.ts +145 -0
- package/src/d1/migrations/index.tsx +3 -0
- package/src/d1/migrations/list.tsx +79 -0
- package/src/d1/utils.ts +1 -1
- package/src/dev/dev.tsx +1 -0
- package/src/dev/local.tsx +4 -2
- package/src/dev/start-server.ts +8 -1
- package/src/dev/use-esbuild.ts +4 -0
- package/src/index.tsx +7 -2
- package/src/pages/functions/buildPlugin.ts +1 -0
- package/src/pages/functions/buildWorker.ts +1 -0
- package/src/publish/publish.ts +1 -0
- package/src/queues/cli/commands/index.ts +3 -0
- package/src/queues/utils.ts +18 -0
- package/src/user/user.tsx +11 -5
- package/templates/middleware/loader-sw.ts +45 -31
- package/templates/middleware/middleware-miniflare3-json-error.ts +20 -0
- package/wrangler-dist/cli.js +1141 -1063
- package/src/d1/migrations.tsx +0 -446
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { confirm } from "../../dialogs";
|
|
4
|
+
import { logger } from "../../logger";
|
|
5
|
+
import { DEFAULT_MIGRATION_PATH } from "../constants";
|
|
6
|
+
import { executeSql } from "../execute";
|
|
7
|
+
import type { ConfigFields, DevConfig, Environment } from "../../config";
|
|
8
|
+
import type { QueryResult } from "../execute";
|
|
9
|
+
import type { Migration } from "../types";
|
|
10
|
+
|
|
11
|
+
export async function getMigrationsPath(
|
|
12
|
+
projectPath: string,
|
|
13
|
+
migrationsFolderPath: string,
|
|
14
|
+
createIfMissing: boolean
|
|
15
|
+
): Promise<string> {
|
|
16
|
+
const dir = path.resolve(projectPath, migrationsFolderPath);
|
|
17
|
+
if (fs.existsSync(dir)) return dir;
|
|
18
|
+
|
|
19
|
+
const warning = `No migrations folder found.${
|
|
20
|
+
migrationsFolderPath === DEFAULT_MIGRATION_PATH
|
|
21
|
+
? " Set `migrations_dir` in wrangler.toml to choose a different path."
|
|
22
|
+
: ""
|
|
23
|
+
}`;
|
|
24
|
+
|
|
25
|
+
if (createIfMissing && (await confirm(`${warning}\nOk to create ${dir}?`))) {
|
|
26
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
27
|
+
return dir;
|
|
28
|
+
} else {
|
|
29
|
+
logger.warn(warning);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
throw new Error(`No migrations present at ${dir}.`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function getUnappliedMigrations(
|
|
36
|
+
migrationsTableName: string,
|
|
37
|
+
migrationsPath: string,
|
|
38
|
+
local: undefined | boolean,
|
|
39
|
+
config: ConfigFields<DevConfig> & Environment,
|
|
40
|
+
name: string,
|
|
41
|
+
persistTo: undefined | string
|
|
42
|
+
): Promise<Array<string>> {
|
|
43
|
+
const appliedMigrations = (
|
|
44
|
+
await listAppliedMigrations(
|
|
45
|
+
migrationsTableName,
|
|
46
|
+
local,
|
|
47
|
+
config,
|
|
48
|
+
name,
|
|
49
|
+
persistTo
|
|
50
|
+
)
|
|
51
|
+
).map((migration) => {
|
|
52
|
+
return migration.name;
|
|
53
|
+
});
|
|
54
|
+
const projectMigrations = getMigrationNames(migrationsPath);
|
|
55
|
+
|
|
56
|
+
const unappliedMigrations: Array<string> = [];
|
|
57
|
+
|
|
58
|
+
for (const migration of projectMigrations) {
|
|
59
|
+
if (!appliedMigrations.includes(migration)) {
|
|
60
|
+
unappliedMigrations.push(migration);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return unappliedMigrations;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const listAppliedMigrations = async (
|
|
68
|
+
migrationsTableName: string,
|
|
69
|
+
local: undefined | boolean,
|
|
70
|
+
config: ConfigFields<DevConfig> & Environment,
|
|
71
|
+
name: string,
|
|
72
|
+
persistTo: undefined | string
|
|
73
|
+
): Promise<Migration[]> => {
|
|
74
|
+
const Query = `SELECT *
|
|
75
|
+
FROM ${migrationsTableName}
|
|
76
|
+
ORDER BY id`;
|
|
77
|
+
|
|
78
|
+
const response: QueryResult[] | null = await executeSql(
|
|
79
|
+
local,
|
|
80
|
+
config,
|
|
81
|
+
name,
|
|
82
|
+
undefined,
|
|
83
|
+
persistTo,
|
|
84
|
+
undefined,
|
|
85
|
+
Query
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
if (!response || response[0].results.length === 0) return [];
|
|
89
|
+
|
|
90
|
+
return response[0].results as Migration[];
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
function getMigrationNames(migrationsPath: string): Array<string> {
|
|
94
|
+
const migrations = [];
|
|
95
|
+
|
|
96
|
+
const dir = fs.opendirSync(migrationsPath);
|
|
97
|
+
|
|
98
|
+
let dirent;
|
|
99
|
+
while ((dirent = dir.readSync()) !== null) {
|
|
100
|
+
if (dirent.name.endsWith(".sql")) migrations.push(dirent.name);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
dir.closeSync();
|
|
104
|
+
|
|
105
|
+
return migrations;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function getNextMigrationNumber(migrationsPath: string): number {
|
|
109
|
+
let highestMigrationNumber = -1;
|
|
110
|
+
|
|
111
|
+
for (const migration in getMigrationNames(migrationsPath)) {
|
|
112
|
+
const migrationNumber = parseInt(migration.split("_")[0]);
|
|
113
|
+
|
|
114
|
+
if (migrationNumber > highestMigrationNumber) {
|
|
115
|
+
highestMigrationNumber = migrationNumber;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return highestMigrationNumber + 1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const initMigrationsTable = async (
|
|
123
|
+
migrationsTableName: string,
|
|
124
|
+
local: undefined | boolean,
|
|
125
|
+
config: ConfigFields<DevConfig> & Environment,
|
|
126
|
+
name: string,
|
|
127
|
+
persistTo: undefined | string
|
|
128
|
+
) => {
|
|
129
|
+
return executeSql(
|
|
130
|
+
local,
|
|
131
|
+
config,
|
|
132
|
+
name,
|
|
133
|
+
undefined,
|
|
134
|
+
persistTo,
|
|
135
|
+
undefined,
|
|
136
|
+
`
|
|
137
|
+
CREATE TABLE IF NOT EXISTS ${migrationsTableName}
|
|
138
|
+
(
|
|
139
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
140
|
+
name TEXT UNIQUE,
|
|
141
|
+
applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
|
|
142
|
+
);
|
|
143
|
+
`
|
|
144
|
+
);
|
|
145
|
+
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { Box, render, Text } from "ink";
|
|
3
|
+
import Table from "ink-table";
|
|
4
|
+
import React from "react";
|
|
5
|
+
import { withConfig } from "../../config";
|
|
6
|
+
import { logger } from "../../logger";
|
|
7
|
+
import { requireAuth } from "../../user";
|
|
8
|
+
import { Database } from "../options";
|
|
9
|
+
import { d1BetaWarning, getDatabaseInfoFromConfig } from "../utils";
|
|
10
|
+
import {
|
|
11
|
+
getMigrationsPath,
|
|
12
|
+
getUnappliedMigrations,
|
|
13
|
+
initMigrationsTable,
|
|
14
|
+
} from "./helpers";
|
|
15
|
+
import type { BaseSqlExecuteArgs } from "../execute";
|
|
16
|
+
import type { Argv } from "yargs";
|
|
17
|
+
|
|
18
|
+
export function ListOptions(yargs: Argv): Argv<BaseSqlExecuteArgs> {
|
|
19
|
+
return Database(yargs);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const ListHandler = withConfig<BaseSqlExecuteArgs>(
|
|
23
|
+
async ({ config, database, local, persistTo }): Promise<void> => {
|
|
24
|
+
await requireAuth({});
|
|
25
|
+
logger.log(d1BetaWarning);
|
|
26
|
+
|
|
27
|
+
const databaseInfo = await getDatabaseInfoFromConfig(config, database);
|
|
28
|
+
if (!databaseInfo) {
|
|
29
|
+
throw new Error(
|
|
30
|
+
`Can't find a DB with name/binding '${database}' in local config. Check info in wrangler.toml...`
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!config.configPath) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const { migrationsTableName, migrationsFolderPath } = databaseInfo;
|
|
38
|
+
|
|
39
|
+
const migrationsPath = await getMigrationsPath(
|
|
40
|
+
path.dirname(config.configPath),
|
|
41
|
+
migrationsFolderPath,
|
|
42
|
+
false
|
|
43
|
+
);
|
|
44
|
+
await initMigrationsTable(
|
|
45
|
+
migrationsTableName,
|
|
46
|
+
local,
|
|
47
|
+
config,
|
|
48
|
+
database,
|
|
49
|
+
persistTo
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const unappliedMigrations = (
|
|
53
|
+
await getUnappliedMigrations(
|
|
54
|
+
migrationsTableName,
|
|
55
|
+
migrationsPath,
|
|
56
|
+
local,
|
|
57
|
+
config,
|
|
58
|
+
database,
|
|
59
|
+
persistTo
|
|
60
|
+
)
|
|
61
|
+
).map((migration) => {
|
|
62
|
+
return {
|
|
63
|
+
Name: migration,
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
if (unappliedMigrations.length === 0) {
|
|
68
|
+
render(<Text>✅ No migrations to apply!</Text>);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
render(
|
|
73
|
+
<Box flexDirection="column">
|
|
74
|
+
<Text>Migrations to be applied:</Text>
|
|
75
|
+
<Table data={unappliedMigrations} columns={["Name"]}></Table>
|
|
76
|
+
</Box>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
);
|
package/src/d1/utils.ts
CHANGED
|
@@ -45,4 +45,4 @@ export const getDatabaseByNameOrBinding = async (
|
|
|
45
45
|
|
|
46
46
|
export const d1BetaWarning = process.env.NO_D1_WARNING
|
|
47
47
|
? ""
|
|
48
|
-
: "🚧
|
|
48
|
+
: "🚧 D1 is currently in open alpha and is not recommended for production data and traffic.\nPlease report any bugs to https://github.com/cloudflare/wrangler2/issues/new/choose.\nTo request features, visit https://community.cloudflare.com/c/developers/d1.\nTo give feedback, visit https://discord.gg/cloudflaredev";
|
package/src/dev/dev.tsx
CHANGED
|
@@ -288,6 +288,7 @@ function DevSession(props: DevSessionProps) {
|
|
|
288
288
|
// Enable the bundling to know whether we are using dev or publish
|
|
289
289
|
targetConsumer: "dev",
|
|
290
290
|
testScheduled: props.testScheduled ?? false,
|
|
291
|
+
experimentalLocal: props.experimentalLocal,
|
|
291
292
|
});
|
|
292
293
|
|
|
293
294
|
// TODO(queues) support remote wrangler dev
|
package/src/dev/local.tsx
CHANGED
|
@@ -839,17 +839,19 @@ export async function transformMf2OptionsToMf3Options({
|
|
|
839
839
|
const root = path.dirname(bundle.path);
|
|
840
840
|
|
|
841
841
|
assert.strictEqual(bundle.type, "esm");
|
|
842
|
+
// Required for source mapped paths to resolve correctly
|
|
843
|
+
options.modulesRoot = root;
|
|
842
844
|
options.modules = [
|
|
843
845
|
// Entrypoint
|
|
844
846
|
{
|
|
845
847
|
type: "ESModule",
|
|
846
|
-
path:
|
|
848
|
+
path: bundle.path,
|
|
847
849
|
contents: await readFile(bundle.path, "utf-8"),
|
|
848
850
|
},
|
|
849
851
|
// Misc (WebAssembly, etc, ...)
|
|
850
852
|
...bundle.modules.map((module) => ({
|
|
851
853
|
type: ModuleTypeToRuleType[module.type ?? "esm"],
|
|
852
|
-
path: module.name,
|
|
854
|
+
path: path.resolve(root, module.name),
|
|
853
855
|
contents: module.content,
|
|
854
856
|
})),
|
|
855
857
|
];
|
package/src/dev/start-server.ts
CHANGED
|
@@ -99,6 +99,8 @@ export async function startDevServer(
|
|
|
99
99
|
services: props.bindings.services,
|
|
100
100
|
firstPartyWorkerDevFacade: props.firstPartyWorker,
|
|
101
101
|
testScheduled: props.testScheduled,
|
|
102
|
+
local: props.local,
|
|
103
|
+
experimentalLocal: props.experimentalLocal,
|
|
102
104
|
});
|
|
103
105
|
|
|
104
106
|
if (props.local) {
|
|
@@ -206,6 +208,8 @@ async function runEsbuild({
|
|
|
206
208
|
services,
|
|
207
209
|
firstPartyWorkerDevFacade,
|
|
208
210
|
testScheduled,
|
|
211
|
+
local,
|
|
212
|
+
experimentalLocal,
|
|
209
213
|
}: {
|
|
210
214
|
entry: Entry;
|
|
211
215
|
destination: string | undefined;
|
|
@@ -224,6 +228,8 @@ async function runEsbuild({
|
|
|
224
228
|
workerDefinitions: WorkerRegistry;
|
|
225
229
|
firstPartyWorkerDevFacade: boolean | undefined;
|
|
226
230
|
testScheduled?: boolean;
|
|
231
|
+
local: boolean;
|
|
232
|
+
experimentalLocal: boolean | undefined;
|
|
227
233
|
}): Promise<EsbuildBundle | undefined> {
|
|
228
234
|
if (!destination) return;
|
|
229
235
|
|
|
@@ -262,8 +268,9 @@ async function runEsbuild({
|
|
|
262
268
|
services,
|
|
263
269
|
firstPartyWorkerDevFacade,
|
|
264
270
|
targetConsumer: "dev", // We are starting a dev server
|
|
265
|
-
local: true,
|
|
266
271
|
testScheduled,
|
|
272
|
+
local,
|
|
273
|
+
experimentalLocal,
|
|
267
274
|
});
|
|
268
275
|
|
|
269
276
|
return {
|
package/src/dev/use-esbuild.ts
CHANGED
|
@@ -41,6 +41,7 @@ export function useEsbuild({
|
|
|
41
41
|
local,
|
|
42
42
|
targetConsumer,
|
|
43
43
|
testScheduled,
|
|
44
|
+
experimentalLocal,
|
|
44
45
|
}: {
|
|
45
46
|
entry: Entry;
|
|
46
47
|
destination: string | undefined;
|
|
@@ -62,6 +63,7 @@ export function useEsbuild({
|
|
|
62
63
|
local: boolean;
|
|
63
64
|
targetConsumer: "dev" | "publish";
|
|
64
65
|
testScheduled: boolean;
|
|
66
|
+
experimentalLocal: boolean | undefined;
|
|
65
67
|
}): EsbuildBundle | undefined {
|
|
66
68
|
const [bundle, setBundle] = useState<EsbuildBundle>();
|
|
67
69
|
const { exit } = useApp();
|
|
@@ -134,6 +136,7 @@ export function useEsbuild({
|
|
|
134
136
|
local,
|
|
135
137
|
targetConsumer,
|
|
136
138
|
testScheduled,
|
|
139
|
+
experimentalLocal,
|
|
137
140
|
});
|
|
138
141
|
|
|
139
142
|
// Capture the `stop()` method to use as the `useEffect()` destructor.
|
|
@@ -195,6 +198,7 @@ export function useEsbuild({
|
|
|
195
198
|
local,
|
|
196
199
|
targetConsumer,
|
|
197
200
|
testScheduled,
|
|
201
|
+
experimentalLocal,
|
|
198
202
|
]);
|
|
199
203
|
return bundle;
|
|
200
204
|
}
|
package/src/index.tsx
CHANGED
|
@@ -445,6 +445,11 @@ export function createCLIParser(argv: string[]) {
|
|
|
445
445
|
.option("scopes-list", {
|
|
446
446
|
describe: "List all the available OAuth scopes with descriptions",
|
|
447
447
|
})
|
|
448
|
+
.option("browser", {
|
|
449
|
+
default: true,
|
|
450
|
+
type: "boolean",
|
|
451
|
+
describe: "Automatically open the OAuth link in a browser",
|
|
452
|
+
})
|
|
448
453
|
.option("scopes", {
|
|
449
454
|
describe: "Pick the set of applicable OAuth scopes when logging in",
|
|
450
455
|
array: true,
|
|
@@ -471,10 +476,10 @@ export function createCLIParser(argv: string[]) {
|
|
|
471
476
|
`One of ${args.scopes} is not a valid authentication scope. Run "wrangler login --list-scopes" to see the valid scopes.`
|
|
472
477
|
);
|
|
473
478
|
}
|
|
474
|
-
await login({ scopes: args.scopes });
|
|
479
|
+
await login({ scopes: args.scopes, browser: args.browser });
|
|
475
480
|
return;
|
|
476
481
|
}
|
|
477
|
-
await login();
|
|
482
|
+
await login({ browser: args.browser });
|
|
478
483
|
const config = readConfig(args.config as ConfigPath, args);
|
|
479
484
|
await metrics.sendMetricsEvent("login user", {
|
|
480
485
|
sendMetrics: config.send_metrics,
|
package/src/publish/publish.ts
CHANGED
|
@@ -487,6 +487,7 @@ See https://developers.cloudflare.com/workers/platform/compatibility-dates for m
|
|
|
487
487
|
// This could potentially cause issues as we no longer have identical behaviour between dev and publish?
|
|
488
488
|
targetConsumer: "publish",
|
|
489
489
|
local: false,
|
|
490
|
+
experimentalLocal: false,
|
|
490
491
|
}
|
|
491
492
|
);
|
|
492
493
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type BuilderCallback } from "yargs";
|
|
2
2
|
import { type CommonYargsOptions } from "../../../yargs-types";
|
|
3
|
+
import { HandleUnauthorizedError } from "../../utils";
|
|
3
4
|
import { consumers } from "./consumer";
|
|
4
5
|
|
|
5
6
|
import { options as createOptions, handler as createHandler } from "./create";
|
|
@@ -30,4 +31,6 @@ export const queues: BuilderCallback<CommonYargsOptions, unknown> = (yargs) => {
|
|
|
30
31
|
await consumers(consumersYargs);
|
|
31
32
|
}
|
|
32
33
|
);
|
|
34
|
+
|
|
35
|
+
yargs.fail(HandleUnauthorizedError);
|
|
33
36
|
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { logger } from "../logger";
|
|
2
|
+
import { type ParseError } from "../parse";
|
|
3
|
+
import { getAccountId } from "../user";
|
|
4
|
+
|
|
5
|
+
const isFetchError = (err: unknown): err is ParseError => err instanceof Error;
|
|
6
|
+
|
|
7
|
+
export const HandleUnauthorizedError = async (_msg: string, err: Error) => {
|
|
8
|
+
//@ts-expect-error non-standard property on Error
|
|
9
|
+
if (isFetchError(err) && err.code === 10023) {
|
|
10
|
+
const accountId = await getAccountId();
|
|
11
|
+
if (accountId) {
|
|
12
|
+
return logger.log(
|
|
13
|
+
`Queues is not currently enabled on this account. Go to https://dash.cloudflare.com/${accountId}/workers/queues to enable it.`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
throw err;
|
|
18
|
+
};
|
package/src/user/user.tsx
CHANGED
|
@@ -875,6 +875,7 @@ export function readAuthConfigFile(): UserAuthConfig {
|
|
|
875
875
|
|
|
876
876
|
type LoginProps = {
|
|
877
877
|
scopes?: Scope[];
|
|
878
|
+
browser: boolean;
|
|
878
879
|
};
|
|
879
880
|
|
|
880
881
|
export async function loginOrRefreshIfRequired(): Promise<boolean> {
|
|
@@ -901,7 +902,9 @@ export async function loginOrRefreshIfRequired(): Promise<boolean> {
|
|
|
901
902
|
}
|
|
902
903
|
}
|
|
903
904
|
|
|
904
|
-
export async function login(
|
|
905
|
+
export async function login(
|
|
906
|
+
props: LoginProps = { browser: true }
|
|
907
|
+
): Promise<boolean> {
|
|
905
908
|
logger.log("Attempting to login via OAuth...");
|
|
906
909
|
const urlToOpen = await getAuthURL(props?.scopes);
|
|
907
910
|
let server: http.Server;
|
|
@@ -914,7 +917,7 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
914
917
|
server.close();
|
|
915
918
|
clearTimeout(loginTimeoutHandle);
|
|
916
919
|
resolve(false);
|
|
917
|
-
},
|
|
920
|
+
}, 120000); // wait for 120 seconds for the user to authorize
|
|
918
921
|
});
|
|
919
922
|
|
|
920
923
|
const loginPromise = new Promise<boolean>((resolve, reject) => {
|
|
@@ -986,9 +989,12 @@ export async function login(props?: LoginProps): Promise<boolean> {
|
|
|
986
989
|
|
|
987
990
|
server.listen(8976);
|
|
988
991
|
});
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
+
if (props?.browser) {
|
|
993
|
+
logger.log(`Opening a link in your default browser: ${urlToOpen}`);
|
|
994
|
+
await openInBrowser(urlToOpen);
|
|
995
|
+
} else {
|
|
996
|
+
logger.log(`Visit this link to authenticate: ${urlToOpen}`);
|
|
997
|
+
}
|
|
992
998
|
|
|
993
999
|
return Promise.race([timerPromise, loginPromise]);
|
|
994
1000
|
}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import {
|
|
2
|
+
Awaitable,
|
|
3
|
+
Dispatcher,
|
|
4
|
+
Middleware,
|
|
5
|
+
__facade_invoke__,
|
|
6
|
+
__facade_register__,
|
|
7
|
+
__facade_registerInternal__,
|
|
8
|
+
} from "./common";
|
|
9
|
+
export { __facade_register__, __facade_registerInternal__ };
|
|
10
|
+
|
|
11
|
+
// Miniflare 2's `EventTarget` follows the spec and doesn't allow exceptions to
|
|
12
|
+
// be caught by `dispatchEvent`. Instead it has a custom `ThrowingEventTarget`
|
|
6
13
|
// class that rethrows errors from event listeners in `dispatchEvent`.
|
|
7
14
|
// We'd like errors to be propagated to the top-level `addEventListener`, so
|
|
8
15
|
// we'd like to use `ThrowingEventTarget`. Unfortunately, `ThrowingEventTarget`
|
|
@@ -15,45 +22,52 @@ if ((globalThis as any).MINIFLARE) {
|
|
|
15
22
|
__FACADE_EVENT_TARGET__ = new EventTarget();
|
|
16
23
|
}
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
listener: EventListenerOrEventListenerObject,
|
|
22
|
-
options?: EventTargetAddEventListenerOptions | boolean
|
|
23
|
-
) => void;
|
|
24
|
-
var __facade_removeEventListener__: (
|
|
25
|
-
type: string,
|
|
26
|
-
listener: EventListenerOrEventListenerObject,
|
|
27
|
-
options?: EventTargetEventListenerOptions | boolean
|
|
28
|
-
) => void;
|
|
29
|
-
var __facade_dispatchEvent__: (event: Event) => void;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function __facade_isSpecialEvent__(type: string) {
|
|
25
|
+
function __facade_isSpecialEvent__(
|
|
26
|
+
type: string
|
|
27
|
+
): type is "fetch" | "scheduled" {
|
|
33
28
|
return type === "fetch" || type === "scheduled";
|
|
34
29
|
}
|
|
35
|
-
|
|
30
|
+
const __facade__originalAddEventListener__ = globalThis.addEventListener;
|
|
31
|
+
const __facade__originalRemoveEventListener__ = globalThis.removeEventListener;
|
|
32
|
+
const __facade__originalDispatchEvent__ = globalThis.dispatchEvent;
|
|
33
|
+
|
|
34
|
+
globalThis.addEventListener = function (type, listener, options) {
|
|
36
35
|
if (__facade_isSpecialEvent__(type)) {
|
|
37
|
-
__FACADE_EVENT_TARGET__.addEventListener(
|
|
36
|
+
__FACADE_EVENT_TARGET__.addEventListener(
|
|
37
|
+
type,
|
|
38
|
+
listener as EventListenerOrEventListenerObject,
|
|
39
|
+
options
|
|
40
|
+
);
|
|
38
41
|
} else {
|
|
39
|
-
|
|
42
|
+
__facade__originalAddEventListener__(type, listener, options);
|
|
40
43
|
}
|
|
41
44
|
};
|
|
42
|
-
globalThis.
|
|
45
|
+
globalThis.removeEventListener = function (type, listener, options) {
|
|
43
46
|
if (__facade_isSpecialEvent__(type)) {
|
|
44
|
-
__FACADE_EVENT_TARGET__.removeEventListener(
|
|
47
|
+
__FACADE_EVENT_TARGET__.removeEventListener(
|
|
48
|
+
type,
|
|
49
|
+
listener as EventListenerOrEventListenerObject,
|
|
50
|
+
options
|
|
51
|
+
);
|
|
45
52
|
} else {
|
|
46
|
-
|
|
53
|
+
__facade__originalRemoveEventListener__(type, listener, options);
|
|
47
54
|
}
|
|
48
55
|
};
|
|
49
|
-
globalThis.
|
|
56
|
+
globalThis.dispatchEvent = function (event) {
|
|
50
57
|
if (__facade_isSpecialEvent__(event.type)) {
|
|
51
|
-
__FACADE_EVENT_TARGET__.dispatchEvent(event);
|
|
58
|
+
return __FACADE_EVENT_TARGET__.dispatchEvent(event);
|
|
52
59
|
} else {
|
|
53
|
-
|
|
60
|
+
return __facade__originalDispatchEvent__(event);
|
|
54
61
|
}
|
|
55
62
|
};
|
|
56
63
|
|
|
64
|
+
declare global {
|
|
65
|
+
var addMiddleware: typeof __facade_register__;
|
|
66
|
+
var addMiddlewareInternal: typeof __facade_registerInternal__;
|
|
67
|
+
}
|
|
68
|
+
globalThis.addMiddleware = __facade_register__;
|
|
69
|
+
globalThis.addMiddlewareInternal = __facade_registerInternal__;
|
|
70
|
+
|
|
57
71
|
const __facade_waitUntil__ = Symbol("__facade_waitUntil__");
|
|
58
72
|
const __facade_response__ = Symbol("__facade_response__");
|
|
59
73
|
const __facade_dispatched__ = Symbol("__facade_dispatched__");
|
|
@@ -154,7 +168,7 @@ class __Facade_ScheduledEvent__ extends __Facade_ExtendableEvent__ {
|
|
|
154
168
|
}
|
|
155
169
|
}
|
|
156
170
|
|
|
157
|
-
|
|
171
|
+
__facade__originalAddEventListener__("fetch", (event) => {
|
|
158
172
|
const ctx: ExecutionContext = {
|
|
159
173
|
waitUntil: event.waitUntil.bind(event),
|
|
160
174
|
passThroughOnException: event.passThroughOnException.bind(event),
|
|
@@ -201,7 +215,7 @@ globalThis.addEventListener("fetch", (event) => {
|
|
|
201
215
|
);
|
|
202
216
|
});
|
|
203
217
|
|
|
204
|
-
|
|
218
|
+
__facade__originalAddEventListener__("scheduled", (event) => {
|
|
205
219
|
const facadeEvent = new __Facade_ScheduledEvent__("scheduled", {
|
|
206
220
|
scheduledTime: event.scheduledTime,
|
|
207
221
|
cron: event.cron,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Middleware } from "./common";
|
|
2
|
+
|
|
3
|
+
// See comment in `bundle.ts` for details on why this is needed
|
|
4
|
+
const jsonError: Middleware = async (request, env, _ctx, middlewareCtx) => {
|
|
5
|
+
try {
|
|
6
|
+
return await middlewareCtx.next(request, env);
|
|
7
|
+
} catch (e: any) {
|
|
8
|
+
const error = {
|
|
9
|
+
name: e?.name,
|
|
10
|
+
message: e?.message ?? String(e),
|
|
11
|
+
stack: e?.stack,
|
|
12
|
+
};
|
|
13
|
+
return Response.json(error, {
|
|
14
|
+
status: 500,
|
|
15
|
+
headers: { "MF-Experimental-Error-Stack": "true" },
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default jsonError;
|