tippen 0.1.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/LICENSE +21 -0
- package/README.md +25 -0
- package/dist/index.d.mts +12 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +858 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +41 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tippen contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Tippen
|
|
2
|
+
|
|
3
|
+
Tippen provides Console Ninja-style inline console output for local Cloudflare
|
|
4
|
+
Workers. It captures workerd inspector events through the Cloudflare Vite dev
|
|
5
|
+
server, resolves source maps, and sends bounded log snapshots to the Tippen VS
|
|
6
|
+
Code extension.
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
nub add -D tippen
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { cloudflare } from "@cloudflare/vite-plugin"
|
|
14
|
+
import { workerConsole } from "tippen"
|
|
15
|
+
import { defineConfig } from "vite"
|
|
16
|
+
|
|
17
|
+
export default defineConfig({
|
|
18
|
+
plugins: [cloudflare(), workerConsole()],
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Tippen is local-development tooling only. It does not inject runtime code, use
|
|
23
|
+
`eval`, invoke logged getters, or capture deployed Worker logs.
|
|
24
|
+
|
|
25
|
+
See the repository README for VS Code extension setup and detailed behavior.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
interface WorkerConsoleOptions {
|
|
5
|
+
inspectorUrl?: string;
|
|
6
|
+
sessionDirectory?: string;
|
|
7
|
+
inlineMaxLength?: number;
|
|
8
|
+
}
|
|
9
|
+
declare function workerConsole(options?: WorkerConsoleOptions): Plugin;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { WorkerConsoleOptions, workerConsole };
|
|
12
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;UASiB,oBAAA;;EAAA,gBAAA,CAAA,EAAA,MAAoB;EAMrB,eAAA,CAAa,EAAA,MAAA;;iBAAb,aAAA,WAAuB,uBAA4B"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
import { randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
5
|
+
import WebSocket, { WebSocketServer } from "ws";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { TraceMap, originalPositionFor } from "@jridgewell/trace-mapping";
|
|
8
|
+
|
|
9
|
+
//#region ../protocol/dist/index.mjs
|
|
10
|
+
const PROTOCOL_VERSION = 1;
|
|
11
|
+
var ProtocolError = class extends Error {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "ProtocolError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
function parseClientMessage(value) {
|
|
18
|
+
const record = expectRecord(value, "client message");
|
|
19
|
+
const type = expectString(record.type, "message type");
|
|
20
|
+
switch (type) {
|
|
21
|
+
case "capture.pause":
|
|
22
|
+
case "capture.resume": break;
|
|
23
|
+
case "ping":
|
|
24
|
+
case "pong":
|
|
25
|
+
expectString(record.nonce, "nonce");
|
|
26
|
+
break;
|
|
27
|
+
default: throw new ProtocolError(`Unsupported client message type: ${type}`);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function expectRecord(value, label) {
|
|
32
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new ProtocolError(`${label} must be an object`);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function expectString(value, label) {
|
|
36
|
+
if (typeof value !== "string" || value.length === 0) throw new ProtocolError(`${label} must be a non-empty string`);
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
//#region src/bridge.ts
|
|
42
|
+
async function createBridgeServer(options) {
|
|
43
|
+
const token = randomBytes(32).toString("base64url");
|
|
44
|
+
const server = createServer((_request, response) => response.writeHead(404).end());
|
|
45
|
+
const sockets = new WebSocketServer({
|
|
46
|
+
noServer: true,
|
|
47
|
+
maxPayload: 64 * 1024
|
|
48
|
+
});
|
|
49
|
+
await listen(server);
|
|
50
|
+
const address = server.address();
|
|
51
|
+
if (address === null || typeof address === "string") throw new Error("Bridge did not acquire a port");
|
|
52
|
+
const bridgeUrl = `ws://127.0.0.1:${address.port}/events`;
|
|
53
|
+
const sessionDirectory = resolve(options.sessionDirectory ?? join(options.root, ".worker-console"));
|
|
54
|
+
const manifestPath = join(sessionDirectory, "session.json");
|
|
55
|
+
let workers = [];
|
|
56
|
+
let lastState;
|
|
57
|
+
let closed = false;
|
|
58
|
+
const writeManifest = async () => {
|
|
59
|
+
const manifest = {
|
|
60
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
61
|
+
root: resolve(options.root),
|
|
62
|
+
bridgeUrl,
|
|
63
|
+
token,
|
|
64
|
+
pid: process.pid,
|
|
65
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
66
|
+
workers
|
|
67
|
+
};
|
|
68
|
+
await mkdir(sessionDirectory, { recursive: true });
|
|
69
|
+
await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, { mode: 384 });
|
|
70
|
+
};
|
|
71
|
+
server.on("upgrade", (request, socket, head) => {
|
|
72
|
+
const url = new URL(request.url ?? "/", `http://${request.headers.host ?? "127.0.0.1"}`);
|
|
73
|
+
if (!isSafeOrigin(request.headers.origin) || !safeTokenEquals(url.searchParams.get("token"), token)) {
|
|
74
|
+
socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
75
|
+
socket.destroy();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (url.pathname === "/events") {
|
|
79
|
+
sockets.handleUpgrade(request, socket, head, (webSocket) => {
|
|
80
|
+
sockets.emit("connection", webSocket, request);
|
|
81
|
+
});
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (url.pathname.startsWith("/debug/")) {
|
|
85
|
+
const worker = decodeURIComponent(url.pathname.slice(7));
|
|
86
|
+
if (!workers.some((candidate) => candidate.name === worker) || options.onDebugger === void 0) {
|
|
87
|
+
socket.write("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n");
|
|
88
|
+
socket.destroy();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
sockets.handleUpgrade(request, socket, head, (webSocket) => {
|
|
92
|
+
options.onDebugger?.(worker, webSocket);
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
socket.write("HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n");
|
|
97
|
+
socket.destroy();
|
|
98
|
+
});
|
|
99
|
+
sockets.on("connection", (socket) => {
|
|
100
|
+
socket.send(JSON.stringify({
|
|
101
|
+
type: "session.ready",
|
|
102
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
103
|
+
workers
|
|
104
|
+
}));
|
|
105
|
+
if (lastState !== void 0) socket.send(JSON.stringify(lastState));
|
|
106
|
+
socket.on("message", (data) => {
|
|
107
|
+
try {
|
|
108
|
+
const message = parseClientMessage(JSON.parse(data.toString()));
|
|
109
|
+
options.onControl?.(message);
|
|
110
|
+
} catch (error) {
|
|
111
|
+
socket.close(1008, error instanceof Error ? error.message.slice(0, 120) : "Invalid message");
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
const broadcast = (message) => {
|
|
116
|
+
if (message.type === "session.state") lastState = message;
|
|
117
|
+
const payload = JSON.stringify(message);
|
|
118
|
+
for (const socket of sockets.clients) if (socket.readyState === WebSocket.OPEN) socket.send(payload);
|
|
119
|
+
};
|
|
120
|
+
await writeManifest();
|
|
121
|
+
return {
|
|
122
|
+
bridgeUrl,
|
|
123
|
+
token,
|
|
124
|
+
manifestPath,
|
|
125
|
+
get workerDescriptors() {
|
|
126
|
+
return workers;
|
|
127
|
+
},
|
|
128
|
+
async setWorkers(names) {
|
|
129
|
+
workers = names.map((name) => ({
|
|
130
|
+
name,
|
|
131
|
+
debuggerUrl: `ws://127.0.0.1:${address.port}/debug/${encodeURIComponent(name)}?token=${token}`
|
|
132
|
+
}));
|
|
133
|
+
await writeManifest();
|
|
134
|
+
broadcast({
|
|
135
|
+
type: "session.ready",
|
|
136
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
137
|
+
workers
|
|
138
|
+
});
|
|
139
|
+
},
|
|
140
|
+
broadcast,
|
|
141
|
+
async close() {
|
|
142
|
+
if (closed) return;
|
|
143
|
+
closed = true;
|
|
144
|
+
for (const socket of sockets.clients) {
|
|
145
|
+
socket.close(1001, "Vite server stopped");
|
|
146
|
+
socket.terminate();
|
|
147
|
+
}
|
|
148
|
+
await closeWebSocketServer(sockets);
|
|
149
|
+
await closeHttpServer(server);
|
|
150
|
+
await rm(manifestPath, { force: true });
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function listen(server) {
|
|
155
|
+
return new Promise((resolve$1, reject) => {
|
|
156
|
+
server.once("error", reject);
|
|
157
|
+
server.listen(0, "127.0.0.1", resolve$1);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
function closeWebSocketServer(server) {
|
|
161
|
+
return new Promise((resolve$1) => server.close(() => resolve$1()));
|
|
162
|
+
}
|
|
163
|
+
function closeHttpServer(server) {
|
|
164
|
+
return new Promise((resolve$1) => server.close(() => resolve$1()));
|
|
165
|
+
}
|
|
166
|
+
function safeTokenEquals(candidate, token) {
|
|
167
|
+
if (candidate === null) return false;
|
|
168
|
+
const left = Buffer.from(candidate);
|
|
169
|
+
const right = Buffer.from(token);
|
|
170
|
+
return left.length === right.length && timingSafeEqual(left, right);
|
|
171
|
+
}
|
|
172
|
+
function isSafeOrigin(origin) {
|
|
173
|
+
if (origin === void 0) return true;
|
|
174
|
+
try {
|
|
175
|
+
const url = new URL(origin);
|
|
176
|
+
return [
|
|
177
|
+
"127.0.0.1",
|
|
178
|
+
"localhost",
|
|
179
|
+
"::1",
|
|
180
|
+
"[::1]",
|
|
181
|
+
"vscode-webview.net"
|
|
182
|
+
].some((host) => url.hostname === host || url.hostname.endsWith(`.${host}`));
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/cdp/types.ts
|
|
190
|
+
function isRecord(value) {
|
|
191
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/discovery.ts
|
|
196
|
+
async function discoverInspectorTargets(options) {
|
|
197
|
+
const fetcher = options.fetch ?? globalThis.fetch;
|
|
198
|
+
const inspector = options.inspectorUrl === void 0 ? await discoverInspectorBase(options.viteUrl, fetcher) : normalizeInspectorBase(options.inspectorUrl);
|
|
199
|
+
const response = await fetcher(new URL("/json/list", inspector.replace(/^ws:/, "http:").replace(/^wss:/, "https:")));
|
|
200
|
+
if (!response.ok) throw new Error(`Inspector target list returned HTTP ${response.status}`);
|
|
201
|
+
const value = await response.json();
|
|
202
|
+
if (!Array.isArray(value)) throw new Error("Inspector target list must be an array");
|
|
203
|
+
const targets = value.flatMap((candidate) => {
|
|
204
|
+
if (!isRecord(candidate) || typeof candidate.webSocketDebuggerUrl !== "string") return [];
|
|
205
|
+
const url = new URL(candidate.webSocketDebuggerUrl);
|
|
206
|
+
if (!isLoopback(url.hostname)) return [];
|
|
207
|
+
return [{
|
|
208
|
+
name: decodeURIComponent(url.pathname.split("/").filter(Boolean).at(-1) ?? "worker"),
|
|
209
|
+
webSocketUrl: candidate.webSocketDebuggerUrl
|
|
210
|
+
}];
|
|
211
|
+
});
|
|
212
|
+
if (targets.length === 0) throw new Error("Cloudflare inspector advertised no Worker targets");
|
|
213
|
+
return targets;
|
|
214
|
+
}
|
|
215
|
+
async function discoverInspectorBase(viteUrl, fetcher) {
|
|
216
|
+
const response = await fetcher(new URL("/__debug", viteUrl));
|
|
217
|
+
if (!response.ok) throw new Error(`Cloudflare debug page returned HTTP ${response.status}`);
|
|
218
|
+
let html = await response.text();
|
|
219
|
+
for (let index = 0; index < 2; index += 1) try {
|
|
220
|
+
html = decodeURIComponent(html);
|
|
221
|
+
} catch {
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
const match = /[?&]ws=([^&"'<>\s]+)/.exec(html);
|
|
225
|
+
if (match?.[1] === void 0) throw new Error("Cloudflare debug page did not advertise an inspector");
|
|
226
|
+
const workerUrl = new URL(`ws://${match[1]}`);
|
|
227
|
+
if (!isLoopback(workerUrl.hostname)) throw new Error("Cloudflare inspector must use loopback");
|
|
228
|
+
return `${workerUrl.protocol}//${workerUrl.host}`;
|
|
229
|
+
}
|
|
230
|
+
function normalizeInspectorBase(value) {
|
|
231
|
+
const url = new URL(value.includes("://") ? value : `ws://${value}`);
|
|
232
|
+
if (!isLoopback(url.hostname)) throw new Error("Cloudflare inspector must use loopback");
|
|
233
|
+
if (url.protocol !== "ws:" && url.protocol !== "wss:" && url.protocol !== "http:" && url.protocol !== "https:") throw new Error(`Unsupported inspector protocol ${url.protocol}`);
|
|
234
|
+
return `${url.protocol}//${url.host}`;
|
|
235
|
+
}
|
|
236
|
+
function isLoopback(hostname) {
|
|
237
|
+
return [
|
|
238
|
+
"127.0.0.1",
|
|
239
|
+
"localhost",
|
|
240
|
+
"::1",
|
|
241
|
+
"[::1]"
|
|
242
|
+
].includes(hostname);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/cdp/router.ts
|
|
247
|
+
var CdpRouter = class {
|
|
248
|
+
#pending = /* @__PURE__ */ new Map();
|
|
249
|
+
#sendUpstream;
|
|
250
|
+
#onEvent;
|
|
251
|
+
#nextUpstreamId = 1;
|
|
252
|
+
#sendDebugger;
|
|
253
|
+
#closed = false;
|
|
254
|
+
constructor(sendUpstream, onEvent) {
|
|
255
|
+
this.#sendUpstream = sendUpstream;
|
|
256
|
+
this.#onEvent = onEvent;
|
|
257
|
+
}
|
|
258
|
+
send(method, params) {
|
|
259
|
+
if (this.#closed) return Promise.reject(/* @__PURE__ */ new Error("CDP router is closed"));
|
|
260
|
+
const id = this.#nextUpstreamId++;
|
|
261
|
+
const message = params === void 0 ? {
|
|
262
|
+
id,
|
|
263
|
+
method
|
|
264
|
+
} : {
|
|
265
|
+
id,
|
|
266
|
+
method,
|
|
267
|
+
params
|
|
268
|
+
};
|
|
269
|
+
return new Promise((resolve$1, reject) => {
|
|
270
|
+
this.#pending.set(id, {
|
|
271
|
+
target: "internal",
|
|
272
|
+
resolve: resolve$1,
|
|
273
|
+
reject
|
|
274
|
+
});
|
|
275
|
+
this.#sendUpstream(message);
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
attachDebugger(send) {
|
|
279
|
+
if (this.#sendDebugger !== void 0) throw new Error("Only one debugger can be attached per Worker");
|
|
280
|
+
this.#sendDebugger = send;
|
|
281
|
+
}
|
|
282
|
+
detachDebugger() {
|
|
283
|
+
this.#sendDebugger = void 0;
|
|
284
|
+
}
|
|
285
|
+
routeFromDebugger(value) {
|
|
286
|
+
const message = expectMessage(value, "debugger message");
|
|
287
|
+
if (typeof message.id !== "number" || !Number.isSafeInteger(message.id)) throw new Error("Debugger commands must include a numeric id");
|
|
288
|
+
if (typeof message.method !== "string" || message.method.length === 0) throw new Error("Debugger commands must include a method");
|
|
289
|
+
const upstreamId = this.#nextUpstreamId++;
|
|
290
|
+
const { id: originalId,...rest } = message;
|
|
291
|
+
this.#pending.set(upstreamId, {
|
|
292
|
+
target: "debugger",
|
|
293
|
+
originalId
|
|
294
|
+
});
|
|
295
|
+
this.#sendUpstream({
|
|
296
|
+
...rest,
|
|
297
|
+
id: upstreamId
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
handleUpstream(value) {
|
|
301
|
+
const message = expectMessage(value, "upstream message");
|
|
302
|
+
if (typeof message.id !== "number") {
|
|
303
|
+
this.#onEvent?.(message);
|
|
304
|
+
this.#sendDebugger?.(message);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const pending = this.#pending.get(message.id);
|
|
308
|
+
if (pending === void 0) return;
|
|
309
|
+
this.#pending.delete(message.id);
|
|
310
|
+
if (pending.target === "debugger") {
|
|
311
|
+
const { id: _upstreamId,...rest } = message;
|
|
312
|
+
this.#sendDebugger?.({
|
|
313
|
+
...rest,
|
|
314
|
+
id: pending.originalId
|
|
315
|
+
});
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (isRecord(message.error)) {
|
|
319
|
+
const detail = typeof message.error.message === "string" ? message.error.message : JSON.stringify(message.error);
|
|
320
|
+
pending.reject(/* @__PURE__ */ new Error(`CDP command failed: ${detail}`));
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
pending.resolve(message.result ?? {});
|
|
324
|
+
}
|
|
325
|
+
close(error = /* @__PURE__ */ new Error("CDP connection closed")) {
|
|
326
|
+
if (this.#closed) return;
|
|
327
|
+
this.#closed = true;
|
|
328
|
+
for (const pending of this.#pending.values()) if (pending.target === "internal") pending.reject(error);
|
|
329
|
+
this.#pending.clear();
|
|
330
|
+
this.#sendDebugger = void 0;
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
function expectMessage(value, label) {
|
|
334
|
+
if (!isRecord(value)) throw new Error(`${label} must be an object`);
|
|
335
|
+
return value;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
//#endregion
|
|
339
|
+
//#region src/cdp/serialize.ts
|
|
340
|
+
const DEFAULT_LIMITS = {
|
|
341
|
+
maxDepth: 2,
|
|
342
|
+
maxProperties: 50,
|
|
343
|
+
maxStringLength: 2e3
|
|
344
|
+
};
|
|
345
|
+
async function snapshotRemoteObjects(client, values, limits = DEFAULT_LIMITS) {
|
|
346
|
+
const seen = /* @__PURE__ */ new Set();
|
|
347
|
+
const snapshots = [];
|
|
348
|
+
for (const value of values) snapshots.push(await snapshot(client, value, limits, 0, seen));
|
|
349
|
+
return snapshots;
|
|
350
|
+
}
|
|
351
|
+
async function snapshot(client, remote, limits, depth, seen) {
|
|
352
|
+
const primitive = snapshotPrimitive(remote, limits.maxStringLength);
|
|
353
|
+
if (primitive !== void 0) return primitive;
|
|
354
|
+
const objectId = remote.objectId;
|
|
355
|
+
if (objectId === void 0) return {
|
|
356
|
+
kind: "unavailable",
|
|
357
|
+
description: truncate(remote.description ?? remote.className ?? remote.type, limits.maxStringLength)
|
|
358
|
+
};
|
|
359
|
+
if (seen.has(objectId)) return { kind: "circular" };
|
|
360
|
+
if (depth >= limits.maxDepth) return {
|
|
361
|
+
kind: "unavailable",
|
|
362
|
+
description: `[${remote.className ?? remote.subtype ?? "Object"}: depth limit]`
|
|
363
|
+
};
|
|
364
|
+
seen.add(objectId);
|
|
365
|
+
try {
|
|
366
|
+
const descriptors = await getProperties(client, objectId);
|
|
367
|
+
if (remote.subtype === "error") return snapshotError(remote, descriptors, limits.maxStringLength);
|
|
368
|
+
if (remote.subtype === "array") {
|
|
369
|
+
const indexed = descriptors.filter((descriptor) => /^\d+$/.test(descriptor.name)).sort((left, right) => Number(left.name) - Number(right.name));
|
|
370
|
+
const selected$1 = indexed.slice(0, limits.maxProperties);
|
|
371
|
+
const items = [];
|
|
372
|
+
for (const descriptor of selected$1) items.push(await snapshotDescriptor(client, descriptor, limits, depth + 1, seen));
|
|
373
|
+
return {
|
|
374
|
+
kind: "array",
|
|
375
|
+
items,
|
|
376
|
+
truncated: indexed.length > selected$1.length
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
const visible = descriptors.filter((descriptor) => descriptor.name !== "__proto__");
|
|
380
|
+
const selected = visible.slice(0, limits.maxProperties);
|
|
381
|
+
const entries = [];
|
|
382
|
+
for (const descriptor of selected) entries.push({
|
|
383
|
+
key: descriptor.name,
|
|
384
|
+
value: await snapshotDescriptor(client, descriptor, limits, depth + 1, seen)
|
|
385
|
+
});
|
|
386
|
+
return {
|
|
387
|
+
kind: "object",
|
|
388
|
+
className: remote.className ?? "Object",
|
|
389
|
+
entries,
|
|
390
|
+
truncated: visible.length > selected.length
|
|
391
|
+
};
|
|
392
|
+
} finally {
|
|
393
|
+
seen.delete(objectId);
|
|
394
|
+
await client.send("Runtime.releaseObject", { objectId }).catch(() => void 0);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
async function snapshotDescriptor(client, descriptor, limits, depth, seen) {
|
|
398
|
+
if (descriptor.value !== void 0) return snapshot(client, descriptor.value, limits, depth, seen);
|
|
399
|
+
if (descriptor.get !== void 0 && descriptor.set !== void 0) return {
|
|
400
|
+
kind: "accessor",
|
|
401
|
+
label: "[Getter/Setter]"
|
|
402
|
+
};
|
|
403
|
+
if (descriptor.get !== void 0) return {
|
|
404
|
+
kind: "accessor",
|
|
405
|
+
label: "[Getter]"
|
|
406
|
+
};
|
|
407
|
+
if (descriptor.set !== void 0) return {
|
|
408
|
+
kind: "accessor",
|
|
409
|
+
label: "[Setter]"
|
|
410
|
+
};
|
|
411
|
+
return {
|
|
412
|
+
kind: "unavailable",
|
|
413
|
+
description: "[Unavailable]"
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function snapshotPrimitive(remote, maxStringLength) {
|
|
417
|
+
if (remote.subtype === "null") return {
|
|
418
|
+
kind: "primitive",
|
|
419
|
+
type: "null",
|
|
420
|
+
value: "null"
|
|
421
|
+
};
|
|
422
|
+
if (remote.type === "object" || remote.type === "function") return void 0;
|
|
423
|
+
const type = remote.type;
|
|
424
|
+
if (!isPrimitiveType(type)) return {
|
|
425
|
+
kind: "unavailable",
|
|
426
|
+
description: truncate(remote.description ?? type, maxStringLength)
|
|
427
|
+
};
|
|
428
|
+
return {
|
|
429
|
+
kind: "primitive",
|
|
430
|
+
type,
|
|
431
|
+
value: truncate(remote.unserializableValue ?? (type === "undefined" ? "undefined" : String(remote.value ?? remote.description ?? "")), maxStringLength)
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
function snapshotError(remote, descriptors, maxStringLength) {
|
|
435
|
+
const read = (name) => {
|
|
436
|
+
const value = descriptors.find((descriptor) => descriptor.name === name)?.value;
|
|
437
|
+
if (value?.type !== "string") return void 0;
|
|
438
|
+
return truncate(String(value.value ?? ""), maxStringLength);
|
|
439
|
+
};
|
|
440
|
+
const description = remote.description ?? "Error";
|
|
441
|
+
const firstLine = description.split("\n", 1)[0] ?? "Error";
|
|
442
|
+
const separator = firstLine.indexOf(":");
|
|
443
|
+
const fallbackName = separator === -1 ? firstLine : firstLine.slice(0, separator);
|
|
444
|
+
const fallbackMessage = separator === -1 ? "" : firstLine.slice(separator + 1).trim();
|
|
445
|
+
const stack = read("stack") ?? (description.includes("\n") ? description : void 0);
|
|
446
|
+
return {
|
|
447
|
+
kind: "error",
|
|
448
|
+
name: read("name") ?? fallbackName,
|
|
449
|
+
message: read("message") ?? fallbackMessage,
|
|
450
|
+
...stack === void 0 ? {} : { stack: truncate(stack, maxStringLength) }
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
async function getProperties(client, objectId) {
|
|
454
|
+
const response = await client.send("Runtime.getProperties", {
|
|
455
|
+
objectId,
|
|
456
|
+
ownProperties: true,
|
|
457
|
+
generatePreview: false
|
|
458
|
+
});
|
|
459
|
+
if (!isRecord(response) || !Array.isArray(response.result)) return [];
|
|
460
|
+
return response.result.filter(isPropertyDescriptor);
|
|
461
|
+
}
|
|
462
|
+
function isPropertyDescriptor(value) {
|
|
463
|
+
return isRecord(value) && typeof value.name === "string";
|
|
464
|
+
}
|
|
465
|
+
function isPrimitiveType(value) {
|
|
466
|
+
return [
|
|
467
|
+
"string",
|
|
468
|
+
"number",
|
|
469
|
+
"boolean",
|
|
470
|
+
"undefined",
|
|
471
|
+
"bigint",
|
|
472
|
+
"symbol"
|
|
473
|
+
].includes(value);
|
|
474
|
+
}
|
|
475
|
+
function truncate(value, maxLength) {
|
|
476
|
+
return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 1))}…`;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
//#endregion
|
|
480
|
+
//#region src/source-maps.ts
|
|
481
|
+
var SourceMapRegistry = class {
|
|
482
|
+
#root;
|
|
483
|
+
#scripts = /* @__PURE__ */ new Map();
|
|
484
|
+
constructor(root) {
|
|
485
|
+
this.#root = resolve(root);
|
|
486
|
+
}
|
|
487
|
+
register(metadata) {
|
|
488
|
+
this.#scripts.set(metadata.scriptId, { metadata });
|
|
489
|
+
}
|
|
490
|
+
clear() {
|
|
491
|
+
this.#scripts.clear();
|
|
492
|
+
}
|
|
493
|
+
async resolve(frame) {
|
|
494
|
+
const script = this.#scripts.get(frame.scriptId);
|
|
495
|
+
if (script === void 0) return { diagnostic: `No source map metadata for script ${frame.scriptId}` };
|
|
496
|
+
try {
|
|
497
|
+
script.map ??= loadTraceMap(script.metadata);
|
|
498
|
+
const original = originalPositionFor(await script.map, {
|
|
499
|
+
line: frame.lineNumber + 1,
|
|
500
|
+
column: frame.columnNumber
|
|
501
|
+
});
|
|
502
|
+
if (original.source === null || original.line === null || original.column === null) return { diagnostic: "Source map did not contain the console call location" };
|
|
503
|
+
const file = normalizeSourcePath(original.source, script.metadata.url);
|
|
504
|
+
if (!isInside(this.#root, file)) return { diagnostic: "Original source is outside the Vite workspace" };
|
|
505
|
+
return { location: {
|
|
506
|
+
file,
|
|
507
|
+
line: original.line,
|
|
508
|
+
column: original.column + 1
|
|
509
|
+
} };
|
|
510
|
+
} catch (error) {
|
|
511
|
+
return { diagnostic: `Unable to resolve source map: ${error instanceof Error ? error.message : String(error)}` };
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
async function loadTraceMap(metadata) {
|
|
516
|
+
return new TraceMap(await loadSourceMapText(metadata.sourceMapURL, metadata.url), metadata.url);
|
|
517
|
+
}
|
|
518
|
+
async function loadSourceMapText(sourceMapURL, scriptURL) {
|
|
519
|
+
if (sourceMapURL.startsWith("data:")) return decodeDataUrl(sourceMapURL);
|
|
520
|
+
const normalized = sourceMapURL.replace(/^wrangler-file:/, "file:");
|
|
521
|
+
const url = new URL(normalized, scriptURL);
|
|
522
|
+
if (url.protocol !== "file:") throw new Error(`Unsupported source map protocol ${url.protocol}`);
|
|
523
|
+
return readFile(fileURLToPath(url), "utf8");
|
|
524
|
+
}
|
|
525
|
+
function decodeDataUrl(value) {
|
|
526
|
+
const comma = value.indexOf(",");
|
|
527
|
+
if (comma === -1) throw new Error("Malformed source map data URL");
|
|
528
|
+
const metadata = value.slice(0, comma);
|
|
529
|
+
const payload = value.slice(comma + 1);
|
|
530
|
+
return metadata.endsWith(";base64") ? Buffer.from(payload, "base64").toString("utf8") : decodeURIComponent(payload);
|
|
531
|
+
}
|
|
532
|
+
function normalizeSourcePath(source, scriptURL) {
|
|
533
|
+
const normalized = source.replace(/^wrangler-file:/, "file:");
|
|
534
|
+
if (normalized.startsWith("file:")) return resolve(fileURLToPath(normalized));
|
|
535
|
+
if (normalized.startsWith("/@fs/")) return resolve(normalized.slice(4));
|
|
536
|
+
if (isAbsolute(normalized)) return resolve(normalized);
|
|
537
|
+
if (scriptURL.startsWith("file:")) return resolve(fileURLToPath(new URL(normalized, scriptURL)));
|
|
538
|
+
return resolve(normalized);
|
|
539
|
+
}
|
|
540
|
+
function isInside(root, file) {
|
|
541
|
+
const path = relative(root, file);
|
|
542
|
+
return path === "" || !path.startsWith("..") && !isAbsolute(path);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
//#endregion
|
|
546
|
+
//#region src/worker-session.ts
|
|
547
|
+
var WorkerSession = class {
|
|
548
|
+
#options;
|
|
549
|
+
#maps;
|
|
550
|
+
#socket;
|
|
551
|
+
#router;
|
|
552
|
+
#disposed = false;
|
|
553
|
+
#generation = 0;
|
|
554
|
+
#reconnectTimer;
|
|
555
|
+
constructor(options) {
|
|
556
|
+
this.#options = options;
|
|
557
|
+
this.#maps = new SourceMapRegistry(options.root);
|
|
558
|
+
}
|
|
559
|
+
async start() {
|
|
560
|
+
this.#disposed = false;
|
|
561
|
+
await this.#connect();
|
|
562
|
+
}
|
|
563
|
+
attachDebugger(socket) {
|
|
564
|
+
const router = this.#router;
|
|
565
|
+
if (router === void 0) {
|
|
566
|
+
socket.close(1013, "Worker inspector is reconnecting");
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
try {
|
|
570
|
+
router.attachDebugger((message) => {
|
|
571
|
+
if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify(message));
|
|
572
|
+
});
|
|
573
|
+
} catch (error) {
|
|
574
|
+
socket.close(1013, error instanceof Error ? error.message : "Debugger already attached");
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
socket.on("message", (data) => {
|
|
578
|
+
try {
|
|
579
|
+
router.routeFromDebugger(JSON.parse(data.toString()));
|
|
580
|
+
} catch (error) {
|
|
581
|
+
socket.close(1008, error instanceof Error ? error.message.slice(0, 120) : "Invalid CDP message");
|
|
582
|
+
}
|
|
583
|
+
});
|
|
584
|
+
socket.once("close", () => router.detachDebugger());
|
|
585
|
+
}
|
|
586
|
+
async dispose() {
|
|
587
|
+
this.#disposed = true;
|
|
588
|
+
if (this.#reconnectTimer !== void 0) clearTimeout(this.#reconnectTimer);
|
|
589
|
+
this.#reconnectTimer = void 0;
|
|
590
|
+
this.#router?.close(/* @__PURE__ */ new Error("Worker session disposed"));
|
|
591
|
+
this.#router = void 0;
|
|
592
|
+
const socket = this.#socket;
|
|
593
|
+
this.#socket = void 0;
|
|
594
|
+
if (socket !== void 0 && socket.readyState < WebSocket.CLOSING) socket.close(1e3, "Worker Console stopped");
|
|
595
|
+
}
|
|
596
|
+
async #connect() {
|
|
597
|
+
const socket = new WebSocket(this.#options.target.webSocketUrl);
|
|
598
|
+
this.#socket = socket;
|
|
599
|
+
await new Promise((resolve$1, reject) => {
|
|
600
|
+
socket.once("open", resolve$1);
|
|
601
|
+
socket.once("error", reject);
|
|
602
|
+
});
|
|
603
|
+
if (this.#disposed) {
|
|
604
|
+
socket.close();
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
const router = new CdpRouter((message) => socket.send(JSON.stringify(message)), (event) => void this.#handleEvent(event));
|
|
608
|
+
this.#router = router;
|
|
609
|
+
socket.on("message", (data) => {
|
|
610
|
+
try {
|
|
611
|
+
router.handleUpstream(JSON.parse(data.toString()));
|
|
612
|
+
} catch (error) {
|
|
613
|
+
this.#options.onState?.({
|
|
614
|
+
type: "session.state",
|
|
615
|
+
state: "waiting",
|
|
616
|
+
message: error instanceof Error ? error.message : String(error)
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
socket.once("close", () => this.#handleClose(router));
|
|
621
|
+
socket.once("error", () => this.#handleClose(router));
|
|
622
|
+
this.#generation += 1;
|
|
623
|
+
this.#maps.clear();
|
|
624
|
+
this.#options.onClear?.(this.#generation);
|
|
625
|
+
await router.send("Runtime.enable");
|
|
626
|
+
await router.send("Debugger.enable");
|
|
627
|
+
this.#options.onState?.({
|
|
628
|
+
type: "session.state",
|
|
629
|
+
state: "connected"
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
#handleClose(router) {
|
|
633
|
+
if (this.#router !== router) return;
|
|
634
|
+
router.close(/* @__PURE__ */ new Error("Worker inspector disconnected"));
|
|
635
|
+
this.#router = void 0;
|
|
636
|
+
this.#options.onState?.({
|
|
637
|
+
type: "session.state",
|
|
638
|
+
state: "waiting"
|
|
639
|
+
});
|
|
640
|
+
this.#scheduleReconnect(250);
|
|
641
|
+
}
|
|
642
|
+
#scheduleReconnect(delay) {
|
|
643
|
+
if (this.#disposed || this.#options.reconnect === false || this.#reconnectTimer !== void 0) return;
|
|
644
|
+
this.#reconnectTimer = setTimeout(() => {
|
|
645
|
+
this.#reconnectTimer = void 0;
|
|
646
|
+
this.#connect().catch((error) => {
|
|
647
|
+
this.#options.onState?.({
|
|
648
|
+
type: "session.state",
|
|
649
|
+
state: "waiting",
|
|
650
|
+
message: error instanceof Error ? error.message : String(error)
|
|
651
|
+
});
|
|
652
|
+
this.#scheduleReconnect(1e3);
|
|
653
|
+
});
|
|
654
|
+
}, delay);
|
|
655
|
+
}
|
|
656
|
+
async #handleEvent(event) {
|
|
657
|
+
if (event.method === "Debugger.scriptParsed") {
|
|
658
|
+
const params$1 = event.params;
|
|
659
|
+
if (isRecord(params$1) && typeof params$1.scriptId === "string" && typeof params$1.url === "string" && typeof params$1.sourceMapURL === "string" && params$1.sourceMapURL.length > 0) this.#maps.register({
|
|
660
|
+
scriptId: params$1.scriptId,
|
|
661
|
+
url: params$1.url,
|
|
662
|
+
sourceMapURL: params$1.sourceMapURL
|
|
663
|
+
});
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
if (event.method !== "Runtime.consoleAPICalled" || !isRecord(event.params)) return;
|
|
667
|
+
const params = event.params;
|
|
668
|
+
const args = Array.isArray(params.args) ? params.args.filter(isRemoteObject) : [];
|
|
669
|
+
const router = this.#router;
|
|
670
|
+
if (router === void 0) return;
|
|
671
|
+
const values = await snapshotRemoteObjects(router, args);
|
|
672
|
+
const frame = firstCallFrame(params.stackTrace);
|
|
673
|
+
const resolution = frame === void 0 ? { diagnostic: "Console event did not include a call frame" } : await this.#maps.resolve(frame);
|
|
674
|
+
const location = "location" in resolution ? resolution.location : void 0;
|
|
675
|
+
const diagnostic = "diagnostic" in resolution ? resolution.diagnostic : void 0;
|
|
676
|
+
this.#options.onLog({
|
|
677
|
+
id: randomUUID(),
|
|
678
|
+
worker: this.#options.target.name,
|
|
679
|
+
level: normalizeLevel(params.type),
|
|
680
|
+
timestamp: typeof params.timestamp === "number" ? params.timestamp : Date.now(),
|
|
681
|
+
summary: summarizeValues(values),
|
|
682
|
+
arguments: values,
|
|
683
|
+
...location === void 0 ? {} : { location },
|
|
684
|
+
...diagnostic === void 0 ? {} : { diagnostic }
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
function isRemoteObject(value) {
|
|
689
|
+
return isRecord(value) && typeof value.type === "string";
|
|
690
|
+
}
|
|
691
|
+
function firstCallFrame(value) {
|
|
692
|
+
if (!isRecord(value) || !Array.isArray(value.callFrames)) return void 0;
|
|
693
|
+
for (const candidate of value.callFrames) if (isRecord(candidate) && typeof candidate.scriptId === "string" && typeof candidate.url === "string" && typeof candidate.lineNumber === "number" && typeof candidate.columnNumber === "number") return {
|
|
694
|
+
scriptId: candidate.scriptId,
|
|
695
|
+
url: candidate.url,
|
|
696
|
+
lineNumber: candidate.lineNumber,
|
|
697
|
+
columnNumber: candidate.columnNumber
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
function normalizeLevel(value) {
|
|
701
|
+
switch (value) {
|
|
702
|
+
case "info":
|
|
703
|
+
case "warn":
|
|
704
|
+
case "error":
|
|
705
|
+
case "debug": return value;
|
|
706
|
+
case "warning": return "warn";
|
|
707
|
+
default: return "log";
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
function summarizeValues(values) {
|
|
711
|
+
return values.map(summarizeValue).join(" ");
|
|
712
|
+
}
|
|
713
|
+
function summarizeValue(value) {
|
|
714
|
+
switch (value.kind) {
|
|
715
|
+
case "primitive": return value.value;
|
|
716
|
+
case "circular": return "[Circular]";
|
|
717
|
+
case "accessor": return value.label;
|
|
718
|
+
case "unavailable": return value.description;
|
|
719
|
+
case "error": return `${value.name}${value.message.length === 0 ? "" : `: ${value.message}`}`;
|
|
720
|
+
case "array": return `[${value.items.map(summarizeValue).join(", ")}${value.truncated ? ", …" : ""}]`;
|
|
721
|
+
case "object": return `{ ${value.entries.map((entry) => `${entry.key}: ${summarizeValue(entry.value)}`).join(", ")}${value.truncated ? ", …" : ""} }`;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
//#endregion
|
|
726
|
+
//#region src/index.ts
|
|
727
|
+
function workerConsole(options = {}) {
|
|
728
|
+
return {
|
|
729
|
+
name: "worker-console",
|
|
730
|
+
apply: "serve",
|
|
731
|
+
enforce: "post",
|
|
732
|
+
configureServer(server) {
|
|
733
|
+
return () => {
|
|
734
|
+
startWorkerConsole(server, options).catch((error) => {
|
|
735
|
+
server.config.logger.error(`[worker-console] ${error instanceof Error ? error.message : String(error)}`);
|
|
736
|
+
});
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
async function startWorkerConsole(server, options) {
|
|
742
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
743
|
+
let paused = false;
|
|
744
|
+
let disposed = false;
|
|
745
|
+
let bridge;
|
|
746
|
+
const onControl = (message) => {
|
|
747
|
+
if (message.type === "capture.pause") {
|
|
748
|
+
paused = true;
|
|
749
|
+
bridge.broadcast({
|
|
750
|
+
type: "session.state",
|
|
751
|
+
state: "paused"
|
|
752
|
+
});
|
|
753
|
+
} else if (message.type === "capture.resume") {
|
|
754
|
+
paused = false;
|
|
755
|
+
bridge.broadcast({
|
|
756
|
+
type: "session.state",
|
|
757
|
+
state: "connected"
|
|
758
|
+
});
|
|
759
|
+
} else if (message.type === "ping") bridge.broadcast({
|
|
760
|
+
type: "pong",
|
|
761
|
+
nonce: message.nonce
|
|
762
|
+
});
|
|
763
|
+
};
|
|
764
|
+
bridge = await createBridgeServer({
|
|
765
|
+
root: server.config.root,
|
|
766
|
+
...options.sessionDirectory === void 0 ? {} : { sessionDirectory: options.sessionDirectory },
|
|
767
|
+
onControl,
|
|
768
|
+
onDebugger(worker, socket) {
|
|
769
|
+
const session = sessions.get(worker);
|
|
770
|
+
if (session === void 0) socket.close(1013, "Worker inspector is not ready");
|
|
771
|
+
else session.attachDebugger(socket);
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
bridge.broadcast({
|
|
775
|
+
type: "session.state",
|
|
776
|
+
state: "waiting"
|
|
777
|
+
});
|
|
778
|
+
const dispose = async () => {
|
|
779
|
+
if (disposed) return;
|
|
780
|
+
disposed = true;
|
|
781
|
+
await Promise.all([...sessions.values()].map((session) => session.dispose()));
|
|
782
|
+
sessions.clear();
|
|
783
|
+
await bridge.close();
|
|
784
|
+
};
|
|
785
|
+
server.httpServer?.once("close", () => void dispose());
|
|
786
|
+
const viteUrl = await waitForViteUrl(server);
|
|
787
|
+
let attempt = 0;
|
|
788
|
+
while (!disposed) try {
|
|
789
|
+
const targets = await discoverInspectorTargets({
|
|
790
|
+
viteUrl,
|
|
791
|
+
...options.inspectorUrl === void 0 ? {} : { inspectorUrl: options.inspectorUrl }
|
|
792
|
+
});
|
|
793
|
+
await bridge.setWorkers(targets.map((target) => target.name));
|
|
794
|
+
for (const target of targets) {
|
|
795
|
+
if (sessions.has(target.name)) continue;
|
|
796
|
+
const session = new WorkerSession({
|
|
797
|
+
target,
|
|
798
|
+
root: server.config.root,
|
|
799
|
+
onLog(entry) {
|
|
800
|
+
if (!paused) bridge.broadcast({
|
|
801
|
+
type: "log.entry",
|
|
802
|
+
entry
|
|
803
|
+
});
|
|
804
|
+
},
|
|
805
|
+
onClear(generation) {
|
|
806
|
+
bridge.broadcast({
|
|
807
|
+
type: "logs.clear",
|
|
808
|
+
worker: target.name,
|
|
809
|
+
generation
|
|
810
|
+
});
|
|
811
|
+
},
|
|
812
|
+
onState(message) {
|
|
813
|
+
bridge.broadcast(message);
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
try {
|
|
817
|
+
await session.start();
|
|
818
|
+
sessions.set(target.name, session);
|
|
819
|
+
} catch (error) {
|
|
820
|
+
await session.dispose();
|
|
821
|
+
throw error;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
bridge.broadcast({
|
|
825
|
+
type: "session.state",
|
|
826
|
+
state: "connected"
|
|
827
|
+
});
|
|
828
|
+
return;
|
|
829
|
+
} catch (error) {
|
|
830
|
+
const message = {
|
|
831
|
+
type: "session.state",
|
|
832
|
+
state: String(error).includes("Too many clients") ? "inspector-occupied" : "waiting",
|
|
833
|
+
message: error instanceof Error ? error.message : String(error)
|
|
834
|
+
};
|
|
835
|
+
bridge.broadcast(message);
|
|
836
|
+
const delay = [
|
|
837
|
+
100,
|
|
838
|
+
200,
|
|
839
|
+
400,
|
|
840
|
+
800,
|
|
841
|
+
1600
|
|
842
|
+
][Math.min(attempt, 4)] ?? 1600;
|
|
843
|
+
attempt += 1;
|
|
844
|
+
await new Promise((resolve$1) => setTimeout(resolve$1, delay));
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
async function waitForViteUrl(server) {
|
|
848
|
+
const httpServer = server.httpServer;
|
|
849
|
+
if (httpServer === null) throw new Error("Worker Console requires Vite's HTTP server");
|
|
850
|
+
if (!httpServer.listening) await new Promise((resolve$1) => httpServer.once("listening", resolve$1));
|
|
851
|
+
const address = httpServer.address();
|
|
852
|
+
if (address === null) throw new Error("Vite did not expose a listening address");
|
|
853
|
+
return `${server.config.server.https ? "https" : "http"}://127.0.0.1:${address.port}`;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
//#endregion
|
|
857
|
+
export { workerConsole };
|
|
858
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["workers: WorkerDescriptor[]","lastState: Extract<BridgeServerMessage, { type: \"session.state\" }> | undefined","manifest: SessionManifest","resolve","value: unknown","#pending","#sendUpstream","#onEvent","#closed","#nextUpstreamId","message: CdpRecord","#sendDebugger","DEFAULT_LIMITS: SnapshotLimits","snapshots: StructuredValue[]","selected","items: StructuredValue[]","entries: Array<{ key: string; value: StructuredValue }>","#root","#scripts","#options","#maps","#disposed","#connect","#router","#reconnectTimer","#socket","resolve","#handleEvent","#handleClose","#generation","#scheduleReconnect","params","bridge: BridgeServer","message: BridgeServerMessage","resolve"],"sources":["../../protocol/dist/index.mjs","../src/bridge.ts","../src/cdp/types.ts","../src/discovery.ts","../src/cdp/router.ts","../src/cdp/serialize.ts","../src/source-maps.ts","../src/worker-session.ts","../src/index.ts"],"sourcesContent":["//#region src/index.ts\nconst PROTOCOL_VERSION = 1;\nvar ProtocolError = class extends Error {\n\tconstructor(message) {\n\t\tsuper(message);\n\t\tthis.name = \"ProtocolError\";\n\t}\n};\nfunction parseSessionManifest(value) {\n\tconst record = expectRecord(value, \"session manifest\");\n\texpectProtocolVersion(record.protocolVersion);\n\texpectString(record.root, \"root\");\n\texpectLoopbackWebSocket(record.bridgeUrl, \"bridgeUrl\");\n\texpectString(record.token, \"token\");\n\texpectPositiveInteger(record.pid, \"pid\");\n\texpectIsoDate(record.createdAt, \"createdAt\");\n\texpectWorkers(record.workers);\n\treturn value;\n}\nfunction parseServerMessage(value) {\n\tconst record = expectRecord(value, \"server message\");\n\tconst type = expectString(record.type, \"message type\");\n\tswitch (type) {\n\t\tcase \"session.ready\":\n\t\t\texpectProtocolVersion(record.protocolVersion);\n\t\t\texpectWorkers(record.workers);\n\t\t\tbreak;\n\t\tcase \"session.state\":\n\t\t\texpectSessionState(record.state);\n\t\t\texpectOptionalString(record.message, \"message\");\n\t\t\tbreak;\n\t\tcase \"log.entry\":\n\t\t\texpectLogEntry(record.entry);\n\t\t\tbreak;\n\t\tcase \"logs.clear\":\n\t\t\texpectOptionalString(record.worker, \"worker\");\n\t\t\texpectNonNegativeInteger(record.generation, \"generation\");\n\t\t\tbreak;\n\t\tcase \"ping\":\n\t\tcase \"pong\":\n\t\t\texpectString(record.nonce, \"nonce\");\n\t\t\tbreak;\n\t\tdefault: throw new ProtocolError(`Unsupported server message type: ${type}`);\n\t}\n\treturn value;\n}\nfunction parseClientMessage(value) {\n\tconst record = expectRecord(value, \"client message\");\n\tconst type = expectString(record.type, \"message type\");\n\tswitch (type) {\n\t\tcase \"capture.pause\":\n\t\tcase \"capture.resume\": break;\n\t\tcase \"ping\":\n\t\tcase \"pong\":\n\t\t\texpectString(record.nonce, \"nonce\");\n\t\t\tbreak;\n\t\tdefault: throw new ProtocolError(`Unsupported client message type: ${type}`);\n\t}\n\treturn value;\n}\nfunction expectLogEntry(value) {\n\tconst record = expectRecord(value, \"log entry\");\n\texpectString(record.id, \"entry.id\");\n\texpectString(record.worker, \"entry.worker\");\n\texpectConsoleLevel(record.level);\n\texpectFiniteNumber(record.timestamp, \"entry.timestamp\");\n\texpectString(record.summary, \"entry.summary\");\n\tif (!Array.isArray(record.arguments)) throw new ProtocolError(\"entry.arguments must be an array\");\n\tfor (const argument of record.arguments) expectStructuredValue(argument);\n\tif (record.location !== void 0) expectSourceLocation(record.location);\n\texpectOptionalString(record.diagnostic, \"entry.diagnostic\");\n}\nfunction expectStructuredValue(value) {\n\tconst record = expectRecord(value, \"structured value\");\n\tconst kind = expectString(record.kind, \"structured value kind\");\n\tswitch (kind) {\n\t\tcase \"primitive\":\n\t\t\texpectOneOf(record.type, [\n\t\t\t\t\"string\",\n\t\t\t\t\"number\",\n\t\t\t\t\"boolean\",\n\t\t\t\t\"undefined\",\n\t\t\t\t\"null\",\n\t\t\t\t\"bigint\",\n\t\t\t\t\"symbol\"\n\t\t\t], \"primitive type\");\n\t\t\texpectString(record.value, \"primitive value\");\n\t\t\treturn;\n\t\tcase \"object\":\n\t\t\texpectString(record.className, \"object className\");\n\t\t\texpectBoolean(record.truncated, \"object truncated\");\n\t\t\tif (!Array.isArray(record.entries)) throw new ProtocolError(\"object entries must be an array\");\n\t\t\tfor (const entry of record.entries) {\n\t\t\t\tconst item = expectRecord(entry, \"object entry\");\n\t\t\t\texpectString(item.key, \"object entry key\");\n\t\t\t\texpectStructuredValue(item.value);\n\t\t\t}\n\t\t\treturn;\n\t\tcase \"array\":\n\t\t\texpectBoolean(record.truncated, \"array truncated\");\n\t\t\tif (!Array.isArray(record.items)) throw new ProtocolError(\"array items must be an array\");\n\t\t\tfor (const item of record.items) expectStructuredValue(item);\n\t\t\treturn;\n\t\tcase \"error\":\n\t\t\texpectString(record.name, \"error name\");\n\t\t\texpectString(record.message, \"error message\");\n\t\t\texpectOptionalString(record.stack, \"error stack\");\n\t\t\treturn;\n\t\tcase \"accessor\":\n\t\t\texpectOneOf(record.label, [\n\t\t\t\t\"[Getter]\",\n\t\t\t\t\"[Setter]\",\n\t\t\t\t\"[Getter/Setter]\"\n\t\t\t], \"accessor label\");\n\t\t\treturn;\n\t\tcase \"circular\": return;\n\t\tcase \"unavailable\":\n\t\t\texpectString(record.description, \"unavailable description\");\n\t\t\treturn;\n\t\tdefault: throw new ProtocolError(`Unsupported structured value kind: ${kind}`);\n\t}\n}\nfunction expectSourceLocation(value) {\n\tconst record = expectRecord(value, \"source location\");\n\texpectString(record.file, \"location.file\");\n\texpectPositiveInteger(record.line, \"location.line\");\n\texpectPositiveInteger(record.column, \"location.column\");\n}\nfunction expectWorkers(value) {\n\tif (!Array.isArray(value)) throw new ProtocolError(\"workers must be an array\");\n\tfor (const worker of value) {\n\t\tconst record = expectRecord(worker, \"worker\");\n\t\texpectString(record.name, \"worker.name\");\n\t\texpectLoopbackWebSocket(record.debuggerUrl, \"worker.debuggerUrl\");\n\t}\n}\nfunction expectProtocolVersion(value) {\n\tif (value !== PROTOCOL_VERSION) throw new ProtocolError(`Unsupported protocol version: expected ${PROTOCOL_VERSION}, received ${String(value)}`);\n}\nfunction expectConsoleLevel(value) {\n\texpectOneOf(value, [\n\t\t\"log\",\n\t\t\"info\",\n\t\t\"warn\",\n\t\t\"error\",\n\t\t\"debug\"\n\t], \"console level\");\n}\nfunction expectSessionState(value) {\n\texpectOneOf(value, [\n\t\t\"waiting\",\n\t\t\"connected\",\n\t\t\"paused\",\n\t\t\"inspector-occupied\",\n\t\t\"disconnected\"\n\t], \"session state\");\n}\nfunction expectRecord(value, label) {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) throw new ProtocolError(`${label} must be an object`);\n\treturn value;\n}\nfunction expectString(value, label) {\n\tif (typeof value !== \"string\" || value.length === 0) throw new ProtocolError(`${label} must be a non-empty string`);\n\treturn value;\n}\nfunction expectOptionalString(value, label) {\n\tif (value !== void 0) expectString(value, label);\n}\nfunction expectBoolean(value, label) {\n\tif (typeof value !== \"boolean\") throw new ProtocolError(`${label} must be a boolean`);\n}\nfunction expectFiniteNumber(value, label) {\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) throw new ProtocolError(`${label} must be a finite number`);\n}\nfunction expectPositiveInteger(value, label) {\n\tif (!Number.isInteger(value) || value <= 0) throw new ProtocolError(`${label} must be a positive integer`);\n}\nfunction expectNonNegativeInteger(value, label) {\n\tif (!Number.isInteger(value) || value < 0) throw new ProtocolError(`${label} must be a non-negative integer`);\n}\nfunction expectIsoDate(value, label) {\n\tconst stringValue = expectString(value, label);\n\tif (!Number.isFinite(Date.parse(stringValue))) throw new ProtocolError(`${label} must be an ISO date string`);\n}\nfunction expectLoopbackWebSocket(value, label) {\n\tconst stringValue = expectString(value, label);\n\tlet url;\n\ttry {\n\t\turl = new URL(stringValue);\n\t} catch {\n\t\tthrow new ProtocolError(`${label} must be a valid URL`);\n\t}\n\tif (url.protocol !== \"ws:\" || ![\n\t\t\"127.0.0.1\",\n\t\t\"localhost\",\n\t\t\"[::1]\",\n\t\t\"::1\"\n\t].includes(url.hostname)) throw new ProtocolError(`${label} must use a loopback WebSocket`);\n}\nfunction expectOneOf(value, choices, label) {\n\tif (typeof value !== \"string\" || !choices.includes(value)) throw new ProtocolError(`${label} must be one of ${choices.join(\", \")}`);\n}\n\n//#endregion\nexport { PROTOCOL_VERSION, ProtocolError, parseClientMessage, parseServerMessage, parseSessionManifest };\n//# sourceMappingURL=index.mjs.map","import { randomBytes, timingSafeEqual } from \"node:crypto\"\nimport { mkdir, rm, writeFile } from \"node:fs/promises\"\nimport { createServer, type Server } from \"node:http\"\nimport { join, resolve } from \"node:path\"\n\nimport {\n PROTOCOL_VERSION,\n parseClientMessage,\n type BridgeClientMessage,\n type BridgeServerMessage,\n type SessionManifest,\n type WorkerDescriptor,\n} from \"@worker-console/protocol\"\nimport WebSocket, { WebSocketServer } from \"ws\"\n\nexport interface BridgeServerOptions {\n root: string\n sessionDirectory?: string\n onControl?: (message: BridgeClientMessage) => void\n onDebugger?: (worker: string, socket: WebSocket) => void\n}\n\nexport interface BridgeServer {\n readonly bridgeUrl: string\n readonly token: string\n readonly manifestPath: string\n readonly workerDescriptors: WorkerDescriptor[]\n setWorkers(names: string[]): Promise<void>\n broadcast(message: BridgeServerMessage): void\n close(): Promise<void>\n}\n\nexport async function createBridgeServer(options: BridgeServerOptions): Promise<BridgeServer> {\n const token = randomBytes(32).toString(\"base64url\")\n const server = createServer((_request, response) => response.writeHead(404).end())\n const sockets = new WebSocketServer({ noServer: true, maxPayload: 64 * 1024 })\n await listen(server)\n const address = server.address()\n if (address === null || typeof address === \"string\") throw new Error(\"Bridge did not acquire a port\")\n\n const bridgeUrl = `ws://127.0.0.1:${address.port}/events`\n const sessionDirectory = resolve(options.sessionDirectory ?? join(options.root, \".worker-console\"))\n const manifestPath = join(sessionDirectory, \"session.json\")\n let workers: WorkerDescriptor[] = []\n let lastState: Extract<BridgeServerMessage, { type: \"session.state\" }> | undefined\n let closed = false\n\n const writeManifest = async (): Promise<void> => {\n const manifest: SessionManifest = {\n protocolVersion: PROTOCOL_VERSION,\n root: resolve(options.root),\n bridgeUrl,\n token,\n pid: process.pid,\n createdAt: new Date().toISOString(),\n workers,\n }\n await mkdir(sessionDirectory, { recursive: true })\n await writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\\n`, { mode: 0o600 })\n }\n\n server.on(\"upgrade\", (request, socket, head) => {\n const url = new URL(request.url ?? \"/\", `http://${request.headers.host ?? \"127.0.0.1\"}`)\n if (!isSafeOrigin(request.headers.origin) || !safeTokenEquals(url.searchParams.get(\"token\"), token)) {\n socket.write(\"HTTP/1.1 401 Unauthorized\\r\\nConnection: close\\r\\n\\r\\n\")\n socket.destroy()\n return\n }\n\n if (url.pathname === \"/events\") {\n sockets.handleUpgrade(request, socket, head, (webSocket) => {\n sockets.emit(\"connection\", webSocket, request)\n })\n return\n }\n\n if (url.pathname.startsWith(\"/debug/\")) {\n const worker = decodeURIComponent(url.pathname.slice(\"/debug/\".length))\n if (!workers.some((candidate) => candidate.name === worker) || options.onDebugger === undefined) {\n socket.write(\"HTTP/1.1 404 Not Found\\r\\nConnection: close\\r\\n\\r\\n\")\n socket.destroy()\n return\n }\n sockets.handleUpgrade(request, socket, head, (webSocket) => {\n options.onDebugger?.(worker, webSocket)\n })\n return\n }\n\n socket.write(\"HTTP/1.1 404 Not Found\\r\\nConnection: close\\r\\n\\r\\n\")\n socket.destroy()\n })\n\n sockets.on(\"connection\", (socket) => {\n socket.send(\n JSON.stringify({\n type: \"session.ready\",\n protocolVersion: PROTOCOL_VERSION,\n workers,\n } satisfies BridgeServerMessage),\n )\n if (lastState !== undefined) socket.send(JSON.stringify(lastState))\n socket.on(\"message\", (data) => {\n try {\n const message = parseClientMessage(JSON.parse(data.toString()))\n options.onControl?.(message)\n } catch (error) {\n socket.close(1008, error instanceof Error ? error.message.slice(0, 120) : \"Invalid message\")\n }\n })\n })\n\n const broadcast = (message: BridgeServerMessage): void => {\n if (message.type === \"session.state\") lastState = message\n const payload = JSON.stringify(message)\n for (const socket of sockets.clients) {\n if (socket.readyState === WebSocket.OPEN) socket.send(payload)\n }\n }\n\n await writeManifest()\n\n return {\n bridgeUrl,\n token,\n manifestPath,\n get workerDescriptors() {\n return workers\n },\n async setWorkers(names) {\n workers = names.map((name) => ({\n name,\n debuggerUrl: `ws://127.0.0.1:${address.port}/debug/${encodeURIComponent(name)}?token=${token}`,\n }))\n await writeManifest()\n broadcast({\n type: \"session.ready\",\n protocolVersion: PROTOCOL_VERSION,\n workers,\n })\n },\n broadcast,\n async close() {\n if (closed) return\n closed = true\n for (const socket of sockets.clients) {\n socket.close(1001, \"Vite server stopped\")\n socket.terminate()\n }\n await closeWebSocketServer(sockets)\n await closeHttpServer(server)\n await rm(manifestPath, { force: true })\n },\n }\n}\n\nfunction listen(server: Server): Promise<void> {\n return new Promise((resolve, reject) => {\n server.once(\"error\", reject)\n server.listen(0, \"127.0.0.1\", resolve)\n })\n}\n\nfunction closeWebSocketServer(server: WebSocketServer): Promise<void> {\n return new Promise((resolve) => server.close(() => resolve()))\n}\n\nfunction closeHttpServer(server: Server): Promise<void> {\n return new Promise((resolve) => server.close(() => resolve()))\n}\n\nfunction safeTokenEquals(candidate: string | null, token: string): boolean {\n if (candidate === null) return false\n const left = Buffer.from(candidate)\n const right = Buffer.from(token)\n return left.length === right.length && timingSafeEqual(left, right)\n}\n\nfunction isSafeOrigin(origin: string | undefined): boolean {\n if (origin === undefined) return true\n try {\n const url = new URL(origin)\n return [\"127.0.0.1\", \"localhost\", \"::1\", \"[::1]\", \"vscode-webview.net\"].some(\n (host) => url.hostname === host || url.hostname.endsWith(`.${host}`),\n )\n } catch {\n return false\n }\n}\n","export type CdpRecord = Record<string, unknown>\n\nexport interface CdpCallFrame {\n functionName?: string\n scriptId: string\n url: string\n lineNumber: number\n columnNumber: number\n}\n\nexport interface CdpRemoteObject {\n type: string\n subtype?: string\n className?: string\n value?: unknown\n unserializableValue?: string\n description?: string\n objectId?: string\n}\n\nexport interface CdpPropertyDescriptor {\n name: string\n value?: CdpRemoteObject\n get?: CdpRemoteObject\n set?: CdpRemoteObject\n enumerable?: boolean\n}\n\nexport function isRecord(value: unknown): value is CdpRecord {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n}\n","import { isRecord } from \"./cdp/types\"\n\nexport interface InspectorTarget {\n name: string\n webSocketUrl: string\n}\n\nexport interface InspectorDiscoveryOptions {\n viteUrl: string\n inspectorUrl?: string\n fetch?: typeof globalThis.fetch\n}\n\nexport async function discoverInspectorTargets(\n options: InspectorDiscoveryOptions,\n): Promise<InspectorTarget[]> {\n const fetcher = options.fetch ?? globalThis.fetch\n const inspector =\n options.inspectorUrl === undefined\n ? await discoverInspectorBase(options.viteUrl, fetcher)\n : normalizeInspectorBase(options.inspectorUrl)\n const listUrl = new URL(\"/json/list\", inspector.replace(/^ws:/, \"http:\").replace(/^wss:/, \"https:\"))\n const response = await fetcher(listUrl)\n if (!response.ok) throw new Error(`Inspector target list returned HTTP ${response.status}`)\n const value: unknown = await response.json()\n if (!Array.isArray(value)) throw new Error(\"Inspector target list must be an array\")\n\n const targets = value.flatMap((candidate): InspectorTarget[] => {\n if (!isRecord(candidate) || typeof candidate.webSocketDebuggerUrl !== \"string\") return []\n const url = new URL(candidate.webSocketDebuggerUrl)\n if (!isLoopback(url.hostname)) return []\n const name = decodeURIComponent(url.pathname.split(\"/\").filter(Boolean).at(-1) ?? \"worker\")\n return [{ name, webSocketUrl: candidate.webSocketDebuggerUrl }]\n })\n if (targets.length === 0) throw new Error(\"Cloudflare inspector advertised no Worker targets\")\n return targets\n}\n\nasync function discoverInspectorBase(viteUrl: string, fetcher: typeof globalThis.fetch): Promise<string> {\n const response = await fetcher(new URL(\"/__debug\", viteUrl))\n if (!response.ok) throw new Error(`Cloudflare debug page returned HTTP ${response.status}`)\n let html = await response.text()\n for (let index = 0; index < 2; index += 1) {\n try {\n html = decodeURIComponent(html)\n } catch {\n break\n }\n }\n const match = /[?&]ws=([^&\"'<>\\s]+)/.exec(html)\n if (match?.[1] === undefined) throw new Error(\"Cloudflare debug page did not advertise an inspector\")\n const workerUrl = new URL(`ws://${match[1]}`)\n if (!isLoopback(workerUrl.hostname)) throw new Error(\"Cloudflare inspector must use loopback\")\n return `${workerUrl.protocol}//${workerUrl.host}`\n}\n\nfunction normalizeInspectorBase(value: string): string {\n const url = new URL(value.includes(\"://\") ? value : `ws://${value}`)\n if (!isLoopback(url.hostname)) throw new Error(\"Cloudflare inspector must use loopback\")\n if (url.protocol !== \"ws:\" && url.protocol !== \"wss:\" && url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new Error(`Unsupported inspector protocol ${url.protocol}`)\n }\n return `${url.protocol}//${url.host}`\n}\n\nfunction isLoopback(hostname: string): boolean {\n return [\"127.0.0.1\", \"localhost\", \"::1\", \"[::1]\"].includes(hostname)\n}\n","import { isRecord, type CdpRecord } from \"./types\"\n\ntype SendMessage = (message: CdpRecord) => void\ntype EventListener = (event: CdpRecord) => void\n\ntype Pending =\n | {\n target: \"internal\"\n resolve: (result: unknown) => void\n reject: (error: Error) => void\n }\n | { target: \"debugger\"; originalId: number }\n\nexport class CdpRouter {\n readonly #pending = new Map<number, Pending>()\n readonly #sendUpstream: SendMessage\n readonly #onEvent: EventListener | undefined\n #nextUpstreamId = 1\n #sendDebugger: SendMessage | undefined\n #closed = false\n\n constructor(sendUpstream: SendMessage, onEvent?: EventListener) {\n this.#sendUpstream = sendUpstream\n this.#onEvent = onEvent\n }\n\n send(method: string, params?: CdpRecord): Promise<unknown> {\n if (this.#closed) return Promise.reject(new Error(\"CDP router is closed\"))\n const id = this.#nextUpstreamId++\n const message: CdpRecord = params === undefined ? { id, method } : { id, method, params }\n\n return new Promise((resolve, reject) => {\n this.#pending.set(id, { target: \"internal\", resolve, reject })\n this.#sendUpstream(message)\n })\n }\n\n attachDebugger(send: SendMessage): void {\n if (this.#sendDebugger !== undefined) {\n throw new Error(\"Only one debugger can be attached per Worker\")\n }\n this.#sendDebugger = send\n }\n\n detachDebugger(): void {\n this.#sendDebugger = undefined\n }\n\n routeFromDebugger(value: unknown): void {\n const message = expectMessage(value, \"debugger message\")\n if (typeof message.id !== \"number\" || !Number.isSafeInteger(message.id)) {\n throw new Error(\"Debugger commands must include a numeric id\")\n }\n if (typeof message.method !== \"string\" || message.method.length === 0) {\n throw new Error(\"Debugger commands must include a method\")\n }\n\n const upstreamId = this.#nextUpstreamId++\n const { id: originalId, ...rest } = message\n this.#pending.set(upstreamId, { target: \"debugger\", originalId })\n this.#sendUpstream({ ...rest, id: upstreamId })\n }\n\n handleUpstream(value: unknown): void {\n const message = expectMessage(value, \"upstream message\")\n if (typeof message.id !== \"number\") {\n this.#onEvent?.(message)\n this.#sendDebugger?.(message)\n return\n }\n\n const pending = this.#pending.get(message.id)\n if (pending === undefined) return\n this.#pending.delete(message.id)\n\n if (pending.target === \"debugger\") {\n const { id: _upstreamId, ...rest } = message\n this.#sendDebugger?.({ ...rest, id: pending.originalId })\n return\n }\n\n if (isRecord(message.error)) {\n const detail =\n typeof message.error.message === \"string\"\n ? message.error.message\n : JSON.stringify(message.error)\n pending.reject(new Error(`CDP command failed: ${detail}`))\n return\n }\n pending.resolve(message.result ?? {})\n }\n\n close(error: Error = new Error(\"CDP connection closed\")): void {\n if (this.#closed) return\n this.#closed = true\n for (const pending of this.#pending.values()) {\n if (pending.target === \"internal\") pending.reject(error)\n }\n this.#pending.clear()\n this.#sendDebugger = undefined\n }\n}\n\nfunction expectMessage(value: unknown, label: string): CdpRecord {\n if (!isRecord(value)) throw new Error(`${label} must be an object`)\n return value\n}\n","import type { StructuredValue } from \"@worker-console/protocol\"\n\nimport {\n isRecord,\n type CdpPropertyDescriptor,\n type CdpRemoteObject,\n} from \"./types\"\n\nexport type { CdpRemoteObject } from \"./types\"\n\nexport interface CdpCommandClient {\n send(method: string, params?: Record<string, unknown>): Promise<unknown>\n}\n\nexport interface SnapshotLimits {\n maxDepth: number\n maxProperties: number\n maxStringLength: number\n}\n\nconst DEFAULT_LIMITS: SnapshotLimits = {\n maxDepth: 2,\n maxProperties: 50,\n maxStringLength: 2_000,\n}\n\nexport async function snapshotRemoteObjects(\n client: CdpCommandClient,\n values: CdpRemoteObject[],\n limits: SnapshotLimits = DEFAULT_LIMITS,\n): Promise<StructuredValue[]> {\n const seen = new Set<string>()\n const snapshots: StructuredValue[] = []\n for (const value of values) {\n snapshots.push(await snapshot(client, value, limits, 0, seen))\n }\n return snapshots\n}\n\nasync function snapshot(\n client: CdpCommandClient,\n remote: CdpRemoteObject,\n limits: SnapshotLimits,\n depth: number,\n seen: Set<string>,\n): Promise<StructuredValue> {\n const primitive = snapshotPrimitive(remote, limits.maxStringLength)\n if (primitive !== undefined) return primitive\n\n const objectId = remote.objectId\n if (objectId === undefined) {\n return {\n kind: \"unavailable\",\n description: truncate(remote.description ?? remote.className ?? remote.type, limits.maxStringLength),\n }\n }\n if (seen.has(objectId)) return { kind: \"circular\" }\n if (depth >= limits.maxDepth) {\n return {\n kind: \"unavailable\",\n description: `[${remote.className ?? remote.subtype ?? \"Object\"}: depth limit]`,\n }\n }\n\n seen.add(objectId)\n try {\n const descriptors = await getProperties(client, objectId)\n if (remote.subtype === \"error\") {\n return snapshotError(remote, descriptors, limits.maxStringLength)\n }\n if (remote.subtype === \"array\") {\n const indexed = descriptors\n .filter((descriptor) => /^\\d+$/.test(descriptor.name))\n .sort((left, right) => Number(left.name) - Number(right.name))\n const selected = indexed.slice(0, limits.maxProperties)\n const items: StructuredValue[] = []\n for (const descriptor of selected) {\n items.push(await snapshotDescriptor(client, descriptor, limits, depth + 1, seen))\n }\n return {\n kind: \"array\",\n items,\n truncated: indexed.length > selected.length,\n }\n }\n\n const visible = descriptors.filter((descriptor) => descriptor.name !== \"__proto__\")\n const selected = visible.slice(0, limits.maxProperties)\n const entries: Array<{ key: string; value: StructuredValue }> = []\n for (const descriptor of selected) {\n entries.push({\n key: descriptor.name,\n value: await snapshotDescriptor(client, descriptor, limits, depth + 1, seen),\n })\n }\n return {\n kind: \"object\",\n className: remote.className ?? \"Object\",\n entries,\n truncated: visible.length > selected.length,\n }\n } finally {\n seen.delete(objectId)\n await client.send(\"Runtime.releaseObject\", { objectId }).catch(() => undefined)\n }\n}\n\nasync function snapshotDescriptor(\n client: CdpCommandClient,\n descriptor: CdpPropertyDescriptor,\n limits: SnapshotLimits,\n depth: number,\n seen: Set<string>,\n): Promise<StructuredValue> {\n if (descriptor.value !== undefined) {\n return snapshot(client, descriptor.value, limits, depth, seen)\n }\n if (descriptor.get !== undefined && descriptor.set !== undefined) {\n return { kind: \"accessor\", label: \"[Getter/Setter]\" }\n }\n if (descriptor.get !== undefined) return { kind: \"accessor\", label: \"[Getter]\" }\n if (descriptor.set !== undefined) return { kind: \"accessor\", label: \"[Setter]\" }\n return { kind: \"unavailable\", description: \"[Unavailable]\" }\n}\n\nfunction snapshotPrimitive(\n remote: CdpRemoteObject,\n maxStringLength: number,\n): StructuredValue | undefined {\n if (remote.subtype === \"null\") {\n return { kind: \"primitive\", type: \"null\", value: \"null\" }\n }\n if (remote.type === \"object\" || remote.type === \"function\") return undefined\n\n const type = remote.type\n if (!isPrimitiveType(type)) {\n return {\n kind: \"unavailable\",\n description: truncate(remote.description ?? type, maxStringLength),\n }\n }\n const raw =\n remote.unserializableValue ??\n (type === \"undefined\" ? \"undefined\" : String(remote.value ?? remote.description ?? \"\"))\n return { kind: \"primitive\", type, value: truncate(raw, maxStringLength) }\n}\n\nfunction snapshotError(\n remote: CdpRemoteObject,\n descriptors: CdpPropertyDescriptor[],\n maxStringLength: number,\n): StructuredValue {\n const read = (name: string): string | undefined => {\n const value = descriptors.find((descriptor) => descriptor.name === name)?.value\n if (value?.type !== \"string\") return undefined\n return truncate(String(value.value ?? \"\"), maxStringLength)\n }\n const description = remote.description ?? \"Error\"\n const firstLine = description.split(\"\\n\", 1)[0] ?? \"Error\"\n const separator = firstLine.indexOf(\":\")\n const fallbackName = separator === -1 ? firstLine : firstLine.slice(0, separator)\n const fallbackMessage = separator === -1 ? \"\" : firstLine.slice(separator + 1).trim()\n const stack = read(\"stack\") ?? (description.includes(\"\\n\") ? description : undefined)\n return {\n kind: \"error\",\n name: read(\"name\") ?? fallbackName,\n message: read(\"message\") ?? fallbackMessage,\n ...(stack === undefined ? {} : { stack: truncate(stack, maxStringLength) }),\n }\n}\n\nasync function getProperties(\n client: CdpCommandClient,\n objectId: string,\n): Promise<CdpPropertyDescriptor[]> {\n const response = await client.send(\"Runtime.getProperties\", {\n objectId,\n ownProperties: true,\n generatePreview: false,\n })\n if (!isRecord(response) || !Array.isArray(response.result)) return []\n return response.result.filter(isPropertyDescriptor)\n}\n\nfunction isPropertyDescriptor(value: unknown): value is CdpPropertyDescriptor {\n return isRecord(value) && typeof value.name === \"string\"\n}\n\nfunction isPrimitiveType(value: string): value is \"string\" | \"number\" | \"boolean\" | \"undefined\" | \"bigint\" | \"symbol\" {\n return [\"string\", \"number\", \"boolean\", \"undefined\", \"bigint\", \"symbol\"].includes(value)\n}\n\nfunction truncate(value: string, maxLength: number): string {\n return value.length <= maxLength ? value : `${value.slice(0, Math.max(0, maxLength - 1))}…`\n}\n","import { readFile } from \"node:fs/promises\"\nimport { isAbsolute, relative, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nimport type { SourceLocation } from \"@worker-console/protocol\"\nimport { originalPositionFor, TraceMap } from \"@jridgewell/trace-mapping\"\n\nimport type { CdpCallFrame } from \"./cdp/types\"\n\nexport interface ScriptMetadata {\n scriptId: string\n url: string\n sourceMapURL: string\n}\n\nexport type SourceResolution =\n | { location: SourceLocation }\n | { diagnostic: string }\n\ninterface RegisteredScript {\n metadata: ScriptMetadata\n map?: Promise<TraceMap>\n}\n\nexport class SourceMapRegistry {\n readonly #root: string\n readonly #scripts = new Map<string, RegisteredScript>()\n\n constructor(root: string) {\n this.#root = resolve(root)\n }\n\n register(metadata: ScriptMetadata): void {\n this.#scripts.set(metadata.scriptId, { metadata })\n }\n\n clear(): void {\n this.#scripts.clear()\n }\n\n async resolve(frame: CdpCallFrame): Promise<SourceResolution> {\n const script = this.#scripts.get(frame.scriptId)\n if (script === undefined) {\n return { diagnostic: `No source map metadata for script ${frame.scriptId}` }\n }\n\n try {\n script.map ??= loadTraceMap(script.metadata)\n const map = await script.map\n const original = originalPositionFor(map, {\n line: frame.lineNumber + 1,\n column: frame.columnNumber,\n })\n if (original.source === null || original.line === null || original.column === null) {\n return { diagnostic: \"Source map did not contain the console call location\" }\n }\n const file = normalizeSourcePath(original.source, script.metadata.url)\n if (!isInside(this.#root, file)) {\n return { diagnostic: \"Original source is outside the Vite workspace\" }\n }\n return {\n location: {\n file,\n line: original.line,\n column: original.column + 1,\n },\n }\n } catch (error) {\n return {\n diagnostic: `Unable to resolve source map: ${error instanceof Error ? error.message : String(error)}`,\n }\n }\n }\n}\n\nasync function loadTraceMap(metadata: ScriptMetadata): Promise<TraceMap> {\n const source = await loadSourceMapText(metadata.sourceMapURL, metadata.url)\n return new TraceMap(source, metadata.url)\n}\n\nasync function loadSourceMapText(sourceMapURL: string, scriptURL: string): Promise<string> {\n if (sourceMapURL.startsWith(\"data:\")) return decodeDataUrl(sourceMapURL)\n const normalized = sourceMapURL.replace(/^wrangler-file:/, \"file:\")\n const url = new URL(normalized, scriptURL)\n if (url.protocol !== \"file:\") {\n throw new Error(`Unsupported source map protocol ${url.protocol}`)\n }\n return readFile(fileURLToPath(url), \"utf8\")\n}\n\nfunction decodeDataUrl(value: string): string {\n const comma = value.indexOf(\",\")\n if (comma === -1) throw new Error(\"Malformed source map data URL\")\n const metadata = value.slice(0, comma)\n const payload = value.slice(comma + 1)\n return metadata.endsWith(\";base64\")\n ? Buffer.from(payload, \"base64\").toString(\"utf8\")\n : decodeURIComponent(payload)\n}\n\nfunction normalizeSourcePath(source: string, scriptURL: string): string {\n const normalized = source.replace(/^wrangler-file:/, \"file:\")\n if (normalized.startsWith(\"file:\")) return resolve(fileURLToPath(normalized))\n if (normalized.startsWith(\"/@fs/\")) return resolve(normalized.slice(4))\n if (isAbsolute(normalized)) return resolve(normalized)\n if (scriptURL.startsWith(\"file:\")) {\n return resolve(fileURLToPath(new URL(normalized, scriptURL)))\n }\n return resolve(normalized)\n}\n\nfunction isInside(root: string, file: string): boolean {\n const path = relative(root, file)\n return path === \"\" || (!path.startsWith(\"..\") && !isAbsolute(path))\n}\n","import { randomUUID } from \"node:crypto\"\n\nimport type {\n BridgeServerMessage,\n ConsoleLevel,\n LogEntry,\n StructuredValue,\n} from \"@worker-console/protocol\"\nimport WebSocket from \"ws\"\n\nimport { CdpRouter } from \"./cdp/router\"\nimport { snapshotRemoteObjects, type CdpRemoteObject } from \"./cdp/serialize\"\nimport { isRecord, type CdpCallFrame, type CdpRecord } from \"./cdp/types\"\nimport type { InspectorTarget } from \"./discovery\"\nimport { SourceMapRegistry } from \"./source-maps\"\n\nexport interface WorkerSessionOptions {\n target: InspectorTarget\n root: string\n reconnect?: boolean\n onLog: (entry: LogEntry) => void\n onClear?: (generation: number) => void\n onState?: (state: Extract<BridgeServerMessage, { type: \"session.state\" }>) => void\n}\n\nexport class WorkerSession {\n readonly #options: WorkerSessionOptions\n readonly #maps: SourceMapRegistry\n #socket: WebSocket | undefined\n #router: CdpRouter | undefined\n #disposed = false\n #generation = 0\n #reconnectTimer: NodeJS.Timeout | undefined\n\n constructor(options: WorkerSessionOptions) {\n this.#options = options\n this.#maps = new SourceMapRegistry(options.root)\n }\n\n async start(): Promise<void> {\n this.#disposed = false\n await this.#connect()\n }\n\n attachDebugger(socket: WebSocket): void {\n const router = this.#router\n if (router === undefined) {\n socket.close(1013, \"Worker inspector is reconnecting\")\n return\n }\n try {\n router.attachDebugger((message) => {\n if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify(message))\n })\n } catch (error) {\n socket.close(1013, error instanceof Error ? error.message : \"Debugger already attached\")\n return\n }\n socket.on(\"message\", (data) => {\n try {\n router.routeFromDebugger(JSON.parse(data.toString()))\n } catch (error) {\n socket.close(1008, error instanceof Error ? error.message.slice(0, 120) : \"Invalid CDP message\")\n }\n })\n socket.once(\"close\", () => router.detachDebugger())\n }\n\n async dispose(): Promise<void> {\n this.#disposed = true\n if (this.#reconnectTimer !== undefined) clearTimeout(this.#reconnectTimer)\n this.#reconnectTimer = undefined\n this.#router?.close(new Error(\"Worker session disposed\"))\n this.#router = undefined\n const socket = this.#socket\n this.#socket = undefined\n if (socket !== undefined && socket.readyState < WebSocket.CLOSING) {\n socket.close(1000, \"Worker Console stopped\")\n }\n }\n\n async #connect(): Promise<void> {\n const socket = new WebSocket(this.#options.target.webSocketUrl)\n this.#socket = socket\n await new Promise<void>((resolve, reject) => {\n socket.once(\"open\", resolve)\n socket.once(\"error\", reject)\n })\n if (this.#disposed) {\n socket.close()\n return\n }\n\n const router = new CdpRouter(\n (message) => socket.send(JSON.stringify(message)),\n (event) => void this.#handleEvent(event),\n )\n this.#router = router\n socket.on(\"message\", (data) => {\n try {\n router.handleUpstream(JSON.parse(data.toString()))\n } catch (error) {\n this.#options.onState?.({\n type: \"session.state\",\n state: \"waiting\",\n message: error instanceof Error ? error.message : String(error),\n })\n }\n })\n socket.once(\"close\", () => this.#handleClose(router))\n socket.once(\"error\", () => this.#handleClose(router))\n\n this.#generation += 1\n this.#maps.clear()\n this.#options.onClear?.(this.#generation)\n await router.send(\"Runtime.enable\")\n await router.send(\"Debugger.enable\")\n this.#options.onState?.({ type: \"session.state\", state: \"connected\" })\n }\n\n #handleClose(router: CdpRouter): void {\n if (this.#router !== router) return\n router.close(new Error(\"Worker inspector disconnected\"))\n this.#router = undefined\n this.#options.onState?.({ type: \"session.state\", state: \"waiting\" })\n this.#scheduleReconnect(250)\n }\n\n #scheduleReconnect(delay: number): void {\n if (\n this.#disposed ||\n this.#options.reconnect === false ||\n this.#reconnectTimer !== undefined\n ) {\n return\n }\n this.#reconnectTimer = setTimeout(() => {\n this.#reconnectTimer = undefined\n void this.#connect().catch((error) => {\n this.#options.onState?.({\n type: \"session.state\",\n state: \"waiting\",\n message: error instanceof Error ? error.message : String(error),\n })\n this.#scheduleReconnect(1_000)\n })\n }, delay)\n }\n\n async #handleEvent(event: CdpRecord): Promise<void> {\n if (event.method === \"Debugger.scriptParsed\") {\n const params = event.params\n if (\n isRecord(params) &&\n typeof params.scriptId === \"string\" &&\n typeof params.url === \"string\" &&\n typeof params.sourceMapURL === \"string\" &&\n params.sourceMapURL.length > 0\n ) {\n this.#maps.register({\n scriptId: params.scriptId,\n url: params.url,\n sourceMapURL: params.sourceMapURL,\n })\n }\n return\n }\n if (event.method !== \"Runtime.consoleAPICalled\" || !isRecord(event.params)) return\n\n const params = event.params\n const args = Array.isArray(params.args) ? params.args.filter(isRemoteObject) : []\n const router = this.#router\n if (router === undefined) return\n const values = await snapshotRemoteObjects(router, args)\n const frame = firstCallFrame(params.stackTrace)\n const resolution =\n frame === undefined\n ? { diagnostic: \"Console event did not include a call frame\" }\n : await this.#maps.resolve(frame)\n const location = \"location\" in resolution ? resolution.location : undefined\n const diagnostic = \"diagnostic\" in resolution ? resolution.diagnostic : undefined\n this.#options.onLog({\n id: randomUUID(),\n worker: this.#options.target.name,\n level: normalizeLevel(params.type),\n timestamp: typeof params.timestamp === \"number\" ? params.timestamp : Date.now(),\n summary: summarizeValues(values),\n arguments: values,\n ...(location === undefined ? {} : { location }),\n ...(diagnostic === undefined ? {} : { diagnostic }),\n })\n }\n}\n\nfunction isRemoteObject(value: unknown): value is CdpRemoteObject {\n return isRecord(value) && typeof value.type === \"string\"\n}\n\nfunction firstCallFrame(value: unknown): CdpCallFrame | undefined {\n if (!isRecord(value) || !Array.isArray(value.callFrames)) return undefined\n for (const candidate of value.callFrames) {\n if (\n isRecord(candidate) &&\n typeof candidate.scriptId === \"string\" &&\n typeof candidate.url === \"string\" &&\n typeof candidate.lineNumber === \"number\" &&\n typeof candidate.columnNumber === \"number\"\n ) {\n return {\n scriptId: candidate.scriptId,\n url: candidate.url,\n lineNumber: candidate.lineNumber,\n columnNumber: candidate.columnNumber,\n }\n }\n }\n return undefined\n}\n\nfunction normalizeLevel(value: unknown): ConsoleLevel {\n switch (value) {\n case \"info\":\n case \"warn\":\n case \"error\":\n case \"debug\":\n return value\n case \"warning\":\n return \"warn\"\n default:\n return \"log\"\n }\n}\n\nexport function summarizeValues(values: StructuredValue[]): string {\n return values.map(summarizeValue).join(\" \")\n}\n\nfunction summarizeValue(value: StructuredValue): string {\n switch (value.kind) {\n case \"primitive\":\n return value.value\n case \"circular\":\n return \"[Circular]\"\n case \"accessor\":\n return value.label\n case \"unavailable\":\n return value.description\n case \"error\":\n return `${value.name}${value.message.length === 0 ? \"\" : `: ${value.message}`}`\n case \"array\":\n return `[${value.items.map(summarizeValue).join(\", \")}${value.truncated ? \", …\" : \"\"}]`\n case \"object\":\n return `{ ${value.entries.map((entry) => `${entry.key}: ${summarizeValue(entry.value)}`).join(\", \")}${value.truncated ? \", …\" : \"\"} }`\n }\n}\n","import type { AddressInfo } from \"node:net\"\n\nimport type { BridgeClientMessage, BridgeServerMessage } from \"@worker-console/protocol\"\nimport type { Plugin, ViteDevServer } from \"vite\"\n\nimport { createBridgeServer, type BridgeServer } from \"./bridge\"\nimport { discoverInspectorTargets } from \"./discovery\"\nimport { WorkerSession } from \"./worker-session\"\n\nexport interface WorkerConsoleOptions {\n inspectorUrl?: string\n sessionDirectory?: string\n inlineMaxLength?: number\n}\n\nexport function workerConsole(options: WorkerConsoleOptions = {}): Plugin {\n return {\n name: \"worker-console\",\n apply: \"serve\",\n enforce: \"post\",\n configureServer(server) {\n return () => {\n void startWorkerConsole(server, options).catch((error) => {\n server.config.logger.error(\n `[worker-console] ${error instanceof Error ? error.message : String(error)}`,\n )\n })\n }\n },\n }\n}\n\nasync function startWorkerConsole(\n server: ViteDevServer,\n options: WorkerConsoleOptions,\n): Promise<void> {\n const sessions = new Map<string, WorkerSession>()\n let paused = false\n let disposed = false\n let bridge: BridgeServer\n\n const onControl = (message: BridgeClientMessage): void => {\n if (message.type === \"capture.pause\") {\n paused = true\n bridge.broadcast({ type: \"session.state\", state: \"paused\" })\n } else if (message.type === \"capture.resume\") {\n paused = false\n bridge.broadcast({ type: \"session.state\", state: \"connected\" })\n } else if (message.type === \"ping\") {\n bridge.broadcast({ type: \"pong\", nonce: message.nonce })\n }\n }\n\n bridge = await createBridgeServer({\n root: server.config.root,\n ...(options.sessionDirectory === undefined\n ? {}\n : { sessionDirectory: options.sessionDirectory }),\n onControl,\n onDebugger(worker, socket) {\n const session = sessions.get(worker)\n if (session === undefined) socket.close(1013, \"Worker inspector is not ready\")\n else session.attachDebugger(socket)\n },\n })\n bridge.broadcast({ type: \"session.state\", state: \"waiting\" })\n\n const dispose = async (): Promise<void> => {\n if (disposed) return\n disposed = true\n await Promise.all([...sessions.values()].map((session) => session.dispose()))\n sessions.clear()\n await bridge.close()\n }\n server.httpServer?.once(\"close\", () => void dispose())\n\n const viteUrl = await waitForViteUrl(server)\n let attempt = 0\n while (!disposed) {\n try {\n const targets = await discoverInspectorTargets({\n viteUrl,\n ...(options.inspectorUrl === undefined ? {} : { inspectorUrl: options.inspectorUrl }),\n })\n await bridge.setWorkers(targets.map((target) => target.name))\n for (const target of targets) {\n if (sessions.has(target.name)) continue\n const session = new WorkerSession({\n target,\n root: server.config.root,\n onLog(entry) {\n if (!paused) bridge.broadcast({ type: \"log.entry\", entry })\n },\n onClear(generation) {\n bridge.broadcast({ type: \"logs.clear\", worker: target.name, generation })\n },\n onState(message) {\n bridge.broadcast(message)\n },\n })\n try {\n await session.start()\n sessions.set(target.name, session)\n } catch (error) {\n await session.dispose()\n throw error\n }\n }\n bridge.broadcast({ type: \"session.state\", state: \"connected\" })\n return\n } catch (error) {\n const message: BridgeServerMessage = {\n type: \"session.state\",\n state: String(error).includes(\"Too many clients\") ? \"inspector-occupied\" : \"waiting\",\n message: error instanceof Error ? error.message : String(error),\n }\n bridge.broadcast(message)\n const delay = [100, 200, 400, 800, 1_600][Math.min(attempt, 4)] ?? 1_600\n attempt += 1\n await new Promise((resolve) => setTimeout(resolve, delay))\n }\n }\n}\n\nasync function waitForViteUrl(server: ViteDevServer): Promise<string> {\n const httpServer = server.httpServer\n if (httpServer === null) throw new Error(\"Worker Console requires Vite's HTTP server\")\n if (!httpServer.listening) {\n await new Promise<void>((resolve) => httpServer.once(\"listening\", resolve))\n }\n const address = httpServer.address() as AddressInfo | null\n if (address === null) throw new Error(\"Vite did not expose a listening address\")\n const protocol = server.config.server.https ? \"https\" : \"http\"\n return `${protocol}://127.0.0.1:${address.port}`\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tippen",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Console Ninja-style inline console output for local Cloudflare Workers",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": ["cloudflare", "workers", "vite", "console", "vscode", "debugging"],
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=22"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist", "README.md", "LICENSE"],
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"import": "./dist/index.mjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "tsdown",
|
|
24
|
+
"check": "tsc --noEmit",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"prepublishOnly": "nub run check && nub run test && nub run build"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@jridgewell/trace-mapping": "0.3.31",
|
|
30
|
+
"ws": "8.21.0"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"vite": "^6.1.0 || ^7.0.0 || ^8.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@worker-console/protocol": "workspace:*",
|
|
37
|
+
"@types/ws": "8.18.1",
|
|
38
|
+
"tsdown": "0.16.3",
|
|
39
|
+
"vite": "8.1.4"
|
|
40
|
+
}
|
|
41
|
+
}
|