@saptools/cf-inspector 0.4.0 → 0.4.1

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
@@ -140,11 +140,15 @@ interface CdpTransport {
140
140
  on<E extends keyof CdpTransportEventMap>(event: E, listener: CdpTransportEventMap[E]): void;
141
141
  off<E extends keyof CdpTransportEventMap>(event: E, listener: CdpTransportEventMap[E]): void;
142
142
  }
143
- type CdpTransportFactory = (url: string) => Promise<CdpTransport>;
143
+ interface CdpTransportFactoryOptions {
144
+ readonly connectTimeoutMs?: number;
145
+ }
146
+ type CdpTransportFactory = (url: string, options?: CdpTransportFactoryOptions) => Promise<CdpTransport>;
144
147
  interface CdpClientOptions {
145
148
  readonly url: string;
146
149
  readonly transportFactory?: CdpTransportFactory;
147
150
  readonly requestTimeoutMs?: number;
151
+ readonly connectTimeoutMs?: number;
148
152
  }
149
153
  declare class CdpClient {
150
154
  private readonly transport;
@@ -155,6 +159,7 @@ declare class CdpClient {
155
159
  private closed;
156
160
  private closeReason;
157
161
  private readonly handleMessage;
162
+ private safeEmit;
158
163
  private readonly handleClose;
159
164
  private readonly handleError;
160
165
  private constructor();
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env node
2
1
  var __defProp = Object.defineProperty;
3
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
3
  var __esm = (fn, res) => function __init() {
@@ -35,10 +34,19 @@ __export(wsTransport_exports, {
35
34
  wsTransportFactory: () => wsTransportFactory
36
35
  });
37
36
  import { WebSocket } from "ws";
38
- async function wsTransportFactory(url) {
37
+ async function wsTransportFactory(url, options = {}) {
39
38
  const socket = new WebSocket(url, { perMessageDeflate: false });
40
- await waitForOpen(socket, url);
41
- const wrappers = /* @__PURE__ */ new WeakMap();
39
+ await waitForOpen(socket, url, options.connectTimeoutMs);
40
+ const wrappers = /* @__PURE__ */ new Map();
41
+ function wrappersFor(event) {
42
+ const existing = wrappers.get(event);
43
+ if (existing !== void 0) {
44
+ return existing;
45
+ }
46
+ const created = /* @__PURE__ */ new WeakMap();
47
+ wrappers.set(event, created);
48
+ return created;
49
+ }
42
50
  return {
43
51
  send(payload) {
44
52
  socket.send(payload);
@@ -50,11 +58,12 @@ async function wsTransportFactory(url) {
50
58
  return socket.readyState;
51
59
  },
52
60
  on(event, listener) {
53
- const wrapped = wrapListener(event, listener, wrappers);
61
+ const wrapped = wrapListener(event, listener);
62
+ wrappersFor(event).set(listener, wrapped);
54
63
  socket.on(event, wrapped);
55
64
  },
56
65
  off(event, listener) {
57
- const wrapped = wrappers.get(listener);
66
+ const wrapped = wrappersFor(event).get(listener);
58
67
  if (!wrapped) {
59
68
  return;
60
69
  }
@@ -62,14 +71,30 @@ async function wsTransportFactory(url) {
62
71
  }
63
72
  };
64
73
  }
65
- async function waitForOpen(socket, url) {
74
+ async function waitForOpen(socket, url, timeoutMs) {
66
75
  await new Promise((resolve, reject) => {
67
- const onOpen = () => {
76
+ let settled = false;
77
+ const cleanup = () => {
78
+ socket.off("open", onOpen);
68
79
  socket.off("error", onError);
80
+ if (timer !== void 0) {
81
+ clearTimeout(timer);
82
+ }
83
+ };
84
+ const onOpen = () => {
85
+ if (settled) {
86
+ return;
87
+ }
88
+ settled = true;
89
+ cleanup();
69
90
  resolve();
70
91
  };
71
92
  const onError = (err) => {
72
- socket.off("open", onOpen);
93
+ if (settled) {
94
+ return;
95
+ }
96
+ settled = true;
97
+ cleanup();
73
98
  reject(
74
99
  new CfInspectorError(
75
100
  "INSPECTOR_CONNECTION_FAILED",
@@ -77,29 +102,45 @@ async function waitForOpen(socket, url) {
77
102
  )
78
103
  );
79
104
  };
105
+ const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
106
+ if (settled) {
107
+ return;
108
+ }
109
+ settled = true;
110
+ cleanup();
111
+ socket.on("error", () => {
112
+ });
113
+ try {
114
+ socket.terminate();
115
+ } catch {
116
+ }
117
+ reject(
118
+ new CfInspectorError(
119
+ "INSPECTOR_CONNECTION_FAILED",
120
+ `WebSocket handshake to ${url} timed out after ${timeoutMs.toString()}ms`
121
+ )
122
+ );
123
+ }, timeoutMs);
80
124
  socket.once("open", onOpen);
81
125
  socket.once("error", onError);
82
126
  });
83
127
  }
84
- function wrapListener(event, listener, wrappers) {
128
+ function wrapListener(event, listener) {
85
129
  if (event === "message") {
86
130
  const wrapped2 = (data) => {
87
131
  listener(data.toString("utf8"));
88
132
  };
89
- wrappers.set(listener, wrapped2);
90
133
  return wrapped2;
91
134
  }
92
135
  if (event === "close") {
93
136
  const wrapped2 = () => {
94
137
  listener();
95
138
  };
96
- wrappers.set(listener, wrapped2);
97
139
  return wrapped2;
98
140
  }
99
141
  const wrapped = (err) => {
100
142
  listener(err);
101
143
  };
102
- wrappers.set(listener, wrapped);
103
144
  return wrapped;
104
145
  }
105
146
  var init_wsTransport = __esm({
@@ -212,9 +253,11 @@ function normalizeRegexRootPattern(pattern) {
212
253
  const withoutEndAnchor = withoutStartAnchor.endsWith("$") && !isEscaped(withoutStartAnchor, withoutStartAnchor.length - 1) ? withoutStartAnchor.slice(0, -1) : withoutStartAnchor;
213
254
  return stripTrailingSlash(withoutEndAnchor);
214
255
  }
215
- function buildFileUrlRegex(rootPattern, tail) {
216
- const separator = rootPattern.endsWith("/") ? "" : "/";
217
- return `^file://${rootPattern}${separator}${tail}$`;
256
+ function buildFileUrlRegex(embeddedRoot, tail, separator) {
257
+ return `^file://${embeddedRoot}${separator}${tail}$`;
258
+ }
259
+ function rootSeparator(rawPattern) {
260
+ return rawPattern.endsWith("/") ? "" : "/";
218
261
  }
219
262
  function escapeRegExp(value) {
220
263
  return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
@@ -241,12 +284,12 @@ function buildBreakpointUrlRegex(input) {
241
284
  return `(?:^|/)${tail}$`;
242
285
  }
243
286
  case "literal": {
244
- const escapedRoot = escapeRegExp(input.remoteRoot.value);
245
- return buildFileUrlRegex(escapedRoot, tail);
287
+ const root = input.remoteRoot.value;
288
+ return buildFileUrlRegex(escapeRegExp(root), tail, rootSeparator(root));
246
289
  }
247
290
  case "regex": {
248
- const rootPattern = normalizeRegexRootPattern(input.remoteRoot.pattern);
249
- return buildFileUrlRegex(rootPattern, tail);
291
+ const root = normalizeRegexRootPattern(input.remoteRoot.pattern);
292
+ return buildFileUrlRegex(`(?:${root})`, tail, rootSeparator(root));
250
293
  }
251
294
  }
252
295
  }
@@ -757,10 +800,19 @@ var CdpClient = class _CdpClient {
757
800
  return;
758
801
  }
759
802
  if (typeof parsed.method === "string") {
760
- this.emitter.emit(parsed.method, parsed.params);
761
- this.emitter.emit("event", { method: parsed.method, params: parsed.params });
803
+ this.safeEmit(parsed.method, parsed.params);
804
+ this.safeEmit("event", { method: parsed.method, params: parsed.params });
762
805
  }
763
806
  };
807
+ safeEmit(event, payload) {
808
+ const listeners = this.emitter.listeners(event);
809
+ for (const listener of listeners) {
810
+ try {
811
+ listener(payload);
812
+ } catch {
813
+ }
814
+ }
815
+ }
764
816
  handleClose = () => {
765
817
  this.markClosed(new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Inspector connection closed"));
766
818
  };
@@ -778,7 +830,8 @@ var CdpClient = class _CdpClient {
778
830
  }
779
831
  static async connect(options) {
780
832
  const factory = options.transportFactory ?? await loadDefaultFactory();
781
- const transport = await factory(options.url);
833
+ const factoryOptions = options.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: options.connectTimeoutMs };
834
+ const transport = await factory(options.url, factoryOptions);
782
835
  return new _CdpClient(transport, options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
783
836
  }
784
837
  async send(method, params = {}) {
@@ -828,8 +881,16 @@ var CdpClient = class _CdpClient {
828
881
  return;
829
882
  }
830
883
  const params = raw;
831
- if (options.predicate && !options.predicate(params)) {
832
- return;
884
+ if (options.predicate) {
885
+ let accepted;
886
+ try {
887
+ accepted = options.predicate(params);
888
+ } catch {
889
+ return;
890
+ }
891
+ if (!accepted) {
892
+ return;
893
+ }
833
894
  }
834
895
  finish(params);
835
896
  });
@@ -959,7 +1020,18 @@ async function connectInspector(options) {
959
1020
  `No inspector targets available on ${host}:${options.port.toString()}`
960
1021
  );
961
1022
  }
962
- const client = await CdpClient.connect({ url: target.webSocketDebuggerUrl });
1023
+ const client = await CdpClient.connect({
1024
+ url: target.webSocketDebuggerUrl,
1025
+ connectTimeoutMs
1026
+ });
1027
+ try {
1028
+ return await initSession(client, target);
1029
+ } catch (err) {
1030
+ client.dispose();
1031
+ throw err;
1032
+ }
1033
+ }
1034
+ async function initSession(client, target) {
963
1035
  const scripts = /* @__PURE__ */ new Map();
964
1036
  client.on("Debugger.scriptParsed", (raw) => {
965
1037
  const params = raw;
@@ -1080,19 +1152,32 @@ function scalarFromVariable(variable) {
1080
1152
  }
1081
1153
  return value === "null" ? null : value;
1082
1154
  }
1155
+ function isArrayLikeChildren(children) {
1156
+ let hasNumeric = false;
1157
+ for (const child of children) {
1158
+ if (child.name === "length") {
1159
+ continue;
1160
+ }
1161
+ if (parseNumericIndex(child.name) === void 0) {
1162
+ return false;
1163
+ }
1164
+ hasNumeric = true;
1165
+ }
1166
+ return hasNumeric;
1167
+ }
1083
1168
  function toStructuredValue(variable) {
1084
1169
  const children = variable.children;
1085
1170
  if (children === void 0 || children.length === 0) {
1086
1171
  return scalarFromVariable(variable);
1087
1172
  }
1088
- const indexed = children.flatMap((child) => {
1089
- const index = parseNumericIndex(child.name);
1090
- if (index === void 0) {
1091
- return [];
1092
- }
1093
- return [[index, toStructuredValue(child)]];
1094
- });
1095
- if (indexed.length > 0) {
1173
+ if (isArrayLikeChildren(children)) {
1174
+ const indexed = children.flatMap((child) => {
1175
+ const index = parseNumericIndex(child.name);
1176
+ if (index === void 0) {
1177
+ return [];
1178
+ }
1179
+ return [[index, toStructuredValue(child)]];
1180
+ });
1096
1181
  const maxIndex = Math.max(...indexed.map(([index]) => index));
1097
1182
  const out2 = Array.from({ length: maxIndex + 1 }, () => null);
1098
1183
  for (const [index, entry] of indexed) {
@@ -1694,7 +1779,10 @@ async function streamLogpoint(session, options) {
1694
1779
  return;
1695
1780
  }
1696
1781
  emitted += 1;
1697
- options.onEvent(event);
1782
+ try {
1783
+ options.onEvent(event);
1784
+ } catch {
1785
+ }
1698
1786
  if (maxEvents !== void 0 && emitted >= maxEvents) {
1699
1787
  maxEventsReached = true;
1700
1788
  stopMaxEvents?.();