@saptools/cf-inspector 0.4.7 → 0.4.10

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.d.ts CHANGED
@@ -117,6 +117,7 @@ interface InspectorConnectOptions {
117
117
  readonly port: number;
118
118
  readonly host?: string;
119
119
  readonly connectTimeoutMs?: number;
120
+ readonly targetIndex?: number;
120
121
  }
121
122
 
122
123
  declare function parseBreakpointSpec(input: string): BreakpointLocation;
@@ -338,7 +339,7 @@ interface TunnelTarget {
338
339
  }
339
340
  interface OpenedTunnel {
340
341
  readonly localPort: number;
341
- readonly handle: DebuggerHandle;
342
+ readonly handle?: DebuggerHandle;
342
343
  dispose(): Promise<void>;
343
344
  }
344
345
  declare function openCfTunnel(target: TunnelTarget): Promise<OpenedTunnel>;
package/dist/index.js CHANGED
@@ -1004,11 +1004,12 @@ async function connectInspector(options) {
1004
1004
  const host = options.host ?? DEFAULT_HOST;
1005
1005
  const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
1006
1006
  const targets = await discoverInspectorTargets(host, options.port, connectTimeoutMs);
1007
- const target = targets[0];
1007
+ const targetIndex = options.targetIndex ?? 0;
1008
+ const target = targets[targetIndex];
1008
1009
  if (!target) {
1009
1010
  throw new CfInspectorError(
1010
1011
  "INSPECTOR_DISCOVERY_FAILED",
1011
- `No inspector targets available on ${host}:${options.port.toString()}`
1012
+ `No inspector target at index ${targetIndex.toString()} on ${host}:${options.port.toString()} (available: ${targets.length.toString()})`
1012
1013
  );
1013
1014
  }
1014
1015
  const client = await CdpClient.connect({
@@ -1871,15 +1872,54 @@ async function openCfTunnel(target) {
1871
1872
  ...target.signal === void 0 ? {} : { signal: target.signal },
1872
1873
  ...target.onStatus === void 0 ? {} : { onStatus: target.onStatus }
1873
1874
  };
1874
- const handle = await startDebugger(opts);
1875
+ try {
1876
+ const handle = await startDebugger(opts);
1877
+ return {
1878
+ localPort: handle.session.localPort,
1879
+ handle,
1880
+ dispose: async () => {
1881
+ await handle.dispose();
1882
+ }
1883
+ };
1884
+ } catch (err) {
1885
+ return reuseExistingTunnelOrThrow(err, target.onStatus);
1886
+ }
1887
+ }
1888
+ function reuseExistingTunnelOrThrow(err, onStatus) {
1889
+ if (!isSessionAlreadyRunningError(err)) {
1890
+ throw err;
1891
+ }
1892
+ const message = err instanceof Error ? err.message : String(err);
1893
+ const port = extractExistingTunnelPort(message);
1894
+ if (port === void 0) {
1895
+ throw err;
1896
+ }
1897
+ const warning = `Reusing existing tunnel on port ${port.toString()}`;
1898
+ onStatus?.("ready", warning);
1875
1899
  return {
1876
- localPort: handle.session.localPort,
1877
- handle,
1878
- dispose: async () => {
1879
- await handle.dispose();
1880
- }
1900
+ localPort: port,
1901
+ dispose: () => Promise.resolve()
1881
1902
  };
1882
1903
  }
1904
+ function isSessionAlreadyRunningError(err) {
1905
+ if (typeof err !== "object" || err === null) {
1906
+ return false;
1907
+ }
1908
+ const code = err.code;
1909
+ return code === "SESSION_ALREADY_RUNNING";
1910
+ }
1911
+ function extractExistingTunnelPort(message) {
1912
+ const match = /on port (\d+)/i.exec(message);
1913
+ if (match === null) {
1914
+ return void 0;
1915
+ }
1916
+ const rawPort = match[1];
1917
+ if (rawPort === void 0) {
1918
+ return void 0;
1919
+ }
1920
+ const port = Number.parseInt(rawPort, 10);
1921
+ return Number.isNaN(port) ? void 0 : port;
1922
+ }
1883
1923
  export {
1884
1924
  CfInspectorError,
1885
1925
  buildBreakpointUrlRegex,