@saptools/cf-inspector 0.3.16 → 0.3.17
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/dist/cli.d.ts +1 -2
- package/dist/cli.js +1376 -1256
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +24 -41
- package/dist/index.js +723 -675
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ var init_types = __esm({
|
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
// src/wsTransport.ts
|
|
32
|
+
// src/cdp/wsTransport.ts
|
|
33
33
|
var wsTransport_exports = {};
|
|
34
34
|
__export(wsTransport_exports, {
|
|
35
35
|
wsTransportFactory: () => wsTransportFactory
|
|
@@ -37,6 +37,32 @@ __export(wsTransport_exports, {
|
|
|
37
37
|
import { WebSocket } from "ws";
|
|
38
38
|
async function wsTransportFactory(url) {
|
|
39
39
|
const socket = new WebSocket(url, { perMessageDeflate: false });
|
|
40
|
+
await waitForOpen(socket, url);
|
|
41
|
+
const wrappers = /* @__PURE__ */ new WeakMap();
|
|
42
|
+
return {
|
|
43
|
+
send(payload) {
|
|
44
|
+
socket.send(payload);
|
|
45
|
+
},
|
|
46
|
+
close() {
|
|
47
|
+
socket.close();
|
|
48
|
+
},
|
|
49
|
+
get readyState() {
|
|
50
|
+
return socket.readyState;
|
|
51
|
+
},
|
|
52
|
+
on(event, listener) {
|
|
53
|
+
const wrapped = wrapListener(event, listener, wrappers);
|
|
54
|
+
socket.on(event, wrapped);
|
|
55
|
+
},
|
|
56
|
+
off(event, listener) {
|
|
57
|
+
const wrapped = wrappers.get(listener);
|
|
58
|
+
if (!wrapped) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
socket.off(event, wrapped);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
async function waitForOpen(socket, url) {
|
|
40
66
|
await new Promise((resolve, reject) => {
|
|
41
67
|
const onOpen = () => {
|
|
42
68
|
socket.off("error", onError);
|
|
@@ -54,64 +80,30 @@ async function wsTransportFactory(url) {
|
|
|
54
80
|
socket.once("open", onOpen);
|
|
55
81
|
socket.once("error", onError);
|
|
56
82
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
83
|
+
}
|
|
84
|
+
function wrapListener(event, listener, wrappers) {
|
|
85
|
+
if (event === "message") {
|
|
86
|
+
const wrapped2 = (data) => {
|
|
60
87
|
listener(data.toString("utf8"));
|
|
61
88
|
};
|
|
62
|
-
wrappers.set(listener,
|
|
63
|
-
return
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const
|
|
89
|
+
wrappers.set(listener, wrapped2);
|
|
90
|
+
return wrapped2;
|
|
91
|
+
}
|
|
92
|
+
if (event === "close") {
|
|
93
|
+
const wrapped2 = () => {
|
|
67
94
|
listener();
|
|
68
95
|
};
|
|
69
|
-
wrappers.set(listener,
|
|
70
|
-
return
|
|
71
|
-
}
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
listener(err);
|
|
75
|
-
};
|
|
76
|
-
wrappers.set(listener, wrapped);
|
|
77
|
-
return wrapped;
|
|
78
|
-
};
|
|
79
|
-
return {
|
|
80
|
-
send(payload) {
|
|
81
|
-
socket.send(payload);
|
|
82
|
-
},
|
|
83
|
-
close() {
|
|
84
|
-
socket.close();
|
|
85
|
-
},
|
|
86
|
-
get readyState() {
|
|
87
|
-
return socket.readyState;
|
|
88
|
-
},
|
|
89
|
-
on(event, listener) {
|
|
90
|
-
if (event === "message") {
|
|
91
|
-
socket.on("message", wrapMessage(listener));
|
|
92
|
-
} else if (event === "close") {
|
|
93
|
-
socket.on("close", wrapClose(listener));
|
|
94
|
-
} else {
|
|
95
|
-
socket.on("error", wrapError(listener));
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
off(event, listener) {
|
|
99
|
-
const wrapped = wrappers.get(listener);
|
|
100
|
-
if (!wrapped) {
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (event === "message") {
|
|
104
|
-
socket.off("message", wrapped);
|
|
105
|
-
} else if (event === "close") {
|
|
106
|
-
socket.off("close", wrapped);
|
|
107
|
-
} else {
|
|
108
|
-
socket.off("error", wrapped);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
96
|
+
wrappers.set(listener, wrapped2);
|
|
97
|
+
return wrapped2;
|
|
98
|
+
}
|
|
99
|
+
const wrapped = (err) => {
|
|
100
|
+
listener(err);
|
|
111
101
|
};
|
|
102
|
+
wrappers.set(listener, wrapped);
|
|
103
|
+
return wrapped;
|
|
112
104
|
}
|
|
113
105
|
var init_wsTransport = __esm({
|
|
114
|
-
"src/wsTransport.ts"() {
|
|
106
|
+
"src/cdp/wsTransport.ts"() {
|
|
115
107
|
"use strict";
|
|
116
108
|
init_types();
|
|
117
109
|
}
|
|
@@ -259,220 +251,137 @@ function buildBreakpointUrlRegex(input) {
|
|
|
259
251
|
}
|
|
260
252
|
}
|
|
261
253
|
|
|
262
|
-
// src/inspector.ts
|
|
263
|
-
import { performance } from "perf_hooks";
|
|
264
|
-
|
|
265
|
-
// src/cdp.ts
|
|
254
|
+
// src/inspector/breakpoints.ts
|
|
266
255
|
init_types();
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
function
|
|
270
|
-
|
|
271
|
-
const value = JSON.parse(raw);
|
|
272
|
-
if (typeof value !== "object" || value === null) {
|
|
273
|
-
return void 0;
|
|
274
|
-
}
|
|
275
|
-
return value;
|
|
276
|
-
} catch {
|
|
277
|
-
return void 0;
|
|
278
|
-
}
|
|
256
|
+
|
|
257
|
+
// src/inspector/conversions.ts
|
|
258
|
+
function asString(value, fallback = "") {
|
|
259
|
+
return typeof value === "string" ? value : fallback;
|
|
279
260
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
return mod.wsTransportFactory;
|
|
261
|
+
function asNumber(value, fallback = 0) {
|
|
262
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
283
263
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
closeReason;
|
|
292
|
-
handleMessage = (raw) => {
|
|
293
|
-
const parsed = parseMessage(raw);
|
|
294
|
-
if (!parsed) {
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
|
-
if (typeof parsed.id === "number") {
|
|
298
|
-
const pending = this.pending.get(parsed.id);
|
|
299
|
-
if (!pending) {
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
this.pending.delete(parsed.id);
|
|
303
|
-
clearTimeout(pending.timer);
|
|
304
|
-
if (parsed.error) {
|
|
305
|
-
pending.reject(
|
|
306
|
-
new CfInspectorError(
|
|
307
|
-
"CDP_REQUEST_FAILED",
|
|
308
|
-
`CDP request ${parsed.id.toString()} failed: ${parsed.error.message}`,
|
|
309
|
-
JSON.stringify(parsed.error)
|
|
310
|
-
)
|
|
311
|
-
);
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
pending.resolve(parsed.result);
|
|
315
|
-
return;
|
|
264
|
+
function toResolvedLocations(value) {
|
|
265
|
+
if (!Array.isArray(value)) {
|
|
266
|
+
return [];
|
|
267
|
+
}
|
|
268
|
+
return value.flatMap((entry) => {
|
|
269
|
+
if (typeof entry !== "object" || entry === null) {
|
|
270
|
+
return [];
|
|
316
271
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
272
|
+
const candidate = entry;
|
|
273
|
+
const scriptId = asString(candidate.scriptId);
|
|
274
|
+
if (scriptId.length === 0) {
|
|
275
|
+
return [];
|
|
320
276
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
constructor(transport, requestTimeoutMs) {
|
|
331
|
-
this.transport = transport;
|
|
332
|
-
this.requestTimeoutMs = requestTimeoutMs;
|
|
333
|
-
transport.on("message", this.handleMessage);
|
|
334
|
-
transport.on("close", this.handleClose);
|
|
335
|
-
transport.on("error", this.handleError);
|
|
336
|
-
}
|
|
337
|
-
static async connect(options) {
|
|
338
|
-
const factory = options.transportFactory ?? await loadDefaultFactory();
|
|
339
|
-
const transport = await factory(options.url);
|
|
340
|
-
return new _CdpClient(transport, options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
|
|
277
|
+
const url = typeof candidate.url === "string" ? candidate.url : void 0;
|
|
278
|
+
const lineNumber = asNumber(candidate.lineNumber);
|
|
279
|
+
const result = url === void 0 ? { scriptId, lineNumber, columnNumber: asNumber(candidate.columnNumber) } : { scriptId, url, lineNumber, columnNumber: asNumber(candidate.columnNumber) };
|
|
280
|
+
return [result];
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
function toScopeChain(value) {
|
|
284
|
+
if (!Array.isArray(value)) {
|
|
285
|
+
return [];
|
|
341
286
|
}
|
|
342
|
-
|
|
343
|
-
if (
|
|
344
|
-
|
|
287
|
+
return value.flatMap((entry) => {
|
|
288
|
+
if (typeof entry !== "object" || entry === null) {
|
|
289
|
+
return [];
|
|
345
290
|
}
|
|
346
|
-
const
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
this.pending.delete(id);
|
|
351
|
-
reject(
|
|
352
|
-
new CfInspectorError(
|
|
353
|
-
"CDP_REQUEST_FAILED",
|
|
354
|
-
`CDP method ${method} timed out after ${this.requestTimeoutMs.toString()}ms`
|
|
355
|
-
)
|
|
356
|
-
);
|
|
357
|
-
}, this.requestTimeoutMs);
|
|
358
|
-
this.pending.set(id, {
|
|
359
|
-
resolve: (value) => {
|
|
360
|
-
resolve(value);
|
|
361
|
-
},
|
|
362
|
-
reject,
|
|
363
|
-
timer
|
|
364
|
-
});
|
|
365
|
-
try {
|
|
366
|
-
this.transport.send(payload);
|
|
367
|
-
} catch (err) {
|
|
368
|
-
clearTimeout(timer);
|
|
369
|
-
this.pending.delete(id);
|
|
370
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
371
|
-
reject(new CfInspectorError("CDP_REQUEST_FAILED", `Failed to send ${method}: ${message}`));
|
|
372
|
-
}
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
on(method, listener) {
|
|
376
|
-
this.emitter.on(method, listener);
|
|
377
|
-
return () => {
|
|
378
|
-
this.emitter.off(method, listener);
|
|
379
|
-
};
|
|
380
|
-
}
|
|
381
|
-
async waitFor(method, options = {
|
|
382
|
-
timeoutMs: this.requestTimeoutMs
|
|
383
|
-
}) {
|
|
384
|
-
if (this.closed) {
|
|
385
|
-
throw this.closeReason ?? new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Connection closed");
|
|
291
|
+
const candidate = entry;
|
|
292
|
+
const type = asString(candidate.type);
|
|
293
|
+
if (type.length === 0) {
|
|
294
|
+
return [];
|
|
386
295
|
}
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return;
|
|
397
|
-
}
|
|
398
|
-
const params = raw;
|
|
399
|
-
if (options.predicate && !options.predicate(params)) {
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
settled = true;
|
|
403
|
-
cleanup();
|
|
404
|
-
resolve(params);
|
|
405
|
-
});
|
|
406
|
-
const offClose = this.onClose((err) => {
|
|
407
|
-
if (settled) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
settled = true;
|
|
411
|
-
cleanup();
|
|
412
|
-
reject(err);
|
|
413
|
-
});
|
|
414
|
-
const timer = setTimeout(() => {
|
|
415
|
-
if (settled) {
|
|
416
|
-
return;
|
|
417
|
-
}
|
|
418
|
-
settled = true;
|
|
419
|
-
cleanup();
|
|
420
|
-
reject(
|
|
421
|
-
new CfInspectorError(
|
|
422
|
-
"BREAKPOINT_NOT_HIT",
|
|
423
|
-
`Timed out waiting for ${method} after ${options.timeoutMs.toString()}ms`
|
|
424
|
-
)
|
|
425
|
-
);
|
|
426
|
-
}, options.timeoutMs);
|
|
427
|
-
});
|
|
296
|
+
const objectId = typeof candidate.object?.objectId === "string" ? candidate.object.objectId : void 0;
|
|
297
|
+
const name = typeof candidate.name === "string" ? candidate.name : void 0;
|
|
298
|
+
const base = name === void 0 ? { type } : { type, name };
|
|
299
|
+
return [objectId === void 0 ? base : { ...base, objectId }];
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
function toCallFrames(value) {
|
|
303
|
+
if (!Array.isArray(value)) {
|
|
304
|
+
return [];
|
|
428
305
|
}
|
|
429
|
-
|
|
430
|
-
if (
|
|
431
|
-
|
|
432
|
-
queueMicrotask(() => {
|
|
433
|
-
listener(reason);
|
|
434
|
-
});
|
|
435
|
-
return () => {
|
|
436
|
-
};
|
|
306
|
+
return value.flatMap((entry) => {
|
|
307
|
+
if (typeof entry !== "object" || entry === null) {
|
|
308
|
+
return [];
|
|
437
309
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
310
|
+
const candidate = entry;
|
|
311
|
+
const callFrameId = asString(candidate.callFrameId);
|
|
312
|
+
if (callFrameId.length === 0) {
|
|
313
|
+
return [];
|
|
314
|
+
}
|
|
315
|
+
const url = typeof candidate.url === "string" ? candidate.url : void 0;
|
|
316
|
+
const base = {
|
|
317
|
+
callFrameId,
|
|
318
|
+
functionName: asString(candidate.functionName),
|
|
319
|
+
lineNumber: asNumber(candidate.location?.lineNumber),
|
|
320
|
+
columnNumber: asNumber(candidate.location?.columnNumber),
|
|
321
|
+
scopeChain: toScopeChain(candidate.scopeChain)
|
|
441
322
|
};
|
|
323
|
+
return [url === void 0 ? base : { ...base, url }];
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
function toPauseEvent(params, receivedAtMs) {
|
|
327
|
+
return {
|
|
328
|
+
reason: asString(params.reason),
|
|
329
|
+
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
330
|
+
callFrames: toCallFrames(params.callFrames),
|
|
331
|
+
receivedAtMs
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function topFrameLocation(pause) {
|
|
335
|
+
const top = pause.callFrames[0];
|
|
336
|
+
if (top === void 0) {
|
|
337
|
+
return "(no call frame)";
|
|
442
338
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
339
|
+
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
340
|
+
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
341
|
+
}
|
|
342
|
+
function pauseDetail(pause) {
|
|
343
|
+
return JSON.stringify({
|
|
344
|
+
reason: pause.reason,
|
|
345
|
+
hitBreakpoints: pause.hitBreakpoints,
|
|
346
|
+
topFrame: topFrameLocation(pause)
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/inspector/breakpoints.ts
|
|
351
|
+
async function setBreakpoint(session, input) {
|
|
352
|
+
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
353
|
+
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
354
|
+
const params = {
|
|
355
|
+
lineNumber: input.line - 1,
|
|
356
|
+
urlRegex
|
|
357
|
+
};
|
|
358
|
+
if (input.condition !== void 0 && input.condition.length > 0) {
|
|
359
|
+
params["condition"] = input.condition;
|
|
458
360
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
this.pending.clear();
|
|
470
|
-
this.emitter.emit("__close__", reason);
|
|
471
|
-
this.emitter.removeAllListeners();
|
|
361
|
+
const result = await session.client.send(
|
|
362
|
+
"Debugger.setBreakpointByUrl",
|
|
363
|
+
params
|
|
364
|
+
);
|
|
365
|
+
const breakpointId = asString(result.breakpointId);
|
|
366
|
+
if (breakpointId.length === 0) {
|
|
367
|
+
throw new CfInspectorError(
|
|
368
|
+
"CDP_REQUEST_FAILED",
|
|
369
|
+
`setBreakpointByUrl did not return a breakpointId for ${input.file}:${input.line.toString()}`
|
|
370
|
+
);
|
|
472
371
|
}
|
|
473
|
-
|
|
372
|
+
return {
|
|
373
|
+
breakpointId,
|
|
374
|
+
file: input.file,
|
|
375
|
+
line: input.line,
|
|
376
|
+
urlRegex,
|
|
377
|
+
resolvedLocations: toResolvedLocations(result.locations)
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
async function removeBreakpoint(session, breakpointId) {
|
|
381
|
+
await session.client.send("Debugger.removeBreakpoint", { breakpointId });
|
|
382
|
+
}
|
|
474
383
|
|
|
475
|
-
// src/
|
|
384
|
+
// src/inspector/discovery.ts
|
|
476
385
|
init_types();
|
|
477
386
|
import { request } from "http";
|
|
478
387
|
async function fetchJson(url, timeoutMs) {
|
|
@@ -484,25 +393,13 @@ async function fetchJson(url, timeoutMs) {
|
|
|
484
393
|
});
|
|
485
394
|
res.on("end", () => {
|
|
486
395
|
try {
|
|
487
|
-
|
|
488
|
-
resolve(JSON.parse(text));
|
|
396
|
+
resolve(parseJsonResponse(chunks));
|
|
489
397
|
} catch (err) {
|
|
490
|
-
|
|
491
|
-
reject(
|
|
492
|
-
new CfInspectorError(
|
|
493
|
-
"INSPECTOR_DISCOVERY_FAILED",
|
|
494
|
-
`Failed to parse inspector discovery response from ${url}: ${message}`
|
|
495
|
-
)
|
|
496
|
-
);
|
|
398
|
+
reject(parseDiscoveryError(url, err));
|
|
497
399
|
}
|
|
498
400
|
});
|
|
499
401
|
res.on("error", (err) => {
|
|
500
|
-
reject(
|
|
501
|
-
new CfInspectorError(
|
|
502
|
-
"INSPECTOR_DISCOVERY_FAILED",
|
|
503
|
-
`Inspector discovery response error: ${err.message}`
|
|
504
|
-
)
|
|
505
|
-
);
|
|
402
|
+
reject(newDiscoveryError(`Inspector discovery response error: ${err.message}`));
|
|
506
403
|
});
|
|
507
404
|
});
|
|
508
405
|
req.setTimeout(timeoutMs, () => {
|
|
@@ -524,6 +421,17 @@ async function fetchJson(url, timeoutMs) {
|
|
|
524
421
|
req.end();
|
|
525
422
|
});
|
|
526
423
|
}
|
|
424
|
+
function parseJsonResponse(chunks) {
|
|
425
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
426
|
+
return JSON.parse(text);
|
|
427
|
+
}
|
|
428
|
+
function parseDiscoveryError(url, err) {
|
|
429
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
430
|
+
return newDiscoveryError(`Failed to parse inspector discovery response from ${url}: ${message}`);
|
|
431
|
+
}
|
|
432
|
+
function newDiscoveryError(message) {
|
|
433
|
+
return new CfInspectorError("INSPECTOR_DISCOVERY_FAILED", message);
|
|
434
|
+
}
|
|
527
435
|
function toInspectorTarget(value, source) {
|
|
528
436
|
if (typeof value !== "object" || value === null) {
|
|
529
437
|
throw new CfInspectorError(
|
|
@@ -591,202 +499,18 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
|
591
499
|
return { browser, protocolVersion };
|
|
592
500
|
}
|
|
593
501
|
|
|
594
|
-
// src/inspector.ts
|
|
502
|
+
// src/inspector/pause.ts
|
|
595
503
|
init_types();
|
|
596
|
-
|
|
597
|
-
var DEFAULT_HOST = "127.0.0.1";
|
|
598
|
-
async function connectInspector(options) {
|
|
599
|
-
const host = options.host ?? DEFAULT_HOST;
|
|
600
|
-
const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
601
|
-
const targets = await discoverInspectorTargets(host, options.port, connectTimeoutMs);
|
|
602
|
-
const target = targets[0];
|
|
603
|
-
if (!target) {
|
|
604
|
-
throw new CfInspectorError(
|
|
605
|
-
"INSPECTOR_DISCOVERY_FAILED",
|
|
606
|
-
`No inspector targets available on ${host}:${options.port.toString()}`
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
const client = await CdpClient.connect({ url: target.webSocketDebuggerUrl });
|
|
610
|
-
const scripts = /* @__PURE__ */ new Map();
|
|
611
|
-
client.on("Debugger.scriptParsed", (raw) => {
|
|
612
|
-
const params = raw;
|
|
613
|
-
const scriptId = asString(params.scriptId);
|
|
614
|
-
const url = asString(params.url);
|
|
615
|
-
if (scriptId.length === 0) {
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
scripts.set(scriptId, { scriptId, url });
|
|
619
|
-
});
|
|
620
|
-
const PAUSE_BUFFER_LIMIT = 32;
|
|
621
|
-
const pauseBuffer = [];
|
|
622
|
-
const pauseWaitGate = { active: false };
|
|
623
|
-
const debuggerState = {};
|
|
624
|
-
client.on("Debugger.paused", (raw) => {
|
|
625
|
-
if (pauseWaitGate.active) {
|
|
626
|
-
return;
|
|
627
|
-
}
|
|
628
|
-
const params = raw;
|
|
629
|
-
const event = toPauseEvent(params, performance.now());
|
|
630
|
-
if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
|
|
631
|
-
pauseBuffer.shift();
|
|
632
|
-
}
|
|
633
|
-
pauseBuffer.push(event);
|
|
634
|
-
});
|
|
635
|
-
client.on("Debugger.resumed", () => {
|
|
636
|
-
debuggerState.lastResumedAtMs = performance.now();
|
|
637
|
-
});
|
|
638
|
-
await client.send("Runtime.enable");
|
|
639
|
-
await client.send("Debugger.enable");
|
|
640
|
-
return {
|
|
641
|
-
client,
|
|
642
|
-
target,
|
|
643
|
-
scripts,
|
|
644
|
-
pauseBuffer,
|
|
645
|
-
pauseWaitGate,
|
|
646
|
-
debuggerState,
|
|
647
|
-
dispose: async () => {
|
|
648
|
-
try {
|
|
649
|
-
await client.send("Debugger.disable");
|
|
650
|
-
} catch {
|
|
651
|
-
}
|
|
652
|
-
client.dispose();
|
|
653
|
-
}
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
function asString(value, fallback = "") {
|
|
657
|
-
return typeof value === "string" ? value : fallback;
|
|
658
|
-
}
|
|
659
|
-
function asNumber(value, fallback = 0) {
|
|
660
|
-
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
661
|
-
}
|
|
662
|
-
function toResolvedLocations(value) {
|
|
663
|
-
if (!Array.isArray(value)) {
|
|
664
|
-
return [];
|
|
665
|
-
}
|
|
666
|
-
return value.flatMap((entry) => {
|
|
667
|
-
if (typeof entry !== "object" || entry === null) {
|
|
668
|
-
return [];
|
|
669
|
-
}
|
|
670
|
-
const candidate = entry;
|
|
671
|
-
const scriptId = asString(candidate.scriptId);
|
|
672
|
-
if (scriptId.length === 0) {
|
|
673
|
-
return [];
|
|
674
|
-
}
|
|
675
|
-
const url = typeof candidate.url === "string" ? candidate.url : void 0;
|
|
676
|
-
const lineNumber = asNumber(candidate.lineNumber);
|
|
677
|
-
const result = url === void 0 ? { scriptId, lineNumber, columnNumber: asNumber(candidate.columnNumber) } : { scriptId, url, lineNumber, columnNumber: asNumber(candidate.columnNumber) };
|
|
678
|
-
return [result];
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
async function setBreakpoint(session, input) {
|
|
682
|
-
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
683
|
-
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
684
|
-
const params = {
|
|
685
|
-
lineNumber: input.line - 1,
|
|
686
|
-
urlRegex
|
|
687
|
-
};
|
|
688
|
-
if (input.condition !== void 0 && input.condition.length > 0) {
|
|
689
|
-
params["condition"] = input.condition;
|
|
690
|
-
}
|
|
691
|
-
const result = await session.client.send(
|
|
692
|
-
"Debugger.setBreakpointByUrl",
|
|
693
|
-
params
|
|
694
|
-
);
|
|
695
|
-
const breakpointId = asString(result.breakpointId);
|
|
696
|
-
if (breakpointId.length === 0) {
|
|
697
|
-
throw new CfInspectorError(
|
|
698
|
-
"CDP_REQUEST_FAILED",
|
|
699
|
-
`setBreakpointByUrl did not return a breakpointId for ${input.file}:${input.line.toString()}`
|
|
700
|
-
);
|
|
701
|
-
}
|
|
702
|
-
return {
|
|
703
|
-
breakpointId,
|
|
704
|
-
file: input.file,
|
|
705
|
-
line: input.line,
|
|
706
|
-
urlRegex,
|
|
707
|
-
resolvedLocations: toResolvedLocations(result.locations)
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
async function removeBreakpoint(session, breakpointId) {
|
|
711
|
-
await session.client.send("Debugger.removeBreakpoint", { breakpointId });
|
|
712
|
-
}
|
|
713
|
-
function toScopeChain(value) {
|
|
714
|
-
if (!Array.isArray(value)) {
|
|
715
|
-
return [];
|
|
716
|
-
}
|
|
717
|
-
return value.flatMap((entry) => {
|
|
718
|
-
if (typeof entry !== "object" || entry === null) {
|
|
719
|
-
return [];
|
|
720
|
-
}
|
|
721
|
-
const candidate = entry;
|
|
722
|
-
const type = asString(candidate.type);
|
|
723
|
-
if (type.length === 0) {
|
|
724
|
-
return [];
|
|
725
|
-
}
|
|
726
|
-
const objectId = typeof candidate.object?.objectId === "string" ? candidate.object.objectId : void 0;
|
|
727
|
-
const name = typeof candidate.name === "string" ? candidate.name : void 0;
|
|
728
|
-
const base = name === void 0 ? { type } : { type, name };
|
|
729
|
-
return [objectId === void 0 ? base : { ...base, objectId }];
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
function toCallFrames(value) {
|
|
733
|
-
if (!Array.isArray(value)) {
|
|
734
|
-
return [];
|
|
735
|
-
}
|
|
736
|
-
return value.flatMap((entry) => {
|
|
737
|
-
if (typeof entry !== "object" || entry === null) {
|
|
738
|
-
return [];
|
|
739
|
-
}
|
|
740
|
-
const candidate = entry;
|
|
741
|
-
const callFrameId = asString(candidate.callFrameId);
|
|
742
|
-
if (callFrameId.length === 0) {
|
|
743
|
-
return [];
|
|
744
|
-
}
|
|
745
|
-
const url = typeof candidate.url === "string" ? candidate.url : void 0;
|
|
746
|
-
const lineNumber = asNumber(candidate.location?.lineNumber);
|
|
747
|
-
const columnNumber = asNumber(candidate.location?.columnNumber);
|
|
748
|
-
const base = {
|
|
749
|
-
callFrameId,
|
|
750
|
-
functionName: asString(candidate.functionName),
|
|
751
|
-
lineNumber,
|
|
752
|
-
columnNumber,
|
|
753
|
-
scopeChain: toScopeChain(candidate.scopeChain)
|
|
754
|
-
};
|
|
755
|
-
return [url === void 0 ? base : { ...base, url }];
|
|
756
|
-
});
|
|
757
|
-
}
|
|
504
|
+
import { performance } from "perf_hooks";
|
|
758
505
|
function pauseMatches(pause, breakpointIds) {
|
|
759
506
|
if (breakpointIds === void 0 || breakpointIds.length === 0) {
|
|
760
507
|
return true;
|
|
761
508
|
}
|
|
762
509
|
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
763
510
|
}
|
|
764
|
-
function toPauseEvent(params, receivedAtMs) {
|
|
765
|
-
return {
|
|
766
|
-
reason: asString(params.reason),
|
|
767
|
-
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
768
|
-
callFrames: toCallFrames(params.callFrames),
|
|
769
|
-
receivedAtMs
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
511
|
function remainingUntil(deadlineMs) {
|
|
773
512
|
return Math.max(0, deadlineMs - performance.now());
|
|
774
513
|
}
|
|
775
|
-
function topFrameLocation(pause) {
|
|
776
|
-
const top = pause.callFrames[0];
|
|
777
|
-
if (top === void 0) {
|
|
778
|
-
return "(no call frame)";
|
|
779
|
-
}
|
|
780
|
-
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
781
|
-
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
782
|
-
}
|
|
783
|
-
function pauseDetail(pause) {
|
|
784
|
-
return JSON.stringify({
|
|
785
|
-
reason: pause.reason,
|
|
786
|
-
hitBreakpoints: pause.hitBreakpoints,
|
|
787
|
-
topFrame: topFrameLocation(pause)
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
514
|
function hasResumedSincePause(session, pause) {
|
|
791
515
|
const pauseAt = pause.receivedAtMs;
|
|
792
516
|
const resumedAt = session.debuggerState.lastResumedAtMs;
|
|
@@ -851,32 +575,38 @@ async function waitForPause(session, options) {
|
|
|
851
575
|
}
|
|
852
576
|
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
853
577
|
}
|
|
854
|
-
const
|
|
855
|
-
if (
|
|
856
|
-
|
|
857
|
-
}
|
|
858
|
-
session.pauseWaitGate.active = true;
|
|
859
|
-
let receivedAtMs;
|
|
860
|
-
let params;
|
|
861
|
-
try {
|
|
862
|
-
params = await session.client.waitFor("Debugger.paused", {
|
|
863
|
-
timeoutMs: remainingMs,
|
|
864
|
-
predicate: () => {
|
|
865
|
-
receivedAtMs = performance.now();
|
|
866
|
-
return true;
|
|
867
|
-
}
|
|
868
|
-
});
|
|
869
|
-
} finally {
|
|
870
|
-
session.pauseWaitGate.active = false;
|
|
871
|
-
}
|
|
872
|
-
const pause = toPauseEvent(params, receivedAtMs ?? performance.now());
|
|
873
|
-
if (pauseMatches(pause, options.breakpointIds)) {
|
|
874
|
-
return pause;
|
|
578
|
+
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
579
|
+
if (pauseMatches(pause, options.breakpointIds)) {
|
|
580
|
+
return pause;
|
|
875
581
|
}
|
|
876
582
|
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
877
583
|
}
|
|
878
584
|
throwBreakpointTimeout(options.timeoutMs);
|
|
879
585
|
}
|
|
586
|
+
async function waitForLivePause(session, options, deadlineMs) {
|
|
587
|
+
const remainingMs = remainingUntil(deadlineMs);
|
|
588
|
+
if (remainingMs <= 0) {
|
|
589
|
+
throwBreakpointTimeout(options.timeoutMs);
|
|
590
|
+
}
|
|
591
|
+
session.pauseWaitGate.active = true;
|
|
592
|
+
let receivedAtMs;
|
|
593
|
+
let params;
|
|
594
|
+
try {
|
|
595
|
+
params = await session.client.waitFor("Debugger.paused", {
|
|
596
|
+
timeoutMs: remainingMs,
|
|
597
|
+
predicate: () => {
|
|
598
|
+
receivedAtMs = performance.now();
|
|
599
|
+
return true;
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
} finally {
|
|
603
|
+
session.pauseWaitGate.active = false;
|
|
604
|
+
}
|
|
605
|
+
return toPauseEvent(params, receivedAtMs ?? performance.now());
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/inspector/runtime.ts
|
|
609
|
+
init_types();
|
|
880
610
|
async function resume(session) {
|
|
881
611
|
await session.client.send("Debugger.resume");
|
|
882
612
|
}
|
|
@@ -925,194 +655,328 @@ async function getProperties(session, objectId) {
|
|
|
925
655
|
return result.result;
|
|
926
656
|
}
|
|
927
657
|
|
|
928
|
-
// src/
|
|
658
|
+
// src/inspector/session.ts
|
|
659
|
+
import { performance as performance2 } from "perf_hooks";
|
|
660
|
+
|
|
661
|
+
// src/cdp/client.ts
|
|
929
662
|
init_types();
|
|
930
|
-
|
|
931
|
-
var
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
with: 5,
|
|
942
|
-
module: 6,
|
|
943
|
-
script: 7
|
|
944
|
-
};
|
|
945
|
-
function buildDescribed(value, type, objectId) {
|
|
946
|
-
const base = { value };
|
|
947
|
-
if (type !== void 0) {
|
|
948
|
-
base.type = type;
|
|
949
|
-
}
|
|
950
|
-
if (objectId !== void 0) {
|
|
951
|
-
base.objectId = objectId;
|
|
663
|
+
import { EventEmitter } from "events";
|
|
664
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 15e3;
|
|
665
|
+
function parseMessage(raw) {
|
|
666
|
+
try {
|
|
667
|
+
const value = JSON.parse(raw);
|
|
668
|
+
if (typeof value !== "object" || value === null) {
|
|
669
|
+
return void 0;
|
|
670
|
+
}
|
|
671
|
+
return value;
|
|
672
|
+
} catch {
|
|
673
|
+
return void 0;
|
|
952
674
|
}
|
|
953
|
-
return base;
|
|
954
675
|
}
|
|
955
|
-
function
|
|
956
|
-
const
|
|
957
|
-
|
|
958
|
-
|
|
676
|
+
async function loadDefaultFactory() {
|
|
677
|
+
const mod = await Promise.resolve().then(() => (init_wsTransport(), wsTransport_exports));
|
|
678
|
+
return mod.wsTransportFactory;
|
|
679
|
+
}
|
|
680
|
+
var CdpClient = class _CdpClient {
|
|
681
|
+
transport;
|
|
682
|
+
requestTimeoutMs;
|
|
683
|
+
pending = /* @__PURE__ */ new Map();
|
|
684
|
+
emitter = new EventEmitter();
|
|
685
|
+
nextId = 1;
|
|
686
|
+
closed = false;
|
|
687
|
+
closeReason;
|
|
688
|
+
handleMessage = (raw) => {
|
|
689
|
+
const parsed = parseMessage(raw);
|
|
690
|
+
if (!parsed) {
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
if (typeof parsed.id === "number") {
|
|
694
|
+
this.handleResponse(parsed);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
if (typeof parsed.method === "string") {
|
|
698
|
+
this.emitter.emit(parsed.method, parsed.params);
|
|
699
|
+
this.emitter.emit("event", { method: parsed.method, params: parsed.params });
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
handleClose = () => {
|
|
703
|
+
this.markClosed(new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Inspector connection closed"));
|
|
704
|
+
};
|
|
705
|
+
handleError = (err) => {
|
|
706
|
+
this.markClosed(
|
|
707
|
+
err instanceof CfInspectorError ? err : new CfInspectorError("INSPECTOR_CONNECTION_FAILED", err.message)
|
|
708
|
+
);
|
|
709
|
+
};
|
|
710
|
+
constructor(transport, requestTimeoutMs) {
|
|
711
|
+
this.transport = transport;
|
|
712
|
+
this.requestTimeoutMs = requestTimeoutMs;
|
|
713
|
+
transport.on("message", this.handleMessage);
|
|
714
|
+
transport.on("close", this.handleClose);
|
|
715
|
+
transport.on("error", this.handleError);
|
|
959
716
|
}
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
return
|
|
717
|
+
static async connect(options) {
|
|
718
|
+
const factory = options.transportFactory ?? await loadDefaultFactory();
|
|
719
|
+
const transport = await factory(options.url);
|
|
720
|
+
return new _CdpClient(transport, options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
|
|
964
721
|
}
|
|
965
|
-
|
|
966
|
-
|
|
722
|
+
async send(method, params = {}) {
|
|
723
|
+
if (this.closed) {
|
|
724
|
+
throw this.closeReason ?? new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Connection closed");
|
|
725
|
+
}
|
|
726
|
+
const id = this.nextId++;
|
|
727
|
+
const payload = JSON.stringify({ id, method, params });
|
|
728
|
+
return await new Promise((resolve, reject) => {
|
|
729
|
+
const timer = this.createRequestTimer(id, method, reject);
|
|
730
|
+
this.pending.set(id, {
|
|
731
|
+
resolve: (value) => {
|
|
732
|
+
resolve(value);
|
|
733
|
+
},
|
|
734
|
+
reject,
|
|
735
|
+
timer
|
|
736
|
+
});
|
|
737
|
+
this.sendPayload(id, method, payload, timer, reject);
|
|
738
|
+
});
|
|
967
739
|
}
|
|
968
|
-
|
|
969
|
-
|
|
740
|
+
on(method, listener) {
|
|
741
|
+
this.emitter.on(method, listener);
|
|
742
|
+
return () => {
|
|
743
|
+
this.emitter.off(method, listener);
|
|
744
|
+
};
|
|
970
745
|
}
|
|
971
|
-
|
|
972
|
-
|
|
746
|
+
async waitFor(method, options = {
|
|
747
|
+
timeoutMs: this.requestTimeoutMs
|
|
748
|
+
}) {
|
|
749
|
+
if (this.closed) {
|
|
750
|
+
throw this.closeReason ?? new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Connection closed");
|
|
751
|
+
}
|
|
752
|
+
return await new Promise((resolve, reject) => {
|
|
753
|
+
let settled = false;
|
|
754
|
+
const cleanup = () => {
|
|
755
|
+
clearTimeout(timer);
|
|
756
|
+
offEvent();
|
|
757
|
+
offClose();
|
|
758
|
+
};
|
|
759
|
+
const finish = (value) => {
|
|
760
|
+
settled = true;
|
|
761
|
+
cleanup();
|
|
762
|
+
resolve(value);
|
|
763
|
+
};
|
|
764
|
+
const offEvent = this.on(method, (raw) => {
|
|
765
|
+
if (settled) {
|
|
766
|
+
return;
|
|
767
|
+
}
|
|
768
|
+
const params = raw;
|
|
769
|
+
if (options.predicate && !options.predicate(params)) {
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
finish(params);
|
|
773
|
+
});
|
|
774
|
+
const offClose = this.onClose((err) => {
|
|
775
|
+
if (settled) {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
settled = true;
|
|
779
|
+
cleanup();
|
|
780
|
+
reject(err);
|
|
781
|
+
});
|
|
782
|
+
const timer = setTimeout(() => {
|
|
783
|
+
if (settled) {
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
settled = true;
|
|
787
|
+
cleanup();
|
|
788
|
+
reject(this.createWaitTimeoutError(method, options.timeoutMs));
|
|
789
|
+
}, options.timeoutMs);
|
|
790
|
+
});
|
|
973
791
|
}
|
|
974
|
-
|
|
975
|
-
|
|
792
|
+
onClose(listener) {
|
|
793
|
+
if (this.closed) {
|
|
794
|
+
const reason = this.closeReason ?? new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Connection closed");
|
|
795
|
+
queueMicrotask(() => {
|
|
796
|
+
listener(reason);
|
|
797
|
+
});
|
|
798
|
+
return () => {
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
this.emitter.on("__close__", listener);
|
|
802
|
+
return () => {
|
|
803
|
+
this.emitter.off("__close__", listener);
|
|
804
|
+
};
|
|
976
805
|
}
|
|
977
|
-
|
|
978
|
-
|
|
806
|
+
dispose() {
|
|
807
|
+
if (this.closed) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
this.transport.off("message", this.handleMessage);
|
|
811
|
+
this.transport.off("close", this.handleClose);
|
|
812
|
+
this.transport.off("error", this.handleError);
|
|
813
|
+
try {
|
|
814
|
+
this.transport.close();
|
|
815
|
+
} catch {
|
|
816
|
+
}
|
|
817
|
+
this.markClosed(new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Connection disposed"));
|
|
979
818
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
function isPrimitive(value) {
|
|
983
|
-
const t = typeof value;
|
|
984
|
-
return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
|
|
985
|
-
}
|
|
986
|
-
function formatPrimitive(value) {
|
|
987
|
-
if (typeof value === "symbol") {
|
|
988
|
-
return value.toString();
|
|
819
|
+
get isClosed() {
|
|
820
|
+
return this.closed;
|
|
989
821
|
}
|
|
990
|
-
|
|
991
|
-
|
|
822
|
+
handleResponse(parsed) {
|
|
823
|
+
const pending = this.pending.get(parsed.id ?? -1);
|
|
824
|
+
if (!pending) {
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
this.pending.delete(parsed.id ?? -1);
|
|
828
|
+
clearTimeout(pending.timer);
|
|
829
|
+
if (parsed.error) {
|
|
830
|
+
pending.reject(
|
|
831
|
+
new CfInspectorError(
|
|
832
|
+
"CDP_REQUEST_FAILED",
|
|
833
|
+
`CDP request ${(parsed.id ?? -1).toString()} failed: ${parsed.error.message}`,
|
|
834
|
+
JSON.stringify(parsed.error)
|
|
835
|
+
)
|
|
836
|
+
);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
pending.resolve(parsed.result);
|
|
992
840
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
841
|
+
createRequestTimer(id, method, reject) {
|
|
842
|
+
return setTimeout(() => {
|
|
843
|
+
this.pending.delete(id);
|
|
844
|
+
reject(
|
|
845
|
+
new CfInspectorError(
|
|
846
|
+
"CDP_REQUEST_FAILED",
|
|
847
|
+
`CDP method ${method} timed out after ${this.requestTimeoutMs.toString()}ms`
|
|
848
|
+
)
|
|
849
|
+
);
|
|
850
|
+
}, this.requestTimeoutMs);
|
|
998
851
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
"
|
|
1002
|
-
`
|
|
852
|
+
createWaitTimeoutError(method, timeoutMs) {
|
|
853
|
+
return new CfInspectorError(
|
|
854
|
+
"BREAKPOINT_NOT_HIT",
|
|
855
|
+
`Timed out waiting for ${method} after ${timeoutMs.toString()}ms`
|
|
1003
856
|
);
|
|
1004
857
|
}
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
858
|
+
sendPayload(id, method, payload, timer, reject) {
|
|
859
|
+
try {
|
|
860
|
+
this.transport.send(payload);
|
|
861
|
+
} catch (err) {
|
|
862
|
+
clearTimeout(timer);
|
|
863
|
+
this.pending.delete(id);
|
|
864
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
865
|
+
reject(new CfInspectorError("CDP_REQUEST_FAILED", `Failed to send ${method}: ${message}`));
|
|
866
|
+
}
|
|
1010
867
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
);
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
}
|
|
1054
|
-
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
868
|
+
markClosed(reason) {
|
|
869
|
+
if (this.closed) {
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
this.closed = true;
|
|
873
|
+
this.closeReason = reason;
|
|
874
|
+
for (const [, pending] of this.pending) {
|
|
875
|
+
clearTimeout(pending.timer);
|
|
876
|
+
pending.reject(reason);
|
|
877
|
+
}
|
|
878
|
+
this.pending.clear();
|
|
879
|
+
this.emitter.emit("__close__", reason);
|
|
880
|
+
this.emitter.removeAllListeners();
|
|
881
|
+
}
|
|
882
|
+
};
|
|
883
|
+
|
|
884
|
+
// src/inspector/session.ts
|
|
885
|
+
init_types();
|
|
886
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
|
|
887
|
+
var DEFAULT_HOST = "127.0.0.1";
|
|
888
|
+
var PAUSE_BUFFER_LIMIT = 32;
|
|
889
|
+
async function connectInspector(options) {
|
|
890
|
+
const host = options.host ?? DEFAULT_HOST;
|
|
891
|
+
const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
892
|
+
const targets = await discoverInspectorTargets(host, options.port, connectTimeoutMs);
|
|
893
|
+
const target = targets[0];
|
|
894
|
+
if (!target) {
|
|
895
|
+
throw new CfInspectorError(
|
|
896
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
897
|
+
`No inspector targets available on ${host}:${options.port.toString()}`
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
const client = await CdpClient.connect({ url: target.webSocketDebuggerUrl });
|
|
901
|
+
const scripts = /* @__PURE__ */ new Map();
|
|
902
|
+
client.on("Debugger.scriptParsed", (raw) => {
|
|
903
|
+
const params = raw;
|
|
904
|
+
const scriptId = asString(params.scriptId);
|
|
905
|
+
const url = asString(params.url);
|
|
906
|
+
if (scriptId.length === 0) {
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
scripts.set(scriptId, { scriptId, url });
|
|
910
|
+
});
|
|
911
|
+
const pauseBuffer = [];
|
|
912
|
+
const pauseWaitGate = { active: false };
|
|
913
|
+
const debuggerState = {};
|
|
914
|
+
client.on("Debugger.paused", (raw) => {
|
|
915
|
+
if (pauseWaitGate.active) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const params = raw;
|
|
919
|
+
const event = toPauseEvent(params, performance2.now());
|
|
920
|
+
if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
|
|
921
|
+
pauseBuffer.shift();
|
|
922
|
+
}
|
|
923
|
+
pauseBuffer.push(event);
|
|
924
|
+
});
|
|
925
|
+
client.on("Debugger.resumed", () => {
|
|
926
|
+
debuggerState.lastResumedAtMs = performance2.now();
|
|
927
|
+
});
|
|
928
|
+
await client.send("Runtime.enable");
|
|
929
|
+
await client.send("Debugger.enable");
|
|
930
|
+
return {
|
|
931
|
+
client,
|
|
932
|
+
target,
|
|
933
|
+
scripts,
|
|
934
|
+
pauseBuffer,
|
|
935
|
+
pauseWaitGate,
|
|
936
|
+
debuggerState,
|
|
937
|
+
dispose: async () => {
|
|
938
|
+
try {
|
|
939
|
+
await client.send("Debugger.disable");
|
|
1071
940
|
} catch {
|
|
1072
|
-
return { type: scope.type, variables: [] };
|
|
1073
941
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
}
|
|
1077
|
-
function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1078
|
-
if (result.exceptionDetails !== void 0) {
|
|
1079
|
-
const text = typeof result.exceptionDetails.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails.text === "string" ? result.exceptionDetails.text : "evaluation failed";
|
|
1080
|
-
return { expression, error: limitValueLength(text, maxValueLength) };
|
|
1081
|
-
}
|
|
1082
|
-
const inner = result.result;
|
|
1083
|
-
if (!inner) {
|
|
1084
|
-
return { expression, error: "no result returned" };
|
|
1085
|
-
}
|
|
1086
|
-
const type = typeof inner.type === "string" ? inner.type : void 0;
|
|
1087
|
-
const buildCaptured = (rendered) => {
|
|
1088
|
-
const sanitized = limitValueLength(rendered, maxValueLength);
|
|
1089
|
-
const base = { expression, value: sanitized };
|
|
1090
|
-
return type === void 0 ? base : { ...base, type };
|
|
942
|
+
client.dispose();
|
|
943
|
+
}
|
|
1091
944
|
};
|
|
1092
|
-
|
|
1093
|
-
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// src/snapshot/values.ts
|
|
948
|
+
init_types();
|
|
949
|
+
var DEFAULT_MAX_VALUE_LENGTH = 4096;
|
|
950
|
+
function isPrimitive(value) {
|
|
951
|
+
const t = typeof value;
|
|
952
|
+
return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
|
|
953
|
+
}
|
|
954
|
+
function formatPrimitive(value) {
|
|
955
|
+
if (typeof value === "symbol") {
|
|
956
|
+
return value.toString();
|
|
1094
957
|
}
|
|
1095
|
-
if (
|
|
1096
|
-
return
|
|
958
|
+
if (typeof value === "bigint") {
|
|
959
|
+
return `${value.toString()}n`;
|
|
1097
960
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
961
|
+
return String(value);
|
|
962
|
+
}
|
|
963
|
+
function resolveMaxValueLength(value) {
|
|
964
|
+
if (value === void 0) {
|
|
965
|
+
return DEFAULT_MAX_VALUE_LENGTH;
|
|
1100
966
|
}
|
|
1101
|
-
if (
|
|
1102
|
-
|
|
967
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
968
|
+
throw new CfInspectorError(
|
|
969
|
+
"INVALID_ARGUMENT",
|
|
970
|
+
`Invalid maxValueLength: ${value.toString()} \u2014 expected a positive integer`
|
|
971
|
+
);
|
|
1103
972
|
}
|
|
1104
|
-
return
|
|
973
|
+
return value;
|
|
1105
974
|
}
|
|
1106
|
-
function
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
return void 0;
|
|
1110
|
-
}
|
|
1111
|
-
const objectId = inner.objectId;
|
|
1112
|
-
if (typeof objectId !== "string" || objectId.length === 0) {
|
|
1113
|
-
return void 0;
|
|
975
|
+
function limitValueLength(raw, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
976
|
+
if (raw.length <= maxValueLength) {
|
|
977
|
+
return raw;
|
|
1114
978
|
}
|
|
1115
|
-
return
|
|
979
|
+
return `${raw.slice(0, maxValueLength)}...`;
|
|
1116
980
|
}
|
|
1117
981
|
function parseQuotedString(value) {
|
|
1118
982
|
try {
|
|
@@ -1180,6 +1044,134 @@ function toStructuredValue(variable) {
|
|
|
1180
1044
|
}
|
|
1181
1045
|
return out;
|
|
1182
1046
|
}
|
|
1047
|
+
|
|
1048
|
+
// src/snapshot/evaluation.ts
|
|
1049
|
+
function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1050
|
+
if (result.exceptionDetails !== void 0) {
|
|
1051
|
+
return { expression, error: readEvalError(result, maxValueLength) };
|
|
1052
|
+
}
|
|
1053
|
+
const inner = result.result;
|
|
1054
|
+
if (!inner) {
|
|
1055
|
+
return { expression, error: "no result returned" };
|
|
1056
|
+
}
|
|
1057
|
+
const type = typeof inner.type === "string" ? inner.type : void 0;
|
|
1058
|
+
const buildCaptured = (rendered) => {
|
|
1059
|
+
const sanitized = limitValueLength(rendered, maxValueLength);
|
|
1060
|
+
const base = { expression, value: sanitized };
|
|
1061
|
+
return type === void 0 ? base : { ...base, type };
|
|
1062
|
+
};
|
|
1063
|
+
if (type === "string" && typeof inner.value === "string") {
|
|
1064
|
+
return buildCaptured(JSON.stringify(inner.value));
|
|
1065
|
+
}
|
|
1066
|
+
if ((type === "number" || type === "boolean" || type === "bigint") && isPrimitive(inner.value)) {
|
|
1067
|
+
return buildCaptured(formatPrimitive(inner.value));
|
|
1068
|
+
}
|
|
1069
|
+
if (typeof inner.description === "string") {
|
|
1070
|
+
return buildCaptured(inner.description);
|
|
1071
|
+
}
|
|
1072
|
+
if (isPrimitive(inner.value)) {
|
|
1073
|
+
return buildCaptured(formatPrimitive(inner.value));
|
|
1074
|
+
}
|
|
1075
|
+
return buildCaptured("undefined");
|
|
1076
|
+
}
|
|
1077
|
+
function readEvalError(result, maxValueLength) {
|
|
1078
|
+
const text = typeof result.exceptionDetails?.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails?.text === "string" ? result.exceptionDetails.text : "evaluation failed";
|
|
1079
|
+
return limitValueLength(text, maxValueLength);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// src/snapshot/properties.ts
|
|
1083
|
+
var MAX_SCOPE_VARIABLES = 20;
|
|
1084
|
+
var MAX_CHILD_VARIABLES = 8;
|
|
1085
|
+
var MAX_VARIABLE_DEPTH = 2;
|
|
1086
|
+
function buildDescribed(value, type, objectId) {
|
|
1087
|
+
const base = { value };
|
|
1088
|
+
if (type !== void 0) {
|
|
1089
|
+
base.type = type;
|
|
1090
|
+
}
|
|
1091
|
+
if (objectId !== void 0) {
|
|
1092
|
+
base.objectId = objectId;
|
|
1093
|
+
}
|
|
1094
|
+
return base;
|
|
1095
|
+
}
|
|
1096
|
+
function describeProperty(prop) {
|
|
1097
|
+
const value = prop.value;
|
|
1098
|
+
if (value === void 0) {
|
|
1099
|
+
return { value: "undefined" };
|
|
1100
|
+
}
|
|
1101
|
+
const type = typeof value.type === "string" ? value.type : void 0;
|
|
1102
|
+
const objectId = typeof value.objectId === "string" ? value.objectId : void 0;
|
|
1103
|
+
if (type === "undefined") {
|
|
1104
|
+
return buildDescribed("undefined", type);
|
|
1105
|
+
}
|
|
1106
|
+
if (type === "string" && typeof value.value === "string") {
|
|
1107
|
+
return buildDescribed(JSON.stringify(value.value), type);
|
|
1108
|
+
}
|
|
1109
|
+
if ((type === "number" || type === "boolean" || type === "bigint" || type === "symbol") && isPrimitive(value.value)) {
|
|
1110
|
+
return buildDescribed(formatPrimitive(value.value), type);
|
|
1111
|
+
}
|
|
1112
|
+
if (typeof value.description === "string") {
|
|
1113
|
+
return buildDescribed(value.description, type, objectId);
|
|
1114
|
+
}
|
|
1115
|
+
if (isPrimitive(value.value)) {
|
|
1116
|
+
return buildDescribed(formatPrimitive(value.value), type);
|
|
1117
|
+
}
|
|
1118
|
+
if (objectId === void 0) {
|
|
1119
|
+
return buildDescribed("undefined", type);
|
|
1120
|
+
}
|
|
1121
|
+
return buildDescribed("[object]", type, objectId);
|
|
1122
|
+
}
|
|
1123
|
+
function isExpandable(type) {
|
|
1124
|
+
return type === "object" || type === "function";
|
|
1125
|
+
}
|
|
1126
|
+
async function captureProperties(session, objectId, limit, depth, maxValueLength) {
|
|
1127
|
+
const properties = await getProperties(session, objectId);
|
|
1128
|
+
const limited = properties.slice(0, limit);
|
|
1129
|
+
const variables = await Promise.all(
|
|
1130
|
+
limited.map(async (prop) => {
|
|
1131
|
+
return await captureProperty(session, prop, depth, maxValueLength);
|
|
1132
|
+
})
|
|
1133
|
+
);
|
|
1134
|
+
return variables;
|
|
1135
|
+
}
|
|
1136
|
+
async function captureProperty(session, prop, depth, maxValueLength) {
|
|
1137
|
+
const name = typeof prop.name === "string" ? prop.name : "?";
|
|
1138
|
+
const described = describeProperty(prop);
|
|
1139
|
+
const children = await capturePropertyChildren(session, described, depth, maxValueLength);
|
|
1140
|
+
const sanitizedValue = limitValueLength(described.value, maxValueLength);
|
|
1141
|
+
const base = { name, value: sanitizedValue };
|
|
1142
|
+
const withType = described.type === void 0 ? base : { ...base, type: described.type };
|
|
1143
|
+
return children === void 0 ? withType : { ...withType, children };
|
|
1144
|
+
}
|
|
1145
|
+
async function capturePropertyChildren(session, described, depth, maxValueLength) {
|
|
1146
|
+
if (depth <= 0 || described.objectId === void 0 || !isExpandable(described.type)) {
|
|
1147
|
+
return void 0;
|
|
1148
|
+
}
|
|
1149
|
+
try {
|
|
1150
|
+
const nested = await captureProperties(
|
|
1151
|
+
session,
|
|
1152
|
+
described.objectId,
|
|
1153
|
+
MAX_CHILD_VARIABLES,
|
|
1154
|
+
depth - 1,
|
|
1155
|
+
maxValueLength
|
|
1156
|
+
);
|
|
1157
|
+
return nested.length > 0 ? nested : void 0;
|
|
1158
|
+
} catch {
|
|
1159
|
+
return void 0;
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// src/snapshot/objects.ts
|
|
1164
|
+
function objectIdFromEvalResult(result) {
|
|
1165
|
+
const inner = result.result;
|
|
1166
|
+
if (inner?.type !== "object") {
|
|
1167
|
+
return void 0;
|
|
1168
|
+
}
|
|
1169
|
+
const objectId = inner.objectId;
|
|
1170
|
+
if (typeof objectId !== "string" || objectId.length === 0) {
|
|
1171
|
+
return void 0;
|
|
1172
|
+
}
|
|
1173
|
+
return objectId;
|
|
1174
|
+
}
|
|
1183
1175
|
async function renderObjectCapture(session, objectId, maxValueLength) {
|
|
1184
1176
|
try {
|
|
1185
1177
|
const properties = await captureProperties(
|
|
@@ -1226,6 +1218,51 @@ async function withSerializedObjectCapture(session, expression, evalResult, capt
|
|
|
1226
1218
|
const value = limitValueLength(normalized, maxValueLength);
|
|
1227
1219
|
return captured.type === void 0 ? { expression, value } : { expression, value, type: captured.type };
|
|
1228
1220
|
}
|
|
1221
|
+
|
|
1222
|
+
// src/snapshot/scopes.ts
|
|
1223
|
+
var MAX_SCOPES = 3;
|
|
1224
|
+
var PRIORITY_BY_TYPE = {
|
|
1225
|
+
local: 0,
|
|
1226
|
+
arguments: 1,
|
|
1227
|
+
block: 2,
|
|
1228
|
+
closure: 3,
|
|
1229
|
+
catch: 4,
|
|
1230
|
+
with: 5,
|
|
1231
|
+
module: 6,
|
|
1232
|
+
script: 7
|
|
1233
|
+
};
|
|
1234
|
+
function selectScopes(scopeChain) {
|
|
1235
|
+
const eligible = scopeChain.filter((scope) => scope.objectId !== void 0 && scope.type !== "global");
|
|
1236
|
+
return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type)).slice(0, MAX_SCOPES);
|
|
1237
|
+
}
|
|
1238
|
+
function priorityOf(type) {
|
|
1239
|
+
return PRIORITY_BY_TYPE[type] ?? Number.MAX_SAFE_INTEGER;
|
|
1240
|
+
}
|
|
1241
|
+
async function captureScopes(session, frame, maxValueLength) {
|
|
1242
|
+
const scopes = selectScopes(frame.scopeChain);
|
|
1243
|
+
return await Promise.all(
|
|
1244
|
+
scopes.map(async (scope) => {
|
|
1245
|
+
const objectId = scope.objectId;
|
|
1246
|
+
if (objectId === void 0) {
|
|
1247
|
+
return { type: scope.type, variables: [] };
|
|
1248
|
+
}
|
|
1249
|
+
try {
|
|
1250
|
+
const variables = await captureProperties(
|
|
1251
|
+
session,
|
|
1252
|
+
objectId,
|
|
1253
|
+
MAX_SCOPE_VARIABLES,
|
|
1254
|
+
MAX_VARIABLE_DEPTH,
|
|
1255
|
+
maxValueLength
|
|
1256
|
+
);
|
|
1257
|
+
return { type: scope.type, variables };
|
|
1258
|
+
} catch {
|
|
1259
|
+
return { type: scope.type, variables: [] };
|
|
1260
|
+
}
|
|
1261
|
+
})
|
|
1262
|
+
);
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// src/snapshot/capture.ts
|
|
1229
1266
|
async function captureSnapshot(session, pause, options = {}) {
|
|
1230
1267
|
const maxValueLength = resolveMaxValueLength(options.maxValueLength);
|
|
1231
1268
|
const top = pause.callFrames[0];
|
|
@@ -1242,26 +1279,7 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
1242
1279
|
const scopes = await captureScopes(session, top, maxValueLength);
|
|
1243
1280
|
topFrame = { ...topFrame, scopes };
|
|
1244
1281
|
}
|
|
1245
|
-
|
|
1246
|
-
captures = await Promise.all(
|
|
1247
|
-
options.captures.map(async (expression) => {
|
|
1248
|
-
try {
|
|
1249
|
-
const result = await evaluateOnFrame(session, top.callFrameId, expression);
|
|
1250
|
-
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1251
|
-
return await withSerializedObjectCapture(
|
|
1252
|
-
session,
|
|
1253
|
-
expression,
|
|
1254
|
-
result,
|
|
1255
|
-
captured,
|
|
1256
|
-
maxValueLength
|
|
1257
|
-
);
|
|
1258
|
-
} catch (err) {
|
|
1259
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1260
|
-
return { expression, error: limitValueLength(message, maxValueLength) };
|
|
1261
|
-
}
|
|
1262
|
-
})
|
|
1263
|
-
);
|
|
1264
|
-
}
|
|
1282
|
+
captures = await captureExpressions(session, top.callFrameId, options.captures, maxValueLength);
|
|
1265
1283
|
}
|
|
1266
1284
|
return {
|
|
1267
1285
|
reason: pause.reason,
|
|
@@ -1271,8 +1289,28 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
1271
1289
|
captures
|
|
1272
1290
|
};
|
|
1273
1291
|
}
|
|
1292
|
+
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
1293
|
+
if (captures === void 0 || captures.length === 0) {
|
|
1294
|
+
return [];
|
|
1295
|
+
}
|
|
1296
|
+
return await Promise.all(
|
|
1297
|
+
captures.map(async (expression) => {
|
|
1298
|
+
return await captureExpression(session, callFrameId, expression, maxValueLength);
|
|
1299
|
+
})
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
async function captureExpression(session, callFrameId, expression, maxValueLength) {
|
|
1303
|
+
try {
|
|
1304
|
+
const result = await evaluateOnFrame(session, callFrameId, expression);
|
|
1305
|
+
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1306
|
+
return await withSerializedObjectCapture(session, expression, result, captured, maxValueLength);
|
|
1307
|
+
} catch (err) {
|
|
1308
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1309
|
+
return { expression, error: limitValueLength(message, maxValueLength) };
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1274
1312
|
|
|
1275
|
-
// src/logpoint.ts
|
|
1313
|
+
// src/logpoint/condition.ts
|
|
1276
1314
|
import { randomBytes } from "crypto";
|
|
1277
1315
|
var SENTINEL_PREFIX = "__CFI_LOG_";
|
|
1278
1316
|
var SENTINEL_SUFFIX = "__";
|
|
@@ -1291,6 +1329,11 @@ function buildLogpointCondition(sentinel, expression) {
|
|
|
1291
1329
|
"})()"
|
|
1292
1330
|
].join("");
|
|
1293
1331
|
}
|
|
1332
|
+
function generateSentinel() {
|
|
1333
|
+
return `${SENTINEL_PREFIX}${randomBytes(8).toString("hex")}${SENTINEL_SUFFIX}`;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
// src/logpoint/events.ts
|
|
1294
1337
|
function asString2(value) {
|
|
1295
1338
|
return typeof value === "string" ? value : void 0;
|
|
1296
1339
|
}
|
|
@@ -1323,6 +1366,9 @@ function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
|
1323
1366
|
if (payload.startsWith("!err:")) {
|
|
1324
1367
|
return { ts, at, error: payload.slice("!err:".length) };
|
|
1325
1368
|
}
|
|
1369
|
+
return parsePayload(ts, at, payload);
|
|
1370
|
+
}
|
|
1371
|
+
function parsePayload(ts, at, payload) {
|
|
1326
1372
|
try {
|
|
1327
1373
|
const parsed = JSON.parse(payload);
|
|
1328
1374
|
if (typeof parsed === "string") {
|
|
@@ -1333,20 +1379,14 @@ function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
|
1333
1379
|
return { ts, at, value: payload, raw: payload };
|
|
1334
1380
|
}
|
|
1335
1381
|
}
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
}
|
|
1382
|
+
|
|
1383
|
+
// src/logpoint/stream.ts
|
|
1339
1384
|
async function streamLogpoint(session, options) {
|
|
1340
1385
|
const sentinel = generateSentinel();
|
|
1341
1386
|
const condition = buildLogpointCondition(sentinel, options.expression);
|
|
1342
1387
|
let emitted = 0;
|
|
1343
1388
|
const offEvent = session.client.on("Runtime.consoleAPICalled", (raw) => {
|
|
1344
|
-
const
|
|
1345
|
-
if (asString2(params.type) !== "log") {
|
|
1346
|
-
return;
|
|
1347
|
-
}
|
|
1348
|
-
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
1349
|
-
const event = parseLogEvent(params.args, sentinel, options.location, ts);
|
|
1389
|
+
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
1350
1390
|
if (event === void 0) {
|
|
1351
1391
|
return;
|
|
1352
1392
|
}
|
|
@@ -1366,18 +1406,26 @@ async function streamLogpoint(session, options) {
|
|
|
1366
1406
|
throw err;
|
|
1367
1407
|
}
|
|
1368
1408
|
options.onBreakpointSet?.(handle);
|
|
1369
|
-
const cleanup = async () => {
|
|
1370
|
-
offEvent();
|
|
1371
|
-
try {
|
|
1372
|
-
await removeBreakpoint(session, handle.breakpointId);
|
|
1373
|
-
} catch {
|
|
1374
|
-
}
|
|
1375
|
-
};
|
|
1376
1409
|
try {
|
|
1377
1410
|
const reason = await waitForStop(session, options);
|
|
1378
1411
|
return { handle, sentinel, emitted, stoppedReason: reason };
|
|
1379
1412
|
} finally {
|
|
1380
|
-
|
|
1413
|
+
offEvent();
|
|
1414
|
+
await removeBreakpointBestEffort(session, handle.breakpointId);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
function toLogpointEvent(raw, sentinel, location) {
|
|
1418
|
+
const params = raw;
|
|
1419
|
+
if (asString2(params.type) !== "log") {
|
|
1420
|
+
return void 0;
|
|
1421
|
+
}
|
|
1422
|
+
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
1423
|
+
return parseLogEvent(params.args, sentinel, location, ts);
|
|
1424
|
+
}
|
|
1425
|
+
async function removeBreakpointBestEffort(session, breakpointId) {
|
|
1426
|
+
try {
|
|
1427
|
+
await removeBreakpoint(session, breakpointId);
|
|
1428
|
+
} catch {
|
|
1381
1429
|
}
|
|
1382
1430
|
}
|
|
1383
1431
|
async function waitForStop(session, options) {
|
|
@@ -1414,7 +1462,7 @@ async function waitForStop(session, options) {
|
|
|
1414
1462
|
});
|
|
1415
1463
|
}
|
|
1416
1464
|
|
|
1417
|
-
// src/tunnel.ts
|
|
1465
|
+
// src/cf/tunnel.ts
|
|
1418
1466
|
import { startDebugger } from "@saptools/cf-debugger";
|
|
1419
1467
|
async function openCfTunnel(target) {
|
|
1420
1468
|
const opts = {
|