nothing-browser 0.1.0 → 0.1.2

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.
@@ -1,4 +1,4 @@
1
- // piggy/client/index.ts
1
+ //piggy/client/index.ts
2
2
  import { connect, type Socket } from "net";
3
3
  import { writeFileSync, mkdirSync } from "fs";
4
4
  import { dirname } from "path";
@@ -280,6 +280,23 @@ export class PiggyClient {
280
280
  }
281
281
  }
282
282
  }
283
+
284
+ if (event.event === "dialog") {
285
+ const key = `dialog:${event.tabId ?? "default"}`;
286
+ const handlers = this.globalEventHandlers.get(key);
287
+ if (handlers) {
288
+ for (const h of handlers) {
289
+ try {
290
+ h({
291
+ dialogType: event.dialogType,
292
+ message: event.message,
293
+ defaultValue: event.defaultValue,
294
+ tabId: event.tabId,
295
+ });
296
+ } catch (e) { logger.error(`dialog handler error: ${e}`); }
297
+ }
298
+ }
299
+ }
283
300
  }
284
301
 
285
302
  onEvent(eventName: string, tabId: string, handler: (data: any) => void): () => void {
@@ -384,9 +401,9 @@ export class PiggyClient {
384
401
 
385
402
  // ── Cookies ───────────────────────────────────────────────────────────────
386
403
  async setCookie(name: string, value: string, domain: string, path = "/", tabId = "default"): Promise<void> { await this.send("cookie.set", { name, value, domain, path, tabId }); }
387
- async getCookie(name: string, tabId = "default"): Promise<any> { return this.send("cookie.get", { name, tabId }); }
388
- async deleteCookie(name: string, tabId = "default"): Promise<void> { await this.send("cookie.delete", { name, tabId }); }
389
- async listCookies(tabId = "default"): Promise<any[]> { return this.send<any[]>("cookie.list", { tabId }); }
404
+ async getCookie(name: string, domain = "", tabId = "default"): Promise<any> { return this.send("cookie.get", { name, domain, tabId }); }
405
+ async deleteCookie(name: string, domain: string, tabId = "default"): Promise<void> { await this.send("cookie.delete", { name, domain, tabId }); }
406
+ async listCookies(domain = "", tabId = "default"): Promise<any[]> { return this.send<any[]>("cookie.list", { domain, tabId }); }
390
407
 
391
408
  // ── Interception ──────────────────────────────────────────────────────────
392
409
  async addInterceptRule(action: "block" | "redirect" | "modifyHeaders", pattern: string, options: { redirectUrl?: string; headers?: Record<string, string> } = {}, tabId = "default"): Promise<void> {
@@ -407,55 +424,14 @@ export class PiggyClient {
407
424
  async sessionExport(tabId = "default"): Promise<any> { return this.send("session.export", { tabId }); }
408
425
  async sessionImport(data: any, tabId = "default"): Promise<void> { await this.send("session.import", { data, tabId }); }
409
426
 
410
- // ── Session persistence (opt-in) ──────────────────────────────────────────
411
- // WS frames and pings are NOT saved by default you must opt in.
412
- // Files are written to cwd (same folder as cookies.json / profile.json).
413
-
414
- /** Enable or disable saving WebSocket frames to ws.json in cwd */
415
- async sessionWsSave(enabled = true): Promise<void> {
416
- await this.send("session.ws.save", { enabled });
417
- }
418
-
419
- /** Enable or disable saving ping log to pings.json in cwd */
420
- async sessionPingsSave(enabled = true): Promise<void> {
421
- await this.send("session.pings.save", { enabled });
422
- }
423
-
424
- /** Get all data file paths for the current session */
425
- async sessionPaths(): Promise<{
426
- workDir: string;
427
- cookies: string;
428
- profile: string;
429
- ws: string;
430
- pings: string;
431
- }> {
432
- return this.send("session.paths", {});
433
- }
434
-
435
- /** Get path to cookies.json */
436
- async sessionCookiesPath(): Promise<string> {
437
- return this.send("session.cookies.path", {});
438
- }
439
-
440
- /** Get path to profile.json */
441
- async sessionProfilePath(): Promise<string> {
442
- return this.send("session.profile.path", {});
443
- }
444
-
445
- /** Get path to ws.json */
446
- async sessionWsPath(): Promise<string> {
447
- return this.send("session.ws.path", {});
448
- }
449
-
450
- /** Get path to pings.json */
451
- async sessionPingsPath(): Promise<string> {
452
- return this.send("session.pings.path", {});
453
- }
454
-
455
- /** Reload cookies.json and profile.json from disk without restarting */
456
- async sessionReload(): Promise<void> {
457
- await this.send("session.reload", {});
458
- }
427
+ async sessionWsSave(enabled = true): Promise<void> { await this.send("session.ws.save", { enabled }); }
428
+ async sessionPingsSave(enabled = true): Promise<void> { await this.send("session.pings.save", { enabled }); }
429
+ async sessionPaths(): Promise<{ workDir: string; cookies: string; profile: string; ws: string; pings: string; }> { return this.send("session.paths", {}); }
430
+ async sessionCookiesPath(): Promise<string> { return this.send("session.cookies.path", {}); }
431
+ async sessionProfilePath(): Promise<string> { return this.send("session.profile.path", {}); }
432
+ async sessionWsPath(): Promise<string> { return this.send("session.ws.path", {}); }
433
+ async sessionPingsPath(): Promise<string> { return this.send("session.pings.path", {}); }
434
+ async sessionReload(): Promise<void> { await this.send("session.reload", {}); }
459
435
 
460
436
  // ── Expose Function ───────────────────────────────────────────────────────
461
437
  async exposeFunction(name: string, handler: (data: any) => Promise<any> | any, tabId = "default"): Promise<void> {
@@ -485,82 +461,28 @@ export class PiggyClient {
485
461
  }
486
462
 
487
463
  // ── Proxy ─────────────────────────────────────────────────────────────────
488
-
489
- async proxyLoad(path: string): Promise<void> {
490
- await this.send("proxy.load", { path });
491
- }
492
-
493
- async proxyFetch(url: string): Promise<void> {
494
- await this.send("proxy.fetch", { url });
495
- }
496
-
497
- async proxyOvpn(path: string): Promise<void> {
498
- await this.send("proxy.ovpn", { path });
499
- }
464
+ async proxyLoad(path: string): Promise<void> { await this.send("proxy.load", { path }); }
465
+ async proxyFetch(url: string): Promise<void> { await this.send("proxy.fetch", { url }); }
466
+ async proxyOvpn(path: string): Promise<void> { await this.send("proxy.ovpn", { path }); }
500
467
 
501
468
  async proxySet(opts: {
502
- host?: string;
503
- port?: number;
469
+ host?: string; port?: number;
504
470
  type?: "http" | "https" | "socks5" | "socks4";
505
- user?: string;
506
- pass?: string;
507
- proxy?: string;
508
- }): Promise<void> {
509
- await this.send("proxy.set", opts as Record<string, any>);
510
- }
511
-
512
- async proxyTest(): Promise<void> {
513
- await this.send("proxy.test", {});
514
- }
515
-
516
- async proxyTestStop(): Promise<void> {
517
- await this.send("proxy.test.stop", {});
518
- }
519
-
520
- async proxyNext(): Promise<void> {
521
- await this.send("proxy.next", {});
522
- }
523
-
524
- async proxyDisable(): Promise<void> {
525
- await this.send("proxy.disable", {});
526
- }
527
-
528
- async proxyEnable(): Promise<void> {
529
- await this.send("proxy.enable", {});
530
- }
531
-
532
- async proxyCurrent(): Promise<{
533
- host: string; port: number; type: string;
534
- user?: string; alive: boolean; latencyMs?: number;
535
- }> {
536
- return this.send("proxy.current", {});
537
- }
538
-
539
- async proxyStats(): Promise<{
540
- total: number; alive: number; dead: number;
541
- index: number; checking: boolean;
542
- }> {
543
- return this.send("proxy.stats", {});
544
- }
545
-
546
- async proxyList(limit?: number): Promise<{
547
- host: string; port: number; type: string;
548
- alive: boolean; latencyMs?: number;
549
- }[]> {
550
- return this.send("proxy.list", limit !== undefined ? { limit } : {});
551
- }
552
-
553
- async proxyRotation(mode: "none" | "timed" | "perrequest", interval?: number): Promise<void> {
554
- await this.send("proxy.rotation", { mode, ...(interval !== undefined ? { interval } : {}) });
555
- }
556
-
557
- async proxyConfig(opts: { skipDead?: boolean; autoCheck?: boolean }): Promise<void> {
558
- await this.send("proxy.config", opts as Record<string, any>);
559
- }
560
-
561
- async proxySave(path: string, filter: "alive" | "dead" | "all" = "all"): Promise<void> {
562
- await this.send("proxy.save", { path, filter });
563
- }
471
+ user?: string; pass?: string; proxy?: string;
472
+ }): Promise<void> { await this.send("proxy.set", opts as Record<string, any>); }
473
+
474
+ async proxyTest(): Promise<void> { await this.send("proxy.test", {}); }
475
+ async proxyTestStop(): Promise<void> { await this.send("proxy.test.stop", {}); }
476
+ async proxyNext(): Promise<void> { await this.send("proxy.next", {}); }
477
+ async proxyDisable(): Promise<void> { await this.send("proxy.disable", {}); }
478
+ async proxyEnable(): Promise<void> { await this.send("proxy.enable", {}); }
479
+
480
+ async proxyCurrent(): Promise<{ host: string; port: number; type: string; user?: string; alive: boolean; latencyMs?: number; }> { return this.send("proxy.current", {}); }
481
+ async proxyStats(): Promise<{ total: number; alive: number; dead: number; index: number; checking: boolean; }> { return this.send("proxy.stats", {}); }
482
+ async proxyList(limit?: number): Promise<{ host: string; port: number; type: string; alive: boolean; latencyMs?: number; }[]> { return this.send("proxy.list", limit !== undefined ? { limit } : {}); }
483
+ async proxyRotation(mode: "none" | "timed" | "perrequest", interval?: number): Promise<void> { await this.send("proxy.rotation", { mode, ...(interval !== undefined ? { interval } : {}) }); }
484
+ async proxyConfig(opts: { skipDead?: boolean; autoCheck?: boolean }): Promise<void> { await this.send("proxy.config", opts as Record<string, any>); }
485
+ async proxySave(path: string, filter: "alive" | "dead" | "all" = "all"): Promise<void> { await this.send("proxy.save", { path, filter }); }
564
486
 
565
487
  onProxyEvent(event: string, handler: (data: any) => void): () => void {
566
488
  return this.onEvent(event, "*", handler);
@@ -0,0 +1,42 @@
1
+ import { PiggyClient } from "../client";
2
+ import logger from "../logger";
3
+
4
+ export async function exposeFunction(
5
+ client: PiggyClient,
6
+ fnName: string,
7
+ handler: (data: any) => Promise<any> | any,
8
+ tabId: string
9
+ ): Promise<void> {
10
+ await client.exposeFunction(fnName, handler, tabId);
11
+ logger.success(`[${tabId}] exposed function: ${fnName}`);
12
+ }
13
+
14
+ export async function unexposeFunction(
15
+ client: PiggyClient,
16
+ fnName: string,
17
+ tabId: string
18
+ ): Promise<void> {
19
+ await client.unexposeFunction(fnName, tabId);
20
+ logger.info(`[${tabId}] unexposed function: ${fnName}`);
21
+ }
22
+
23
+ export async function clearExposedFunctions(
24
+ client: PiggyClient,
25
+ tabId: string
26
+ ): Promise<void> {
27
+ await client.clearExposedFunctions(tabId);
28
+ logger.info(`[${tabId}] cleared all exposed functions`);
29
+ }
30
+
31
+ export async function exposeAndInject(
32
+ client: PiggyClient,
33
+ fnName: string,
34
+ handler: (data: any) => Promise<any> | any,
35
+ injectionJs: string | ((fnName: string) => string),
36
+ tabId: string
37
+ ): Promise<void> {
38
+ await client.exposeFunction(fnName, handler, tabId);
39
+ const js = typeof injectionJs === "function" ? injectionJs(fnName) : injectionJs;
40
+ await client.evaluate(js, tabId);
41
+ logger.success(`[${tabId}] exposed and injected: ${fnName}`);
42
+ }
@@ -5,69 +5,35 @@ import { PiggyClient } from "../client";
5
5
  // Mirrors __nb_serialize() in PiggyFind.cpp
6
6
 
7
7
  export interface ElementDescriptor {
8
- tag: string;
9
- id: string;
10
- cls: string;
8
+ tag: string;
9
+ id: string;
10
+ cls: string;
11
11
  /** First 400 chars of innerText */
12
- text: string;
12
+ text: string;
13
13
  /** First 800 chars of innerHTML */
14
- html: string;
15
- href: string;
16
- src: string;
14
+ html: string;
15
+ href: string;
16
+ src: string;
17
17
  value: string;
18
18
  attrs: Record<string, string>;
19
19
  }
20
20
 
21
- // ─── Option types ─────────────────────────────────────────────────────────────
22
-
23
- export interface FindByTextOptions {
24
- text: string;
25
- /** Narrow the search to descendants of this CSS selector. */
26
- selector?: string;
27
- /** If true, innerText must match exactly (trimmed). Default: false. */
28
- exact?: boolean;
29
- }
30
-
31
- export interface FindByAttrOptions {
32
- attr: string;
33
- /** If omitted, matches any element that has the attribute at all. */
34
- value?: string;
35
- /** Optionally scope to a parent selector. */
36
- selector?: string;
37
- }
38
-
39
- export interface FindByRoleOptions {
40
- role: string;
41
- /** Filter by aria-label or innerText containing this string. */
42
- name?: string;
43
- }
44
-
45
- export interface FindClosestOptions {
46
- /** CSS selector for the starting element. */
47
- selector: string;
48
- /** CSS selector for the ancestor to climb to. */
49
- ancestor: string;
50
- }
51
-
52
- export interface FindFilterOptions {
53
- selector: string;
54
- attr: string;
55
- value: string;
56
- }
57
-
58
21
  // ─── FindClient ───────────────────────────────────────────────────────────────
22
+ // find answers ONE question: "is this thing here, and where?"
23
+ // All methods take plain selector strings — no option objects, no parent scoping.
24
+ // If you need a value out of an element, use provide instead.
59
25
 
60
26
  export class FindClient {
61
27
  constructor(private client: PiggyClient) {}
62
28
 
63
- // ── Multi-result queries ─────────────────────────────────────────────────────
29
+ // ── Multi-result ─────────────────────────────────────────────────────────────
64
30
 
65
31
  /** querySelectorAll — returns all matching elements. */
66
32
  css(selector: string, tabId = "default"): Promise<ElementDescriptor[]> {
67
33
  return this.client.send("find.css", { selector, tabId });
68
34
  }
69
35
 
70
- /** Alias for css() — querySelectorAll. */
36
+ /** Alias for css(). */
71
37
  all(selector: string, tabId = "default"): Promise<ElementDescriptor[]> {
72
38
  return this.client.send("find.all", { selector, tabId });
73
39
  }
@@ -77,14 +43,14 @@ export class FindClient {
77
43
  return this.client.send("find.first", { selector, tabId });
78
44
  }
79
45
 
80
- /** Find elements whose innerText contains (or exactly matches) the given text. */
81
- byText(opts: FindByTextOptions, tabId = "default"): Promise<ElementDescriptor[]> {
82
- return this.client.send("find.byText", { ...opts, tabId });
46
+ /** Find elements whose innerText contains the given text. */
47
+ byText(text: string, tabId = "default"): Promise<ElementDescriptor[]> {
48
+ return this.client.send("find.byText", { text, tabId });
83
49
  }
84
50
 
85
- /** Find elements by attribute name and optional value. */
86
- byAttr(opts: FindByAttrOptions, tabId = "default"): Promise<ElementDescriptor[]> {
87
- return this.client.send("find.byAttr", { ...opts, tabId });
51
+ /** Find elements by attribute name (and optional value). */
52
+ byAttr(attr: string, value?: string, tabId = "default"): Promise<ElementDescriptor[]> {
53
+ return this.client.send("find.byAttr", { attr, value, tabId });
88
54
  }
89
55
 
90
56
  /** getElementsByTagName. */
@@ -97,9 +63,9 @@ export class FindClient {
97
63
  return this.client.send("find.byPlaceholder", { text, tabId });
98
64
  }
99
65
 
100
- /** Find elements by ARIA role, optionally filtered by aria-label / innerText. */
101
- byRole(opts: FindByRoleOptions, tabId = "default"): Promise<ElementDescriptor[]> {
102
- return this.client.send("find.byRole", { ...opts, tabId });
66
+ /** Find elements by ARIA role, optionally filtered by accessible name. */
67
+ byRole(role: string, name?: string, tabId = "default"): Promise<ElementDescriptor[]> {
68
+ return this.client.send("find.byRole", { role, name, tabId });
103
69
  }
104
70
 
105
71
  /** Direct children of the matched element. */
@@ -107,42 +73,29 @@ export class FindClient {
107
73
  return this.client.send("find.children", { selector, tabId });
108
74
  }
109
75
 
110
- /**
111
- * Filter querySelectorAll results by attribute value substring.
112
- * Equivalent to: querySelectorAll(selector).filter(el => el.attr.includes(value))
113
- */
114
- filter(opts: FindFilterOptions, tabId = "default"): Promise<ElementDescriptor[]> {
115
- return this.client.send("find.filter", { ...opts, tabId });
116
- }
117
-
118
- // ── Single-element traversal ──────────────────────────────────────────────
119
-
120
- /** Walk up the DOM from selector until ancestor matches. */
121
- closest(opts: FindClosestOptions, tabId = "default"): Promise<ElementDescriptor[]> {
122
- return this.client.send("find.closest", { ...opts, tabId });
123
- }
124
-
125
76
  /** parentElement of the matched element. */
126
77
  parent(selector: string, tabId = "default"): Promise<ElementDescriptor[]> {
127
78
  return this.client.send("find.parent", { selector, tabId });
128
79
  }
129
80
 
130
- // ── Boolean / numeric queries ─────────────────────────────────────────────
81
+ /** Walk up the DOM from selector until ancestor matches. */
82
+ closest(selector: string, ancestor: string, tabId = "default"): Promise<ElementDescriptor[]> {
83
+ return this.client.send("find.closest", { selector, ancestor, tabId });
84
+ }
85
+
86
+ // ── Boolean / numeric ────────────────────────────────────────────────────────
131
87
 
132
88
  /** Number of elements matching the selector. */
133
89
  count(selector: string, tabId = "default"): Promise<number> {
134
90
  return this.client.send("find.count", { selector, tabId });
135
91
  }
136
92
 
137
- /** True if at least one element matches the selector. */
93
+ /** True if at least one element matches. */
138
94
  exists(selector: string, tabId = "default"): Promise<boolean> {
139
95
  return this.client.send("find.exists", { selector, tabId });
140
96
  }
141
97
 
142
- /**
143
- * True if the first matched element is visible
144
- * (display !== none, visibility !== hidden, opacity !== 0).
145
- */
98
+ /** True if the first matched element is visible. */
146
99
  visible(selector: string, tabId = "default"): Promise<boolean> {
147
100
  return this.client.send("find.visible", { selector, tabId });
148
101
  }
@@ -158,7 +111,7 @@ export class FindClient {
158
111
  }
159
112
  }
160
113
 
161
- // ── Factory helper ────────────────────────────────────────────────────────────
114
+ // ─── Factory helper ───────────────────────────────────────────────────────────
162
115
 
163
116
  export function createFindAPI(client: PiggyClient): FindClient {
164
117
  return new FindClient(client);
@@ -1,6 +1,29 @@
1
1
  // piggy/provide/index.ts
2
2
  import { PiggyClient } from "../client";
3
3
 
4
+ // ─── Shared base option ───────────────────────────────────────────────────────
5
+ // Every provide method targets a selector, and optionally scopes under a parent.
6
+
7
+ export interface ProvideOptions {
8
+ /** CSS selector for the target element(s). */
9
+ selector: string;
10
+ /**
11
+ * Optional parent selector to scope the query under.
12
+ * Equivalent to: parent.querySelector(selector)
13
+ */
14
+ parent?: string;
15
+ }
16
+
17
+ export interface ProvideAttrOptions extends ProvideOptions {
18
+ /** The attribute name to extract. */
19
+ attr: string;
20
+ }
21
+
22
+ export interface ProvideListOptions extends ProvideOptions {
23
+ /** Optional child selector to scope list items. */
24
+ itemSel?: string;
25
+ }
26
+
4
27
  // ─── Return types ─────────────────────────────────────────────────────────────
5
28
 
6
29
  export interface ProvideTable {
@@ -54,86 +77,89 @@ export interface ProvideSelect {
54
77
  }
55
78
 
56
79
  // ─── ProvideClient ────────────────────────────────────────────────────────────
80
+ // provide answers ONE question: "give me the actual value from this element."
81
+ // All methods take an options object { selector, parent? } so scoping is consistent.
82
+ // If you just want to know if something exists, use find instead.
57
83
 
58
84
  export class ProvideClient {
59
85
  constructor(private client: PiggyClient) {}
60
86
 
61
87
  /** innerText of the first matched element. */
62
- text(selector: string, tabId = "default"): Promise<string> {
63
- return this.client.send("provide.text", { selector, tabId });
88
+ text(opts: ProvideOptions, tabId = "default"): Promise<string> {
89
+ return this.client.send("provide.text", { ...opts, tabId });
64
90
  }
65
91
 
66
92
  /** innerText of all matched elements. */
67
- textAll(selector: string, tabId = "default"): Promise<string[]> {
68
- return this.client.send("provide.textAll", { selector, tabId });
93
+ textAll(opts: ProvideOptions, tabId = "default"): Promise<string[]> {
94
+ return this.client.send("provide.textAll", { ...opts, tabId });
69
95
  }
70
96
 
71
97
  /** Single attribute value from the first matched element. */
72
- attr(selector: string, attr: string, tabId = "default"): Promise<string> {
73
- return this.client.send("provide.attr", { selector, attr, tabId });
98
+ attr(opts: ProvideAttrOptions, tabId = "default"): Promise<string> {
99
+ return this.client.send("provide.attr", { ...opts, tabId });
74
100
  }
75
101
 
76
102
  /** Attribute value from all matched elements. */
77
- attrAll(selector: string, attr: string, tabId = "default"): Promise<string[]> {
78
- return this.client.send("provide.attrAll", { selector, attr, tabId });
103
+ attrAll(opts: ProvideAttrOptions, tabId = "default"): Promise<string[]> {
104
+ return this.client.send("provide.attrAll", { ...opts, tabId });
79
105
  }
80
106
 
81
107
  /** innerHTML of the first matched element. */
82
- html(selector: string, tabId = "default"): Promise<string> {
83
- return this.client.send("provide.html", { selector, tabId });
108
+ html(opts: ProvideOptions, tabId = "default"): Promise<string> {
109
+ return this.client.send("provide.html", { ...opts, tabId });
84
110
  }
85
111
 
86
112
  /** Extract a table into headers + rows. */
87
- table(selector: string, tabId = "default"): Promise<ProvideTable> {
88
- return this.client.send("provide.table", { selector, tabId });
113
+ table(opts: ProvideOptions, tabId = "default"): Promise<ProvideTable> {
114
+ return this.client.send("provide.table", { ...opts, tabId });
89
115
  }
90
116
 
91
- /** Extract a list of text items. Optionally scope items with itemSel. */
92
- list(selector: string, itemSel?: string, tabId = "default"): Promise<string[]> {
93
- return this.client.send("provide.list", { selector, itemSel, tabId });
117
+ /** Extract a list of text items, optionally scoped to child items. */
118
+ list(opts: ProvideListOptions, tabId = "default"): Promise<string[]> {
119
+ return this.client.send("provide.list", { ...opts, tabId });
94
120
  }
95
121
 
96
- /** All links inside an optional selector. */
97
- links(selector?: string, tabId = "default"): Promise<ProvideLink[]> {
98
- return this.client.send("provide.links", { selector, tabId });
122
+ /** All links inside the matched selector. */
123
+ links(opts: ProvideOptions, tabId = "default"): Promise<ProvideLink[]> {
124
+ return this.client.send("provide.links", { ...opts, tabId });
99
125
  }
100
126
 
101
- /** All images inside an optional selector. */
102
- images(selector?: string, tabId = "default"): Promise<ProvideImage[]> {
103
- return this.client.send("provide.images", { selector, tabId });
127
+ /** All images inside the matched selector. */
128
+ images(opts: ProvideOptions, tabId = "default"): Promise<ProvideImage[]> {
129
+ return this.client.send("provide.images", { ...opts, tabId });
104
130
  }
105
131
 
106
132
  /** Form field name→value map. */
107
- form(selector: string, tabId = "default"): Promise<ProvideForm> {
108
- return this.client.send("provide.form", { selector, tabId });
133
+ form(opts: ProvideOptions, tabId = "default"): Promise<ProvideForm> {
134
+ return this.client.send("provide.form", { ...opts, tabId });
109
135
  }
110
136
 
111
- /** Full page info: title, url, html, text. */
137
+ /** Full page info: title, url, html, text. No selector needed. */
112
138
  page(tabId = "default"): Promise<ProvidePage> {
113
139
  return this.client.send("provide.page", { tabId });
114
140
  }
115
141
 
116
- /** Structured div: tag, id, cls, text, html, children[]. */
117
- div(selector: string, tabId = "default"): Promise<ProvideDiv> {
118
- return this.client.send("provide.div", { selector, tabId });
142
+ /** Structured div tree: tag, id, cls, text, html, children[]. */
143
+ div(opts: ProvideOptions, tabId = "default"): Promise<ProvideDiv> {
144
+ return this.client.send("provide.div", { ...opts, tabId });
119
145
  }
120
146
 
121
- /** All <meta> name→content pairs. */
147
+ /** All <meta> name→content pairs. No selector needed. */
122
148
  meta(tabId = "default"): Promise<ProvideMeta> {
123
149
  return this.client.send("provide.meta", { tabId });
124
150
  }
125
151
 
126
152
  /** <select> current value + all options. */
127
- select(selector: string, tabId = "default"): Promise<ProvideSelect> {
128
- return this.client.send("provide.select", { selector, tabId });
153
+ select(opts: ProvideOptions, tabId = "default"): Promise<ProvideSelect> {
154
+ return this.client.send("provide.select", { ...opts, tabId });
129
155
  }
130
156
 
131
157
  /**
132
158
  * Parse JSON from element innerText or script[type=application/json].
133
159
  * selector is optional — defaults to the first matching JSON script tag.
134
160
  */
135
- json(selector?: string, tabId = "default"): Promise<unknown> {
136
- return this.client.send("provide.json", { selector, tabId });
161
+ json(opts?: Partial<ProvideOptions>, tabId = "default"): Promise<unknown> {
162
+ return this.client.send("provide.json", { ...opts, tabId });
137
163
  }
138
164
  }
139
165