browser-pilot 0.0.13 → 0.0.15

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.
@@ -0,0 +1,303 @@
1
+ import {
2
+ CDPError
3
+ } from "./chunk-JXAUPHZM.mjs";
4
+
5
+ // src/utils/json.ts
6
+ function isRecord(value) {
7
+ return typeof value === "object" && value !== null;
8
+ }
9
+ function stringifyUnknown(value) {
10
+ if (typeof value === "string") return value;
11
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
12
+ return String(value);
13
+ }
14
+ if (value === null) return "null";
15
+ if (value === void 0) return "undefined";
16
+ try {
17
+ return JSON.stringify(value);
18
+ } catch {
19
+ return Object.prototype.toString.call(value);
20
+ }
21
+ }
22
+
23
+ // src/cdp/transport.ts
24
+ function createTransport(wsUrl, options = {}) {
25
+ const { timeout = 3e4 } = options;
26
+ return new Promise((resolve, reject) => {
27
+ const timeoutId = setTimeout(() => {
28
+ reject(new Error(`WebSocket connection timeout after ${timeout}ms`));
29
+ }, timeout);
30
+ const ws = new WebSocket(wsUrl);
31
+ const messageHandlers = [];
32
+ const closeHandlers = [];
33
+ const errorHandlers = [];
34
+ ws.addEventListener("open", () => {
35
+ clearTimeout(timeoutId);
36
+ const transport = {
37
+ send(message) {
38
+ if (ws.readyState === WebSocket.OPEN) {
39
+ ws.send(message);
40
+ } else {
41
+ throw new Error(
42
+ `Cannot send message, WebSocket is ${getReadyStateString(ws.readyState)}`
43
+ );
44
+ }
45
+ },
46
+ async close() {
47
+ return new Promise((resolveClose) => {
48
+ if (ws.readyState === WebSocket.CLOSED) {
49
+ resolveClose();
50
+ return;
51
+ }
52
+ let settled = false;
53
+ let fallbackTimer;
54
+ const finish = () => {
55
+ if (settled) return;
56
+ settled = true;
57
+ if (fallbackTimer) clearTimeout(fallbackTimer);
58
+ ws.removeEventListener("close", onClose);
59
+ resolveClose();
60
+ };
61
+ const onClose = () => {
62
+ finish();
63
+ };
64
+ ws.addEventListener("close", onClose);
65
+ try {
66
+ if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) {
67
+ ws.close();
68
+ }
69
+ } catch {
70
+ finish();
71
+ return;
72
+ }
73
+ fallbackTimer = setTimeout(finish, 200);
74
+ });
75
+ },
76
+ onMessage(handler) {
77
+ messageHandlers.push(handler);
78
+ },
79
+ onClose(handler) {
80
+ closeHandlers.push(handler);
81
+ },
82
+ onError(handler) {
83
+ errorHandlers.push(handler);
84
+ }
85
+ };
86
+ resolve(transport);
87
+ });
88
+ ws.addEventListener("message", (event) => {
89
+ const data = typeof event.data === "string" ? event.data : String(event.data);
90
+ for (const handler of messageHandlers) {
91
+ handler(data);
92
+ }
93
+ });
94
+ ws.addEventListener("close", () => {
95
+ for (const handler of closeHandlers) {
96
+ handler();
97
+ }
98
+ });
99
+ ws.addEventListener("error", (_event) => {
100
+ clearTimeout(timeoutId);
101
+ const error = new Error("WebSocket connection error");
102
+ for (const handler of errorHandlers) {
103
+ handler(error);
104
+ }
105
+ reject(error);
106
+ });
107
+ });
108
+ }
109
+ function getReadyStateString(state) {
110
+ switch (state) {
111
+ case WebSocket.CONNECTING:
112
+ return "CONNECTING";
113
+ case WebSocket.OPEN:
114
+ return "OPEN";
115
+ case WebSocket.CLOSING:
116
+ return "CLOSING";
117
+ case WebSocket.CLOSED:
118
+ return "CLOSED";
119
+ default:
120
+ return "UNKNOWN";
121
+ }
122
+ }
123
+
124
+ // src/cdp/client.ts
125
+ function createCDPClientFromTransport(transport, options = {}) {
126
+ return buildCDPClient(transport, options);
127
+ }
128
+ async function createCDPClient(wsUrl, options = {}) {
129
+ const { timeout = 3e4 } = options;
130
+ const transport = await createTransport(wsUrl, { timeout });
131
+ return buildCDPClient(transport, options);
132
+ }
133
+ function buildCDPClient(transport, options = {}) {
134
+ const { debug = false, timeout = 3e4 } = options;
135
+ let messageId = 0;
136
+ let currentSessionId;
137
+ let connected = true;
138
+ const pending = /* @__PURE__ */ new Map();
139
+ const eventHandlers = /* @__PURE__ */ new Map();
140
+ const anyEventHandlers = /* @__PURE__ */ new Set();
141
+ transport.onMessage((raw) => {
142
+ let msg;
143
+ try {
144
+ const parsed = JSON.parse(raw);
145
+ if (!isRecord(parsed)) {
146
+ if (debug) console.error("[CDP] Ignoring non-object message:", raw);
147
+ return;
148
+ }
149
+ if ("id" in parsed && typeof parsed["id"] === "number") {
150
+ msg = parsed;
151
+ } else if ("method" in parsed && typeof parsed["method"] === "string") {
152
+ msg = parsed;
153
+ } else {
154
+ if (debug) console.error("[CDP] Ignoring invalid message shape:", raw);
155
+ return;
156
+ }
157
+ } catch {
158
+ if (debug) console.error("[CDP] Failed to parse message:", raw);
159
+ return;
160
+ }
161
+ if (debug) {
162
+ console.log("[CDP] <--", JSON.stringify(msg, null, 2).slice(0, 500));
163
+ }
164
+ if ("id" in msg && typeof msg.id === "number") {
165
+ const response = msg;
166
+ const request = pending.get(response.id);
167
+ if (request) {
168
+ pending.delete(response.id);
169
+ clearTimeout(request.timer);
170
+ if (response.error) {
171
+ const error = typeof response.error === "string" ? { code: -32e3, message: response.error } : response.error;
172
+ request.reject(new CDPError(error));
173
+ } else {
174
+ request.resolve(response.result);
175
+ }
176
+ }
177
+ return;
178
+ }
179
+ if ("method" in msg) {
180
+ const event = msg;
181
+ const params = event.params ?? {};
182
+ for (const handler of anyEventHandlers) {
183
+ try {
184
+ handler(event.method, params);
185
+ } catch (e) {
186
+ if (debug) console.error("[CDP] Error in any-event handler:", e);
187
+ }
188
+ }
189
+ const handlers = eventHandlers.get(event.method);
190
+ if (handlers) {
191
+ for (const handler of handlers) {
192
+ try {
193
+ handler(params);
194
+ } catch (e) {
195
+ if (debug) console.error(`[CDP] Error in handler for ${event.method}:`, e);
196
+ }
197
+ }
198
+ }
199
+ }
200
+ });
201
+ transport.onClose(() => {
202
+ connected = false;
203
+ for (const [id, request] of pending) {
204
+ clearTimeout(request.timer);
205
+ request.reject(new Error("WebSocket connection closed"));
206
+ pending.delete(id);
207
+ }
208
+ });
209
+ transport.onError((error) => {
210
+ if (debug) console.error("[CDP] Transport error:", error);
211
+ });
212
+ const client = {
213
+ async send(method, params, sessionId) {
214
+ if (!connected) {
215
+ throw new Error("CDP client is not connected");
216
+ }
217
+ const id = ++messageId;
218
+ const effectiveSessionId = sessionId === null ? void 0 : sessionId ?? currentSessionId;
219
+ const request = { id, method };
220
+ if (params !== void 0) {
221
+ request.params = params;
222
+ }
223
+ if (effectiveSessionId !== void 0) {
224
+ request.sessionId = effectiveSessionId;
225
+ }
226
+ const message = JSON.stringify(request);
227
+ if (debug) {
228
+ console.log("[CDP] -->", message.slice(0, 500));
229
+ }
230
+ return new Promise((resolve, reject) => {
231
+ const timer = setTimeout(() => {
232
+ pending.delete(id);
233
+ reject(new Error(`CDP command ${method} timed out after ${timeout}ms`));
234
+ }, timeout);
235
+ pending.set(id, {
236
+ resolve,
237
+ reject,
238
+ method,
239
+ timer
240
+ });
241
+ try {
242
+ transport.send(message);
243
+ } catch (e) {
244
+ pending.delete(id);
245
+ clearTimeout(timer);
246
+ reject(e);
247
+ }
248
+ });
249
+ },
250
+ on(event, handler) {
251
+ let handlers = eventHandlers.get(event);
252
+ if (!handlers) {
253
+ handlers = /* @__PURE__ */ new Set();
254
+ eventHandlers.set(event, handlers);
255
+ }
256
+ handlers.add(handler);
257
+ },
258
+ off(event, handler) {
259
+ const handlers = eventHandlers.get(event);
260
+ if (handlers) {
261
+ handlers.delete(handler);
262
+ if (handlers.size === 0) {
263
+ eventHandlers.delete(event);
264
+ }
265
+ }
266
+ },
267
+ onAny(handler) {
268
+ anyEventHandlers.add(handler);
269
+ },
270
+ offAny(handler) {
271
+ anyEventHandlers.delete(handler);
272
+ },
273
+ async close() {
274
+ connected = false;
275
+ await transport.close();
276
+ },
277
+ async attachToTarget(targetId) {
278
+ const result = await this.send("Target.attachToTarget", {
279
+ targetId,
280
+ flatten: true
281
+ });
282
+ currentSessionId = result.sessionId;
283
+ return result.sessionId;
284
+ },
285
+ get sessionId() {
286
+ return currentSessionId;
287
+ },
288
+ setSessionId(sessionId) {
289
+ currentSessionId = sessionId;
290
+ },
291
+ get isConnected() {
292
+ return connected;
293
+ }
294
+ };
295
+ return client;
296
+ }
297
+
298
+ export {
299
+ stringifyUnknown,
300
+ createTransport,
301
+ createCDPClientFromTransport,
302
+ createCDPClient
303
+ };
@@ -0,0 +1,35 @@
1
+ // src/cdp/protocol.ts
2
+ var CDPError = class extends Error {
3
+ code;
4
+ data;
5
+ constructor(error) {
6
+ super(error.message);
7
+ this.name = "CDPError";
8
+ this.code = error.code;
9
+ this.data = error.data;
10
+ }
11
+ };
12
+
13
+ // src/utils/json.ts
14
+ function isRecord(value) {
15
+ return typeof value === "object" && value !== null;
16
+ }
17
+ function stringifyUnknown(value) {
18
+ if (typeof value === "string") return value;
19
+ if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
20
+ return String(value);
21
+ }
22
+ if (value === null) return "null";
23
+ if (value === void 0) return "undefined";
24
+ try {
25
+ return JSON.stringify(value);
26
+ } catch {
27
+ return Object.prototype.toString.call(value);
28
+ }
29
+ }
30
+
31
+ export {
32
+ CDPError,
33
+ isRecord,
34
+ stringifyUnknown
35
+ };
@@ -1,6 +1,7 @@
1
1
  import {
2
- CDPError
3
- } from "./chunk-JXAUPHZM.mjs";
2
+ CDPError,
3
+ isRecord
4
+ } from "./chunk-DTVRFXKI.mjs";
4
5
 
5
6
  // src/cdp/transport.ts
6
7
  function createTransport(wsUrl, options = {}) {
@@ -104,9 +105,16 @@ function getReadyStateString(state) {
104
105
  }
105
106
 
106
107
  // src/cdp/client.ts
108
+ function createCDPClientFromTransport(transport, options = {}) {
109
+ return buildCDPClient(transport, options);
110
+ }
107
111
  async function createCDPClient(wsUrl, options = {}) {
108
- const { debug = false, timeout = 3e4 } = options;
112
+ const { timeout = 3e4 } = options;
109
113
  const transport = await createTransport(wsUrl, { timeout });
114
+ return buildCDPClient(transport, options);
115
+ }
116
+ function buildCDPClient(transport, options = {}) {
117
+ const { debug = false, timeout = 3e4 } = options;
110
118
  let messageId = 0;
111
119
  let currentSessionId;
112
120
  let connected = true;
@@ -116,7 +124,19 @@ async function createCDPClient(wsUrl, options = {}) {
116
124
  transport.onMessage((raw) => {
117
125
  let msg;
118
126
  try {
119
- msg = JSON.parse(raw);
127
+ const parsed = JSON.parse(raw);
128
+ if (!isRecord(parsed)) {
129
+ if (debug) console.error("[CDP] Ignoring non-object message:", raw);
130
+ return;
131
+ }
132
+ if ("id" in parsed && typeof parsed["id"] === "number") {
133
+ msg = parsed;
134
+ } else if ("method" in parsed && typeof parsed["method"] === "string") {
135
+ msg = parsed;
136
+ } else {
137
+ if (debug) console.error("[CDP] Ignoring invalid message shape:", raw);
138
+ return;
139
+ }
120
140
  } catch {
121
141
  if (debug) console.error("[CDP] Failed to parse message:", raw);
122
142
  return;
@@ -131,7 +151,8 @@ async function createCDPClient(wsUrl, options = {}) {
131
151
  pending.delete(response.id);
132
152
  clearTimeout(request.timer);
133
153
  if (response.error) {
134
- request.reject(new CDPError(response.error));
154
+ const error = typeof response.error === "string" ? { code: -32e3, message: response.error } : response.error;
155
+ request.reject(new CDPError(error));
135
156
  } else {
136
157
  request.resolve(response.result);
137
158
  }
@@ -229,6 +250,9 @@ async function createCDPClient(wsUrl, options = {}) {
229
250
  onAny(handler) {
230
251
  anyEventHandlers.add(handler);
231
252
  },
253
+ offAny(handler) {
254
+ anyEventHandlers.delete(handler);
255
+ },
232
256
  async close() {
233
257
  connected = false;
234
258
  await transport.close();
@@ -244,6 +268,9 @@ async function createCDPClient(wsUrl, options = {}) {
244
268
  get sessionId() {
245
269
  return currentSessionId;
246
270
  },
271
+ setSessionId(sessionId) {
272
+ currentSessionId = sessionId;
273
+ },
247
274
  get isConnected() {
248
275
  return connected;
249
276
  }
@@ -252,6 +279,6 @@ async function createCDPClient(wsUrl, options = {}) {
252
279
  }
253
280
 
254
281
  export {
255
- createTransport,
282
+ createCDPClientFromTransport,
256
283
  createCDPClient
257
284
  };
@@ -0,0 +1,11 @@
1
+ // src/daemon/types.ts
2
+ var DAEMON_MAX_AGE_MS = 60 * 60 * 1e3;
3
+ var DAEMON_CONNECT_TIMEOUT_MS = 500;
4
+ var DAEMON_IDLE_TIMEOUT_MS = 60 * 60 * 1e3;
5
+ var DAEMON_READY_TIMEOUT_MS = 3e3;
6
+
7
+ export {
8
+ DAEMON_MAX_AGE_MS,
9
+ DAEMON_CONNECT_TIMEOUT_MS,
10
+ DAEMON_READY_TIMEOUT_MS
11
+ };