@saptools/cf-inspector 0.3.17 → 0.3.18
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.js +548 -546
- package/dist/cli.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -118,275 +118,6 @@ import { Command } from "commander";
|
|
|
118
118
|
// src/cli/commands/attach.ts
|
|
119
119
|
import process2 from "process";
|
|
120
120
|
|
|
121
|
-
// src/pathMapper.ts
|
|
122
|
-
init_types();
|
|
123
|
-
var REGEX_PREFIX = "regex:";
|
|
124
|
-
var REGEX_FLAGS_PATTERN = /^[dgimsuvy]*$/;
|
|
125
|
-
var TS_JS_EXT_PATTERN = /\.(?:ts|js|mts|mjs|cts|cjs)$/i;
|
|
126
|
-
function parseBreakpointSpec(input) {
|
|
127
|
-
const idx = input.lastIndexOf(":");
|
|
128
|
-
if (idx <= 0 || idx === input.length - 1) {
|
|
129
|
-
throw new CfInspectorError(
|
|
130
|
-
"INVALID_BREAKPOINT",
|
|
131
|
-
`Breakpoint must be in 'file:line' form, received: "${input}"`
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
const file = input.slice(0, idx).trim();
|
|
135
|
-
const lineRaw = input.slice(idx + 1).trim();
|
|
136
|
-
const line = Number.parseInt(lineRaw, 10);
|
|
137
|
-
if (!Number.isInteger(line) || line <= 0 || line.toString() !== lineRaw) {
|
|
138
|
-
throw new CfInspectorError(
|
|
139
|
-
"INVALID_BREAKPOINT",
|
|
140
|
-
`Breakpoint line must be a positive integer, received: "${lineRaw}"`
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
if (file.length === 0) {
|
|
144
|
-
throw new CfInspectorError(
|
|
145
|
-
"INVALID_BREAKPOINT",
|
|
146
|
-
`Breakpoint file path is empty in "${input}"`
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
return { file, line };
|
|
150
|
-
}
|
|
151
|
-
function parseRemoteRoot(value) {
|
|
152
|
-
const trimmed = value?.trim();
|
|
153
|
-
if (trimmed === void 0 || trimmed.length === 0) {
|
|
154
|
-
return { kind: "none" };
|
|
155
|
-
}
|
|
156
|
-
if (trimmed.startsWith(REGEX_PREFIX)) {
|
|
157
|
-
return toRegex(trimmed.slice(REGEX_PREFIX.length), "");
|
|
158
|
-
}
|
|
159
|
-
const slashRegex = parseSlashDelimited(trimmed);
|
|
160
|
-
if (slashRegex !== void 0) {
|
|
161
|
-
return toRegex(slashRegex.pattern, slashRegex.flags);
|
|
162
|
-
}
|
|
163
|
-
return { kind: "literal", value: stripTrailingSlash(trimmed) };
|
|
164
|
-
}
|
|
165
|
-
function toRegex(pattern, flags) {
|
|
166
|
-
try {
|
|
167
|
-
const regex = new RegExp(pattern, flags);
|
|
168
|
-
return { kind: "regex", pattern, flags, regex };
|
|
169
|
-
} catch (err) {
|
|
170
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
171
|
-
throw new CfInspectorError(
|
|
172
|
-
"INVALID_REMOTE_ROOT",
|
|
173
|
-
`Failed to compile remote-root regex "${pattern}" with flags "${flags}": ${message}`
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
function parseSlashDelimited(value) {
|
|
178
|
-
if (!value.startsWith("/")) {
|
|
179
|
-
return void 0;
|
|
180
|
-
}
|
|
181
|
-
const closing = findLastUnescapedSlash(value);
|
|
182
|
-
if (closing <= 0) {
|
|
183
|
-
return void 0;
|
|
184
|
-
}
|
|
185
|
-
const flags = value.slice(closing + 1);
|
|
186
|
-
if (flags.length === 0 || !REGEX_FLAGS_PATTERN.test(flags)) {
|
|
187
|
-
return void 0;
|
|
188
|
-
}
|
|
189
|
-
return { pattern: value.slice(1, closing), flags };
|
|
190
|
-
}
|
|
191
|
-
function findLastUnescapedSlash(value) {
|
|
192
|
-
for (let i = value.length - 1; i > 0; i--) {
|
|
193
|
-
if (value[i] === "/" && !isEscaped(value, i)) {
|
|
194
|
-
return i;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
return -1;
|
|
198
|
-
}
|
|
199
|
-
function isEscaped(value, idx) {
|
|
200
|
-
let backslashes = 0;
|
|
201
|
-
for (let i = idx - 1; i >= 0; i--) {
|
|
202
|
-
if (value[i] === "\\") {
|
|
203
|
-
backslashes++;
|
|
204
|
-
} else {
|
|
205
|
-
break;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
return backslashes % 2 === 1;
|
|
209
|
-
}
|
|
210
|
-
function stripTrailingSlash(value) {
|
|
211
|
-
if (value.length > 1 && value.endsWith("/")) {
|
|
212
|
-
return value.slice(0, -1);
|
|
213
|
-
}
|
|
214
|
-
return value;
|
|
215
|
-
}
|
|
216
|
-
function normalizeRegexRootPattern(pattern) {
|
|
217
|
-
const withoutStartAnchor = pattern.startsWith("^") ? pattern.slice(1) : pattern;
|
|
218
|
-
const withoutEndAnchor = withoutStartAnchor.endsWith("$") && !isEscaped(withoutStartAnchor, withoutStartAnchor.length - 1) ? withoutStartAnchor.slice(0, -1) : withoutStartAnchor;
|
|
219
|
-
return stripTrailingSlash(withoutEndAnchor);
|
|
220
|
-
}
|
|
221
|
-
function buildFileUrlRegex(rootPattern, tail) {
|
|
222
|
-
const separator = rootPattern.endsWith("/") ? "" : "/";
|
|
223
|
-
return `^file://${rootPattern}${separator}${tail}$`;
|
|
224
|
-
}
|
|
225
|
-
function escapeRegExp(value) {
|
|
226
|
-
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
227
|
-
}
|
|
228
|
-
function normalizeRelative(file) {
|
|
229
|
-
return file.replaceAll(/^[./\\]+/g, "").replaceAll("\\", "/");
|
|
230
|
-
}
|
|
231
|
-
function dropExtension(file) {
|
|
232
|
-
const match = TS_JS_EXT_PATTERN.exec(file);
|
|
233
|
-
if (!match) {
|
|
234
|
-
return { stem: file, matchedExt: false };
|
|
235
|
-
}
|
|
236
|
-
return { stem: file.slice(0, match.index), matchedExt: true };
|
|
237
|
-
}
|
|
238
|
-
var EXT_GROUP = String.raw`\.(?:ts|js|mts|mjs|cts|cjs)`;
|
|
239
|
-
var OPTIONAL_EXT_GROUP = String.raw`(?:\.(?:ts|js|mts|mjs|cts|cjs))?`;
|
|
240
|
-
function buildBreakpointUrlRegex(input) {
|
|
241
|
-
const normalized = normalizeRelative(input.file);
|
|
242
|
-
const { stem, matchedExt } = dropExtension(normalized);
|
|
243
|
-
const escapedStem = escapeRegExp(stem);
|
|
244
|
-
const tail = matchedExt ? `${escapedStem}${EXT_GROUP}` : `${escapedStem}${OPTIONAL_EXT_GROUP}`;
|
|
245
|
-
switch (input.remoteRoot.kind) {
|
|
246
|
-
case "none": {
|
|
247
|
-
return `(?:^|/)${tail}$`;
|
|
248
|
-
}
|
|
249
|
-
case "literal": {
|
|
250
|
-
const escapedRoot = escapeRegExp(input.remoteRoot.value);
|
|
251
|
-
return buildFileUrlRegex(escapedRoot, tail);
|
|
252
|
-
}
|
|
253
|
-
case "regex": {
|
|
254
|
-
const rootPattern = normalizeRegexRootPattern(input.remoteRoot.pattern);
|
|
255
|
-
return buildFileUrlRegex(rootPattern, tail);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// src/inspector/breakpoints.ts
|
|
261
|
-
init_types();
|
|
262
|
-
|
|
263
|
-
// src/inspector/conversions.ts
|
|
264
|
-
function asString(value, fallback = "") {
|
|
265
|
-
return typeof value === "string" ? value : fallback;
|
|
266
|
-
}
|
|
267
|
-
function asNumber(value, fallback = 0) {
|
|
268
|
-
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
269
|
-
}
|
|
270
|
-
function toResolvedLocations(value) {
|
|
271
|
-
if (!Array.isArray(value)) {
|
|
272
|
-
return [];
|
|
273
|
-
}
|
|
274
|
-
return value.flatMap((entry) => {
|
|
275
|
-
if (typeof entry !== "object" || entry === null) {
|
|
276
|
-
return [];
|
|
277
|
-
}
|
|
278
|
-
const candidate = entry;
|
|
279
|
-
const scriptId = asString(candidate.scriptId);
|
|
280
|
-
if (scriptId.length === 0) {
|
|
281
|
-
return [];
|
|
282
|
-
}
|
|
283
|
-
const url = typeof candidate.url === "string" ? candidate.url : void 0;
|
|
284
|
-
const lineNumber = asNumber(candidate.lineNumber);
|
|
285
|
-
const result = url === void 0 ? { scriptId, lineNumber, columnNumber: asNumber(candidate.columnNumber) } : { scriptId, url, lineNumber, columnNumber: asNumber(candidate.columnNumber) };
|
|
286
|
-
return [result];
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
function toScopeChain(value) {
|
|
290
|
-
if (!Array.isArray(value)) {
|
|
291
|
-
return [];
|
|
292
|
-
}
|
|
293
|
-
return value.flatMap((entry) => {
|
|
294
|
-
if (typeof entry !== "object" || entry === null) {
|
|
295
|
-
return [];
|
|
296
|
-
}
|
|
297
|
-
const candidate = entry;
|
|
298
|
-
const type = asString(candidate.type);
|
|
299
|
-
if (type.length === 0) {
|
|
300
|
-
return [];
|
|
301
|
-
}
|
|
302
|
-
const objectId = typeof candidate.object?.objectId === "string" ? candidate.object.objectId : void 0;
|
|
303
|
-
const name = typeof candidate.name === "string" ? candidate.name : void 0;
|
|
304
|
-
const base = name === void 0 ? { type } : { type, name };
|
|
305
|
-
return [objectId === void 0 ? base : { ...base, objectId }];
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
function toCallFrames(value) {
|
|
309
|
-
if (!Array.isArray(value)) {
|
|
310
|
-
return [];
|
|
311
|
-
}
|
|
312
|
-
return value.flatMap((entry) => {
|
|
313
|
-
if (typeof entry !== "object" || entry === null) {
|
|
314
|
-
return [];
|
|
315
|
-
}
|
|
316
|
-
const candidate = entry;
|
|
317
|
-
const callFrameId = asString(candidate.callFrameId);
|
|
318
|
-
if (callFrameId.length === 0) {
|
|
319
|
-
return [];
|
|
320
|
-
}
|
|
321
|
-
const url = typeof candidate.url === "string" ? candidate.url : void 0;
|
|
322
|
-
const base = {
|
|
323
|
-
callFrameId,
|
|
324
|
-
functionName: asString(candidate.functionName),
|
|
325
|
-
lineNumber: asNumber(candidate.location?.lineNumber),
|
|
326
|
-
columnNumber: asNumber(candidate.location?.columnNumber),
|
|
327
|
-
scopeChain: toScopeChain(candidate.scopeChain)
|
|
328
|
-
};
|
|
329
|
-
return [url === void 0 ? base : { ...base, url }];
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
function toPauseEvent(params, receivedAtMs) {
|
|
333
|
-
return {
|
|
334
|
-
reason: asString(params.reason),
|
|
335
|
-
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
336
|
-
callFrames: toCallFrames(params.callFrames),
|
|
337
|
-
receivedAtMs
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
function topFrameLocation(pause) {
|
|
341
|
-
const top = pause.callFrames[0];
|
|
342
|
-
if (top === void 0) {
|
|
343
|
-
return "(no call frame)";
|
|
344
|
-
}
|
|
345
|
-
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
346
|
-
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
347
|
-
}
|
|
348
|
-
function pauseDetail(pause) {
|
|
349
|
-
return JSON.stringify({
|
|
350
|
-
reason: pause.reason,
|
|
351
|
-
hitBreakpoints: pause.hitBreakpoints,
|
|
352
|
-
topFrame: topFrameLocation(pause)
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
// src/inspector/breakpoints.ts
|
|
357
|
-
async function setBreakpoint(session, input) {
|
|
358
|
-
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
359
|
-
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
360
|
-
const params = {
|
|
361
|
-
lineNumber: input.line - 1,
|
|
362
|
-
urlRegex
|
|
363
|
-
};
|
|
364
|
-
if (input.condition !== void 0 && input.condition.length > 0) {
|
|
365
|
-
params["condition"] = input.condition;
|
|
366
|
-
}
|
|
367
|
-
const result = await session.client.send(
|
|
368
|
-
"Debugger.setBreakpointByUrl",
|
|
369
|
-
params
|
|
370
|
-
);
|
|
371
|
-
const breakpointId = asString(result.breakpointId);
|
|
372
|
-
if (breakpointId.length === 0) {
|
|
373
|
-
throw new CfInspectorError(
|
|
374
|
-
"CDP_REQUEST_FAILED",
|
|
375
|
-
`setBreakpointByUrl did not return a breakpointId for ${input.file}:${input.line.toString()}`
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
return {
|
|
379
|
-
breakpointId,
|
|
380
|
-
file: input.file,
|
|
381
|
-
line: input.line,
|
|
382
|
-
urlRegex,
|
|
383
|
-
resolvedLocations: toResolvedLocations(result.locations)
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
async function removeBreakpoint(session, breakpointId) {
|
|
387
|
-
await session.client.send("Debugger.removeBreakpoint", { breakpointId });
|
|
388
|
-
}
|
|
389
|
-
|
|
390
121
|
// src/inspector/discovery.ts
|
|
391
122
|
init_types();
|
|
392
123
|
import { request } from "http";
|
|
@@ -505,164 +236,93 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
|
505
236
|
return { browser, protocolVersion };
|
|
506
237
|
}
|
|
507
238
|
|
|
508
|
-
// src/
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
return true;
|
|
514
|
-
}
|
|
515
|
-
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
516
|
-
}
|
|
517
|
-
function remainingUntil(deadlineMs) {
|
|
518
|
-
return Math.max(0, deadlineMs - performance.now());
|
|
519
|
-
}
|
|
520
|
-
function hasResumedSincePause(session, pause) {
|
|
521
|
-
const pauseAt = pause.receivedAtMs;
|
|
522
|
-
const resumedAt = session.debuggerState.lastResumedAtMs;
|
|
523
|
-
return pauseAt !== void 0 && resumedAt !== void 0 && resumedAt >= pauseAt;
|
|
524
|
-
}
|
|
525
|
-
function throwBreakpointTimeout(timeoutMs) {
|
|
526
|
-
throw new CfInspectorError(
|
|
527
|
-
"BREAKPOINT_NOT_HIT",
|
|
528
|
-
`Timed out waiting for matching Debugger.paused after ${timeoutMs.toString()}ms`
|
|
529
|
-
);
|
|
530
|
-
}
|
|
531
|
-
function throwUnrelatedPauseTimeout(pause, timeoutMs) {
|
|
532
|
-
throw new CfInspectorError(
|
|
533
|
-
"UNRELATED_PAUSE_TIMEOUT",
|
|
534
|
-
`Target stayed paused by another debugger event before this command's breakpoint could hit within ${timeoutMs.toString()}ms`,
|
|
535
|
-
pauseDetail(pause)
|
|
536
|
-
);
|
|
239
|
+
// src/cli/output.ts
|
|
240
|
+
import process from "process";
|
|
241
|
+
function writeJson(value) {
|
|
242
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
243
|
+
`);
|
|
537
244
|
}
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
245
|
+
function writeHumanSnapshot(snapshot) {
|
|
246
|
+
const pausedDuration = snapshot.pausedDurationMs === null ? "unknown" : `${snapshot.pausedDurationMs.toFixed(1)}ms`;
|
|
247
|
+
const lines = [];
|
|
248
|
+
lines.push(
|
|
249
|
+
`Snapshot @ ${snapshot.capturedAt}`,
|
|
250
|
+
` reason: ${snapshot.reason}`,
|
|
251
|
+
` paused: ${pausedDuration}`
|
|
252
|
+
);
|
|
253
|
+
if (snapshot.topFrame) {
|
|
254
|
+
appendFrameLines(lines, snapshot);
|
|
545
255
|
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
256
|
+
if (snapshot.captures.length > 0) {
|
|
257
|
+
lines.push(" captures:");
|
|
258
|
+
for (const capture of snapshot.captures) {
|
|
259
|
+
const detail = capture.error ?? capture.value ?? "undefined";
|
|
260
|
+
lines.push(` ${capture.expression} = ${detail}`);
|
|
552
261
|
}
|
|
553
|
-
throw err;
|
|
554
262
|
}
|
|
263
|
+
process.stdout.write(`${lines.join("\n")}
|
|
264
|
+
`);
|
|
555
265
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
"Target paused before this command's breakpoint was reached",
|
|
561
|
-
pauseDetail(pause)
|
|
562
|
-
);
|
|
266
|
+
function appendFrameLines(lines, snapshot) {
|
|
267
|
+
const frame = snapshot.topFrame;
|
|
268
|
+
if (frame === void 0) {
|
|
269
|
+
return;
|
|
563
270
|
}
|
|
564
|
-
|
|
271
|
+
const fnName = frame.functionName.length === 0 ? "(anonymous)" : frame.functionName;
|
|
272
|
+
const sourceUrl = frame.url !== void 0 && frame.url.length > 0 ? frame.url : "(unknown)";
|
|
273
|
+
lines.push(
|
|
274
|
+
` frame: ${fnName} ${sourceUrl}:${frame.line.toString()}:${frame.column.toString()}`
|
|
275
|
+
);
|
|
276
|
+
if (frame.scopes === void 0) {
|
|
565
277
|
return;
|
|
566
278
|
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const deadlineMs = performance.now() + options.timeoutMs;
|
|
572
|
-
const buffer = session.pauseBuffer;
|
|
573
|
-
while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
|
|
574
|
-
while (buffer.length > 0) {
|
|
575
|
-
const buffered = buffer.shift();
|
|
576
|
-
if (buffered === void 0) {
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
if (pauseMatches(buffered, options.breakpointIds)) {
|
|
580
|
-
return buffered;
|
|
581
|
-
}
|
|
582
|
-
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
583
|
-
}
|
|
584
|
-
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
585
|
-
if (pauseMatches(pause, options.breakpointIds)) {
|
|
586
|
-
return pause;
|
|
279
|
+
for (const scope of frame.scopes) {
|
|
280
|
+
lines.push(` scope ${scope.type} (${scope.variables.length.toString()} vars):`);
|
|
281
|
+
for (const variable of scope.variables) {
|
|
282
|
+
lines.push(` ${variable.name} = ${variable.value}`);
|
|
587
283
|
}
|
|
588
|
-
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
589
284
|
}
|
|
590
|
-
throwBreakpointTimeout(options.timeoutMs);
|
|
591
285
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
session.pauseWaitGate.active = true;
|
|
598
|
-
let receivedAtMs;
|
|
599
|
-
let params;
|
|
600
|
-
try {
|
|
601
|
-
params = await session.client.waitFor("Debugger.paused", {
|
|
602
|
-
timeoutMs: remainingMs,
|
|
603
|
-
predicate: () => {
|
|
604
|
-
receivedAtMs = performance.now();
|
|
605
|
-
return true;
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
} finally {
|
|
609
|
-
session.pauseWaitGate.active = false;
|
|
286
|
+
function writeLogEvent(event, json) {
|
|
287
|
+
if (json) {
|
|
288
|
+
process.stdout.write(`${JSON.stringify(event)}
|
|
289
|
+
`);
|
|
290
|
+
return;
|
|
610
291
|
}
|
|
611
|
-
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// src/inspector/runtime.ts
|
|
615
|
-
init_types();
|
|
616
|
-
async function resume(session) {
|
|
617
|
-
await session.client.send("Debugger.resume");
|
|
618
|
-
}
|
|
619
|
-
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
620
|
-
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
621
|
-
callFrameId,
|
|
622
|
-
expression,
|
|
623
|
-
returnByValue: false,
|
|
624
|
-
generatePreview: true,
|
|
625
|
-
silent: true
|
|
626
|
-
});
|
|
627
|
-
}
|
|
628
|
-
async function evaluateGlobal(session, expression) {
|
|
629
|
-
return await session.client.send("Runtime.evaluate", {
|
|
630
|
-
expression,
|
|
631
|
-
returnByValue: false,
|
|
632
|
-
generatePreview: true,
|
|
633
|
-
silent: true
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
function listScripts(session) {
|
|
637
|
-
return [...session.scripts.values()];
|
|
638
|
-
}
|
|
639
|
-
async function validateExpression(session, expression) {
|
|
640
|
-
const result = await session.client.send("Runtime.compileScript", {
|
|
641
|
-
expression,
|
|
642
|
-
sourceURL: "<cf-inspector-validate>",
|
|
643
|
-
persistScript: false
|
|
644
|
-
});
|
|
645
|
-
if (result.exceptionDetails === void 0) {
|
|
292
|
+
if (event.error !== void 0) {
|
|
293
|
+
process.stdout.write(`[${event.ts}] ${event.at} !err ${event.error}
|
|
294
|
+
`);
|
|
646
295
|
return;
|
|
647
296
|
}
|
|
648
|
-
|
|
649
|
-
|
|
297
|
+
process.stdout.write(`[${event.ts}] ${event.at} ${event.value ?? ""}
|
|
298
|
+
`);
|
|
650
299
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
300
|
+
|
|
301
|
+
// src/cf/tunnel.ts
|
|
302
|
+
import { startDebugger } from "@saptools/cf-debugger";
|
|
303
|
+
async function openCfTunnel(target) {
|
|
304
|
+
const opts = {
|
|
305
|
+
region: target.region,
|
|
306
|
+
org: target.org,
|
|
307
|
+
space: target.space,
|
|
308
|
+
app: target.app,
|
|
309
|
+
...target.tunnelReadyTimeoutMs === void 0 ? {} : { tunnelReadyTimeoutMs: target.tunnelReadyTimeoutMs },
|
|
310
|
+
...target.preferredPort === void 0 ? {} : { preferredPort: target.preferredPort },
|
|
311
|
+
...target.verbose === void 0 ? {} : { verbose: target.verbose },
|
|
312
|
+
...target.signal === void 0 ? {} : { signal: target.signal }
|
|
313
|
+
};
|
|
314
|
+
const handle = await startDebugger(opts);
|
|
315
|
+
return {
|
|
316
|
+
localPort: handle.session.localPort,
|
|
317
|
+
handle,
|
|
318
|
+
dispose: async () => {
|
|
319
|
+
await handle.dispose();
|
|
320
|
+
}
|
|
321
|
+
};
|
|
662
322
|
}
|
|
663
323
|
|
|
664
324
|
// src/inspector/session.ts
|
|
665
|
-
import { performance
|
|
325
|
+
import { performance } from "perf_hooks";
|
|
666
326
|
|
|
667
327
|
// src/cdp/client.ts
|
|
668
328
|
init_types();
|
|
@@ -888,7 +548,102 @@ var CdpClient = class _CdpClient {
|
|
|
888
548
|
};
|
|
889
549
|
|
|
890
550
|
// src/inspector/session.ts
|
|
891
|
-
init_types();
|
|
551
|
+
init_types();
|
|
552
|
+
|
|
553
|
+
// src/inspector/conversions.ts
|
|
554
|
+
function asString(value, fallback = "") {
|
|
555
|
+
return typeof value === "string" ? value : fallback;
|
|
556
|
+
}
|
|
557
|
+
function asNumber(value, fallback = 0) {
|
|
558
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
559
|
+
}
|
|
560
|
+
function toResolvedLocations(value) {
|
|
561
|
+
if (!Array.isArray(value)) {
|
|
562
|
+
return [];
|
|
563
|
+
}
|
|
564
|
+
return value.flatMap((entry) => {
|
|
565
|
+
if (typeof entry !== "object" || entry === null) {
|
|
566
|
+
return [];
|
|
567
|
+
}
|
|
568
|
+
const candidate = entry;
|
|
569
|
+
const scriptId = asString(candidate.scriptId);
|
|
570
|
+
if (scriptId.length === 0) {
|
|
571
|
+
return [];
|
|
572
|
+
}
|
|
573
|
+
const url = typeof candidate.url === "string" ? candidate.url : void 0;
|
|
574
|
+
const lineNumber = asNumber(candidate.lineNumber);
|
|
575
|
+
const result = url === void 0 ? { scriptId, lineNumber, columnNumber: asNumber(candidate.columnNumber) } : { scriptId, url, lineNumber, columnNumber: asNumber(candidate.columnNumber) };
|
|
576
|
+
return [result];
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
function toScopeChain(value) {
|
|
580
|
+
if (!Array.isArray(value)) {
|
|
581
|
+
return [];
|
|
582
|
+
}
|
|
583
|
+
return value.flatMap((entry) => {
|
|
584
|
+
if (typeof entry !== "object" || entry === null) {
|
|
585
|
+
return [];
|
|
586
|
+
}
|
|
587
|
+
const candidate = entry;
|
|
588
|
+
const type = asString(candidate.type);
|
|
589
|
+
if (type.length === 0) {
|
|
590
|
+
return [];
|
|
591
|
+
}
|
|
592
|
+
const objectId = typeof candidate.object?.objectId === "string" ? candidate.object.objectId : void 0;
|
|
593
|
+
const name = typeof candidate.name === "string" ? candidate.name : void 0;
|
|
594
|
+
const base = name === void 0 ? { type } : { type, name };
|
|
595
|
+
return [objectId === void 0 ? base : { ...base, objectId }];
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
function toCallFrames(value) {
|
|
599
|
+
if (!Array.isArray(value)) {
|
|
600
|
+
return [];
|
|
601
|
+
}
|
|
602
|
+
return value.flatMap((entry) => {
|
|
603
|
+
if (typeof entry !== "object" || entry === null) {
|
|
604
|
+
return [];
|
|
605
|
+
}
|
|
606
|
+
const candidate = entry;
|
|
607
|
+
const callFrameId = asString(candidate.callFrameId);
|
|
608
|
+
if (callFrameId.length === 0) {
|
|
609
|
+
return [];
|
|
610
|
+
}
|
|
611
|
+
const url = typeof candidate.url === "string" ? candidate.url : void 0;
|
|
612
|
+
const base = {
|
|
613
|
+
callFrameId,
|
|
614
|
+
functionName: asString(candidate.functionName),
|
|
615
|
+
lineNumber: asNumber(candidate.location?.lineNumber),
|
|
616
|
+
columnNumber: asNumber(candidate.location?.columnNumber),
|
|
617
|
+
scopeChain: toScopeChain(candidate.scopeChain)
|
|
618
|
+
};
|
|
619
|
+
return [url === void 0 ? base : { ...base, url }];
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function toPauseEvent(params, receivedAtMs) {
|
|
623
|
+
return {
|
|
624
|
+
reason: asString(params.reason),
|
|
625
|
+
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
626
|
+
callFrames: toCallFrames(params.callFrames),
|
|
627
|
+
receivedAtMs
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
function topFrameLocation(pause) {
|
|
631
|
+
const top = pause.callFrames[0];
|
|
632
|
+
if (top === void 0) {
|
|
633
|
+
return "(no call frame)";
|
|
634
|
+
}
|
|
635
|
+
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
636
|
+
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
637
|
+
}
|
|
638
|
+
function pauseDetail(pause) {
|
|
639
|
+
return JSON.stringify({
|
|
640
|
+
reason: pause.reason,
|
|
641
|
+
hitBreakpoints: pause.hitBreakpoints,
|
|
642
|
+
topFrame: topFrameLocation(pause)
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// src/inspector/session.ts
|
|
892
647
|
var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
|
|
893
648
|
var DEFAULT_HOST = "127.0.0.1";
|
|
894
649
|
var PAUSE_BUFFER_LIMIT = 32;
|
|
@@ -922,14 +677,14 @@ async function connectInspector(options) {
|
|
|
922
677
|
return;
|
|
923
678
|
}
|
|
924
679
|
const params = raw;
|
|
925
|
-
const event = toPauseEvent(params,
|
|
680
|
+
const event = toPauseEvent(params, performance.now());
|
|
926
681
|
if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
|
|
927
682
|
pauseBuffer.shift();
|
|
928
683
|
}
|
|
929
684
|
pauseBuffer.push(event);
|
|
930
685
|
});
|
|
931
686
|
client.on("Debugger.resumed", () => {
|
|
932
|
-
debuggerState.lastResumedAtMs =
|
|
687
|
+
debuggerState.lastResumedAtMs = performance.now();
|
|
933
688
|
});
|
|
934
689
|
await client.send("Runtime.enable");
|
|
935
690
|
await client.send("Debugger.enable");
|
|
@@ -950,91 +705,6 @@ async function connectInspector(options) {
|
|
|
950
705
|
};
|
|
951
706
|
}
|
|
952
707
|
|
|
953
|
-
// src/cli/output.ts
|
|
954
|
-
import process from "process";
|
|
955
|
-
function writeJson(value) {
|
|
956
|
-
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
957
|
-
`);
|
|
958
|
-
}
|
|
959
|
-
function writeHumanSnapshot(snapshot) {
|
|
960
|
-
const pausedDuration = snapshot.pausedDurationMs === null ? "unknown" : `${snapshot.pausedDurationMs.toFixed(1)}ms`;
|
|
961
|
-
const lines = [];
|
|
962
|
-
lines.push(
|
|
963
|
-
`Snapshot @ ${snapshot.capturedAt}`,
|
|
964
|
-
` reason: ${snapshot.reason}`,
|
|
965
|
-
` paused: ${pausedDuration}`
|
|
966
|
-
);
|
|
967
|
-
if (snapshot.topFrame) {
|
|
968
|
-
appendFrameLines(lines, snapshot);
|
|
969
|
-
}
|
|
970
|
-
if (snapshot.captures.length > 0) {
|
|
971
|
-
lines.push(" captures:");
|
|
972
|
-
for (const capture of snapshot.captures) {
|
|
973
|
-
const detail = capture.error ?? capture.value ?? "undefined";
|
|
974
|
-
lines.push(` ${capture.expression} = ${detail}`);
|
|
975
|
-
}
|
|
976
|
-
}
|
|
977
|
-
process.stdout.write(`${lines.join("\n")}
|
|
978
|
-
`);
|
|
979
|
-
}
|
|
980
|
-
function appendFrameLines(lines, snapshot) {
|
|
981
|
-
const frame = snapshot.topFrame;
|
|
982
|
-
if (frame === void 0) {
|
|
983
|
-
return;
|
|
984
|
-
}
|
|
985
|
-
const fnName = frame.functionName.length === 0 ? "(anonymous)" : frame.functionName;
|
|
986
|
-
const sourceUrl = frame.url !== void 0 && frame.url.length > 0 ? frame.url : "(unknown)";
|
|
987
|
-
lines.push(
|
|
988
|
-
` frame: ${fnName} ${sourceUrl}:${frame.line.toString()}:${frame.column.toString()}`
|
|
989
|
-
);
|
|
990
|
-
if (frame.scopes === void 0) {
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
for (const scope of frame.scopes) {
|
|
994
|
-
lines.push(` scope ${scope.type} (${scope.variables.length.toString()} vars):`);
|
|
995
|
-
for (const variable of scope.variables) {
|
|
996
|
-
lines.push(` ${variable.name} = ${variable.value}`);
|
|
997
|
-
}
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
function writeLogEvent(event, json) {
|
|
1001
|
-
if (json) {
|
|
1002
|
-
process.stdout.write(`${JSON.stringify(event)}
|
|
1003
|
-
`);
|
|
1004
|
-
return;
|
|
1005
|
-
}
|
|
1006
|
-
if (event.error !== void 0) {
|
|
1007
|
-
process.stdout.write(`[${event.ts}] ${event.at} !err ${event.error}
|
|
1008
|
-
`);
|
|
1009
|
-
return;
|
|
1010
|
-
}
|
|
1011
|
-
process.stdout.write(`[${event.ts}] ${event.at} ${event.value ?? ""}
|
|
1012
|
-
`);
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
// src/cf/tunnel.ts
|
|
1016
|
-
import { startDebugger } from "@saptools/cf-debugger";
|
|
1017
|
-
async function openCfTunnel(target) {
|
|
1018
|
-
const opts = {
|
|
1019
|
-
region: target.region,
|
|
1020
|
-
org: target.org,
|
|
1021
|
-
space: target.space,
|
|
1022
|
-
app: target.app,
|
|
1023
|
-
...target.tunnelReadyTimeoutMs === void 0 ? {} : { tunnelReadyTimeoutMs: target.tunnelReadyTimeoutMs },
|
|
1024
|
-
...target.preferredPort === void 0 ? {} : { preferredPort: target.preferredPort },
|
|
1025
|
-
...target.verbose === void 0 ? {} : { verbose: target.verbose },
|
|
1026
|
-
...target.signal === void 0 ? {} : { signal: target.signal }
|
|
1027
|
-
};
|
|
1028
|
-
const handle = await startDebugger(opts);
|
|
1029
|
-
return {
|
|
1030
|
-
localPort: handle.session.localPort,
|
|
1031
|
-
handle,
|
|
1032
|
-
dispose: async () => {
|
|
1033
|
-
await handle.dispose();
|
|
1034
|
-
}
|
|
1035
|
-
};
|
|
1036
|
-
}
|
|
1037
|
-
|
|
1038
708
|
// src/cli/target.ts
|
|
1039
709
|
init_types();
|
|
1040
710
|
|
|
@@ -1134,67 +804,293 @@ async function handleAttach(opts) {
|
|
|
1134
804
|
await tunnel.dispose();
|
|
1135
805
|
}
|
|
1136
806
|
}
|
|
1137
|
-
|
|
1138
|
-
// src/cli/commands/eval.ts
|
|
1139
|
-
import process3 from "process";
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
807
|
+
|
|
808
|
+
// src/cli/commands/eval.ts
|
|
809
|
+
import process3 from "process";
|
|
810
|
+
|
|
811
|
+
// src/inspector/runtime.ts
|
|
812
|
+
init_types();
|
|
813
|
+
async function resume(session) {
|
|
814
|
+
await session.client.send("Debugger.resume");
|
|
815
|
+
}
|
|
816
|
+
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
817
|
+
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
818
|
+
callFrameId,
|
|
819
|
+
expression,
|
|
820
|
+
returnByValue: false,
|
|
821
|
+
generatePreview: true,
|
|
822
|
+
silent: true
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
async function evaluateGlobal(session, expression) {
|
|
826
|
+
return await session.client.send("Runtime.evaluate", {
|
|
827
|
+
expression,
|
|
828
|
+
returnByValue: false,
|
|
829
|
+
generatePreview: true,
|
|
830
|
+
silent: true
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
function listScripts(session) {
|
|
834
|
+
return [...session.scripts.values()];
|
|
835
|
+
}
|
|
836
|
+
async function validateExpression(session, expression) {
|
|
837
|
+
const result = await session.client.send("Runtime.compileScript", {
|
|
838
|
+
expression,
|
|
839
|
+
sourceURL: "<cf-inspector-validate>",
|
|
840
|
+
persistScript: false
|
|
841
|
+
});
|
|
842
|
+
if (result.exceptionDetails === void 0) {
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
const description = typeof result.exceptionDetails.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails.text === "string" ? result.exceptionDetails.text : "expression failed to compile";
|
|
846
|
+
throw new CfInspectorError("INVALID_EXPRESSION", description);
|
|
847
|
+
}
|
|
848
|
+
async function getProperties(session, objectId) {
|
|
849
|
+
const result = await session.client.send("Runtime.getProperties", {
|
|
850
|
+
objectId,
|
|
851
|
+
ownProperties: true,
|
|
852
|
+
accessorPropertiesOnly: false,
|
|
853
|
+
generatePreview: true
|
|
854
|
+
});
|
|
855
|
+
if (!Array.isArray(result.result)) {
|
|
856
|
+
return [];
|
|
857
|
+
}
|
|
858
|
+
return result.result;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// src/cli/commands/eval.ts
|
|
862
|
+
async function handleEval(opts) {
|
|
863
|
+
const target = resolveTarget(opts);
|
|
864
|
+
const result = await withSession(target, async (session) => {
|
|
865
|
+
return await evaluateGlobal(session, opts.expr);
|
|
866
|
+
});
|
|
867
|
+
if (opts.json) {
|
|
868
|
+
writeJson(result);
|
|
869
|
+
if (result.exceptionDetails !== void 0) {
|
|
870
|
+
process3.exitCode = 1;
|
|
871
|
+
}
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
writeHumanEvalResult(result);
|
|
875
|
+
}
|
|
876
|
+
function writeHumanEvalResult(result) {
|
|
877
|
+
if (result.exceptionDetails !== void 0) {
|
|
878
|
+
const detail = typeof result.exceptionDetails.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails.text === "string" ? result.exceptionDetails.text : "evaluation failed";
|
|
879
|
+
process3.stderr.write(`${detail}
|
|
880
|
+
`);
|
|
881
|
+
process3.exitCode = 1;
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
const inner = result.result;
|
|
885
|
+
if (inner === void 0) {
|
|
886
|
+
process3.stdout.write("\n");
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
if (typeof inner.value === "string") {
|
|
890
|
+
process3.stdout.write(`${inner.value}
|
|
891
|
+
`);
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
if (typeof inner.description === "string") {
|
|
895
|
+
process3.stdout.write(`${inner.description}
|
|
896
|
+
`);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
process3.stdout.write(`${JSON.stringify(inner.value)}
|
|
900
|
+
`);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// src/cli/commands/listScripts.ts
|
|
904
|
+
import process4 from "process";
|
|
905
|
+
async function handleListScripts(opts) {
|
|
906
|
+
const target = resolveTarget(opts);
|
|
907
|
+
const scripts = await withSession(target, (session) => Promise.resolve(listScripts(session)));
|
|
908
|
+
if (opts.json) {
|
|
909
|
+
writeJson(scripts);
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
for (const script of scripts) {
|
|
913
|
+
process4.stdout.write(`${script.scriptId} ${script.url}
|
|
914
|
+
`);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
// src/cli/commands/log.ts
|
|
919
|
+
import process6 from "process";
|
|
920
|
+
|
|
921
|
+
// src/pathMapper.ts
|
|
922
|
+
init_types();
|
|
923
|
+
var REGEX_PREFIX = "regex:";
|
|
924
|
+
var REGEX_FLAGS_PATTERN = /^[dgimsuvy]*$/;
|
|
925
|
+
var TS_JS_EXT_PATTERN = /\.(?:ts|js|mts|mjs|cts|cjs)$/i;
|
|
926
|
+
function parseBreakpointSpec(input) {
|
|
927
|
+
const idx = input.lastIndexOf(":");
|
|
928
|
+
if (idx <= 0 || idx === input.length - 1) {
|
|
929
|
+
throw new CfInspectorError(
|
|
930
|
+
"INVALID_BREAKPOINT",
|
|
931
|
+
`Breakpoint must be in 'file:line' form, received: "${input}"`
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
const file = input.slice(0, idx).trim();
|
|
935
|
+
const lineRaw = input.slice(idx + 1).trim();
|
|
936
|
+
const line = Number.parseInt(lineRaw, 10);
|
|
937
|
+
if (!Number.isInteger(line) || line <= 0 || line.toString() !== lineRaw) {
|
|
938
|
+
throw new CfInspectorError(
|
|
939
|
+
"INVALID_BREAKPOINT",
|
|
940
|
+
`Breakpoint line must be a positive integer, received: "${lineRaw}"`
|
|
941
|
+
);
|
|
942
|
+
}
|
|
943
|
+
if (file.length === 0) {
|
|
944
|
+
throw new CfInspectorError(
|
|
945
|
+
"INVALID_BREAKPOINT",
|
|
946
|
+
`Breakpoint file path is empty in "${input}"`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
return { file, line };
|
|
950
|
+
}
|
|
951
|
+
function parseRemoteRoot(value) {
|
|
952
|
+
const trimmed = value?.trim();
|
|
953
|
+
if (trimmed === void 0 || trimmed.length === 0) {
|
|
954
|
+
return { kind: "none" };
|
|
955
|
+
}
|
|
956
|
+
if (trimmed.startsWith(REGEX_PREFIX)) {
|
|
957
|
+
return toRegex(trimmed.slice(REGEX_PREFIX.length), "");
|
|
958
|
+
}
|
|
959
|
+
const slashRegex = parseSlashDelimited(trimmed);
|
|
960
|
+
if (slashRegex !== void 0) {
|
|
961
|
+
return toRegex(slashRegex.pattern, slashRegex.flags);
|
|
962
|
+
}
|
|
963
|
+
return { kind: "literal", value: stripTrailingSlash(trimmed) };
|
|
964
|
+
}
|
|
965
|
+
function toRegex(pattern, flags) {
|
|
966
|
+
try {
|
|
967
|
+
const regex = new RegExp(pattern, flags);
|
|
968
|
+
return { kind: "regex", pattern, flags, regex };
|
|
969
|
+
} catch (err) {
|
|
970
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
971
|
+
throw new CfInspectorError(
|
|
972
|
+
"INVALID_REMOTE_ROOT",
|
|
973
|
+
`Failed to compile remote-root regex "${pattern}" with flags "${flags}": ${message}`
|
|
974
|
+
);
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
function parseSlashDelimited(value) {
|
|
978
|
+
if (!value.startsWith("/")) {
|
|
979
|
+
return void 0;
|
|
980
|
+
}
|
|
981
|
+
const closing = findLastUnescapedSlash(value);
|
|
982
|
+
if (closing <= 0) {
|
|
983
|
+
return void 0;
|
|
984
|
+
}
|
|
985
|
+
const flags = value.slice(closing + 1);
|
|
986
|
+
if (flags.length === 0 || !REGEX_FLAGS_PATTERN.test(flags)) {
|
|
987
|
+
return void 0;
|
|
988
|
+
}
|
|
989
|
+
return { pattern: value.slice(1, closing), flags };
|
|
990
|
+
}
|
|
991
|
+
function findLastUnescapedSlash(value) {
|
|
992
|
+
for (let i = value.length - 1; i > 0; i--) {
|
|
993
|
+
if (value[i] === "/" && !isEscaped(value, i)) {
|
|
994
|
+
return i;
|
|
1149
995
|
}
|
|
1150
|
-
return;
|
|
1151
996
|
}
|
|
1152
|
-
|
|
997
|
+
return -1;
|
|
1153
998
|
}
|
|
1154
|
-
function
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
999
|
+
function isEscaped(value, idx) {
|
|
1000
|
+
let backslashes = 0;
|
|
1001
|
+
for (let i = idx - 1; i >= 0; i--) {
|
|
1002
|
+
if (value[i] === "\\") {
|
|
1003
|
+
backslashes++;
|
|
1004
|
+
} else {
|
|
1005
|
+
break;
|
|
1006
|
+
}
|
|
1161
1007
|
}
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1008
|
+
return backslashes % 2 === 1;
|
|
1009
|
+
}
|
|
1010
|
+
function stripTrailingSlash(value) {
|
|
1011
|
+
if (value.length > 1 && value.endsWith("/")) {
|
|
1012
|
+
return value.slice(0, -1);
|
|
1166
1013
|
}
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1014
|
+
return value;
|
|
1015
|
+
}
|
|
1016
|
+
function normalizeRegexRootPattern(pattern) {
|
|
1017
|
+
const withoutStartAnchor = pattern.startsWith("^") ? pattern.slice(1) : pattern;
|
|
1018
|
+
const withoutEndAnchor = withoutStartAnchor.endsWith("$") && !isEscaped(withoutStartAnchor, withoutStartAnchor.length - 1) ? withoutStartAnchor.slice(0, -1) : withoutStartAnchor;
|
|
1019
|
+
return stripTrailingSlash(withoutEndAnchor);
|
|
1020
|
+
}
|
|
1021
|
+
function buildFileUrlRegex(rootPattern, tail) {
|
|
1022
|
+
const separator = rootPattern.endsWith("/") ? "" : "/";
|
|
1023
|
+
return `^file://${rootPattern}${separator}${tail}$`;
|
|
1024
|
+
}
|
|
1025
|
+
function escapeRegExp(value) {
|
|
1026
|
+
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
1027
|
+
}
|
|
1028
|
+
function normalizeRelative(file) {
|
|
1029
|
+
return file.replaceAll(/^[./\\]+/g, "").replaceAll("\\", "/");
|
|
1030
|
+
}
|
|
1031
|
+
function dropExtension(file) {
|
|
1032
|
+
const match = TS_JS_EXT_PATTERN.exec(file);
|
|
1033
|
+
if (!match) {
|
|
1034
|
+
return { stem: file, matchedExt: false };
|
|
1171
1035
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1036
|
+
return { stem: file.slice(0, match.index), matchedExt: true };
|
|
1037
|
+
}
|
|
1038
|
+
var EXT_GROUP = String.raw`\.(?:ts|js|mts|mjs|cts|cjs)`;
|
|
1039
|
+
var OPTIONAL_EXT_GROUP = String.raw`(?:\.(?:ts|js|mts|mjs|cts|cjs))?`;
|
|
1040
|
+
function buildBreakpointUrlRegex(input) {
|
|
1041
|
+
const normalized = normalizeRelative(input.file);
|
|
1042
|
+
const { stem, matchedExt } = dropExtension(normalized);
|
|
1043
|
+
const escapedStem = escapeRegExp(stem);
|
|
1044
|
+
const tail = matchedExt ? `${escapedStem}${EXT_GROUP}` : `${escapedStem}${OPTIONAL_EXT_GROUP}`;
|
|
1045
|
+
switch (input.remoteRoot.kind) {
|
|
1046
|
+
case "none": {
|
|
1047
|
+
return `(?:^|/)${tail}$`;
|
|
1048
|
+
}
|
|
1049
|
+
case "literal": {
|
|
1050
|
+
const escapedRoot = escapeRegExp(input.remoteRoot.value);
|
|
1051
|
+
return buildFileUrlRegex(escapedRoot, tail);
|
|
1052
|
+
}
|
|
1053
|
+
case "regex": {
|
|
1054
|
+
const rootPattern = normalizeRegexRootPattern(input.remoteRoot.pattern);
|
|
1055
|
+
return buildFileUrlRegex(rootPattern, tail);
|
|
1056
|
+
}
|
|
1176
1057
|
}
|
|
1177
|
-
process3.stdout.write(`${JSON.stringify(inner.value)}
|
|
1178
|
-
`);
|
|
1179
1058
|
}
|
|
1180
1059
|
|
|
1181
|
-
// src/
|
|
1182
|
-
|
|
1183
|
-
async function
|
|
1184
|
-
const
|
|
1185
|
-
const
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1060
|
+
// src/inspector/breakpoints.ts
|
|
1061
|
+
init_types();
|
|
1062
|
+
async function setBreakpoint(session, input) {
|
|
1063
|
+
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
1064
|
+
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
1065
|
+
const params = {
|
|
1066
|
+
lineNumber: input.line - 1,
|
|
1067
|
+
urlRegex
|
|
1068
|
+
};
|
|
1069
|
+
if (input.condition !== void 0 && input.condition.length > 0) {
|
|
1070
|
+
params["condition"] = input.condition;
|
|
1189
1071
|
}
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1072
|
+
const result = await session.client.send(
|
|
1073
|
+
"Debugger.setBreakpointByUrl",
|
|
1074
|
+
params
|
|
1075
|
+
);
|
|
1076
|
+
const breakpointId = asString(result.breakpointId);
|
|
1077
|
+
if (breakpointId.length === 0) {
|
|
1078
|
+
throw new CfInspectorError(
|
|
1079
|
+
"CDP_REQUEST_FAILED",
|
|
1080
|
+
`setBreakpointByUrl did not return a breakpointId for ${input.file}:${input.line.toString()}`
|
|
1081
|
+
);
|
|
1193
1082
|
}
|
|
1083
|
+
return {
|
|
1084
|
+
breakpointId,
|
|
1085
|
+
file: input.file,
|
|
1086
|
+
line: input.line,
|
|
1087
|
+
urlRegex,
|
|
1088
|
+
resolvedLocations: toResolvedLocations(result.locations)
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
async function removeBreakpoint(session, breakpointId) {
|
|
1092
|
+
await session.client.send("Debugger.removeBreakpoint", { breakpointId });
|
|
1194
1093
|
}
|
|
1195
|
-
|
|
1196
|
-
// src/cli/commands/log.ts
|
|
1197
|
-
import process6 from "process";
|
|
1198
1094
|
|
|
1199
1095
|
// src/logpoint/condition.ts
|
|
1200
1096
|
import { randomBytes } from "crypto";
|
|
@@ -1447,6 +1343,112 @@ function writeLogSummary(stoppedReason, emitted, json) {
|
|
|
1447
1343
|
import { performance as performance3 } from "perf_hooks";
|
|
1448
1344
|
import process7 from "process";
|
|
1449
1345
|
|
|
1346
|
+
// src/inspector/pause.ts
|
|
1347
|
+
init_types();
|
|
1348
|
+
import { performance as performance2 } from "perf_hooks";
|
|
1349
|
+
function pauseMatches(pause, breakpointIds) {
|
|
1350
|
+
if (breakpointIds === void 0 || breakpointIds.length === 0) {
|
|
1351
|
+
return true;
|
|
1352
|
+
}
|
|
1353
|
+
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
1354
|
+
}
|
|
1355
|
+
function remainingUntil(deadlineMs) {
|
|
1356
|
+
return Math.max(0, deadlineMs - performance2.now());
|
|
1357
|
+
}
|
|
1358
|
+
function hasResumedSincePause(session, pause) {
|
|
1359
|
+
const pauseAt = pause.receivedAtMs;
|
|
1360
|
+
const resumedAt = session.debuggerState.lastResumedAtMs;
|
|
1361
|
+
return pauseAt !== void 0 && resumedAt !== void 0 && resumedAt >= pauseAt;
|
|
1362
|
+
}
|
|
1363
|
+
function throwBreakpointTimeout(timeoutMs) {
|
|
1364
|
+
throw new CfInspectorError(
|
|
1365
|
+
"BREAKPOINT_NOT_HIT",
|
|
1366
|
+
`Timed out waiting for matching Debugger.paused after ${timeoutMs.toString()}ms`
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
function throwUnrelatedPauseTimeout(pause, timeoutMs) {
|
|
1370
|
+
throw new CfInspectorError(
|
|
1371
|
+
"UNRELATED_PAUSE_TIMEOUT",
|
|
1372
|
+
`Target stayed paused by another debugger event before this command's breakpoint could hit within ${timeoutMs.toString()}ms`,
|
|
1373
|
+
pauseDetail(pause)
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeoutMs) {
|
|
1377
|
+
if (hasResumedSincePause(session, pause)) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
const remainingMs = remainingUntil(deadlineMs);
|
|
1381
|
+
if (remainingMs <= 0) {
|
|
1382
|
+
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1383
|
+
}
|
|
1384
|
+
try {
|
|
1385
|
+
await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
|
|
1386
|
+
session.debuggerState.lastResumedAtMs = performance2.now();
|
|
1387
|
+
} catch (err) {
|
|
1388
|
+
if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
|
|
1389
|
+
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1390
|
+
}
|
|
1391
|
+
throw err;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
async function handleUnmatchedPause(session, pause, options, deadlineMs) {
|
|
1395
|
+
if (options.unmatchedPausePolicy === "fail") {
|
|
1396
|
+
throw new CfInspectorError(
|
|
1397
|
+
"UNRELATED_PAUSE",
|
|
1398
|
+
"Target paused before this command's breakpoint was reached",
|
|
1399
|
+
pauseDetail(pause)
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
if (hasResumedSincePause(session, pause)) {
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
options.onUnmatchedPause?.(pause);
|
|
1406
|
+
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
1407
|
+
}
|
|
1408
|
+
async function waitForPause(session, options) {
|
|
1409
|
+
const deadlineMs = performance2.now() + options.timeoutMs;
|
|
1410
|
+
const buffer = session.pauseBuffer;
|
|
1411
|
+
while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
|
|
1412
|
+
while (buffer.length > 0) {
|
|
1413
|
+
const buffered = buffer.shift();
|
|
1414
|
+
if (buffered === void 0) {
|
|
1415
|
+
continue;
|
|
1416
|
+
}
|
|
1417
|
+
if (pauseMatches(buffered, options.breakpointIds)) {
|
|
1418
|
+
return buffered;
|
|
1419
|
+
}
|
|
1420
|
+
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
1421
|
+
}
|
|
1422
|
+
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
1423
|
+
if (pauseMatches(pause, options.breakpointIds)) {
|
|
1424
|
+
return pause;
|
|
1425
|
+
}
|
|
1426
|
+
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
1427
|
+
}
|
|
1428
|
+
throwBreakpointTimeout(options.timeoutMs);
|
|
1429
|
+
}
|
|
1430
|
+
async function waitForLivePause(session, options, deadlineMs) {
|
|
1431
|
+
const remainingMs = remainingUntil(deadlineMs);
|
|
1432
|
+
if (remainingMs <= 0) {
|
|
1433
|
+
throwBreakpointTimeout(options.timeoutMs);
|
|
1434
|
+
}
|
|
1435
|
+
session.pauseWaitGate.active = true;
|
|
1436
|
+
let receivedAtMs;
|
|
1437
|
+
let params;
|
|
1438
|
+
try {
|
|
1439
|
+
params = await session.client.waitFor("Debugger.paused", {
|
|
1440
|
+
timeoutMs: remainingMs,
|
|
1441
|
+
predicate: () => {
|
|
1442
|
+
receivedAtMs = performance2.now();
|
|
1443
|
+
return true;
|
|
1444
|
+
}
|
|
1445
|
+
});
|
|
1446
|
+
} finally {
|
|
1447
|
+
session.pauseWaitGate.active = false;
|
|
1448
|
+
}
|
|
1449
|
+
return toPauseEvent(params, receivedAtMs ?? performance2.now());
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1450
1452
|
// src/snapshot/values.ts
|
|
1451
1453
|
init_types();
|
|
1452
1454
|
var DEFAULT_MAX_VALUE_LENGTH = 4096;
|