@saptools/cf-inspector 0.5.0 → 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) {
352
+ const location = toScriptLocation(entry);
353
+ if (location === void 0 || !isRecord(entry)) {
307
354
  return [];
308
355
  }
309
- const candidate = entry;
310
- const scriptId = asString(candidate.scriptId);
311
- if (scriptId.length === 0) {
312
- return [];
313
- }
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) {
365
+ const location = toScriptLocation(entry);
366
+ if (location === void 0 || !isRecord(entry)) {
326
367
  return [];
327
368
  }
328
- const candidate = entry;
329
- const type = asString(candidate.type);
330
- if (type.length === 0) {
331
- return [];
332
- }
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,6 +664,83 @@ 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();
@@ -658,6 +929,11 @@ function pauseMatches(pause, breakpointIds, pauseReasons) {
658
929
  function remainingUntil(deadlineMs) {
659
930
  return Math.max(0, deadlineMs - performance2.now());
660
931
  }
932
+ function throwIfAborted(signal) {
933
+ if (signal?.aborted === true) {
934
+ throw new CfInspectorError("ABORTED", "Aborted while waiting for Debugger.paused");
935
+ }
936
+ }
661
937
  function hasResumedSincePause(session, pause) {
662
938
  const pauseAt = pause.receivedAtMs;
663
939
  const resumedAt = session.debuggerState.lastResumedAtMs;
@@ -676,20 +952,23 @@ function throwUnrelatedPauseTimeout(pause, timeoutMs) {
676
952
  pauseDetail(pause)
677
953
  );
678
954
  }
679
- async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeoutMs) {
955
+ async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, options) {
680
956
  if (hasResumedSincePause(session, pause)) {
681
957
  return;
682
958
  }
683
959
  const remainingMs = remainingUntil(deadlineMs);
684
960
  if (remainingMs <= 0) {
685
- throwUnrelatedPauseTimeout(pause, timeoutMs);
961
+ throwUnrelatedPauseTimeout(pause, options.timeoutMs);
686
962
  }
687
963
  try {
688
- await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
964
+ await session.client.waitFor("Debugger.resumed", {
965
+ timeoutMs: remainingMs,
966
+ ...options.signal === void 0 ? {} : { signal: options.signal }
967
+ });
689
968
  session.debuggerState.lastResumedAtMs = performance2.now();
690
969
  } catch (err) {
691
970
  if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
692
- throwUnrelatedPauseTimeout(pause, timeoutMs);
971
+ throwUnrelatedPauseTimeout(pause, options.timeoutMs);
693
972
  }
694
973
  throw err;
695
974
  }
@@ -706,13 +985,16 @@ async function handleUnmatchedPause(session, pause, options, deadlineMs) {
706
985
  return;
707
986
  }
708
987
  options.onUnmatchedPause?.(pause);
709
- await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
988
+ await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options);
710
989
  }
711
990
  async function waitForPause(session, options) {
991
+ throwIfAborted(options.signal);
712
992
  const deadlineMs = performance2.now() + options.timeoutMs;
713
993
  const buffer = session.pauseBuffer;
714
994
  while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
995
+ throwIfAborted(options.signal);
715
996
  while (buffer.length > 0) {
997
+ throwIfAborted(options.signal);
716
998
  const buffered = buffer.shift();
717
999
  if (buffered === void 0) {
718
1000
  continue;
@@ -741,6 +1023,7 @@ async function waitForLivePause(session, options, deadlineMs) {
741
1023
  try {
742
1024
  params = await session.client.waitFor("Debugger.paused", {
743
1025
  timeoutMs: remainingMs,
1026
+ ...options.signal === void 0 ? {} : { signal: options.signal },
744
1027
  predicate: () => {
745
1028
  receivedAtMs = performance2.now();
746
1029
  return true;
@@ -767,7 +1050,8 @@ async function evaluateOnFrame(session, callFrameId, expression, options = {}) {
767
1050
  returnByValue: false,
768
1051
  generatePreview: true,
769
1052
  silent: true,
770
- ...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect }
1053
+ ...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect },
1054
+ ...options.objectGroup === void 0 ? {} : { objectGroup: options.objectGroup }
771
1055
  });
772
1056
  }
773
1057
  function isSideEffectRefusal(result) {
@@ -833,6 +1117,39 @@ async function getProperties(session, objectId) {
833
1117
  }
834
1118
  return result.result;
835
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
+ }
836
1153
 
837
1154
  // src/inspector/session.ts
838
1155
  import { performance as performance3 } from "perf_hooks";
@@ -938,54 +1255,70 @@ var CdpClient = class _CdpClient {
938
1255
  if (this.closed) {
939
1256
  throw this.closeReason ?? new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Connection closed");
940
1257
  }
941
- 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) => {
942
1265
  let settled = false;
1266
+ let offEvent = () => void 0;
1267
+ let offClose = () => void 0;
943
1268
  const cleanup = () => {
944
1269
  clearTimeout(timer);
945
1270
  offEvent();
946
1271
  offClose();
1272
+ options.signal?.removeEventListener("abort", onAbort);
947
1273
  };
948
- const finish = (value) => {
1274
+ const resolveOnce = (value) => {
1275
+ if (settled) {
1276
+ return;
1277
+ }
949
1278
  settled = true;
950
1279
  cleanup();
951
1280
  resolve(value);
952
1281
  };
953
- const offEvent = this.on(method, (raw) => {
954
- if (settled) {
955
- return;
956
- }
957
- const params = raw;
958
- if (options.predicate) {
959
- let accepted;
960
- try {
961
- accepted = options.predicate(params);
962
- } catch {
963
- return;
964
- }
965
- if (!accepted) {
966
- return;
967
- }
968
- }
969
- finish(params);
970
- });
971
- const offClose = this.onClose((err) => {
1282
+ const rejectOnce = (error) => {
972
1283
  if (settled) {
973
1284
  return;
974
1285
  }
975
1286
  settled = true;
976
1287
  cleanup();
977
- reject(err);
978
- });
1288
+ reject(error);
1289
+ };
1290
+ const onAbort = () => {
1291
+ rejectOnce(this.createWaitAbortError(method));
1292
+ };
979
1293
  const timer = setTimeout(() => {
980
- 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)) {
981
1299
  return;
982
1300
  }
983
- settled = true;
984
- cleanup();
985
- reject(this.createWaitTimeoutError(method, options.timeoutMs));
986
- }, 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
+ }
987
1310
  });
988
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
+ }
989
1322
  onClose(listener) {
990
1323
  if (this.closed) {
991
1324
  const reason = this.closeReason ?? new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Connection closed");
@@ -1052,6 +1385,9 @@ var CdpClient = class _CdpClient {
1052
1385
  `Timed out waiting for ${method} after ${timeoutMs.toString()}ms`
1053
1386
  );
1054
1387
  }
1388
+ createWaitAbortError(method) {
1389
+ return new CfInspectorError("ABORTED", `Aborted while waiting for ${method}`);
1390
+ }
1055
1391
  sendPayload(id, method, payload, timer, reject) {
1056
1392
  try {
1057
1393
  this.transport.send(payload);
@@ -1356,24 +1692,29 @@ function workerToInspectorTarget(worker) {
1356
1692
  }
1357
1693
  async function initSession(client, target) {
1358
1694
  const scripts = /* @__PURE__ */ new Map();
1359
- client.on("Debugger.scriptParsed", (raw) => {
1360
- const params = raw;
1361
- const scriptId = asString(params.scriptId);
1362
- const url = asString(params.url);
1363
- if (scriptId.length === 0) {
1364
- return;
1365
- }
1366
- scripts.set(scriptId, { scriptId, url });
1367
- });
1695
+ registerScriptTracking(client, scripts);
1368
1696
  const pauseBuffer = [];
1369
1697
  const pauseWaitGate = { active: false };
1370
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) {
1371
1713
  client.on("Debugger.paused", (raw) => {
1372
1714
  if (pauseWaitGate.active) {
1373
1715
  return;
1374
1716
  }
1375
- const params = raw;
1376
- const event = toPauseEvent(params, performance3.now(), scripts);
1717
+ const event = toPauseEvent(raw, performance3.now(), scripts);
1377
1718
  if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
1378
1719
  pauseBuffer.shift();
1379
1720
  }
@@ -1382,8 +1723,8 @@ async function initSession(client, target) {
1382
1723
  client.on("Debugger.resumed", () => {
1383
1724
  debuggerState.lastResumedAtMs = performance3.now();
1384
1725
  });
1385
- await client.send("Runtime.enable");
1386
- await client.send("Debugger.enable");
1726
+ }
1727
+ function createSession(client, target, scripts, pauseBuffer, pauseWaitGate, debuggerState) {
1387
1728
  return {
1388
1729
  client,
1389
1730
  target,
@@ -2422,66 +2763,70 @@ async function waitForStop(session, options, registerMaxEventsSignal) {
2422
2763
 
2423
2764
  // src/cf/tunnel.ts
2424
2765
  import { startDebugger } from "@saptools/cf-debugger";
2425
- async function openCfTunnel(target) {
2426
- const opts = {
2427
- region: target.region,
2766
+ function targetOptions(target) {
2767
+ return {
2428
2768
  ...target.apiEndpoint === void 0 ? {} : { apiEndpoint: target.apiEndpoint },
2429
- org: target.org,
2430
- space: target.space,
2431
- 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 },
2432
2777
  ...target.tunnelReadyTimeoutMs === void 0 ? {} : { tunnelReadyTimeoutMs: target.tunnelReadyTimeoutMs },
2433
2778
  ...target.preferredPort === void 0 ? {} : { preferredPort: target.preferredPort },
2434
2779
  ...target.verbose === void 0 ? {} : { verbose: target.verbose },
2435
2780
  ...target.signal === void 0 ? {} : { signal: target.signal },
2436
2781
  ...target.onStatus === void 0 ? {} : { onStatus: target.onStatus }
2437
2782
  };
2438
- try {
2439
- const handle = await startDebugger(opts);
2440
- return {
2441
- localPort: handle.session.localPort,
2442
- handle,
2443
- dispose: async () => {
2444
- await handle.dispose();
2445
- }
2446
- };
2447
- } catch (err) {
2448
- return reuseExistingTunnelOrThrow(err, target.onStatus);
2449
- }
2450
2783
  }
2451
- function reuseExistingTunnelOrThrow(err, onStatus) {
2452
- if (!isSessionAlreadyRunningError(err)) {
2453
- throw err;
2454
- }
2455
- const message = err instanceof Error ? err.message : String(err);
2456
- const port = extractExistingTunnelPort(message);
2457
- if (port === void 0) {
2458
- throw err;
2459
- }
2460
- const warning = `Reusing existing tunnel on port ${port.toString()}`;
2461
- onStatus?.("ready", warning);
2784
+ function toStartDebuggerOptions(target) {
2462
2785
  return {
2463
- localPort: port,
2464
- 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)
2465
2792
  };
2466
2793
  }
2467
- function isSessionAlreadyRunningError(err) {
2468
- if (typeof err !== "object" || err === null) {
2469
- return false;
2470
- }
2471
- const code = err.code;
2472
- 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
+ };
2473
2804
  }
2474
- function extractExistingTunnelPort(message) {
2475
- const match = /on port (\d+)/i.exec(message);
2476
- 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)) {
2477
2810
  return void 0;
2478
2811
  }
2479
- const rawPort = match[1];
2812
+ const rawPort = /\bon port (\d+)\b/iu.exec(error.message)?.[1];
2480
2813
  if (rawPort === void 0) {
2481
2814
  return void 0;
2482
2815
  }
2483
2816
  const port = Number.parseInt(rawPort, 10);
2484
- 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
+ }
2485
2830
  }
2486
2831
  export {
2487
2832
  CfInspectorError,
@@ -2495,16 +2840,25 @@ export {
2495
2840
  evaluateGlobal,
2496
2841
  evaluateOnFrame,
2497
2842
  fetchInspectorVersion,
2843
+ getPossibleBreakpoints,
2498
2844
  getProperties,
2845
+ getScriptSource,
2499
2846
  listScripts,
2500
2847
  openCfTunnel,
2848
+ openOwnedCfTunnel,
2501
2849
  parseBreakpointSpec,
2502
2850
  parseRemoteRoot,
2851
+ releaseObject,
2852
+ releaseObjectGroup,
2503
2853
  removeBreakpoint,
2504
2854
  resume,
2505
2855
  runSetupEvals,
2506
2856
  setBreakpoint,
2857
+ setBreakpointAtLocation,
2507
2858
  setPauseOnExceptions,
2859
+ stepInto,
2860
+ stepOut,
2861
+ stepOver,
2508
2862
  streamLogpoint,
2509
2863
  validateExpression,
2510
2864
  waitForPause,