wrangler 2.4.3 → 2.5.0
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/bin/wrangler.js +20 -8
- package/miniflare-dist/index.mjs +8 -3
- package/package.json +1 -1
- package/src/__tests__/delete.test.ts +81 -48
- package/src/__tests__/dev.test.tsx +8 -8
- package/src/__tests__/helpers/mock-oauth-flow.ts +5 -1
- package/src/__tests__/helpers/msw/handlers/oauth.ts +13 -18
- package/src/__tests__/logout.test.ts +47 -0
- package/src/__tests__/queues.test.ts +155 -67
- package/src/__tests__/tail.test.ts +207 -108
- package/src/__tests__/user.test.ts +43 -69
- package/src/d1/backups.tsx +7 -2
- package/src/d1/delete.tsx +4 -4
- package/src/d1/index.ts +2 -0
- package/src/d1/migrations/apply.tsx +6 -5
- package/src/d1/migrations/helpers.ts +4 -2
- package/src/d1/migrations/list.tsx +2 -2
- package/src/d1/migrations/options.ts +18 -0
- package/src/dev/dev.tsx +46 -22
- package/src/dev/local.tsx +18 -14
- package/src/dev/start-server.ts +22 -21
- package/src/dev.tsx +10 -11
- package/src/miniflare-cli/assets.ts +7 -2
- package/src/miniflare-cli/index.ts +1 -1
- package/src/pages/dev.tsx +0 -2
- package/src/proxy.ts +5 -0
- package/wrangler-dist/cli.js +221 -178
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import fetchMock from "jest-fetch-mock";
|
|
4
|
-
import { getGlobalWranglerConfigPath } from "../global-wrangler-config-path";
|
|
1
|
+
import { rest } from "msw";
|
|
5
2
|
import { CI } from "../is-ci";
|
|
6
3
|
import {
|
|
7
4
|
loginOrRefreshIfRequired,
|
|
8
5
|
readAuthConfigFile,
|
|
9
6
|
requireAuth,
|
|
10
|
-
USER_AUTH_CONFIG_FILE,
|
|
11
7
|
writeAuthConfigFile,
|
|
12
8
|
} from "../user";
|
|
13
9
|
import { mockConsoleMethods } from "./helpers/mock-console";
|
|
14
10
|
import { useMockIsTTY } from "./helpers/mock-istty";
|
|
15
11
|
import { mockOAuthFlow } from "./helpers/mock-oauth-flow";
|
|
12
|
+
import {
|
|
13
|
+
msw,
|
|
14
|
+
mswSuccessOauthHandlers,
|
|
15
|
+
mswSuccessUserHandlers,
|
|
16
|
+
} from "./helpers/msw";
|
|
16
17
|
import { runInTempDir } from "./helpers/run-in-tmp";
|
|
17
18
|
import { runWrangler } from "./helpers/run-wrangler";
|
|
18
19
|
import type { Config } from "../config";
|
|
@@ -22,41 +23,44 @@ describe("User", () => {
|
|
|
22
23
|
let isCISpy: jest.SpyInstance;
|
|
23
24
|
runInTempDir();
|
|
24
25
|
const std = mockConsoleMethods();
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
mockGrantAccessToken,
|
|
28
|
-
mockGrantAuthorization,
|
|
29
|
-
mockRevokeAuthorization,
|
|
30
|
-
mockExchangeRefreshTokenForAccessToken,
|
|
31
|
-
} = mockOAuthFlow();
|
|
32
|
-
|
|
26
|
+
// TODO: Implement these two mocks with MSW
|
|
27
|
+
const { mockOAuthServerCallback } = mockOAuthFlow();
|
|
33
28
|
const { setIsTTY } = useMockIsTTY();
|
|
34
29
|
|
|
35
30
|
beforeEach(() => {
|
|
31
|
+
msw.use(...mswSuccessOauthHandlers, ...mswSuccessUserHandlers);
|
|
36
32
|
isCISpy = jest.spyOn(CI, "isCI").mockReturnValue(false);
|
|
37
33
|
});
|
|
38
34
|
|
|
39
35
|
describe("login", () => {
|
|
40
36
|
it("should login a user when `wrangler login` is run", async () => {
|
|
41
|
-
mockOAuthServerCallback();
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
mockOAuthServerCallback("success");
|
|
38
|
+
|
|
39
|
+
let counter = 0;
|
|
40
|
+
msw.use(
|
|
41
|
+
rest.post("*/oauth2/token", async (_, response, context) => {
|
|
42
|
+
counter += 1;
|
|
43
|
+
|
|
44
|
+
return response.once(
|
|
45
|
+
context.status(200),
|
|
46
|
+
context.json({
|
|
47
|
+
access_token: "test-access-token",
|
|
48
|
+
expires_in: 100000,
|
|
49
|
+
refresh_token: "test-refresh-token",
|
|
50
|
+
scope: "account:read",
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
})
|
|
54
|
+
);
|
|
44
55
|
|
|
45
56
|
await runWrangler("login");
|
|
46
57
|
|
|
47
|
-
expect(
|
|
48
|
-
accessTokenRequest.expected.url
|
|
49
|
-
);
|
|
50
|
-
expect(accessTokenRequest.actual.method).toEqual(
|
|
51
|
-
accessTokenRequest.expected.method
|
|
52
|
-
);
|
|
53
|
-
|
|
58
|
+
expect(counter).toBe(1);
|
|
54
59
|
expect(std.out).toMatchInlineSnapshot(`
|
|
55
60
|
"Attempting to login via OAuth...
|
|
56
61
|
Opening a link in your default browser: https://dash.cloudflare.com/oauth2/auth?response_type=code&client_id=54d11594-84e4-41aa-b438-e81b8fa78ee7&redirect_uri=http%3A%2F%2Flocalhost%3A8976%2Foauth%2Fcallback&scope=account%3Aread%20user%3Aread%20workers%3Awrite%20workers_kv%3Awrite%20workers_routes%3Awrite%20workers_scripts%3Awrite%20workers_tail%3Aread%20d1%3Awrite%20pages%3Awrite%20zone%3Aread%20offline_access&state=MOCK_STATE_PARAM&code_challenge=MOCK_CODE_CHALLENGE&code_challenge_method=S256
|
|
57
62
|
Successfully logged in."
|
|
58
63
|
`);
|
|
59
|
-
|
|
60
64
|
expect(readAuthConfigFile()).toEqual<UserAuthConfig>({
|
|
61
65
|
api_token: undefined,
|
|
62
66
|
oauth_token: "test-access-token",
|
|
@@ -67,50 +71,25 @@ describe("User", () => {
|
|
|
67
71
|
});
|
|
68
72
|
});
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
it("should exit with a message stating the user is not logged in", async () => {
|
|
72
|
-
await runWrangler("logout");
|
|
73
|
-
expect(std.out).toMatchInlineSnapshot(`"Not logged in, exiting..."`);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("should logout user that has been properly logged in", async () => {
|
|
77
|
-
writeAuthConfigFile({
|
|
78
|
-
oauth_token: "some-oauth-tok",
|
|
79
|
-
refresh_token: "some-refresh-tok",
|
|
80
|
-
});
|
|
81
|
-
const outcome = mockRevokeAuthorization();
|
|
82
|
-
|
|
83
|
-
await runWrangler("logout");
|
|
84
|
-
|
|
85
|
-
expect(outcome.actual.url).toEqual(
|
|
86
|
-
"https://dash.cloudflare.com/oauth2/revoke"
|
|
87
|
-
);
|
|
88
|
-
expect(outcome.actual.method).toEqual("POST");
|
|
89
|
-
|
|
90
|
-
expect(std.out).toMatchInlineSnapshot(`"Successfully logged out."`);
|
|
91
|
-
|
|
92
|
-
// Make sure that we made the request to logout.
|
|
93
|
-
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
94
|
-
|
|
95
|
-
// Make sure that logout removed the config file containing the auth tokens.
|
|
96
|
-
const config = path.join(
|
|
97
|
-
getGlobalWranglerConfigPath(),
|
|
98
|
-
USER_AUTH_CONFIG_FILE
|
|
99
|
-
);
|
|
100
|
-
expect(fs.existsSync(config)).toBeFalsy();
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// TODO: Improve OAuth mocking to handle `/token` endpoints from different calls
|
|
105
|
-
it("should handle errors for failed token refresh", async () => {
|
|
74
|
+
it("should handle errors for failed token refresh in a non-interactive environment", async () => {
|
|
106
75
|
setIsTTY(false);
|
|
107
76
|
writeAuthConfigFile({
|
|
108
77
|
oauth_token: "hunter2",
|
|
109
78
|
refresh_token: "Order 66",
|
|
110
79
|
});
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
80
|
+
// TODO: Use MSW to handle `/token` endpoints from different calls
|
|
81
|
+
let counter = 0;
|
|
82
|
+
msw.use(
|
|
83
|
+
rest.post("*/oauth2/token", async (request, response, context) => {
|
|
84
|
+
counter += 1;
|
|
85
|
+
return response.once(
|
|
86
|
+
context.status(400),
|
|
87
|
+
context.body(
|
|
88
|
+
`<html> <body> This shouldn't be sent, but should be handled </body> </html>`
|
|
89
|
+
)
|
|
90
|
+
);
|
|
91
|
+
})
|
|
92
|
+
);
|
|
114
93
|
|
|
115
94
|
// Handles the requireAuth error throw from failed login that is unhandled due to directly calling it here
|
|
116
95
|
await expect(
|
|
@@ -118,20 +97,15 @@ describe("User", () => {
|
|
|
118
97
|
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
119
98
|
`"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."`
|
|
120
99
|
);
|
|
100
|
+
expect(counter).toBe(0);
|
|
121
101
|
});
|
|
122
102
|
|
|
123
103
|
it("should confirm no error message when refresh is successful", async () => {
|
|
124
104
|
setIsTTY(false);
|
|
125
|
-
mockOAuthServerCallback();
|
|
126
105
|
writeAuthConfigFile({
|
|
127
106
|
oauth_token: "hunter2",
|
|
128
107
|
refresh_token: "Order 66",
|
|
129
108
|
});
|
|
130
|
-
mockGrantAuthorization({ respondWith: "success" });
|
|
131
|
-
|
|
132
|
-
mockExchangeRefreshTokenForAccessToken({
|
|
133
|
-
respondWith: "refreshSuccess",
|
|
134
|
-
});
|
|
135
109
|
|
|
136
110
|
// Handles the requireAuth error throw from failed login that is unhandled due to directly calling it here
|
|
137
111
|
await expect(requireAuth({} as Config)).rejects.toThrowError();
|
package/src/d1/backups.tsx
CHANGED
|
@@ -141,9 +141,9 @@ export const RestoreHandler = withConfig<BackupRestoreArgs>(
|
|
|
141
141
|
name
|
|
142
142
|
);
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
logger.log(`Restoring ${name} from backup ${backupId}....`);
|
|
145
145
|
await restoreBackup(accountId, db.uuid, backupId);
|
|
146
|
-
|
|
146
|
+
logger.log(`Done!`);
|
|
147
147
|
}
|
|
148
148
|
);
|
|
149
149
|
|
|
@@ -199,6 +199,11 @@ export const DownloadHandler = withConfig<BackupDownloadArgs>(
|
|
|
199
199
|
|
|
200
200
|
logger.log(`🌀 Downloading backup ${backupId} from '${name}'`);
|
|
201
201
|
const response = await getBackupResponse(accountId, db.uuid, backupId);
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
`Failed to download backup ${backupId} from '${name}' - got ${response.status} from the API`
|
|
205
|
+
);
|
|
206
|
+
}
|
|
202
207
|
logger.log(`🌀 Saving to ${filename}`);
|
|
203
208
|
// TODO: stream this once we upgrade to Node18 and can use Writable.fromWeb
|
|
204
209
|
const buffer = await response.arrayBuffer();
|
package/src/d1/delete.tsx
CHANGED
|
@@ -36,21 +36,21 @@ export const Handler = withConfig<CreateArgs>(
|
|
|
36
36
|
name
|
|
37
37
|
);
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
logger.log(`About to delete DB '${name}' (${db.uuid}).`);
|
|
40
40
|
if (!skipConfirmation) {
|
|
41
41
|
const response = await confirm(`Ok to proceed?`);
|
|
42
42
|
if (!response) {
|
|
43
|
-
|
|
43
|
+
logger.log(`Not deleting.`);
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
logger.log("Deleting...");
|
|
49
49
|
|
|
50
50
|
await fetchResult(`/accounts/${accountId}/d1/database/${db.uuid}`, {
|
|
51
51
|
method: "DELETE",
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
logger.log(`Deleted '${name}' successfully.`);
|
|
55
55
|
}
|
|
56
56
|
);
|
package/src/d1/index.ts
CHANGED
|
@@ -26,6 +26,7 @@ export const d1 = (yargs: Argv<CommonYargsOptions>) => {
|
|
|
26
26
|
)
|
|
27
27
|
.command("backup", "Interact with D1 Backups", (yargs2) =>
|
|
28
28
|
yargs2
|
|
29
|
+
.demandCommand()
|
|
29
30
|
.command(
|
|
30
31
|
"list <name>",
|
|
31
32
|
"List your D1 backups",
|
|
@@ -74,6 +75,7 @@ export const d1 = (yargs: Argv<CommonYargsOptions>) => {
|
|
|
74
75
|
)
|
|
75
76
|
.command("migrations", "Interact with D1 Migrations", (yargs2) =>
|
|
76
77
|
yargs2
|
|
78
|
+
.demandCommand()
|
|
77
79
|
.command(
|
|
78
80
|
"list <database>",
|
|
79
81
|
"List your D1 migrations",
|
|
@@ -5,23 +5,25 @@ import Table from "ink-table";
|
|
|
5
5
|
import React from "react";
|
|
6
6
|
import { withConfig } from "../../config";
|
|
7
7
|
import { confirm } from "../../dialogs";
|
|
8
|
+
import { CI } from "../../is-ci";
|
|
9
|
+
import isInteractive from "../../is-interactive";
|
|
8
10
|
import { logger } from "../../logger";
|
|
9
11
|
import { requireAuth } from "../../user";
|
|
10
12
|
import { createBackup } from "../backups";
|
|
11
13
|
import { executeSql } from "../execute";
|
|
12
|
-
import { Database } from "../options";
|
|
13
14
|
import { d1BetaWarning, getDatabaseInfoFromConfig } from "../utils";
|
|
14
15
|
import {
|
|
15
16
|
getMigrationsPath,
|
|
16
17
|
getUnappliedMigrations,
|
|
17
18
|
initMigrationsTable,
|
|
18
19
|
} from "./helpers";
|
|
20
|
+
import { DatabaseWithLocal } from "./options";
|
|
19
21
|
import type { ParseError } from "../../parse";
|
|
20
22
|
import type { BaseSqlExecuteArgs } from "../execute";
|
|
21
23
|
import type { Argv } from "yargs";
|
|
22
24
|
|
|
23
25
|
export function ApplyOptions(yargs: Argv): Argv<BaseSqlExecuteArgs> {
|
|
24
|
-
return
|
|
26
|
+
return DatabaseWithLocal(yargs);
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
export const ApplyHandler = withConfig<BaseSqlExecuteArgs>(
|
|
@@ -88,8 +90,7 @@ export const ApplyHandler = withConfig<BaseSqlExecuteArgs>(
|
|
|
88
90
|
return;
|
|
89
91
|
}
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
if (isInteractive) {
|
|
93
|
+
if (isInteractive() && !CI.isCI()) {
|
|
93
94
|
const ok = await confirm(
|
|
94
95
|
`About to apply ${unappliedMigrations.length} migration(s)\n` +
|
|
95
96
|
"Your database may not be available to serve requests during the migration, continue?",
|
|
@@ -121,7 +122,7 @@ export const ApplyHandler = withConfig<BaseSqlExecuteArgs>(
|
|
|
121
122
|
local,
|
|
122
123
|
config,
|
|
123
124
|
database,
|
|
124
|
-
|
|
125
|
+
isInteractive() && !CI.isCI(),
|
|
125
126
|
persistTo,
|
|
126
127
|
undefined,
|
|
127
128
|
query
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { confirm } from "../../dialogs";
|
|
4
|
+
import { CI } from "../../is-ci";
|
|
5
|
+
import isInteractive from "../../is-interactive";
|
|
4
6
|
import { logger } from "../../logger";
|
|
5
7
|
import { DEFAULT_MIGRATION_PATH } from "../constants";
|
|
6
8
|
import { executeSql } from "../execute";
|
|
@@ -79,7 +81,7 @@ const listAppliedMigrations = async (
|
|
|
79
81
|
local,
|
|
80
82
|
config,
|
|
81
83
|
name,
|
|
82
|
-
|
|
84
|
+
isInteractive() && !CI.isCI(),
|
|
83
85
|
persistTo,
|
|
84
86
|
undefined,
|
|
85
87
|
Query
|
|
@@ -130,7 +132,7 @@ export const initMigrationsTable = async (
|
|
|
130
132
|
local,
|
|
131
133
|
config,
|
|
132
134
|
name,
|
|
133
|
-
|
|
135
|
+
isInteractive() && !CI.isCI(),
|
|
134
136
|
persistTo,
|
|
135
137
|
undefined,
|
|
136
138
|
`
|
|
@@ -5,18 +5,18 @@ import React from "react";
|
|
|
5
5
|
import { withConfig } from "../../config";
|
|
6
6
|
import { logger } from "../../logger";
|
|
7
7
|
import { requireAuth } from "../../user";
|
|
8
|
-
import { Database } from "../options";
|
|
9
8
|
import { d1BetaWarning, getDatabaseInfoFromConfig } from "../utils";
|
|
10
9
|
import {
|
|
11
10
|
getMigrationsPath,
|
|
12
11
|
getUnappliedMigrations,
|
|
13
12
|
initMigrationsTable,
|
|
14
13
|
} from "./helpers";
|
|
14
|
+
import { DatabaseWithLocal } from "./options";
|
|
15
15
|
import type { BaseSqlExecuteArgs } from "../execute";
|
|
16
16
|
import type { Argv } from "yargs";
|
|
17
17
|
|
|
18
18
|
export function ListOptions(yargs: Argv): Argv<BaseSqlExecuteArgs> {
|
|
19
|
-
return
|
|
19
|
+
return DatabaseWithLocal(yargs);
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export const ListHandler = withConfig<BaseSqlExecuteArgs>(
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Database } from "../options";
|
|
2
|
+
import type { Argv } from "yargs";
|
|
3
|
+
|
|
4
|
+
export function DatabaseWithLocal(yargs: Argv) {
|
|
5
|
+
return Database(yargs)
|
|
6
|
+
.option("local", {
|
|
7
|
+
describe:
|
|
8
|
+
"Execute commands/files against a local DB for use with wrangler dev --local",
|
|
9
|
+
type: "boolean",
|
|
10
|
+
})
|
|
11
|
+
.option("persist-to", {
|
|
12
|
+
describe:
|
|
13
|
+
"Specify directory to use for local persistence (you must use --local with this flag)",
|
|
14
|
+
type: "string",
|
|
15
|
+
requiresArg: true,
|
|
16
|
+
})
|
|
17
|
+
.implies("persist-to", "local");
|
|
18
|
+
}
|
package/src/dev/dev.tsx
CHANGED
|
@@ -113,8 +113,8 @@ export type DevProps = {
|
|
|
113
113
|
name: string | undefined;
|
|
114
114
|
noBundle: boolean;
|
|
115
115
|
entry: Entry;
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
initialPort: number;
|
|
117
|
+
initialIp: string;
|
|
118
118
|
inspectorPort: number;
|
|
119
119
|
rules: Config["rules"];
|
|
120
120
|
accountId: string | undefined;
|
|
@@ -171,25 +171,42 @@ export function DevImplementation(props: DevProps): JSX.Element {
|
|
|
171
171
|
);
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
// This is a nasty hack to allow `useHotkeys` and its "[b] open a browser" feature to read these values
|
|
175
|
+
// without triggering a re-render loop when `onReady()` updates them.
|
|
176
|
+
// The initially requested port can be different than what's actually used, if, for example, you request port 0.
|
|
177
|
+
let ip: string;
|
|
178
|
+
let port: number;
|
|
179
|
+
|
|
174
180
|
function InteractiveDevSession(props: DevProps) {
|
|
175
181
|
const toggles = useHotkeys({
|
|
176
182
|
initial: {
|
|
177
183
|
local: props.initialMode === "local",
|
|
178
184
|
tunnel: false,
|
|
179
185
|
},
|
|
180
|
-
port: props.port,
|
|
181
|
-
ip: props.ip,
|
|
182
186
|
inspectorPort: props.inspectorPort,
|
|
183
187
|
inspect: props.inspect,
|
|
184
188
|
localProtocol: props.localProtocol,
|
|
185
189
|
forceLocal: props.forceLocal,
|
|
186
190
|
});
|
|
187
191
|
|
|
192
|
+
ip = props.initialIp;
|
|
193
|
+
port = props.initialPort;
|
|
194
|
+
|
|
188
195
|
useTunnel(toggles.tunnel);
|
|
189
196
|
|
|
197
|
+
const onReady = (newIp: string, newPort: number) => {
|
|
198
|
+
if (newIp !== props.initialIp || newPort !== props.initialPort) {
|
|
199
|
+
ip = newIp;
|
|
200
|
+
port = newPort;
|
|
201
|
+
if (props.onReady) {
|
|
202
|
+
props.onReady(newIp, newPort);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
190
207
|
return (
|
|
191
208
|
<>
|
|
192
|
-
<DevSession {...props} local={toggles.local} />
|
|
209
|
+
<DevSession {...props} local={toggles.local} onReady={onReady} />
|
|
193
210
|
<Box borderStyle="round" paddingLeft={1} paddingRight={1}>
|
|
194
211
|
<Text bold={true}>[b]</Text>
|
|
195
212
|
<Text> open a browser, </Text>
|
|
@@ -301,6 +318,22 @@ function DevSession(props: DevSessionProps) {
|
|
|
301
318
|
);
|
|
302
319
|
}
|
|
303
320
|
|
|
321
|
+
const announceAndOnReady: typeof props.onReady = (finalIp, finalPort) => {
|
|
322
|
+
if (process.send) {
|
|
323
|
+
process.send(
|
|
324
|
+
JSON.stringify({
|
|
325
|
+
event: "DEV_SERVER_READY",
|
|
326
|
+
ip: finalIp,
|
|
327
|
+
port: finalPort,
|
|
328
|
+
})
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (props.onReady) {
|
|
333
|
+
props.onReady(finalIp, finalPort);
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
|
|
304
337
|
return props.local ? (
|
|
305
338
|
<Local
|
|
306
339
|
name={props.name}
|
|
@@ -312,8 +345,8 @@ function DevSession(props: DevSessionProps) {
|
|
|
312
345
|
bindings={props.bindings}
|
|
313
346
|
workerDefinitions={workerDefinitions}
|
|
314
347
|
assetPaths={props.assetPaths}
|
|
315
|
-
|
|
316
|
-
|
|
348
|
+
initialPort={props.initialPort}
|
|
349
|
+
initialIp={props.initialIp}
|
|
317
350
|
rules={props.rules}
|
|
318
351
|
inspectorPort={props.inspectorPort}
|
|
319
352
|
localPersistencePath={props.localPersistencePath}
|
|
@@ -324,7 +357,7 @@ function DevSession(props: DevSessionProps) {
|
|
|
324
357
|
localUpstream={props.localUpstream}
|
|
325
358
|
logPrefix={props.logPrefix}
|
|
326
359
|
inspect={props.inspect}
|
|
327
|
-
onReady={
|
|
360
|
+
onReady={announceAndOnReady}
|
|
328
361
|
enablePagesAssetsServiceBinding={props.enablePagesAssetsServiceBinding}
|
|
329
362
|
experimentalLocal={props.experimentalLocal}
|
|
330
363
|
accountId={props.accountId}
|
|
@@ -339,8 +372,8 @@ function DevSession(props: DevSessionProps) {
|
|
|
339
372
|
bindings={props.bindings}
|
|
340
373
|
assetPaths={props.assetPaths}
|
|
341
374
|
isWorkersSite={props.isWorkersSite}
|
|
342
|
-
port={props.
|
|
343
|
-
ip={props.
|
|
375
|
+
port={props.initialPort}
|
|
376
|
+
ip={props.initialIp}
|
|
344
377
|
localProtocol={props.localProtocol}
|
|
345
378
|
inspectorPort={props.inspectorPort}
|
|
346
379
|
// TODO: @threepointone #1167
|
|
@@ -354,7 +387,7 @@ function DevSession(props: DevSessionProps) {
|
|
|
354
387
|
zone={props.zone}
|
|
355
388
|
host={props.host}
|
|
356
389
|
routes={props.routes}
|
|
357
|
-
onReady={
|
|
390
|
+
onReady={announceAndOnReady}
|
|
358
391
|
sourceMapPath={bundle?.sourceMapPath}
|
|
359
392
|
sendMetrics={props.sendMetrics}
|
|
360
393
|
/>
|
|
@@ -503,25 +536,16 @@ type useHotkeysInitialState = {
|
|
|
503
536
|
};
|
|
504
537
|
function useHotkeys(props: {
|
|
505
538
|
initial: useHotkeysInitialState;
|
|
506
|
-
port: number;
|
|
507
|
-
ip: string;
|
|
508
539
|
inspectorPort: number;
|
|
509
540
|
inspect: boolean;
|
|
510
541
|
localProtocol: "http" | "https";
|
|
511
542
|
forceLocal: boolean | undefined;
|
|
512
543
|
}) {
|
|
513
|
-
const {
|
|
514
|
-
initial,
|
|
515
|
-
port,
|
|
516
|
-
ip,
|
|
517
|
-
inspectorPort,
|
|
518
|
-
inspect,
|
|
519
|
-
localProtocol,
|
|
520
|
-
forceLocal,
|
|
521
|
-
} = props;
|
|
544
|
+
const { initial, inspectorPort, inspect, localProtocol, forceLocal } = props;
|
|
522
545
|
// UGH, we should put port in context instead
|
|
523
546
|
const [toggles, setToggles] = useState(initial);
|
|
524
547
|
const { exit } = useApp();
|
|
548
|
+
|
|
525
549
|
useInput(
|
|
526
550
|
async (
|
|
527
551
|
input,
|
package/src/dev/local.tsx
CHANGED
|
@@ -57,8 +57,8 @@ export interface LocalProps {
|
|
|
57
57
|
bindings: CfWorkerInit["bindings"];
|
|
58
58
|
workerDefinitions: WorkerRegistry | undefined;
|
|
59
59
|
assetPaths: AssetPaths | undefined;
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
initialPort: number;
|
|
61
|
+
initialIp: string;
|
|
62
62
|
rules: Config["rules"];
|
|
63
63
|
inspectorPort: number;
|
|
64
64
|
localPersistencePath: string | null;
|
|
@@ -109,12 +109,12 @@ function useLocalWorker({
|
|
|
109
109
|
bindings,
|
|
110
110
|
workerDefinitions,
|
|
111
111
|
assetPaths,
|
|
112
|
-
|
|
112
|
+
initialPort,
|
|
113
113
|
inspectorPort,
|
|
114
114
|
rules,
|
|
115
115
|
localPersistencePath,
|
|
116
116
|
liveReload,
|
|
117
|
-
|
|
117
|
+
initialIp,
|
|
118
118
|
crons,
|
|
119
119
|
queueConsumers,
|
|
120
120
|
localProtocol,
|
|
@@ -160,7 +160,7 @@ function useLocalWorker({
|
|
|
160
160
|
if (!bundle || !format) return;
|
|
161
161
|
|
|
162
162
|
// port for the worker
|
|
163
|
-
await waitForPortToBeAvailable(
|
|
163
|
+
await waitForPortToBeAvailable(initialPort, {
|
|
164
164
|
retryPeriod: 200,
|
|
165
165
|
timeout: 2000,
|
|
166
166
|
abortSignal: abortController.signal,
|
|
@@ -199,10 +199,10 @@ function useLocalWorker({
|
|
|
199
199
|
|
|
200
200
|
const { forkOptions, miniflareCLIPath, options } = setupMiniflareOptions({
|
|
201
201
|
workerName,
|
|
202
|
-
port,
|
|
202
|
+
port: initialPort,
|
|
203
203
|
scriptPath,
|
|
204
204
|
localProtocol,
|
|
205
|
-
ip,
|
|
205
|
+
ip: initialIp,
|
|
206
206
|
format,
|
|
207
207
|
rules,
|
|
208
208
|
compatibilityDate,
|
|
@@ -295,7 +295,11 @@ function useLocalWorker({
|
|
|
295
295
|
return;
|
|
296
296
|
}
|
|
297
297
|
|
|
298
|
-
const nodeOptions = setupNodeOptions({
|
|
298
|
+
const nodeOptions = setupNodeOptions({
|
|
299
|
+
inspect,
|
|
300
|
+
ip: initialIp,
|
|
301
|
+
inspectorPort,
|
|
302
|
+
});
|
|
299
303
|
logger.log("⎔ Starting a local server...");
|
|
300
304
|
|
|
301
305
|
const child = (local.current = fork(miniflareCLIPath, forkOptions, {
|
|
@@ -316,21 +320,21 @@ function useLocalWorker({
|
|
|
316
320
|
await registerWorker(workerName, {
|
|
317
321
|
protocol: localProtocol,
|
|
318
322
|
mode: "local",
|
|
319
|
-
port,
|
|
320
|
-
host:
|
|
323
|
+
port: message.port,
|
|
324
|
+
host: initialIp,
|
|
321
325
|
durableObjects: internalDurableObjects.map((binding) => ({
|
|
322
326
|
name: binding.name,
|
|
323
327
|
className: binding.class_name,
|
|
324
328
|
})),
|
|
325
329
|
...(message.durableObjectsPort
|
|
326
330
|
? {
|
|
327
|
-
durableObjectsHost:
|
|
331
|
+
durableObjectsHost: initialIp,
|
|
328
332
|
durableObjectsPort: message.durableObjectsPort,
|
|
329
333
|
}
|
|
330
334
|
: {}),
|
|
331
335
|
});
|
|
332
336
|
}
|
|
333
|
-
onReady?.(
|
|
337
|
+
onReady?.(initialIp, message.port);
|
|
334
338
|
}
|
|
335
339
|
});
|
|
336
340
|
|
|
@@ -399,9 +403,9 @@ function useLocalWorker({
|
|
|
399
403
|
bundle,
|
|
400
404
|
workerName,
|
|
401
405
|
format,
|
|
402
|
-
|
|
406
|
+
initialPort,
|
|
403
407
|
inspectorPort,
|
|
404
|
-
|
|
408
|
+
initialIp,
|
|
405
409
|
queueConsumers,
|
|
406
410
|
bindings.queues,
|
|
407
411
|
bindings.durable_objects,
|