Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit 92715ed

Browse files
committed
Add Miniflare#getInspectorURL() for getting inspector base URL
Allows setting `inspectorPort: 0` to get a random inspector port. We'd like this for Wrangler's inspector proxy, so we don't have to use `get-port` and can rely on the OS to give us a random port.
1 parent c575036 commit 92715ed

File tree

3 files changed

+63
-40
lines changed

3 files changed

+63
-40
lines changed

packages/miniflare/src/index.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ import {
8282
SocketIdentifier,
8383
Worker_Binding,
8484
Worker_Module,
85+
kInspectorSocket,
8586
serializeConfig,
8687
} from "./runtime";
8788
import {
@@ -1061,7 +1062,11 @@ export class Miniflare {
10611062
return name;
10621063
}
10631064
);
1064-
1065+
// TODO(now): there's a bug here if the inspector was not enabled initially,
1066+
// fixed by a later commit in this PR
1067+
if (this.#sharedOpts.core.inspectorPort !== undefined) {
1068+
requiredSockets.push(kInspectorSocket);
1069+
}
10651070
const maybeSocketPorts = await this.#runtime.updateConfig(
10661071
configBuffer,
10671072
requiredSockets,
@@ -1159,7 +1164,27 @@ export class Miniflare {
11591164
return this.#waitForReady();
11601165
}
11611166

1162-
async unsafeGetDirectURL(workerName?: string) {
1167+
async getInspectorURL(): Promise<URL> {
1168+
this.#checkDisposed();
1169+
await this.ready;
1170+
1171+
// `#socketPorts` is assigned in `#assembleAndUpdateConfig()`, which is
1172+
// called by `#init()`, and `ready` doesn't resolve until `#init()` returns
1173+
assert(this.#socketPorts !== undefined);
1174+
1175+
// Try to get inspector port for worker
1176+
const maybePort = this.#socketPorts.get(kInspectorSocket);
1177+
if (maybePort === undefined) {
1178+
throw new TypeError(
1179+
"Inspector not enabled in Miniflare instance. " +
1180+
"Set the `inspectorPort` option to enable it."
1181+
);
1182+
}
1183+
1184+
return new URL(`ws://127.0.0.1:${maybePort}`);
1185+
}
1186+
1187+
async unsafeGetDirectURL(workerName?: string): Promise<URL> {
11631188
this.#checkDisposed();
11641189
await this.ready;
11651190

packages/miniflare/src/runtime/index.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,20 @@ import { z } from "zod";
1111
import { SERVICE_LOOPBACK, SOCKET_ENTRY } from "../plugins";
1212
import { Awaitable } from "../workers";
1313

14-
const ControlMessageSchema = z.object({
15-
event: z.literal("listen"),
16-
socket: z.string(),
17-
port: z.number(),
18-
});
19-
20-
export type SocketIdentifier = string;
14+
const ControlMessageSchema = z.discriminatedUnion("event", [
15+
z.object({
16+
event: z.literal("listen"),
17+
socket: z.string(),
18+
port: z.number(),
19+
}),
20+
z.object({
21+
event: z.literal("listen-inspector"),
22+
port: z.number(),
23+
}),
24+
]);
25+
26+
export const kInspectorSocket = Symbol("kInspectorSocket");
27+
export type SocketIdentifier = string | typeof kInspectorSocket;
2128

2229
async function waitForPorts(
2330
requiredSockets: SocketIdentifier[],
@@ -37,12 +44,14 @@ async function waitForPorts(
3744
const message = ControlMessageSchema.safeParse(JSON.parse(line));
3845
// If this was an unrecognised control message, ignore it
3946
if (!message.success) continue;
40-
const socket = message.data.socket;
47+
const data = message.data;
48+
const socket: SocketIdentifier =
49+
data.event === "listen-inspector" ? kInspectorSocket : data.socket;
4150
const index = requiredSockets.indexOf(socket);
4251
// If this wasn't a required socket, ignore it
4352
if (index === -1) continue;
4453
// Record the port of this socket
45-
socketPorts.set(socket, message.data.port);
54+
socketPorts.set(socket, data.port);
4655
// Satisfy the requirement, if there are no more, return the ports map
4756
requiredSockets.splice(index, 1);
4857
if (requiredSockets.length === 0) return socketPorts;

packages/miniflare/test/plugins/core/errors/index.spec.ts

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import assert from "assert";
22
import fs from "fs/promises";
3-
import http from "http";
4-
import { AddressInfo } from "net";
53
import path from "path";
64
import { fileURLToPath } from "url";
75
import test from "ava";
@@ -48,18 +46,8 @@ test("source maps workers", async (t) => {
4846
const serviceWorkerContent = await fs.readFile(serviceWorkerPath, "utf8");
4947
const modulesContent = await fs.readFile(modulesPath, "utf8");
5048

51-
// The OS should assign random ports in sequential order, meaning
52-
// `inspectorPort` is unlikely to be immediately chosen as a random port again
53-
const server = http.createServer();
54-
const inspectorPort = await new Promise<number>((resolve, reject) => {
55-
server.listen(0, () => {
56-
const port = (server.address() as AddressInfo).port;
57-
server.close((err) => (err ? reject(err) : resolve(port)));
58-
});
59-
});
60-
6149
const mf = new Miniflare({
62-
inspectorPort,
50+
inspectorPort: 0,
6351
workers: [
6452
{
6553
bindings: { MESSAGE: "unnamed" },
@@ -190,23 +178,24 @@ addEventListener("fetch", (event) => {
190178
t.regex(String(error?.stack), nestedRegexp);
191179

192180
// Check source mapping URLs rewritten
193-
let sources = await getSources(inspectorPort, "core:user:");
181+
const inspectorBaseURL = await mf.getInspectorURL();
182+
let sources = await getSources(inspectorBaseURL, "core:user:");
194183
t.deepEqual(sources, [REDUCE_PATH, SERVICE_WORKER_ENTRY_PATH]);
195-
sources = await getSources(inspectorPort, "core:user:a");
184+
sources = await getSources(inspectorBaseURL, "core:user:a");
196185
t.deepEqual(sources, [REDUCE_PATH, SERVICE_WORKER_ENTRY_PATH]);
197-
sources = await getSources(inspectorPort, "core:user:b");
186+
sources = await getSources(inspectorBaseURL, "core:user:b");
198187
t.deepEqual(sources, [MODULES_ENTRY_PATH, REDUCE_PATH]);
199-
sources = await getSources(inspectorPort, "core:user:c");
188+
sources = await getSources(inspectorBaseURL, "core:user:c");
200189
t.deepEqual(sources, [MODULES_ENTRY_PATH, REDUCE_PATH]);
201-
sources = await getSources(inspectorPort, "core:user:d");
190+
sources = await getSources(inspectorBaseURL, "core:user:d");
202191
t.deepEqual(sources, [MODULES_ENTRY_PATH, REDUCE_PATH]);
203-
sources = await getSources(inspectorPort, "core:user:e");
192+
sources = await getSources(inspectorBaseURL, "core:user:e");
204193
t.deepEqual(sources, [MODULES_ENTRY_PATH, REDUCE_PATH]);
205-
sources = await getSources(inspectorPort, "core:user:f");
194+
sources = await getSources(inspectorBaseURL, "core:user:f");
206195
t.deepEqual(sources, [MODULES_ENTRY_PATH, REDUCE_PATH]);
207-
sources = await getSources(inspectorPort, "core:user:g");
196+
sources = await getSources(inspectorBaseURL, "core:user:g");
208197
t.deepEqual(sources, [MODULES_ENTRY_PATH, REDUCE_PATH]);
209-
sources = await getSources(inspectorPort, "core:user:h");
198+
sources = await getSources(inspectorBaseURL, "core:user:h");
210199
t.deepEqual(sources, [DEP_ENTRY_PATH, REDUCE_PATH]); // (entry point script overridden)
211200

212201
// Check respects map's existing `sourceRoot`
@@ -217,7 +206,7 @@ addEventListener("fetch", (event) => {
217206
);
218207
serviceWorkerMap.sourceRoot = sourceRoot;
219208
await fs.writeFile(serviceWorkerMapPath, JSON.stringify(serviceWorkerMap));
220-
t.deepEqual(await getSources(inspectorPort, "core:user:"), [
209+
t.deepEqual(await getSources(inspectorBaseURL, "core:user:"), [
221210
path.resolve(tmp, sourceRoot, path.relative(tmp, REDUCE_PATH)),
222211
path.resolve(
223212
tmp,
@@ -227,18 +216,18 @@ addEventListener("fetch", (event) => {
227216
]);
228217

229218
// Check does nothing with URL source mapping URLs
230-
const sourceMapURL = await getSourceMapURL(inspectorPort, "core:user:i");
219+
const sourceMapURL = await getSourceMapURL(inspectorBaseURL, "core:user:i");
231220
t.regex(sourceMapURL, /^data:application\/json;base64/);
232221
});
233222

234223
function getSourceMapURL(
235-
inspectorPort: number,
224+
inspectorBaseURL: URL,
236225
serviceName: string
237226
): Promise<string> {
238227
let sourceMapURL: string | undefined;
239228
const promise = new DeferredPromise<string>();
240-
const inspectorUrl = `ws://127.0.0.1:${inspectorPort}/${serviceName}`;
241-
const ws = new NodeWebSocket(inspectorUrl);
229+
const inspectorURL = new URL(`/${serviceName}`, inspectorBaseURL);
230+
const ws = new NodeWebSocket(inspectorURL);
242231
ws.on("message", async (raw) => {
243232
try {
244233
const message = JSON.parse(raw.toString("utf8"));
@@ -264,8 +253,8 @@ function getSourceMapURL(
264253
return promise;
265254
}
266255

267-
async function getSources(inspectorPort: number, serviceName: string) {
268-
const sourceMapURL = await getSourceMapURL(inspectorPort, serviceName);
256+
async function getSources(inspectorBaseURL: URL, serviceName: string) {
257+
const sourceMapURL = await getSourceMapURL(inspectorBaseURL, serviceName);
269258
assert(sourceMapURL.startsWith("file:"));
270259
const sourceMapPath = fileURLToPath(sourceMapURL);
271260
const sourceMapData = await fs.readFile(sourceMapPath, "utf8");

0 commit comments

Comments
 (0)