wrangler 2.0.24 → 2.0.27
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/miniflare-dist/index.mjs +142 -16
- package/package.json +3 -3
- package/src/__tests__/configuration.test.ts +7 -3
- package/src/__tests__/dev.test.tsx +26 -4
- package/src/__tests__/generate.test.ts +2 -4
- package/src/__tests__/helpers/mock-cfetch.ts +35 -2
- package/src/__tests__/init.test.ts +537 -359
- package/src/__tests__/jest.setup.ts +7 -0
- package/src/__tests__/metrics.test.ts +1 -1
- package/src/__tests__/pages.test.ts +14 -0
- package/src/__tests__/r2.test.ts +22 -3
- package/src/__tests__/tail.test.ts +112 -42
- package/src/__tests__/user.test.ts +11 -0
- package/src/api/dev.ts +7 -0
- package/src/bundle.ts +3 -2
- package/src/cfetch/internal.ts +56 -0
- package/src/config/config.ts +1 -1
- package/src/config/validation-helpers.ts +19 -6
- package/src/config/validation.ts +9 -3
- package/src/config-cache.ts +2 -1
- package/src/dev/dev.tsx +16 -2
- package/src/dev/local.tsx +69 -5
- package/src/dev/use-esbuild.ts +3 -0
- package/src/dev-registry.tsx +3 -0
- package/src/dev.tsx +28 -19
- package/src/generate.ts +1 -1
- package/src/index.tsx +51 -21
- package/src/init.ts +111 -38
- package/src/inspect.ts +1 -4
- package/src/{metrics/is-ci.ts → is-ci.ts} +0 -0
- package/src/metrics/metrics-config.ts +1 -1
- package/src/miniflare-cli/assets.ts +27 -16
- package/src/miniflare-cli/index.ts +124 -2
- package/src/pages/build.tsx +75 -41
- package/src/pages/constants.ts +4 -0
- package/src/pages/deployments.tsx +9 -9
- package/src/pages/dev.tsx +178 -64
- package/src/pages/errors.ts +22 -0
- package/src/pages/functions/buildPlugin.ts +4 -0
- package/src/pages/functions/buildWorker.ts +4 -0
- package/src/pages/functions/routes-consolidation.test.ts +250 -0
- package/src/pages/functions/routes-consolidation.ts +73 -0
- package/src/pages/functions/routes-transformation.test.ts +271 -0
- package/src/pages/functions/routes-transformation.ts +122 -0
- package/src/pages/functions.tsx +96 -0
- package/src/pages/index.tsx +65 -55
- package/src/pages/projects.tsx +9 -3
- package/src/pages/publish.tsx +75 -22
- package/src/pages/types.ts +9 -0
- package/src/pages/upload.tsx +6 -8
- package/src/proxy.ts +10 -0
- package/src/r2.ts +17 -4
- package/src/tail/filters.ts +3 -1
- package/src/tail/index.ts +15 -2
- package/src/tail/printing.ts +43 -3
- package/src/user/user.tsx +6 -4
- package/src/whoami.tsx +5 -5
- package/templates/pages-template-plugin.ts +16 -4
- package/templates/pages-template-worker.ts +16 -5
- package/templates/service-bindings-module-facade.js +10 -7
- package/templates/service-bindings-sw-facade.js +10 -7
- package/wrangler-dist/cli.d.ts +7 -0
- package/wrangler-dist/cli.js +1681 -1091
package/src/tail/index.ts
CHANGED
|
@@ -174,12 +174,12 @@ export type TailEventMessage = {
|
|
|
174
174
|
/**
|
|
175
175
|
* The event that triggered the worker. In the case of an HTTP request,
|
|
176
176
|
* this will be a RequestEvent. If it's a cron trigger, it'll be a
|
|
177
|
-
* ScheduledEvent.
|
|
177
|
+
* ScheduledEvent. If it's a durable object alarm, it's an AlarmEvent.
|
|
178
178
|
*
|
|
179
179
|
* Until workers-types exposes individual types for export, we'll have
|
|
180
180
|
* to just re-define these types ourselves.
|
|
181
181
|
*/
|
|
182
|
-
event: RequestEvent | ScheduledEvent | undefined | null;
|
|
182
|
+
event: RequestEvent | ScheduledEvent | AlarmEvent | undefined | null;
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
/**
|
|
@@ -297,3 +297,16 @@ export type ScheduledEvent = {
|
|
|
297
297
|
*/
|
|
298
298
|
scheduledTime: number;
|
|
299
299
|
};
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* A event that was triggered from a durable object alarm
|
|
303
|
+
*/
|
|
304
|
+
export type AlarmEvent = {
|
|
305
|
+
/**
|
|
306
|
+
* The datetime the alarm was scheduled for.
|
|
307
|
+
*
|
|
308
|
+
* This is sent as an ISO timestamp string (different than ScheduledEvent.scheduledTime),
|
|
309
|
+
* you should parse it later on on your own.
|
|
310
|
+
*/
|
|
311
|
+
scheduledTime: string;
|
|
312
|
+
};
|
package/src/tail/printing.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { logger } from "../logger";
|
|
2
|
-
import type { RequestEvent, ScheduledEvent, TailEventMessage } from ".";
|
|
3
2
|
import type { Outcome } from "./filters";
|
|
3
|
+
import type {
|
|
4
|
+
AlarmEvent,
|
|
5
|
+
RequestEvent,
|
|
6
|
+
ScheduledEvent,
|
|
7
|
+
TailEventMessage,
|
|
8
|
+
} from "./index";
|
|
4
9
|
import type WebSocket from "ws";
|
|
5
10
|
|
|
6
11
|
export function prettyPrintLogs(data: WebSocket.RawData): void {
|
|
@@ -14,7 +19,7 @@ export function prettyPrintLogs(data: WebSocket.RawData): void {
|
|
|
14
19
|
const outcome = prettifyOutcome(eventMessage.outcome);
|
|
15
20
|
|
|
16
21
|
logger.log(`"${cronPattern}" @ ${datetime} - ${outcome}`);
|
|
17
|
-
} else {
|
|
22
|
+
} else if (isRequestEvent(eventMessage.event)) {
|
|
18
23
|
const requestMethod = eventMessage.event?.request.method.toUpperCase();
|
|
19
24
|
const url = eventMessage.event?.request.url;
|
|
20
25
|
const outcome = prettifyOutcome(eventMessage.outcome);
|
|
@@ -25,6 +30,19 @@ export function prettyPrintLogs(data: WebSocket.RawData): void {
|
|
|
25
30
|
? `${requestMethod} ${url} - ${outcome} @ ${datetime}`
|
|
26
31
|
: `[missing request] - ${outcome} @ ${datetime}`
|
|
27
32
|
);
|
|
33
|
+
} else if (isAlarmEvent(eventMessage.event)) {
|
|
34
|
+
const outcome = prettifyOutcome(eventMessage.outcome);
|
|
35
|
+
const datetime = new Date(
|
|
36
|
+
eventMessage.event.scheduledTime
|
|
37
|
+
).toLocaleString();
|
|
38
|
+
|
|
39
|
+
logger.log(`Alarm @ ${datetime} - ${outcome}`);
|
|
40
|
+
} else {
|
|
41
|
+
// Unknown event type
|
|
42
|
+
const outcome = prettifyOutcome(eventMessage.outcome);
|
|
43
|
+
const datetime = new Date(eventMessage.eventTimestamp).toLocaleString();
|
|
44
|
+
|
|
45
|
+
logger.log(`Unknown Event - ${outcome} @ ${datetime}`);
|
|
28
46
|
}
|
|
29
47
|
|
|
30
48
|
if (eventMessage.logs.length > 0) {
|
|
@@ -44,12 +62,32 @@ export function jsonPrintLogs(data: WebSocket.RawData): void {
|
|
|
44
62
|
console.log(JSON.stringify(JSON.parse(data.toString()), null, 2));
|
|
45
63
|
}
|
|
46
64
|
|
|
65
|
+
function isRequestEvent(
|
|
66
|
+
event: TailEventMessage["event"]
|
|
67
|
+
): event is RequestEvent {
|
|
68
|
+
return Boolean(event && "request" in event);
|
|
69
|
+
}
|
|
70
|
+
|
|
47
71
|
function isScheduledEvent(
|
|
48
|
-
event:
|
|
72
|
+
event: TailEventMessage["event"]
|
|
49
73
|
): event is ScheduledEvent {
|
|
50
74
|
return Boolean(event && "cron" in event);
|
|
51
75
|
}
|
|
52
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Check to see if an event sent from a worker is an AlarmEvent.
|
|
79
|
+
*
|
|
80
|
+
* Because the only property on `AlarmEvent` is "scheduledTime", which it
|
|
81
|
+
* shares with `ScheduledEvent`, `isAlarmEvent` checks if there's _not_
|
|
82
|
+
* a "cron" property in `event` to confirm it's an alarm event.
|
|
83
|
+
*
|
|
84
|
+
* @param event An event
|
|
85
|
+
* @returns true if the event is an AlarmEvent
|
|
86
|
+
*/
|
|
87
|
+
function isAlarmEvent(event: TailEventMessage["event"]): event is AlarmEvent {
|
|
88
|
+
return Boolean(event && "scheduledTime" in event && !("cron" in event));
|
|
89
|
+
}
|
|
90
|
+
|
|
53
91
|
function prettifyOutcome(outcome: Outcome): string {
|
|
54
92
|
switch (outcome) {
|
|
55
93
|
case "ok":
|
|
@@ -58,6 +96,8 @@ function prettifyOutcome(outcome: Outcome): string {
|
|
|
58
96
|
return "Canceled";
|
|
59
97
|
case "exceededCpu":
|
|
60
98
|
return "Exceeded CPU Limit";
|
|
99
|
+
case "exceededMemory":
|
|
100
|
+
return "Exceeded Memory Limit";
|
|
61
101
|
case "exception":
|
|
62
102
|
return "Exception Thrown";
|
|
63
103
|
case "unknown":
|
package/src/user/user.tsx
CHANGED
|
@@ -224,6 +224,7 @@ import {
|
|
|
224
224
|
saveToConfigCache,
|
|
225
225
|
} from "../config-cache";
|
|
226
226
|
import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
|
|
227
|
+
import { CI } from "../is-ci";
|
|
227
228
|
import isInteractive from "../is-interactive";
|
|
228
229
|
import { logger } from "../logger";
|
|
229
230
|
import openInBrowser from "../open-in-browser";
|
|
@@ -878,10 +879,11 @@ type LoginProps = {
|
|
|
878
879
|
export async function loginOrRefreshIfRequired(): Promise<boolean> {
|
|
879
880
|
// TODO: if there already is a token, then try refreshing
|
|
880
881
|
// TODO: ask permission before opening browser
|
|
882
|
+
const { isCI } = CI;
|
|
881
883
|
if (!getAPIToken()) {
|
|
882
884
|
// Not logged in.
|
|
883
885
|
// If we are not interactive, we cannot ask the user to login
|
|
884
|
-
return isInteractive() && (await login());
|
|
886
|
+
return isInteractive() && !isCI() && (await login());
|
|
885
887
|
} else if (isAccessTokenExpired()) {
|
|
886
888
|
// We're logged in, but the refresh token seems to have expired,
|
|
887
889
|
// so let's try to refresh it
|
|
@@ -891,7 +893,7 @@ export async function loginOrRefreshIfRequired(): Promise<boolean> {
|
|
|
891
893
|
return true;
|
|
892
894
|
} else {
|
|
893
895
|
// If the refresh token isn't valid, then we ask the user to login again
|
|
894
|
-
return isInteractive() && (await login());
|
|
896
|
+
return isInteractive() && !isCI() && (await login());
|
|
895
897
|
}
|
|
896
898
|
} else {
|
|
897
899
|
return true;
|
|
@@ -1088,7 +1090,7 @@ export async function getAccountId(): Promise<string | undefined> {
|
|
|
1088
1090
|
return accounts[0].id;
|
|
1089
1091
|
}
|
|
1090
1092
|
|
|
1091
|
-
if (isInteractive()) {
|
|
1093
|
+
if (isInteractive() && !CI.isCI()) {
|
|
1092
1094
|
const account = await new Promise<{ id: string; name: string }>(
|
|
1093
1095
|
(resolve, reject) => {
|
|
1094
1096
|
const { unmount } = render(
|
|
@@ -1128,7 +1130,7 @@ export async function requireAuth(config: {
|
|
|
1128
1130
|
}): Promise<string> {
|
|
1129
1131
|
const loggedIn = await loginOrRefreshIfRequired();
|
|
1130
1132
|
if (!loggedIn) {
|
|
1131
|
-
if (!isInteractive()) {
|
|
1133
|
+
if (!isInteractive() || CI.isCI()) {
|
|
1132
1134
|
throw new Error(
|
|
1133
1135
|
"In a non-interactive environment, it's necessary to set a CLOUDFLARE_API_TOKEN environment variable for wrangler to work. Please go to https://developers.cloudflare.com/api/tokens/create/ for instructions on how to create an api token, and assign its value to CLOUDFLARE_API_TOKEN."
|
|
1134
1136
|
);
|
package/src/whoami.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Text, render } from "ink";
|
|
2
2
|
import Table from "ink-table";
|
|
3
|
-
import React from "react";
|
|
3
|
+
import React, { Fragment } from "react";
|
|
4
4
|
import { fetchListResult, fetchResult } from "./cfetch";
|
|
5
5
|
import { logger } from "./logger";
|
|
6
6
|
import { getAPIToken, getAuthFromEnv, getScopes } from "./user";
|
|
@@ -64,12 +64,12 @@ function Permissions(props: {
|
|
|
64
64
|
and re-login.
|
|
65
65
|
</Text>
|
|
66
66
|
<Text>Scope (Access)</Text>
|
|
67
|
-
{permissions.map(([
|
|
68
|
-
|
|
67
|
+
{permissions.map(([scope, access], index) => (
|
|
68
|
+
<Fragment key={`${scope}${index}`}>
|
|
69
69
|
<Text>
|
|
70
|
-
- {
|
|
70
|
+
- {scope} {access && `(${access})`}
|
|
71
71
|
</Text>
|
|
72
|
-
|
|
72
|
+
</Fragment>
|
|
73
73
|
))}
|
|
74
74
|
</>
|
|
75
75
|
) : null
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { match } from "path-to-regexp";
|
|
2
2
|
|
|
3
|
+
//note: this explicitly does not include the * character, as pages requires this
|
|
4
|
+
const escapeRegex = /[.+?^${}()|[\]\\]/g;
|
|
5
|
+
|
|
3
6
|
type HTTPMethod =
|
|
4
7
|
| "HEAD"
|
|
5
8
|
| "OPTIONS"
|
|
@@ -67,8 +70,13 @@ function* executeRequest(request: Request, relativePathname: string) {
|
|
|
67
70
|
continue;
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
|
|
71
|
-
const
|
|
73
|
+
// replaces with "\\$&", this prepends a backslash to the matched string, e.g. "[" becomes "\["
|
|
74
|
+
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
|
|
75
|
+
end: false,
|
|
76
|
+
});
|
|
77
|
+
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
|
|
78
|
+
end: false,
|
|
79
|
+
});
|
|
72
80
|
const matchResult = routeMatcher(relativePathname);
|
|
73
81
|
const mountMatchResult = mountMatcher(relativePathname);
|
|
74
82
|
if (matchResult && mountMatchResult) {
|
|
@@ -88,8 +96,12 @@ function* executeRequest(request: Request, relativePathname: string) {
|
|
|
88
96
|
continue;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
|
-
const routeMatcher = match(route.routePath, {
|
|
92
|
-
|
|
99
|
+
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
|
|
100
|
+
end: true,
|
|
101
|
+
});
|
|
102
|
+
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
|
|
103
|
+
end: false,
|
|
104
|
+
});
|
|
93
105
|
const matchResult = routeMatcher(relativePathname);
|
|
94
106
|
const mountMatchResult = mountMatcher(relativePathname);
|
|
95
107
|
if (matchResult && mountMatchResult && route.modules.length) {
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { match } from "path-to-regexp";
|
|
2
2
|
|
|
3
|
+
//note: this explicitly does not include the * character, as pages requires this
|
|
4
|
+
const escapeRegex = /[.+?^${}()|[\]\\]/g;
|
|
5
|
+
|
|
3
6
|
type HTTPMethod =
|
|
4
7
|
| "HEAD"
|
|
5
8
|
| "OPTIONS"
|
|
@@ -61,8 +64,13 @@ function* executeRequest(request: Request) {
|
|
|
61
64
|
continue;
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
|
|
65
|
-
const
|
|
67
|
+
// replaces with "\\$&", this prepends a backslash to the matched string, e.g. "[" becomes "\["
|
|
68
|
+
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
|
|
69
|
+
end: false,
|
|
70
|
+
});
|
|
71
|
+
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
|
|
72
|
+
end: false,
|
|
73
|
+
});
|
|
66
74
|
const matchResult = routeMatcher(requestPath);
|
|
67
75
|
const mountMatchResult = mountMatcher(requestPath);
|
|
68
76
|
if (matchResult && mountMatchResult) {
|
|
@@ -81,9 +89,12 @@ function* executeRequest(request: Request) {
|
|
|
81
89
|
if (route.method && route.method !== request.method) {
|
|
82
90
|
continue;
|
|
83
91
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
const routeMatcher = match(route.routePath.replace(escapeRegex, "\\$&"), {
|
|
93
|
+
end: true,
|
|
94
|
+
});
|
|
95
|
+
const mountMatcher = match(route.mountPath.replace(escapeRegex, "\\$&"), {
|
|
96
|
+
end: false,
|
|
97
|
+
});
|
|
87
98
|
const matchResult = routeMatcher(requestPath);
|
|
88
99
|
const mountMatchResult = mountMatcher(requestPath);
|
|
89
100
|
if (matchResult && mountMatchResult && route.modules.length) {
|
|
@@ -16,20 +16,23 @@ export default {
|
|
|
16
16
|
if (details) {
|
|
17
17
|
facadeEnv[name] = {
|
|
18
18
|
async fetch(...reqArgs) {
|
|
19
|
+
const reqFromArgs = new Request(...reqArgs);
|
|
19
20
|
if (details.headers) {
|
|
20
|
-
const req = new Request(...reqArgs);
|
|
21
21
|
for (const [key, value] of Object.entries(details.headers)) {
|
|
22
22
|
// In remote mode, you need to add a couple of headers
|
|
23
23
|
// to make sure it's talking to the 'dev' preview session
|
|
24
24
|
// (much like wrangler dev already does via proxy.ts)
|
|
25
|
-
|
|
25
|
+
reqFromArgs.headers.set(key, value);
|
|
26
26
|
}
|
|
27
|
-
return env[name].fetch(
|
|
27
|
+
return env[name].fetch(reqFromArgs);
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
|
|
30
|
+
const url = new URL(reqFromArgs.url);
|
|
31
|
+
url.protocol = details.protocol;
|
|
32
|
+
url.host = details.host;
|
|
33
|
+
if (details.port !== undefined) url.port = details.port;
|
|
34
|
+
|
|
35
|
+
const request = new Request(url.toString(), reqFromArgs);
|
|
33
36
|
return fetch(request);
|
|
34
37
|
},
|
|
35
38
|
};
|
|
@@ -7,20 +7,23 @@ for (const [name, details] of Object.entries(Workers)) {
|
|
|
7
7
|
if (details) {
|
|
8
8
|
globalThis[name] = {
|
|
9
9
|
async fetch(...reqArgs) {
|
|
10
|
+
const reqFromArgs = new Request(...reqArgs);
|
|
10
11
|
if (details.headers) {
|
|
11
|
-
const req = new Request(...reqArgs);
|
|
12
12
|
for (const [key, value] of Object.entries(details.headers)) {
|
|
13
13
|
// In remote mode, you need to add a couple of headers
|
|
14
14
|
// to make sure it's talking to the 'dev' preview session
|
|
15
15
|
// (much like wrangler dev already does via proxy.ts)
|
|
16
|
-
|
|
16
|
+
reqFromArgs.headers.set(key, value);
|
|
17
17
|
}
|
|
18
|
-
return env[name].fetch(
|
|
18
|
+
return env[name].fetch(reqFromArgs);
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
|
|
21
|
+
const url = new URL(reqFromArgs.url);
|
|
22
|
+
url.protocol = details.protocol;
|
|
23
|
+
url.host = details.host;
|
|
24
|
+
if (details.port !== undefined) url.port = details.port;
|
|
25
|
+
|
|
26
|
+
const request = new Request(url.toString(), reqFromArgs);
|
|
24
27
|
return fetch(request);
|
|
25
28
|
},
|
|
26
29
|
};
|
package/wrangler-dist/cli.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ declare interface DevOptions {
|
|
|
33
33
|
env?: string;
|
|
34
34
|
ip?: string;
|
|
35
35
|
port?: number;
|
|
36
|
+
inspectorPort?: number;
|
|
36
37
|
localProtocol?: "http" | "https";
|
|
37
38
|
assets?: string;
|
|
38
39
|
site?: string;
|
|
@@ -40,6 +41,7 @@ declare interface DevOptions {
|
|
|
40
41
|
siteExclude?: string[];
|
|
41
42
|
nodeCompat?: boolean;
|
|
42
43
|
compatibilityDate?: string;
|
|
44
|
+
compatibilityFlags?: string[];
|
|
43
45
|
experimentalEnableLocalPersistence?: boolean;
|
|
44
46
|
liveReload?: boolean;
|
|
45
47
|
watch?: boolean;
|
|
@@ -57,6 +59,11 @@ declare interface DevOptions {
|
|
|
57
59
|
script_name?: string | undefined;
|
|
58
60
|
environment?: string | undefined;
|
|
59
61
|
}[];
|
|
62
|
+
r2?: {
|
|
63
|
+
binding: string;
|
|
64
|
+
bucket_name: string;
|
|
65
|
+
preview_bucket_name?: string;
|
|
66
|
+
}[];
|
|
60
67
|
showInteractiveDevSession?: boolean;
|
|
61
68
|
logLevel?: "none" | "error" | "log" | "warn" | "debug";
|
|
62
69
|
logPrefix?: string;
|