@saptools/cf-inspector 0.4.12 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -289,96 +289,290 @@ function buildBreakpointUrlRegex(input) {
289
289
  init_types();
290
290
 
291
291
  // src/inspector/conversions.ts
292
+ var INTERNAL_SLOT_SUBTYPES = /* @__PURE__ */ new Set([
293
+ "regexp",
294
+ "date",
295
+ "map",
296
+ "set",
297
+ "weakmap",
298
+ "weakset",
299
+ "iterator",
300
+ "generator",
301
+ "promise",
302
+ "typedarray",
303
+ "arraybuffer",
304
+ "dataview",
305
+ "webassemblymemory",
306
+ "wasmvalue",
307
+ "trustedtype"
308
+ ]);
292
309
  function asString(value, fallback = "") {
293
310
  return typeof value === "string" ? value : fallback;
294
311
  }
295
312
  function asNumber(value, fallback = 0) {
296
313
  return typeof value === "number" && Number.isFinite(value) ? value : fallback;
297
314
  }
315
+ function isRecord(value) {
316
+ return typeof value === "object" && value !== null && !Array.isArray(value);
317
+ }
298
318
  function nonEmptyString(value) {
299
319
  return typeof value === "string" && value.length > 0 ? value : void 0;
300
320
  }
321
+ function optionalNumber(value) {
322
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
323
+ }
324
+ function optionalCoordinate(value) {
325
+ const number = optionalNumber(value);
326
+ return number !== void 0 && Number.isSafeInteger(number) && number >= 0 ? number : void 0;
327
+ }
328
+ function optionalBoolean(value) {
329
+ return typeof value === "boolean" ? value : void 0;
330
+ }
331
+ function toScriptLocation(value) {
332
+ if (!isRecord(value)) {
333
+ return void 0;
334
+ }
335
+ const scriptId = nonEmptyString(value["scriptId"]);
336
+ const lineNumber = optionalCoordinate(value["lineNumber"]);
337
+ if (scriptId === void 0 || lineNumber === void 0) {
338
+ return void 0;
339
+ }
340
+ const rawColumnNumber = value["columnNumber"];
341
+ const columnNumber = optionalCoordinate(rawColumnNumber);
342
+ if (rawColumnNumber !== void 0 && columnNumber === void 0) {
343
+ return void 0;
344
+ }
345
+ return columnNumber === void 0 ? { scriptId, lineNumber } : { scriptId, lineNumber, columnNumber };
346
+ }
301
347
  function toResolvedLocations(value) {
302
348
  if (!Array.isArray(value)) {
303
349
  return [];
304
350
  }
305
351
  return value.flatMap((entry) => {
306
- if (typeof entry !== "object" || entry === null) {
307
- return [];
308
- }
309
- const candidate = entry;
310
- const scriptId = asString(candidate.scriptId);
311
- if (scriptId.length === 0) {
352
+ const location = toScriptLocation(entry);
353
+ if (location === void 0 || !isRecord(entry)) {
312
354
  return [];
313
355
  }
314
- const url = typeof candidate.url === "string" ? candidate.url : void 0;
315
- const lineNumber = asNumber(candidate.lineNumber);
316
- const result = url === void 0 ? { scriptId, lineNumber, columnNumber: asNumber(candidate.columnNumber) } : { scriptId, url, lineNumber, columnNumber: asNumber(candidate.columnNumber) };
317
- return [result];
356
+ const url = typeof entry["url"] === "string" ? entry["url"] : void 0;
357
+ return [url === void 0 ? location : { ...location, url }];
318
358
  });
319
359
  }
320
- function toScopeChain(value) {
360
+ function toBreakLocations(value) {
321
361
  if (!Array.isArray(value)) {
322
362
  return [];
323
363
  }
324
364
  return value.flatMap((entry) => {
325
- if (typeof entry !== "object" || entry === null) {
326
- return [];
327
- }
328
- const candidate = entry;
329
- const type = asString(candidate.type);
330
- if (type.length === 0) {
365
+ const location = toScriptLocation(entry);
366
+ if (location === void 0 || !isRecord(entry)) {
331
367
  return [];
332
368
  }
333
- const objectId = typeof candidate.object?.objectId === "string" ? candidate.object.objectId : void 0;
334
- const name = typeof candidate.name === "string" ? candidate.name : void 0;
335
- const base = name === void 0 ? { type } : { type, name };
336
- return [objectId === void 0 ? base : { ...base, objectId }];
369
+ const type = nonEmptyString(entry["type"]);
370
+ return [type === void 0 ? location : { ...location, type }];
337
371
  });
338
372
  }
373
+ function remoteCompleteness(subtype) {
374
+ if (subtype === "proxy") {
375
+ return "unavailable";
376
+ }
377
+ return subtype !== void 0 && INTERNAL_SLOT_SUBTYPES.has(subtype) ? "truncated" : void 0;
378
+ }
379
+ function optionalOwnField(key, value) {
380
+ return Object.hasOwn(value, key) ? { [key]: value[key] } : {};
381
+ }
382
+ function toRemoteObject(value) {
383
+ if (!isRecord(value)) {
384
+ return void 0;
385
+ }
386
+ const type = nonEmptyString(value["type"]);
387
+ if (type === void 0) {
388
+ return void 0;
389
+ }
390
+ const subtype = nonEmptyString(value["subtype"]);
391
+ const completeness = remoteCompleteness(subtype);
392
+ return {
393
+ type,
394
+ ...subtype === void 0 ? {} : { subtype },
395
+ ...optionalTextField("className", value["className"]),
396
+ ...completeness === void 0 ? {} : { completeness },
397
+ ...optionalOwnField("value", value),
398
+ ...optionalTextField("unserializableValue", value["unserializableValue"]),
399
+ ...optionalTextField("description", value["description"]),
400
+ ...optionalOwnField("deepSerializedValue", value),
401
+ ...optionalTextField("objectId", value["objectId"]),
402
+ ...optionalOwnField("preview", value),
403
+ ...optionalOwnField("customPreview", value)
404
+ };
405
+ }
406
+ function optionalTextField(key, value) {
407
+ return typeof value === "string" ? { [key]: value } : {};
408
+ }
409
+ function toScope(value) {
410
+ if (!isRecord(value)) {
411
+ return void 0;
412
+ }
413
+ const type = nonEmptyString(value["type"]);
414
+ if (type === void 0) {
415
+ return void 0;
416
+ }
417
+ const object = toRemoteObject(value["object"]);
418
+ const name = nonEmptyString(value["name"]);
419
+ const startLocation = toScriptLocation(value["startLocation"]);
420
+ const endLocation = toScriptLocation(value["endLocation"]);
421
+ return {
422
+ type,
423
+ ...name === void 0 ? {} : { name },
424
+ ...object === void 0 ? {} : { object },
425
+ ...object?.objectId === void 0 ? {} : { objectId: object.objectId },
426
+ ...startLocation === void 0 ? {} : { startLocation },
427
+ ...endLocation === void 0 ? {} : { endLocation }
428
+ };
429
+ }
430
+ function toScopeChain(value) {
431
+ return Array.isArray(value) ? value.flatMap((entry) => {
432
+ const scope = toScope(entry);
433
+ return scope === void 0 ? [] : [scope];
434
+ }) : [];
435
+ }
339
436
  function resolveCallFrameUrl(frame, scripts) {
340
- const direct = nonEmptyString(frame.url);
437
+ const direct = nonEmptyString(frame["url"]);
341
438
  if (direct !== void 0) {
342
439
  return direct;
343
440
  }
344
- const scriptId = nonEmptyString(frame.location?.scriptId);
345
- if (scriptId === void 0) {
441
+ const scriptId = toScriptLocation(frame["location"])?.scriptId;
442
+ return scriptId === void 0 ? void 0 : nonEmptyString(scripts?.get(scriptId)?.url);
443
+ }
444
+ function toCallFrameMetadata(candidate, location, scripts) {
445
+ const functionLocation = toScriptLocation(candidate["functionLocation"]);
446
+ const thisObject = toRemoteObject(candidate["this"]);
447
+ const returnValue = toRemoteObject(candidate["returnValue"]);
448
+ const url = resolveCallFrameUrl(candidate, scripts);
449
+ return {
450
+ ...location === void 0 ? {} : { scriptId: location.scriptId },
451
+ ...functionLocation === void 0 ? {} : { functionLocation },
452
+ ...url === void 0 ? {} : { url },
453
+ ...thisObject === void 0 ? {} : { thisObject },
454
+ ...returnValue === void 0 ? {} : { returnValue }
455
+ };
456
+ }
457
+ function toCallFrame(value, scripts) {
458
+ if (!isRecord(value)) {
459
+ return void 0;
460
+ }
461
+ const callFrameId = nonEmptyString(value["callFrameId"]);
462
+ if (callFrameId === void 0) {
346
463
  return void 0;
347
464
  }
348
- return nonEmptyString(scripts?.get(scriptId)?.url);
465
+ const location = toScriptLocation(value["location"]);
466
+ return {
467
+ callFrameId,
468
+ functionName: asString(value["functionName"]),
469
+ ...toCallFrameMetadata(value, location, scripts),
470
+ lineNumber: location?.lineNumber ?? 0,
471
+ columnNumber: location?.columnNumber ?? 0,
472
+ scopeChain: toScopeChain(value["scopeChain"])
473
+ };
349
474
  }
350
475
  function toCallFrames(value, scripts) {
351
- if (!Array.isArray(value)) {
352
- return [];
476
+ return Array.isArray(value) ? value.flatMap((entry) => {
477
+ const frame = toCallFrame(entry, scripts);
478
+ return frame === void 0 ? [] : [frame];
479
+ }) : [];
480
+ }
481
+ function toStackTraceId(value) {
482
+ if (!isRecord(value)) {
483
+ return void 0;
353
484
  }
354
- return value.flatMap((entry) => {
355
- if (typeof entry !== "object" || entry === null) {
356
- return [];
357
- }
358
- const candidate = entry;
359
- const callFrameId = asString(candidate.callFrameId);
360
- if (callFrameId.length === 0) {
361
- return [];
362
- }
363
- const url = resolveCallFrameUrl(candidate, scripts);
364
- const base = {
365
- callFrameId,
366
- functionName: asString(candidate.functionName),
367
- lineNumber: asNumber(candidate.location?.lineNumber),
368
- columnNumber: asNumber(candidate.location?.columnNumber),
369
- scopeChain: toScopeChain(candidate.scopeChain)
370
- };
371
- return [url === void 0 ? base : { ...base, url }];
485
+ const id = nonEmptyString(value["id"]);
486
+ if (id === void 0) {
487
+ return void 0;
488
+ }
489
+ const debuggerId = nonEmptyString(value["debuggerId"]);
490
+ return debuggerId === void 0 ? { id } : { id, debuggerId };
491
+ }
492
+ function toStackTraceFrame(value) {
493
+ if (!isRecord(value)) {
494
+ return void 0;
495
+ }
496
+ const scriptId = nonEmptyString(value["scriptId"]);
497
+ if (scriptId === void 0) {
498
+ return void 0;
499
+ }
500
+ return {
501
+ functionName: asString(value["functionName"]),
502
+ scriptId,
503
+ url: asString(value["url"]),
504
+ lineNumber: asNumber(value["lineNumber"]),
505
+ columnNumber: asNumber(value["columnNumber"])
506
+ };
507
+ }
508
+ function toStackTrace(value) {
509
+ if (!isRecord(value) || !Array.isArray(value["callFrames"])) {
510
+ return void 0;
511
+ }
512
+ const callFrames = value["callFrames"].flatMap((entry) => {
513
+ const frame = toStackTraceFrame(entry);
514
+ return frame === void 0 ? [] : [frame];
372
515
  });
516
+ const description = nonEmptyString(value["description"]);
517
+ const parent = toStackTrace(value["parent"]);
518
+ const parentId = toStackTraceId(value["parentId"]);
519
+ return {
520
+ callFrames,
521
+ ...description === void 0 ? {} : { description },
522
+ ...parent === void 0 ? {} : { parent },
523
+ ...parentId === void 0 ? {} : { parentId }
524
+ };
373
525
  }
374
- function toPauseEvent(params, receivedAtMs, scripts) {
375
- const base = {
376
- reason: asString(params.reason),
377
- hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
378
- callFrames: toCallFrames(params.callFrames, scripts),
379
- receivedAtMs
526
+ function toPauseEvent(value, receivedAtMs, scripts) {
527
+ const params = isRecord(value) ? value : {};
528
+ const asyncStackTrace = toStackTrace(params["asyncStackTrace"]);
529
+ const asyncStackTraceId = toStackTraceId(params["asyncStackTraceId"]);
530
+ const asyncCallStackTraceId = toStackTraceId(params["asyncCallStackTraceId"]);
531
+ return {
532
+ reason: asString(params["reason"]),
533
+ hitBreakpoints: Array.isArray(params["hitBreakpoints"]) ? params["hitBreakpoints"].filter((id) => typeof id === "string") : [],
534
+ callFrames: toCallFrames(params["callFrames"], scripts),
535
+ receivedAtMs,
536
+ ...params["data"] === void 0 ? {} : { data: params["data"] },
537
+ ...asyncStackTrace === void 0 ? {} : { asyncStackTrace },
538
+ ...asyncStackTraceId === void 0 ? {} : { asyncStackTraceId },
539
+ ...asyncCallStackTraceId === void 0 ? {} : { asyncCallStackTraceId }
540
+ };
541
+ }
542
+ function toScriptInfo(value) {
543
+ if (!isRecord(value)) {
544
+ return void 0;
545
+ }
546
+ const scriptId = nonEmptyString(value["scriptId"]);
547
+ if (scriptId === void 0) {
548
+ return void 0;
549
+ }
550
+ const stackTrace = toStackTrace(value["stackTrace"]);
551
+ return {
552
+ scriptId,
553
+ url: asString(value["url"]),
554
+ ...optionalNumericField("startLine", value["startLine"]),
555
+ ...optionalNumericField("startColumn", value["startColumn"]),
556
+ ...optionalNumericField("endLine", value["endLine"]),
557
+ ...optionalNumericField("endColumn", value["endColumn"]),
558
+ ...optionalNumericField("executionContextId", value["executionContextId"]),
559
+ ...optionalTextField("hash", value["hash"]),
560
+ ...optionalTextField("buildId", value["buildId"]),
561
+ ...value["executionContextAuxData"] === void 0 ? {} : { executionContextAuxData: value["executionContextAuxData"] },
562
+ ...optionalTextField("sourceMapURL", value["sourceMapURL"]),
563
+ ...optionalBooleanField("hasSourceURL", value["hasSourceURL"]),
564
+ ...optionalBooleanField("isModule", value["isModule"]),
565
+ ...optionalNumericField("length", value["length"]),
566
+ ...stackTrace === void 0 ? {} : { stackTrace }
380
567
  };
381
- return params.data === void 0 ? base : { ...base, data: params.data };
568
+ }
569
+ function optionalNumericField(key, value) {
570
+ const number = optionalNumber(value);
571
+ return number === void 0 ? {} : { [key]: number };
572
+ }
573
+ function optionalBooleanField(key, value) {
574
+ const boolean = optionalBoolean(value);
575
+ return boolean === void 0 ? {} : { [key]: boolean };
382
576
  }
383
577
  function topFrameLocation(pause) {
384
578
  const top = pause.callFrames[0];
@@ -470,41 +664,146 @@ async function setBreakpoint(session, input) {
470
664
  async function removeBreakpoint(session, breakpointId) {
471
665
  await session.client.send("Debugger.removeBreakpoint", { breakpointId });
472
666
  }
667
+ function validateCoordinate(value, label) {
668
+ if (!Number.isInteger(value) || value < 0) {
669
+ throw new CfInspectorError(
670
+ "INVALID_ARGUMENT",
671
+ `${label} must be a non-negative integer, received: ${value.toString()}`
672
+ );
673
+ }
674
+ }
675
+ function validateScriptLocation(location, label) {
676
+ if (location.scriptId.trim().length === 0) {
677
+ throw new CfInspectorError("INVALID_ARGUMENT", `${label}.scriptId must not be empty`);
678
+ }
679
+ validateCoordinate(location.lineNumber, `${label}.lineNumber`);
680
+ if (location.columnNumber !== void 0) {
681
+ validateCoordinate(location.columnNumber, `${label}.columnNumber`);
682
+ }
683
+ }
684
+ function isSameScriptLocation(requested, actual) {
685
+ return requested.scriptId === actual.scriptId && requested.lineNumber === actual.lineNumber && (requested.columnNumber ?? 0) === (actual.columnNumber ?? 0);
686
+ }
687
+ async function getPossibleBreakpoints(session, options) {
688
+ validateScriptLocation(options.start, "start");
689
+ if (options.end !== void 0) {
690
+ validateScriptLocation(options.end, "end");
691
+ if (options.end.scriptId !== options.start.scriptId) {
692
+ throw new CfInspectorError("INVALID_ARGUMENT", "start and end must refer to the same scriptId");
693
+ }
694
+ }
695
+ const result = await session.client.send(
696
+ "Debugger.getPossibleBreakpoints",
697
+ {
698
+ start: options.start,
699
+ ...options.end === void 0 ? {} : { end: options.end },
700
+ ...options.restrictToFunction === void 0 ? {} : { restrictToFunction: options.restrictToFunction }
701
+ }
702
+ );
703
+ if (!Array.isArray(result.locations)) {
704
+ throw new CfInspectorError(
705
+ "CDP_REQUEST_FAILED",
706
+ "Debugger.getPossibleBreakpoints did not return a locations array"
707
+ );
708
+ }
709
+ return toBreakLocations(result.locations);
710
+ }
711
+ async function setBreakpointAtLocation(session, input) {
712
+ validateScriptLocation(input.location, "location");
713
+ const result = await session.client.send("Debugger.setBreakpoint", {
714
+ location: input.location,
715
+ ...input.condition === void 0 || input.condition.length === 0 ? {} : { condition: input.condition }
716
+ });
717
+ const breakpointId = asString(result.breakpointId);
718
+ const actualLocation = toScriptLocation(result.actualLocation);
719
+ const wrongLocation = actualLocation !== void 0 && !isSameScriptLocation(input.location, actualLocation);
720
+ if (breakpointId.length === 0 || actualLocation === void 0 || wrongLocation) {
721
+ if (breakpointId.length > 0) {
722
+ try {
723
+ await removeBreakpoint(session, breakpointId);
724
+ } catch {
725
+ }
726
+ }
727
+ if (wrongLocation) {
728
+ throw new CfInspectorError(
729
+ "INVALID_BREAKPOINT",
730
+ "Debugger.setBreakpoint resolved the breakpoint at a different script, line, or column"
731
+ );
732
+ }
733
+ throw new CfInspectorError(
734
+ "CDP_REQUEST_FAILED",
735
+ "Debugger.setBreakpoint did not return a breakpointId and actualLocation"
736
+ );
737
+ }
738
+ return {
739
+ breakpointId,
740
+ requestedLocation: input.location,
741
+ actualLocation
742
+ };
743
+ }
473
744
 
474
745
  // src/inspector/discovery.ts
475
746
  init_types();
476
747
  import { request } from "http";
748
+ import { performance } from "perf_hooks";
749
+ var InvalidDiscoveryPayloadError = class extends CfInspectorError {
750
+ };
477
751
  async function fetchJson(url, timeoutMs) {
478
- return await new Promise((resolve, reject) => {
479
- const req = request(url, { method: "GET" }, (res) => {
480
- const chunks = [];
481
- res.on("data", (chunk) => {
482
- chunks.push(chunk);
483
- });
484
- res.on("end", () => {
485
- try {
486
- resolve(parseJsonResponse(chunks));
487
- } catch (err) {
488
- reject(parseDiscoveryError(url, err));
489
- }
490
- });
491
- res.on("error", (err) => {
492
- reject(newDiscoveryError(`Inspector discovery response error: ${err.message}`));
752
+ const deadline = performance.now() + timeoutMs;
753
+ let lastError;
754
+ while (performance.now() < deadline) {
755
+ try {
756
+ const remainingMs = deadline - performance.now();
757
+ if (remainingMs <= 0) {
758
+ break;
759
+ }
760
+ return await new Promise((resolve, reject) => {
761
+ const req = request(url, { method: "GET" }, (res) => {
762
+ const chunks = [];
763
+ res.on("data", (chunk) => {
764
+ chunks.push(chunk);
765
+ });
766
+ res.on("end", () => {
767
+ try {
768
+ resolve(parseJsonResponse(chunks));
769
+ } catch (err) {
770
+ reject(parseDiscoveryError(url, err));
771
+ }
772
+ });
773
+ res.on("error", (err) => {
774
+ reject(newDiscoveryError(`Inspector discovery response error: ${err.message}`));
775
+ });
776
+ });
777
+ const attemptTimeoutMs = Math.min(2e3, remainingMs);
778
+ req.setTimeout(attemptTimeoutMs, () => {
779
+ req.destroy(
780
+ new CfInspectorError(
781
+ "INSPECTOR_DISCOVERY_FAILED",
782
+ `Inspector discovery at ${url} timed out after ${timeoutMs.toString()}ms`
783
+ )
784
+ );
785
+ });
786
+ req.on("error", (err) => {
787
+ reject(err instanceof CfInspectorError ? err : formatDiscoveryRequestError(url, err));
788
+ });
789
+ req.end();
493
790
  });
494
- });
495
- req.setTimeout(timeoutMs, () => {
496
- req.destroy(
497
- new CfInspectorError(
498
- "INSPECTOR_DISCOVERY_FAILED",
499
- `Inspector discovery at ${url} timed out after ${timeoutMs.toString()}ms`
500
- )
501
- );
502
- });
503
- req.on("error", (err) => {
504
- reject(err instanceof CfInspectorError ? err : formatDiscoveryRequestError(url, err));
505
- });
506
- req.end();
507
- });
791
+ } catch (err) {
792
+ if (err instanceof InvalidDiscoveryPayloadError) {
793
+ throw err;
794
+ }
795
+ lastError = err;
796
+ const now = performance.now();
797
+ if (now < deadline) {
798
+ const sleepMs = Math.min(1e3, deadline - now);
799
+ await new Promise((r) => setTimeout(r, sleepMs));
800
+ }
801
+ }
802
+ }
803
+ if (lastError instanceof Error) {
804
+ throw lastError;
805
+ }
806
+ throw new CfInspectorError("INSPECTOR_DISCOVERY_FAILED", `Inspector discovery at ${url} timed out after ${timeoutMs.toString()}ms`);
508
807
  }
509
808
  function isNodeSystemError(err) {
510
809
  return err instanceof Error;
@@ -540,7 +839,10 @@ function parseJsonResponse(chunks) {
540
839
  }
541
840
  function parseDiscoveryError(url, err) {
542
841
  const message = err instanceof Error ? err.message : String(err);
543
- return newDiscoveryError(`Failed to parse inspector discovery response from ${url}: ${message}`);
842
+ return new InvalidDiscoveryPayloadError(
843
+ "INSPECTOR_DISCOVERY_FAILED",
844
+ `Failed to parse inspector discovery response from ${url}: ${message}`
845
+ );
544
846
  }
545
847
  function newDiscoveryError(message) {
546
848
  return new CfInspectorError("INSPECTOR_DISCOVERY_FAILED", message);
@@ -614,7 +916,7 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
614
916
 
615
917
  // src/inspector/pause.ts
616
918
  init_types();
617
- import { performance } from "perf_hooks";
919
+ import { performance as performance2 } from "perf_hooks";
618
920
  function pauseMatches(pause, breakpointIds, pauseReasons) {
619
921
  if (pauseReasons !== void 0 && pauseReasons.length > 0) {
620
922
  return pauseReasons.includes(pause.reason);
@@ -625,7 +927,12 @@ function pauseMatches(pause, breakpointIds, pauseReasons) {
625
927
  return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
626
928
  }
627
929
  function remainingUntil(deadlineMs) {
628
- return Math.max(0, deadlineMs - performance.now());
930
+ return Math.max(0, deadlineMs - performance2.now());
931
+ }
932
+ function throwIfAborted(signal) {
933
+ if (signal?.aborted === true) {
934
+ throw new CfInspectorError("ABORTED", "Aborted while waiting for Debugger.paused");
935
+ }
629
936
  }
630
937
  function hasResumedSincePause(session, pause) {
631
938
  const pauseAt = pause.receivedAtMs;
@@ -645,20 +952,23 @@ function throwUnrelatedPauseTimeout(pause, timeoutMs) {
645
952
  pauseDetail(pause)
646
953
  );
647
954
  }
648
- async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeoutMs) {
955
+ async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, options) {
649
956
  if (hasResumedSincePause(session, pause)) {
650
957
  return;
651
958
  }
652
959
  const remainingMs = remainingUntil(deadlineMs);
653
960
  if (remainingMs <= 0) {
654
- throwUnrelatedPauseTimeout(pause, timeoutMs);
961
+ throwUnrelatedPauseTimeout(pause, options.timeoutMs);
655
962
  }
656
963
  try {
657
- await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
658
- session.debuggerState.lastResumedAtMs = performance.now();
964
+ await session.client.waitFor("Debugger.resumed", {
965
+ timeoutMs: remainingMs,
966
+ ...options.signal === void 0 ? {} : { signal: options.signal }
967
+ });
968
+ session.debuggerState.lastResumedAtMs = performance2.now();
659
969
  } catch (err) {
660
970
  if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
661
- throwUnrelatedPauseTimeout(pause, timeoutMs);
971
+ throwUnrelatedPauseTimeout(pause, options.timeoutMs);
662
972
  }
663
973
  throw err;
664
974
  }
@@ -675,13 +985,16 @@ async function handleUnmatchedPause(session, pause, options, deadlineMs) {
675
985
  return;
676
986
  }
677
987
  options.onUnmatchedPause?.(pause);
678
- await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
988
+ await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options);
679
989
  }
680
990
  async function waitForPause(session, options) {
681
- const deadlineMs = performance.now() + options.timeoutMs;
991
+ throwIfAborted(options.signal);
992
+ const deadlineMs = performance2.now() + options.timeoutMs;
682
993
  const buffer = session.pauseBuffer;
683
994
  while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
995
+ throwIfAborted(options.signal);
684
996
  while (buffer.length > 0) {
997
+ throwIfAborted(options.signal);
685
998
  const buffered = buffer.shift();
686
999
  if (buffered === void 0) {
687
1000
  continue;
@@ -710,15 +1023,16 @@ async function waitForLivePause(session, options, deadlineMs) {
710
1023
  try {
711
1024
  params = await session.client.waitFor("Debugger.paused", {
712
1025
  timeoutMs: remainingMs,
1026
+ ...options.signal === void 0 ? {} : { signal: options.signal },
713
1027
  predicate: () => {
714
- receivedAtMs = performance.now();
1028
+ receivedAtMs = performance2.now();
715
1029
  return true;
716
1030
  }
717
1031
  });
718
1032
  } finally {
719
1033
  session.pauseWaitGate.active = false;
720
1034
  }
721
- return toPauseEvent(params, receivedAtMs ?? performance.now(), session.scripts);
1035
+ return toPauseEvent(params, receivedAtMs ?? performance2.now(), session.scripts);
722
1036
  }
723
1037
 
724
1038
  // src/inspector/runtime.ts
@@ -729,15 +1043,31 @@ async function resume(session) {
729
1043
  async function setPauseOnExceptions(session, state) {
730
1044
  await session.client.send("Debugger.setPauseOnExceptions", { state });
731
1045
  }
732
- async function evaluateOnFrame(session, callFrameId, expression) {
1046
+ async function evaluateOnFrame(session, callFrameId, expression, options = {}) {
733
1047
  return await session.client.send("Debugger.evaluateOnCallFrame", {
734
1048
  callFrameId,
735
1049
  expression,
736
1050
  returnByValue: false,
737
1051
  generatePreview: true,
738
- silent: true
1052
+ silent: true,
1053
+ ...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect },
1054
+ ...options.objectGroup === void 0 ? {} : { objectGroup: options.objectGroup }
739
1055
  });
740
1056
  }
1057
+ function isSideEffectRefusal(result) {
1058
+ const classNames = [
1059
+ result.result?.className,
1060
+ result.exceptionDetails?.exception?.className
1061
+ ];
1062
+ const descriptions = [
1063
+ result.result?.description,
1064
+ result.exceptionDetails?.exception?.description
1065
+ ];
1066
+ const isEvalError = classNames.includes("EvalError");
1067
+ return isEvalError && descriptions.some(
1068
+ (description) => typeof description === "string" && description.toLowerCase().includes("possible side-effect in debug-evaluate")
1069
+ );
1070
+ }
741
1071
  async function evaluateGlobal(session, expression) {
742
1072
  return await session.client.send("Runtime.evaluate", {
743
1073
  expression,
@@ -787,9 +1117,42 @@ async function getProperties(session, objectId) {
787
1117
  }
788
1118
  return result.result;
789
1119
  }
1120
+ async function getScriptSource(session, scriptId) {
1121
+ if (scriptId.trim().length === 0) {
1122
+ throw new CfInspectorError("INVALID_ARGUMENT", "scriptId must not be empty");
1123
+ }
1124
+ const result = await session.client.send(
1125
+ "Debugger.getScriptSource",
1126
+ { scriptId }
1127
+ );
1128
+ if (typeof result.scriptSource !== "string") {
1129
+ throw new CfInspectorError(
1130
+ "CDP_REQUEST_FAILED",
1131
+ "Debugger.getScriptSource did not return scriptSource"
1132
+ );
1133
+ }
1134
+ return result.scriptSource;
1135
+ }
1136
+ async function stepInto(session, options = {}) {
1137
+ await session.client.send("Debugger.stepInto", {
1138
+ ...options.breakOnAsyncCall === void 0 ? {} : { breakOnAsyncCall: options.breakOnAsyncCall }
1139
+ });
1140
+ }
1141
+ async function stepOver(session) {
1142
+ await session.client.send("Debugger.stepOver");
1143
+ }
1144
+ async function stepOut(session) {
1145
+ await session.client.send("Debugger.stepOut");
1146
+ }
1147
+ async function releaseObject(session, objectId) {
1148
+ await session.client.send("Runtime.releaseObject", { objectId });
1149
+ }
1150
+ async function releaseObjectGroup(session, objectGroup) {
1151
+ await session.client.send("Runtime.releaseObjectGroup", { objectGroup });
1152
+ }
790
1153
 
791
1154
  // src/inspector/session.ts
792
- import { performance as performance2 } from "perf_hooks";
1155
+ import { performance as performance3 } from "perf_hooks";
793
1156
 
794
1157
  // src/cdp/client.ts
795
1158
  init_types();
@@ -892,54 +1255,70 @@ var CdpClient = class _CdpClient {
892
1255
  if (this.closed) {
893
1256
  throw this.closeReason ?? new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Connection closed");
894
1257
  }
895
- return await new Promise((resolve, reject) => {
1258
+ if (options.signal?.aborted === true) {
1259
+ throw this.createWaitAbortError(method);
1260
+ }
1261
+ return await this.createEventWait(method, options);
1262
+ }
1263
+ createEventWait(method, options) {
1264
+ return new Promise((resolve, reject) => {
896
1265
  let settled = false;
1266
+ let offEvent = () => void 0;
1267
+ let offClose = () => void 0;
897
1268
  const cleanup = () => {
898
1269
  clearTimeout(timer);
899
1270
  offEvent();
900
1271
  offClose();
1272
+ options.signal?.removeEventListener("abort", onAbort);
901
1273
  };
902
- const finish = (value) => {
1274
+ const resolveOnce = (value) => {
1275
+ if (settled) {
1276
+ return;
1277
+ }
903
1278
  settled = true;
904
1279
  cleanup();
905
1280
  resolve(value);
906
1281
  };
907
- const offEvent = this.on(method, (raw) => {
908
- if (settled) {
909
- return;
910
- }
911
- const params = raw;
912
- if (options.predicate) {
913
- let accepted;
914
- try {
915
- accepted = options.predicate(params);
916
- } catch {
917
- return;
918
- }
919
- if (!accepted) {
920
- return;
921
- }
922
- }
923
- finish(params);
924
- });
925
- const offClose = this.onClose((err) => {
1282
+ const rejectOnce = (error) => {
926
1283
  if (settled) {
927
1284
  return;
928
1285
  }
929
1286
  settled = true;
930
1287
  cleanup();
931
- reject(err);
932
- });
1288
+ reject(error);
1289
+ };
1290
+ const onAbort = () => {
1291
+ rejectOnce(this.createWaitAbortError(method));
1292
+ };
933
1293
  const timer = setTimeout(() => {
934
- if (settled) {
1294
+ rejectOnce(this.createWaitTimeoutError(method, options.timeoutMs));
1295
+ }, options.timeoutMs);
1296
+ offEvent = this.on(method, (raw) => {
1297
+ const params = raw;
1298
+ if (!this.eventMatches(params, options.predicate)) {
935
1299
  return;
936
1300
  }
937
- settled = true;
938
- cleanup();
939
- reject(this.createWaitTimeoutError(method, options.timeoutMs));
940
- }, options.timeoutMs);
1301
+ resolveOnce(params);
1302
+ });
1303
+ offClose = this.onClose((error) => {
1304
+ rejectOnce(error);
1305
+ });
1306
+ options.signal?.addEventListener("abort", onAbort, { once: true });
1307
+ if (options.signal?.aborted === true) {
1308
+ onAbort();
1309
+ }
941
1310
  });
942
1311
  }
1312
+ eventMatches(params, predicate) {
1313
+ if (predicate === void 0) {
1314
+ return true;
1315
+ }
1316
+ try {
1317
+ return predicate(params);
1318
+ } catch {
1319
+ return false;
1320
+ }
1321
+ }
943
1322
  onClose(listener) {
944
1323
  if (this.closed) {
945
1324
  const reason = this.closeReason ?? new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Connection closed");
@@ -1006,6 +1385,9 @@ var CdpClient = class _CdpClient {
1006
1385
  `Timed out waiting for ${method} after ${timeoutMs.toString()}ms`
1007
1386
  );
1008
1387
  }
1388
+ createWaitAbortError(method) {
1389
+ return new CfInspectorError("ABORTED", `Aborted while waiting for ${method}`);
1390
+ }
1009
1391
  sendPayload(id, method, payload, timer, reject) {
1010
1392
  try {
1011
1393
  this.transport.send(payload);
@@ -1031,12 +1413,203 @@ var CdpClient = class _CdpClient {
1031
1413
  this.emitter.removeAllListeners();
1032
1414
  }
1033
1415
  };
1416
+ var NodeWorkerTransport = class {
1417
+ constructor(parent, sessionId) {
1418
+ this.parent = parent;
1419
+ this.sessionId = sessionId;
1420
+ this.detachParentListeners = [
1421
+ parent.on("NodeWorker.receivedMessageFromWorker", (raw) => {
1422
+ this.forwardWorkerMessage(raw);
1423
+ }),
1424
+ parent.on("NodeWorker.detachedFromWorker", (raw) => {
1425
+ this.handleWorkerDetach(raw);
1426
+ }),
1427
+ parent.onClose((error) => {
1428
+ this.closeWithError(error);
1429
+ })
1430
+ ];
1431
+ }
1432
+ parent;
1433
+ sessionId;
1434
+ emitter = new EventEmitter();
1435
+ detachParentListeners;
1436
+ readyState = 1;
1437
+ send(payload) {
1438
+ if (this.readyState !== 1) {
1439
+ throw new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Worker inspector session is closed");
1440
+ }
1441
+ void this.parent.send("NodeWorker.sendMessageToWorker", {
1442
+ sessionId: this.sessionId,
1443
+ message: payload
1444
+ }).catch((error) => {
1445
+ const normalized = error instanceof Error ? error : new Error(String(error));
1446
+ this.closeWithError(normalized);
1447
+ });
1448
+ }
1449
+ close() {
1450
+ this.finishClose();
1451
+ }
1452
+ on(event, listener) {
1453
+ this.emitter.on(event, listener);
1454
+ }
1455
+ off(event, listener) {
1456
+ this.emitter.off(event, listener);
1457
+ }
1458
+ forwardWorkerMessage(raw) {
1459
+ const params = asNodeWorkerEventParams(raw);
1460
+ if (params.sessionId !== this.sessionId || typeof params.message !== "string") {
1461
+ return;
1462
+ }
1463
+ this.emitter.emit("message", params.message);
1464
+ }
1465
+ handleWorkerDetach(raw) {
1466
+ const params = asNodeWorkerEventParams(raw);
1467
+ if (params.sessionId === this.sessionId) {
1468
+ this.finishClose();
1469
+ }
1470
+ }
1471
+ closeWithError(error) {
1472
+ if (this.readyState !== 1) {
1473
+ return;
1474
+ }
1475
+ this.emitter.emit("error", error);
1476
+ this.finishClose();
1477
+ }
1478
+ finishClose() {
1479
+ if (this.readyState !== 1) {
1480
+ return;
1481
+ }
1482
+ this.readyState = 3;
1483
+ for (const detach of this.detachParentListeners) {
1484
+ detach();
1485
+ }
1486
+ this.emitter.emit("close");
1487
+ this.emitter.removeAllListeners();
1488
+ }
1489
+ };
1490
+ function asNodeWorkerEventParams(raw) {
1491
+ if (!isUnknownRecord(raw)) {
1492
+ return {};
1493
+ }
1494
+ const sessionId = raw["sessionId"];
1495
+ const message = raw["message"];
1496
+ return {
1497
+ ...typeof sessionId === "string" ? { sessionId } : {},
1498
+ ...typeof message === "string" ? { message } : {}
1499
+ };
1500
+ }
1501
+ function isUnknownRecord(value) {
1502
+ return typeof value === "object" && value !== null;
1503
+ }
1504
+ async function createNodeWorkerClient(parent, sessionId, requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
1505
+ const transport = new NodeWorkerTransport(parent, sessionId);
1506
+ return await CdpClient.connect({
1507
+ url: `node-worker://${sessionId}`,
1508
+ transportFactory: () => Promise.resolve(transport),
1509
+ requestTimeoutMs
1510
+ });
1511
+ }
1034
1512
 
1035
1513
  // src/inspector/session.ts
1036
1514
  init_types();
1037
1515
  var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
1038
1516
  var DEFAULT_HOST = "127.0.0.1";
1039
1517
  var PAUSE_BUFFER_LIMIT = 32;
1518
+ var NodeWorkerDiscovery = class {
1519
+ constructor(client) {
1520
+ this.client = client;
1521
+ this.detachListeners = [
1522
+ client.on("NodeWorker.attachedToWorker", (raw) => {
1523
+ const worker = toInspectorWorkerTarget(raw);
1524
+ if (worker !== void 0) {
1525
+ this.workers.set(worker.sessionId, worker);
1526
+ }
1527
+ }),
1528
+ client.on("NodeWorker.detachedFromWorker", (raw) => {
1529
+ const sessionId = readField(raw, "sessionId");
1530
+ if (typeof sessionId === "string") {
1531
+ this.workers.delete(sessionId);
1532
+ }
1533
+ })
1534
+ ];
1535
+ }
1536
+ client;
1537
+ workers = /* @__PURE__ */ new Map();
1538
+ detachListeners;
1539
+ supported = false;
1540
+ disposed = false;
1541
+ async enable() {
1542
+ try {
1543
+ await this.client.send("NodeWorker.enable", { waitForDebuggerOnStart: false });
1544
+ this.supported = true;
1545
+ } catch (error) {
1546
+ if (!isUnsupportedNodeWorkerDomain(error)) {
1547
+ throw error;
1548
+ }
1549
+ }
1550
+ }
1551
+ list() {
1552
+ return [...this.workers.values()].sort(compareWorkers);
1553
+ }
1554
+ async dispose() {
1555
+ if (this.disposed) {
1556
+ return;
1557
+ }
1558
+ this.disposed = true;
1559
+ if (this.supported && !this.client.isClosed) {
1560
+ try {
1561
+ await this.client.send("NodeWorker.disable");
1562
+ } catch {
1563
+ }
1564
+ }
1565
+ for (const detach of this.detachListeners) {
1566
+ detach();
1567
+ }
1568
+ }
1569
+ };
1570
+ function isUnsupportedNodeWorkerDomain(error) {
1571
+ if (!(error instanceof CfInspectorError) || error.code !== "CDP_REQUEST_FAILED") {
1572
+ return false;
1573
+ }
1574
+ return error.detail?.includes('"code":-32601') === true;
1575
+ }
1576
+ function compareWorkers(left, right) {
1577
+ const leftId = Number.parseInt(left.workerId, 10);
1578
+ const rightId = Number.parseInt(right.workerId, 10);
1579
+ if (!Number.isNaN(leftId) && !Number.isNaN(rightId) && leftId !== rightId) {
1580
+ return leftId - rightId;
1581
+ }
1582
+ return left.workerId.localeCompare(right.workerId);
1583
+ }
1584
+ function toInspectorWorkerTarget(raw) {
1585
+ const sessionId = readField(raw, "sessionId");
1586
+ const info = readField(raw, "workerInfo");
1587
+ if (typeof sessionId !== "string" || !isUnknownRecord2(info)) {
1588
+ return void 0;
1589
+ }
1590
+ const workerId = asString(info["workerId"]);
1591
+ if (workerId.length === 0) {
1592
+ return void 0;
1593
+ }
1594
+ return {
1595
+ sessionId,
1596
+ workerId,
1597
+ type: asString(info["type"]),
1598
+ title: asString(info["title"]),
1599
+ url: asString(info["url"])
1600
+ };
1601
+ }
1602
+ function readField(value, name) {
1603
+ return isUnknownRecord2(value) ? value[name] : void 0;
1604
+ }
1605
+ function isUnknownRecord2(value) {
1606
+ return typeof value === "object" && value !== null;
1607
+ }
1608
+ async function startNodeWorkerDiscovery(client) {
1609
+ const discovery = new NodeWorkerDiscovery(client);
1610
+ await discovery.enable();
1611
+ return discovery;
1612
+ }
1040
1613
  async function connectInspector(options) {
1041
1614
  const host = options.host ?? DEFAULT_HOST;
1042
1615
  const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
@@ -1053,43 +1626,105 @@ async function connectInspector(options) {
1053
1626
  url: target.webSocketDebuggerUrl,
1054
1627
  connectTimeoutMs
1055
1628
  });
1629
+ let workerDiscovery;
1056
1630
  try {
1057
- return await initSession(client, target);
1631
+ workerDiscovery = await startNodeWorkerDiscovery(client);
1632
+ if (options.workerIndex === void 0) {
1633
+ const session = await initSession(client, target);
1634
+ return withWorkerMetadata(session, workerDiscovery, targetIndex, targets.length);
1635
+ }
1636
+ return await initWorkerSession(
1637
+ client,
1638
+ workerDiscovery,
1639
+ options.workerIndex,
1640
+ targetIndex,
1641
+ targets.length
1642
+ );
1058
1643
  } catch (err) {
1644
+ await workerDiscovery?.dispose();
1059
1645
  client.dispose();
1060
1646
  throw err;
1061
1647
  }
1062
1648
  }
1649
+ async function initWorkerSession(parent, discovery, workerIndex, targetIndex, targetCount) {
1650
+ const workers = discovery.list();
1651
+ if (!discovery.supported) {
1652
+ throw new CfInspectorError(
1653
+ "INSPECTOR_DISCOVERY_FAILED",
1654
+ "This runtime does not expose the NodeWorker CDP domain; --worker cannot be used. Run list-targets for available raw targets."
1655
+ );
1656
+ }
1657
+ const worker = workers[workerIndex];
1658
+ if (worker === void 0) {
1659
+ throw new CfInspectorError(
1660
+ "INSPECTOR_DISCOVERY_FAILED",
1661
+ `No NodeWorker sub-session at index ${workerIndex.toString()} (available: ${workers.length.toString()}). Ensure the worker is alive, then rerun list-targets.`
1662
+ );
1663
+ }
1664
+ const client = await createNodeWorkerClient(parent, worker.sessionId);
1665
+ const session = await initSession(client, workerToInspectorTarget(worker));
1666
+ return withWorkerMetadata(session, discovery, targetIndex, targetCount, workerIndex, parent);
1667
+ }
1668
+ function withWorkerMetadata(session, discovery, targetIndex, targetCount, workerIndex, parent) {
1669
+ return {
1670
+ ...session,
1671
+ targetIndex,
1672
+ targetCount,
1673
+ ...workerIndex === void 0 ? {} : { workerIndex },
1674
+ workerTargets: discovery.list(),
1675
+ workerDiscoverySupported: discovery.supported,
1676
+ dispose: async () => {
1677
+ await session.dispose();
1678
+ await discovery.dispose();
1679
+ parent?.dispose();
1680
+ }
1681
+ };
1682
+ }
1683
+ function workerToInspectorTarget(worker) {
1684
+ return {
1685
+ description: "Node worker sub-session",
1686
+ id: worker.workerId,
1687
+ title: worker.title,
1688
+ type: worker.type,
1689
+ url: worker.url,
1690
+ webSocketDebuggerUrl: `node-worker://${worker.sessionId}`
1691
+ };
1692
+ }
1063
1693
  async function initSession(client, target) {
1064
1694
  const scripts = /* @__PURE__ */ new Map();
1065
- client.on("Debugger.scriptParsed", (raw) => {
1066
- const params = raw;
1067
- const scriptId = asString(params.scriptId);
1068
- const url = asString(params.url);
1069
- if (scriptId.length === 0) {
1070
- return;
1071
- }
1072
- scripts.set(scriptId, { scriptId, url });
1073
- });
1695
+ registerScriptTracking(client, scripts);
1074
1696
  const pauseBuffer = [];
1075
1697
  const pauseWaitGate = { active: false };
1076
1698
  const debuggerState = {};
1699
+ registerPauseTracking(client, scripts, pauseBuffer, pauseWaitGate, debuggerState);
1700
+ await client.send("Runtime.enable");
1701
+ await client.send("Debugger.enable");
1702
+ return createSession(client, target, scripts, pauseBuffer, pauseWaitGate, debuggerState);
1703
+ }
1704
+ function registerScriptTracking(client, scripts) {
1705
+ client.on("Debugger.scriptParsed", (raw) => {
1706
+ const script = toScriptInfo(raw);
1707
+ if (script !== void 0) {
1708
+ scripts.set(script.scriptId, script);
1709
+ }
1710
+ });
1711
+ }
1712
+ function registerPauseTracking(client, scripts, pauseBuffer, pauseWaitGate, debuggerState) {
1077
1713
  client.on("Debugger.paused", (raw) => {
1078
1714
  if (pauseWaitGate.active) {
1079
1715
  return;
1080
1716
  }
1081
- const params = raw;
1082
- const event = toPauseEvent(params, performance2.now(), scripts);
1717
+ const event = toPauseEvent(raw, performance3.now(), scripts);
1083
1718
  if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
1084
1719
  pauseBuffer.shift();
1085
1720
  }
1086
1721
  pauseBuffer.push(event);
1087
1722
  });
1088
1723
  client.on("Debugger.resumed", () => {
1089
- debuggerState.lastResumedAtMs = performance2.now();
1724
+ debuggerState.lastResumedAtMs = performance3.now();
1090
1725
  });
1091
- await client.send("Runtime.enable");
1092
- await client.send("Debugger.enable");
1726
+ }
1727
+ function createSession(client, target, scripts, pauseBuffer, pauseWaitGate, debuggerState) {
1093
1728
  return {
1094
1729
  client,
1095
1730
  target,
@@ -1107,9 +1742,61 @@ async function initSession(client, target) {
1107
1742
  };
1108
1743
  }
1109
1744
 
1745
+ // src/cli/captureParser.ts
1746
+ function isQuoteChar(value) {
1747
+ return value === "'" || value === '"' || value === "`";
1748
+ }
1749
+ function consumeQuotedChar(state, char) {
1750
+ if (state.quote === void 0) {
1751
+ return false;
1752
+ }
1753
+ if (state.escaped) {
1754
+ state.escaped = false;
1755
+ return true;
1756
+ }
1757
+ if (char === "\\") {
1758
+ state.escaped = true;
1759
+ return true;
1760
+ }
1761
+ if (char === state.quote) {
1762
+ state.quote = void 0;
1763
+ }
1764
+ return true;
1765
+ }
1766
+ function stripQuotedText(expression) {
1767
+ const state = { quote: void 0, escaped: false };
1768
+ let stripped = "";
1769
+ for (const char of expression) {
1770
+ if (consumeQuotedChar(state, char)) {
1771
+ stripped += " ";
1772
+ continue;
1773
+ }
1774
+ if (isQuoteChar(char)) {
1775
+ state.quote = char;
1776
+ stripped += " ";
1777
+ continue;
1778
+ }
1779
+ stripped += char;
1780
+ }
1781
+ return stripped;
1782
+ }
1783
+ function looksLikeMutation(expression) {
1784
+ const stripped = stripQuotedText(expression);
1785
+ const hasUpdate = /(?:\+\+|--)/u.test(stripped);
1786
+ const hasAssignment = /(?:\*\*=|&&=|\|\|=|\?\?=|[+\-*/%&|^]=|(?:^|[^=!<>])=(?!=|>))/u.test(stripped);
1787
+ const hasDelete = /\bdelete\b/u.test(stripped);
1788
+ const hasMutatingMethod = /\.\s*(?:push|pop|shift|unshift|splice|sort|reverse|fill|copyWithin|set|add|delete|clear)\s*\(/u.test(stripped);
1789
+ const hasObjectMutation = /\bObject\s*\.\s*(?:assign|defineProperty|defineProperties)\s*\(/u.test(stripped);
1790
+ return hasUpdate || hasAssignment || hasDelete || hasMutatingMethod || hasObjectMutation;
1791
+ }
1792
+
1793
+ // src/snapshot/evaluation.ts
1794
+ init_types();
1795
+
1110
1796
  // src/snapshot/values.ts
1111
1797
  init_types();
1112
- var DEFAULT_MAX_VALUE_LENGTH = 4096;
1798
+ var DEFAULT_MAX_VALUE_LENGTH = 131072;
1799
+ var DEFAULT_STREAM_MAX_VALUE_LENGTH = 4096;
1113
1800
  function isPrimitive(value) {
1114
1801
  const t = typeof value;
1115
1802
  return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
@@ -1137,9 +1824,16 @@ function resolveMaxValueLength(value) {
1137
1824
  }
1138
1825
  function limitValueLength(raw, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
1139
1826
  if (raw.length <= maxValueLength) {
1140
- return raw;
1827
+ return { text: raw, truncated: false };
1141
1828
  }
1142
- return `${raw.slice(0, maxValueLength)}...`;
1829
+ return {
1830
+ text: raw.slice(0, maxValueLength),
1831
+ truncated: true,
1832
+ originalLength: raw.length
1833
+ };
1834
+ }
1835
+ function textTruncationFields(limited) {
1836
+ return limited.truncated ? { truncated: true, originalLength: limited.originalLength } : {};
1143
1837
  }
1144
1838
  function parseQuotedString(value) {
1145
1839
  try {
@@ -1224,7 +1918,12 @@ function toStructuredValue(variable) {
1224
1918
  // src/snapshot/evaluation.ts
1225
1919
  function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
1226
1920
  if (result.exceptionDetails !== void 0) {
1227
- return { expression, error: readEvalError(result, maxValueLength) };
1921
+ const limited = readEvalError(result, maxValueLength);
1922
+ return {
1923
+ expression,
1924
+ error: limited.text,
1925
+ ...textTruncationFields(limited)
1926
+ };
1228
1927
  }
1229
1928
  const inner = result.result;
1230
1929
  if (!inner) {
@@ -1232,8 +1931,12 @@ function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_V
1232
1931
  }
1233
1932
  const type = typeof inner.type === "string" ? inner.type : void 0;
1234
1933
  const buildCaptured = (rendered) => {
1235
- const sanitized = limitValueLength(rendered, maxValueLength);
1236
- const base = { expression, value: sanitized };
1934
+ const limited = limitValueLength(rendered, maxValueLength);
1935
+ const base = {
1936
+ expression,
1937
+ value: limited.text,
1938
+ ...textTruncationFields(limited)
1939
+ };
1237
1940
  return type === void 0 ? base : { ...base, type };
1238
1941
  };
1239
1942
  if (type === "string" && typeof inner.value === "string") {
@@ -1250,6 +1953,18 @@ function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_V
1250
1953
  }
1251
1954
  return buildCaptured("undefined");
1252
1955
  }
1956
+ function sideEffectRefusalToCaptured(expression) {
1957
+ const error = new CfInspectorError(
1958
+ "MUTATION_NOT_ALLOWED",
1959
+ `V8 blocked the capture expression "${expression}" because it may have side effects. Pass --allow-mutation to run it explicitly.`
1960
+ );
1961
+ return {
1962
+ expression,
1963
+ error: `${error.code}: ${error.message}`,
1964
+ mutationRisk: true,
1965
+ blocked: true
1966
+ };
1967
+ }
1253
1968
  function readEvalError(result, maxValueLength) {
1254
1969
  const text = typeof result.exceptionDetails?.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails?.text === "string" ? result.exceptionDetails.text : "evaluation failed";
1255
1970
  return limitValueLength(text, maxValueLength);
@@ -1307,56 +2022,88 @@ async function captureProperties(session, objectId, limit, depth, maxValueLength
1307
2022
  return await captureProperty(session, prop, depth, maxValueLength);
1308
2023
  })
1309
2024
  );
1310
- return variables;
2025
+ const omittedCount = Math.max(properties.length - limited.length, 0);
2026
+ return omittedCount === 0 ? { variables } : { variables, omittedCount };
1311
2027
  }
1312
2028
  async function captureProperty(session, prop, depth, maxValueLength) {
1313
2029
  const name = typeof prop.name === "string" ? prop.name : "?";
1314
2030
  const described = describeProperty(prop);
1315
- const children = await capturePropertyChildren(session, described, depth, maxValueLength);
1316
- const sanitizedValue = limitValueLength(described.value, maxValueLength);
1317
- const base = { name, value: sanitizedValue };
2031
+ const capturedChildren = await capturePropertyChildren(
2032
+ session,
2033
+ described,
2034
+ depth,
2035
+ maxValueLength
2036
+ );
2037
+ const limited = limitValueLength(described.value, maxValueLength);
2038
+ const base = {
2039
+ name,
2040
+ value: limited.text,
2041
+ ...textTruncationFields(limited)
2042
+ };
1318
2043
  const withType = described.type === void 0 ? base : { ...base, type: described.type };
1319
- return children === void 0 ? withType : { ...withType, children };
2044
+ const children = capturedChildren?.variables;
2045
+ const withChildren = children === void 0 || children.length === 0 ? withType : { ...withType, children };
2046
+ const omittedCount = capturedChildren?.omittedCount ?? 0;
2047
+ return omittedCount === 0 ? withChildren : { ...withChildren, truncated: true, omittedCount };
1320
2048
  }
1321
2049
  async function capturePropertyChildren(session, described, depth, maxValueLength) {
1322
- if (depth <= 0 || described.objectId === void 0 || !isExpandable(described.type)) {
2050
+ if (described.objectId === void 0 || !isExpandable(described.type)) {
1323
2051
  return void 0;
1324
2052
  }
2053
+ if (depth <= 0) {
2054
+ return await countDepthOmissions(session, described.objectId);
2055
+ }
1325
2056
  try {
1326
- const nested = await captureProperties(
2057
+ return await captureProperties(
1327
2058
  session,
1328
2059
  described.objectId,
1329
2060
  MAX_CHILD_VARIABLES,
1330
2061
  depth - 1,
1331
2062
  maxValueLength
1332
2063
  );
1333
- return nested.length > 0 ? nested : void 0;
1334
2064
  } catch {
1335
2065
  return void 0;
1336
2066
  }
1337
2067
  }
2068
+ async function countDepthOmissions(session, objectId) {
2069
+ try {
2070
+ const properties = await getProperties(session, objectId);
2071
+ return properties.length === 0 ? void 0 : { variables: [], omittedCount: properties.length };
2072
+ } catch {
2073
+ return void 0;
2074
+ }
2075
+ }
2076
+ function countPropertyOmissions(captured) {
2077
+ return (captured.omittedCount ?? 0) + captured.variables.reduce((total, variable) => {
2078
+ const childOmissions = variable.children === void 0 ? 0 : countPropertyOmissions({ variables: variable.children });
2079
+ return total + (variable.omittedCount ?? 0) + childOmissions;
2080
+ }, 0);
2081
+ }
1338
2082
 
1339
2083
  // src/snapshot/exception.ts
1340
2084
  function asString2(value) {
1341
2085
  return typeof value === "string" && value.length > 0 ? value : void 0;
1342
2086
  }
1343
- async function materializeObject(session, objectId, maxValueLength) {
2087
+ async function materializeObject(session, objectId) {
1344
2088
  try {
1345
- const properties = await captureProperties(
2089
+ const captured = await captureProperties(
1346
2090
  session,
1347
2091
  objectId,
1348
2092
  MAX_SCOPE_VARIABLES,
1349
2093
  MAX_VARIABLE_DEPTH,
1350
- maxValueLength
2094
+ Number.MAX_SAFE_INTEGER
1351
2095
  );
1352
- if (properties.length === 0) {
2096
+ if (captured.variables.length === 0) {
1353
2097
  return void 0;
1354
2098
  }
1355
2099
  const structured = {};
1356
- for (const variable of properties) {
2100
+ for (const variable of captured.variables) {
1357
2101
  structured[variable.name] = toStructuredValue(variable);
1358
2102
  }
1359
- return JSON.stringify(structured);
2103
+ return {
2104
+ value: JSON.stringify(structured),
2105
+ omittedCount: countPropertyOmissions(captured)
2106
+ };
1360
2107
  } catch {
1361
2108
  return void 0;
1362
2109
  }
@@ -1409,18 +2156,44 @@ async function captureException(session, pause, maxValueLength) {
1409
2156
  return { error: "exception data has no objectId or value" };
1410
2157
  }
1411
2158
  const message = await readPropertyDescription(session, objectId, "message");
1412
- const rendered = await materializeObject(session, objectId, maxValueLength);
2159
+ const rendered = await materializeObject(session, objectId);
1413
2160
  if (rendered !== void 0) {
1414
- const result = buildResult(type, description, rendered, maxValueLength);
1415
- return message === void 0 ? result : { ...result, description: limitValueLength(message, maxValueLength) };
2161
+ return buildResult(
2162
+ type,
2163
+ message ?? description,
2164
+ rendered.value,
2165
+ maxValueLength,
2166
+ rendered.omittedCount
2167
+ );
1416
2168
  }
1417
2169
  return buildResult(type, description, description ?? "[exception]", maxValueLength);
1418
2170
  }
1419
- function buildResult(type, description, value, maxValueLength) {
1420
- const safeValue = limitValueLength(value, maxValueLength);
1421
- const base = { value: safeValue };
2171
+ function buildResult(type, description, value, maxValueLength, omittedCount = 0) {
2172
+ const limitedValue = limitValueLength(value, maxValueLength);
2173
+ const limitedDescription = description === void 0 ? void 0 : limitValueLength(description, maxValueLength);
2174
+ const base = {
2175
+ value: limitedValue.text,
2176
+ ...exceptionTruncationFields(limitedValue, limitedDescription)
2177
+ };
1422
2178
  const withType = type === void 0 ? base : { ...base, type };
1423
- return description === void 0 ? withType : { ...withType, description: limitValueLength(description, maxValueLength) };
2179
+ const withDescription = limitedDescription === void 0 ? withType : { ...withType, description: limitedDescription.text };
2180
+ return omittedCount === 0 ? withDescription : { ...withDescription, truncated: true, omittedCount };
2181
+ }
2182
+ function exceptionTruncationFields(value, description) {
2183
+ const valueLength = value.truncated ? value.originalLength : void 0;
2184
+ const descriptionLength = description?.truncated === true ? description.originalLength : void 0;
2185
+ const lengths = [valueLength, descriptionLength].filter(
2186
+ (length) => length !== void 0
2187
+ );
2188
+ if (lengths.length === 0) {
2189
+ return {};
2190
+ }
2191
+ return {
2192
+ truncated: true,
2193
+ originalLength: Math.max(...lengths),
2194
+ ...valueLength === void 0 ? {} : { valueOriginalLength: valueLength },
2195
+ ...descriptionLength === void 0 ? {} : { descriptionOriginalLength: descriptionLength }
2196
+ };
1424
2197
  }
1425
2198
 
1426
2199
  // src/snapshot/objects.ts
@@ -1435,20 +2208,23 @@ function objectIdFromEvalResult(result) {
1435
2208
  }
1436
2209
  return objectId;
1437
2210
  }
1438
- async function renderObjectCapture(session, objectId, maxValueLength) {
2211
+ async function renderObjectCapture(session, objectId) {
1439
2212
  try {
1440
- const properties = await captureProperties(
2213
+ const captured = await captureProperties(
1441
2214
  session,
1442
2215
  objectId,
1443
2216
  MAX_SCOPE_VARIABLES,
1444
2217
  MAX_VARIABLE_DEPTH,
1445
- maxValueLength
2218
+ Number.MAX_SAFE_INTEGER
1446
2219
  );
1447
2220
  const structured = {};
1448
- for (const variable of properties) {
2221
+ for (const variable of captured.variables) {
1449
2222
  structured[variable.name] = toStructuredValue(variable);
1450
2223
  }
1451
- return JSON.stringify(structured);
2224
+ return {
2225
+ value: JSON.stringify(structured),
2226
+ omittedCount: countPropertyOmissions(captured)
2227
+ };
1452
2228
  } catch {
1453
2229
  return void 0;
1454
2230
  }
@@ -1470,16 +2246,22 @@ async function withSerializedObjectCapture(session, expression, evalResult, capt
1470
2246
  if (objectId === void 0) {
1471
2247
  return captured;
1472
2248
  }
1473
- const rendered = await renderObjectCapture(session, objectId, maxValueLength);
2249
+ const rendered = await renderObjectCapture(session, objectId);
1474
2250
  if (rendered === void 0) {
1475
2251
  return captured;
1476
2252
  }
1477
- const normalized = normalizeRenderedObjectCapture(rendered, captured.value);
2253
+ const normalized = normalizeRenderedObjectCapture(rendered.value, captured.value);
1478
2254
  if (normalized === void 0) {
1479
2255
  return captured;
1480
2256
  }
1481
- const value = limitValueLength(normalized, maxValueLength);
1482
- return captured.type === void 0 ? { expression, value } : { expression, value, type: captured.type };
2257
+ const limited = limitValueLength(normalized, maxValueLength);
2258
+ const base = {
2259
+ expression,
2260
+ value: limited.text,
2261
+ ...textTruncationFields(limited),
2262
+ ...captured.type === void 0 ? {} : { type: captured.type }
2263
+ };
2264
+ return rendered.omittedCount === 0 ? base : { ...base, truncated: true, omittedCount: rendered.omittedCount };
1483
2265
  }
1484
2266
 
1485
2267
  // src/snapshot/scopes.ts
@@ -1494,35 +2276,39 @@ var PRIORITY_BY_TYPE = {
1494
2276
  module: 6,
1495
2277
  script: 7
1496
2278
  };
1497
- function selectScopes(scopeChain) {
2279
+ function rankedScopes(scopeChain) {
1498
2280
  const eligible = scopeChain.filter((scope) => scope.objectId !== void 0 && scope.type !== "global");
1499
- return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type)).slice(0, MAX_SCOPES);
2281
+ return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type));
1500
2282
  }
1501
2283
  function priorityOf(type) {
1502
2284
  return PRIORITY_BY_TYPE[type] ?? Number.MAX_SAFE_INTEGER;
1503
2285
  }
1504
2286
  async function captureScopes(session, frame, maxValueLength) {
1505
- const scopes = selectScopes(frame.scopeChain);
1506
- return await Promise.all(
2287
+ const ranked = rankedScopes(frame.scopeChain);
2288
+ const scopes = ranked.slice(0, MAX_SCOPES);
2289
+ const capturedScopes = await Promise.all(
1507
2290
  scopes.map(async (scope) => {
1508
2291
  const objectId = scope.objectId;
1509
2292
  if (objectId === void 0) {
1510
2293
  return { type: scope.type, variables: [] };
1511
2294
  }
1512
2295
  try {
1513
- const variables = await captureProperties(
2296
+ const captured = await captureProperties(
1514
2297
  session,
1515
2298
  objectId,
1516
2299
  MAX_SCOPE_VARIABLES,
1517
2300
  MAX_VARIABLE_DEPTH,
1518
2301
  maxValueLength
1519
2302
  );
1520
- return { type: scope.type, variables };
2303
+ const base = { type: scope.type, variables: captured.variables };
2304
+ return captured.omittedCount === void 0 ? base : { ...base, truncated: true, omittedCount: captured.omittedCount };
1521
2305
  } catch {
1522
2306
  return { type: scope.type, variables: [] };
1523
2307
  }
1524
2308
  })
1525
2309
  );
2310
+ const omittedCount = Math.max(ranked.length - capturedScopes.length, 0);
2311
+ return omittedCount === 0 ? { scopes: capturedScopes } : { scopes: capturedScopes, omittedCount };
1526
2312
  }
1527
2313
 
1528
2314
  // src/snapshot/stack.ts
@@ -1542,23 +2328,48 @@ function buildBaseFrame(frame) {
1542
2328
  };
1543
2329
  return frame.url === void 0 ? base : { ...base, url: frame.url };
1544
2330
  }
1545
- async function captureFrameExpression(session, callFrameId, expression, maxValueLength) {
2331
+ async function captureFrameExpression(session, callFrameId, expression, maxValueLength, throwOnSideEffect) {
2332
+ const mutationRisk = throwOnSideEffect === false && looksLikeMutation(expression);
1546
2333
  try {
1547
- const result = await evaluateOnFrame(session, callFrameId, expression);
2334
+ const result = await evaluateOnFrame(session, callFrameId, expression, {
2335
+ ...throwOnSideEffect === void 0 ? {} : { throwOnSideEffect }
2336
+ });
2337
+ if (isSideEffectRefusal(result)) {
2338
+ return sideEffectRefusalToCaptured(expression);
2339
+ }
1548
2340
  const captured = evalResultToCaptured(expression, result, maxValueLength);
1549
- return await withSerializedObjectCapture(session, expression, result, captured, maxValueLength);
2341
+ const serialized = await withSerializedObjectCapture(
2342
+ session,
2343
+ expression,
2344
+ result,
2345
+ captured,
2346
+ maxValueLength
2347
+ );
2348
+ return mutationRisk ? { ...serialized, mutationRisk: true } : serialized;
1550
2349
  } catch (err) {
1551
2350
  const message = err instanceof Error ? err.message : String(err);
1552
- return { expression, error: limitValueLength(message, maxValueLength) };
2351
+ const limited = limitValueLength(message, maxValueLength);
2352
+ const captured = {
2353
+ expression,
2354
+ error: limited.text,
2355
+ ...textTruncationFields(limited)
2356
+ };
2357
+ return mutationRisk ? { ...captured, mutationRisk: true } : captured;
1553
2358
  }
1554
2359
  }
1555
- async function captureFrameExpressions(session, frame, expressions, maxValueLength) {
2360
+ async function captureFrameExpressions(session, frame, expressions, maxValueLength, throwOnSideEffect) {
1556
2361
  if (expressions.length === 0) {
1557
2362
  return [];
1558
2363
  }
1559
2364
  return await Promise.all(
1560
2365
  expressions.map(
1561
- (expression) => captureFrameExpression(session, frame.callFrameId, expression, maxValueLength)
2366
+ (expression) => captureFrameExpression(
2367
+ session,
2368
+ frame.callFrameId,
2369
+ expression,
2370
+ maxValueLength,
2371
+ throwOnSideEffect
2372
+ )
1562
2373
  )
1563
2374
  );
1564
2375
  }
@@ -1578,7 +2389,8 @@ async function walkStack(session, callFrames, options) {
1578
2389
  session,
1579
2390
  frame,
1580
2391
  options.stackCaptures,
1581
- options.maxValueLength
2392
+ options.maxValueLength,
2393
+ options.throwOnSideEffect
1582
2394
  );
1583
2395
  return { ...base, captures };
1584
2396
  })
@@ -1600,14 +2412,25 @@ async function captureSnapshot(session, pause, options = {}) {
1600
2412
  column: top.columnNumber + 1
1601
2413
  };
1602
2414
  if (options.includeScopes === true) {
1603
- const scopes = await captureScopes(session, top, maxValueLength);
1604
- topFrame = { ...topFrame, scopes };
2415
+ const capturedScopes = await captureScopes(session, top, maxValueLength);
2416
+ topFrame = {
2417
+ ...topFrame,
2418
+ scopes: capturedScopes.scopes,
2419
+ ...capturedScopes.omittedCount === void 0 ? {} : { truncated: true, omittedCount: capturedScopes.omittedCount }
2420
+ };
1605
2421
  }
1606
- captures = await captureExpressions(session, top.callFrameId, options.captures, maxValueLength);
2422
+ captures = await captureExpressions(
2423
+ session,
2424
+ top.callFrameId,
2425
+ options.captures,
2426
+ maxValueLength,
2427
+ options.throwOnSideEffect
2428
+ );
1607
2429
  stack = await walkStack(session, pause.callFrames, {
1608
2430
  stackDepth: options.stackDepth ?? DEFAULT_STACK_DEPTH,
1609
2431
  stackCaptures: options.stackCaptures ?? [],
1610
- maxValueLength
2432
+ maxValueLength,
2433
+ ...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect }
1611
2434
  });
1612
2435
  }
1613
2436
  const exception = await captureException(session, pause, maxValueLength);
@@ -1630,24 +2453,49 @@ function buildResult2(input) {
1630
2453
  const withStack = input.stack.length > 0 ? { ...withFrame, stack: input.stack } : withFrame;
1631
2454
  return input.exception === void 0 ? withStack : { ...withStack, exception: input.exception };
1632
2455
  }
1633
- async function captureExpressions(session, callFrameId, captures, maxValueLength) {
2456
+ async function captureExpressions(session, callFrameId, captures, maxValueLength, throwOnSideEffect) {
1634
2457
  if (captures === void 0 || captures.length === 0) {
1635
2458
  return [];
1636
2459
  }
1637
2460
  return await Promise.all(
1638
2461
  captures.map(async (expression) => {
1639
- return await captureExpression(session, callFrameId, expression, maxValueLength);
2462
+ return await captureExpression(
2463
+ session,
2464
+ callFrameId,
2465
+ expression,
2466
+ maxValueLength,
2467
+ throwOnSideEffect
2468
+ );
1640
2469
  })
1641
2470
  );
1642
2471
  }
1643
- async function captureExpression(session, callFrameId, expression, maxValueLength) {
2472
+ async function captureExpression(session, callFrameId, expression, maxValueLength, throwOnSideEffect) {
2473
+ const mutationRisk = throwOnSideEffect === false && looksLikeMutation(expression);
1644
2474
  try {
1645
- const result = await evaluateOnFrame(session, callFrameId, expression);
2475
+ const result = await evaluateOnFrame(session, callFrameId, expression, {
2476
+ ...throwOnSideEffect === void 0 ? {} : { throwOnSideEffect }
2477
+ });
2478
+ if (isSideEffectRefusal(result)) {
2479
+ return sideEffectRefusalToCaptured(expression);
2480
+ }
1646
2481
  const captured = evalResultToCaptured(expression, result, maxValueLength);
1647
- return await withSerializedObjectCapture(session, expression, result, captured, maxValueLength);
2482
+ const serialized = await withSerializedObjectCapture(
2483
+ session,
2484
+ expression,
2485
+ result,
2486
+ captured,
2487
+ maxValueLength
2488
+ );
2489
+ return mutationRisk ? { ...serialized, mutationRisk: true } : serialized;
1648
2490
  } catch (err) {
1649
2491
  const message = err instanceof Error ? err.message : String(err);
1650
- return { expression, error: limitValueLength(message, maxValueLength) };
2492
+ const limited = limitValueLength(message, maxValueLength);
2493
+ const captured = {
2494
+ expression,
2495
+ error: limited.text,
2496
+ ...textTruncationFields(limited)
2497
+ };
2498
+ return mutationRisk ? { ...captured, mutationRisk: true } : captured;
1651
2499
  }
1652
2500
  }
1653
2501
 
@@ -1735,7 +2583,7 @@ function readArg(arg, index) {
1735
2583
  }
1736
2584
  return index === 0 ? void 0 : "";
1737
2585
  }
1738
- function parseLogEvent(rawArgs, sentinel, location, timestamp) {
2586
+ function parseLogEvent(rawArgs, sentinel, location, timestamp, maxValueLength = DEFAULT_STREAM_MAX_VALUE_LENGTH) {
1739
2587
  if (!Array.isArray(rawArgs) || rawArgs.length < 2) {
1740
2588
  return void 0;
1741
2589
  }
@@ -1747,21 +2595,37 @@ function parseLogEvent(rawArgs, sentinel, location, timestamp) {
1747
2595
  const ts = new Date(typeof timestamp === "number" ? timestamp : Date.now()).toISOString();
1748
2596
  const at = `${location.file}:${location.line.toString()}`;
1749
2597
  if (payload.startsWith("!err:")) {
1750
- return { ts, at, error: payload.slice("!err:".length) };
2598
+ const limited = limitValueLength(payload.slice("!err:".length), maxValueLength);
2599
+ return {
2600
+ ts,
2601
+ at,
2602
+ error: limited.text,
2603
+ ...textTruncationFields(limited)
2604
+ };
1751
2605
  }
1752
- return parsePayload(ts, at, payload);
2606
+ return parsePayload(ts, at, payload, maxValueLength);
1753
2607
  }
1754
- function parsePayload(ts, at, payload) {
2608
+ function parsePayload(ts, at, payload, maxValueLength) {
1755
2609
  try {
1756
2610
  const parsed = JSON.parse(payload);
1757
2611
  if (typeof parsed === "string") {
1758
- return { ts, at, value: parsed };
2612
+ return buildValueEvent(ts, at, parsed, maxValueLength);
1759
2613
  }
1760
- return { ts, at, value: JSON.stringify(parsed) };
2614
+ return buildValueEvent(ts, at, JSON.stringify(parsed), maxValueLength);
1761
2615
  } catch {
1762
- return { ts, at, value: payload, raw: payload };
2616
+ return buildValueEvent(ts, at, payload, maxValueLength, true);
1763
2617
  }
1764
2618
  }
2619
+ function buildValueEvent(ts, at, value, maxValueLength, includeRaw = false) {
2620
+ const limited = limitValueLength(value, maxValueLength);
2621
+ return {
2622
+ ts,
2623
+ at,
2624
+ value: limited.text,
2625
+ ...includeRaw ? { raw: limited.text } : {},
2626
+ ...textTruncationFields(limited)
2627
+ };
2628
+ }
1765
2629
 
1766
2630
  // src/logpoint/stream.ts
1767
2631
  function validateMaxEvents(maxEvents) {
@@ -1791,6 +2655,9 @@ function validateHitCount2(hitCount) {
1791
2655
  async function streamLogpoint(session, options) {
1792
2656
  const maxEvents = validateMaxEvents(options.maxEvents);
1793
2657
  const hitCount = validateHitCount2(options.hitCount);
2658
+ const maxValueLength = resolveMaxValueLength(
2659
+ options.maxValueLength ?? DEFAULT_STREAM_MAX_VALUE_LENGTH
2660
+ );
1794
2661
  const sentinel = generateSentinel();
1795
2662
  const condition = buildLogpointCondition(sentinel, options.expression, {
1796
2663
  ...options.condition === void 0 ? {} : { predicate: options.condition },
@@ -1803,7 +2670,7 @@ async function streamLogpoint(session, options) {
1803
2670
  if (maxEventsReached) {
1804
2671
  return;
1805
2672
  }
1806
- const event = toLogpointEvent(raw, sentinel, options.location);
2673
+ const event = toLogpointEvent(raw, sentinel, options.location, maxValueLength);
1807
2674
  if (event === void 0) {
1808
2675
  return;
1809
2676
  }
@@ -1843,13 +2710,13 @@ async function streamLogpoint(session, options) {
1843
2710
  await removeBreakpointBestEffort(session, handle.breakpointId);
1844
2711
  }
1845
2712
  }
1846
- function toLogpointEvent(raw, sentinel, location) {
2713
+ function toLogpointEvent(raw, sentinel, location, maxValueLength) {
1847
2714
  const params = raw;
1848
2715
  if (asString3(params.type) !== "log") {
1849
2716
  return void 0;
1850
2717
  }
1851
2718
  const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
1852
- return parseLogEvent(params.args, sentinel, location, ts);
2719
+ return parseLogEvent(params.args, sentinel, location, ts, maxValueLength);
1853
2720
  }
1854
2721
  async function removeBreakpointBestEffort(session, breakpointId) {
1855
2722
  try {
@@ -1896,66 +2763,70 @@ async function waitForStop(session, options, registerMaxEventsSignal) {
1896
2763
 
1897
2764
  // src/cf/tunnel.ts
1898
2765
  import { startDebugger } from "@saptools/cf-debugger";
1899
- async function openCfTunnel(target) {
1900
- const opts = {
1901
- region: target.region,
2766
+ function targetOptions(target) {
2767
+ return {
1902
2768
  ...target.apiEndpoint === void 0 ? {} : { apiEndpoint: target.apiEndpoint },
1903
- org: target.org,
1904
- space: target.space,
1905
- app: target.app,
2769
+ ...target.process === void 0 ? {} : { process: target.process },
2770
+ ...target.instance === void 0 ? {} : { instance: target.instance },
2771
+ ...target.nodePid === void 0 ? {} : { nodePid: target.nodePid }
2772
+ };
2773
+ }
2774
+ function lifecycleOptions(target) {
2775
+ return {
2776
+ ...target.allowSshEnableRestart === void 0 ? {} : { allowSshEnableRestart: target.allowSshEnableRestart },
1906
2777
  ...target.tunnelReadyTimeoutMs === void 0 ? {} : { tunnelReadyTimeoutMs: target.tunnelReadyTimeoutMs },
1907
2778
  ...target.preferredPort === void 0 ? {} : { preferredPort: target.preferredPort },
1908
2779
  ...target.verbose === void 0 ? {} : { verbose: target.verbose },
1909
2780
  ...target.signal === void 0 ? {} : { signal: target.signal },
1910
2781
  ...target.onStatus === void 0 ? {} : { onStatus: target.onStatus }
1911
2782
  };
1912
- try {
1913
- const handle = await startDebugger(opts);
1914
- return {
1915
- localPort: handle.session.localPort,
1916
- handle,
1917
- dispose: async () => {
1918
- await handle.dispose();
1919
- }
1920
- };
1921
- } catch (err) {
1922
- return reuseExistingTunnelOrThrow(err, target.onStatus);
1923
- }
1924
2783
  }
1925
- function reuseExistingTunnelOrThrow(err, onStatus) {
1926
- if (!isSessionAlreadyRunningError(err)) {
1927
- throw err;
1928
- }
1929
- const message = err instanceof Error ? err.message : String(err);
1930
- const port = extractExistingTunnelPort(message);
1931
- if (port === void 0) {
1932
- throw err;
1933
- }
1934
- const warning = `Reusing existing tunnel on port ${port.toString()}`;
1935
- onStatus?.("ready", warning);
2784
+ function toStartDebuggerOptions(target) {
1936
2785
  return {
1937
- localPort: port,
1938
- dispose: () => Promise.resolve()
2786
+ region: target.region,
2787
+ org: target.org,
2788
+ space: target.space,
2789
+ app: target.app,
2790
+ ...targetOptions(target),
2791
+ ...lifecycleOptions(target)
1939
2792
  };
1940
2793
  }
1941
- function isSessionAlreadyRunningError(err) {
1942
- if (typeof err !== "object" || err === null) {
1943
- return false;
1944
- }
1945
- const code = err.code;
1946
- return code === "SESSION_ALREADY_RUNNING";
2794
+ async function openOwnedCfTunnel(target) {
2795
+ const opts = toStartDebuggerOptions(target);
2796
+ const handle = await startDebugger(opts);
2797
+ return {
2798
+ localPort: handle.session.localPort,
2799
+ handle,
2800
+ dispose: async () => {
2801
+ await handle.dispose();
2802
+ }
2803
+ };
1947
2804
  }
1948
- function extractExistingTunnelPort(message) {
1949
- const match = /on port (\d+)/i.exec(message);
1950
- if (match === null) {
2805
+ function isExistingSessionError(error) {
2806
+ return error instanceof Error && "code" in error && error.code === "SESSION_ALREADY_RUNNING";
2807
+ }
2808
+ function existingTunnelPort(error) {
2809
+ if (!isExistingSessionError(error)) {
1951
2810
  return void 0;
1952
2811
  }
1953
- const rawPort = match[1];
2812
+ const rawPort = /\bon port (\d+)\b/iu.exec(error.message)?.[1];
1954
2813
  if (rawPort === void 0) {
1955
2814
  return void 0;
1956
2815
  }
1957
2816
  const port = Number.parseInt(rawPort, 10);
1958
- return Number.isNaN(port) ? void 0 : port;
2817
+ return Number.isInteger(port) && port >= 1 && port <= 65535 ? port : void 0;
2818
+ }
2819
+ async function openCfTunnel(target) {
2820
+ try {
2821
+ return await openOwnedCfTunnel(target);
2822
+ } catch (error) {
2823
+ const localPort = existingTunnelPort(error);
2824
+ if (localPort === void 0) {
2825
+ throw error;
2826
+ }
2827
+ target.onStatus?.("ready", `Reusing existing tunnel on port ${localPort.toString()}`);
2828
+ return { localPort, dispose: () => Promise.resolve() };
2829
+ }
1959
2830
  }
1960
2831
  export {
1961
2832
  CfInspectorError,
@@ -1969,16 +2840,25 @@ export {
1969
2840
  evaluateGlobal,
1970
2841
  evaluateOnFrame,
1971
2842
  fetchInspectorVersion,
2843
+ getPossibleBreakpoints,
1972
2844
  getProperties,
2845
+ getScriptSource,
1973
2846
  listScripts,
1974
2847
  openCfTunnel,
2848
+ openOwnedCfTunnel,
1975
2849
  parseBreakpointSpec,
1976
2850
  parseRemoteRoot,
2851
+ releaseObject,
2852
+ releaseObjectGroup,
1977
2853
  removeBreakpoint,
1978
2854
  resume,
1979
2855
  runSetupEvals,
1980
2856
  setBreakpoint,
2857
+ setBreakpointAtLocation,
1981
2858
  setPauseOnExceptions,
2859
+ stepInto,
2860
+ stepOut,
2861
+ stepOver,
1982
2862
  streamLogpoint,
1983
2863
  validateExpression,
1984
2864
  waitForPause,