nothing-browser 0.0.20 → 0.1.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.
Files changed (76) hide show
  1. package/LICENSE +1 -1
  2. package/dist/human/index.js +25 -1
  3. package/dist/launch/detect.js +13 -0
  4. package/dist/launch/spawn.js +13 -0
  5. package/dist/piggy/captcha/index.d.ts +39 -0
  6. package/dist/piggy/captcha/index.d.ts.map +1 -0
  7. package/dist/piggy/capture/index.d.ts +48 -0
  8. package/dist/piggy/capture/index.d.ts.map +1 -0
  9. package/dist/piggy/dialog/index.d.ts +28 -0
  10. package/dist/piggy/dialog/index.d.ts.map +1 -0
  11. package/dist/piggy/export/index.d.ts +62 -0
  12. package/dist/piggy/export/index.d.ts.map +1 -0
  13. package/dist/piggy/find/index.d.ts +90 -0
  14. package/dist/piggy/find/index.d.ts.map +1 -0
  15. package/dist/piggy/http/index.d.ts +14 -0
  16. package/dist/piggy/http/index.d.ts.map +1 -0
  17. package/dist/piggy/human/index.d.ts +36 -4
  18. package/dist/piggy/human/index.d.ts.map +1 -1
  19. package/dist/piggy/iframe/index.d.ts +53 -0
  20. package/dist/piggy/iframe/index.d.ts.map +1 -0
  21. package/dist/piggy/interactions/index.d.ts +24 -0
  22. package/dist/piggy/interactions/index.d.ts.map +1 -0
  23. package/dist/piggy/launch/detect.d.ts +1 -1
  24. package/dist/piggy/launch/detect.d.ts.map +1 -1
  25. package/dist/piggy/launch/spawn.d.ts.map +1 -1
  26. package/dist/piggy/media/index.d.ts +11 -0
  27. package/dist/piggy/media/index.d.ts.map +1 -0
  28. package/dist/piggy/navigation/index.d.ts +16 -0
  29. package/dist/piggy/navigation/index.d.ts.map +1 -0
  30. package/dist/piggy/provide/index.d.ts +81 -0
  31. package/dist/piggy/provide/index.d.ts.map +1 -0
  32. package/dist/piggy/proxy/index.d.ts +94 -0
  33. package/dist/piggy/proxy/index.d.ts.map +1 -0
  34. package/dist/piggy/register/index.d.ts.map +1 -1
  35. package/dist/piggy/router/index.d.ts +38 -0
  36. package/dist/piggy/router/index.d.ts.map +1 -0
  37. package/dist/piggy/session/index.d.ts +27 -0
  38. package/dist/piggy/session/index.d.ts.map +1 -0
  39. package/dist/piggy/tabs/index.d.ts +10 -0
  40. package/dist/piggy/tabs/index.d.ts.map +1 -0
  41. package/dist/piggy/wait/index.d.ts +28 -0
  42. package/dist/piggy/wait/index.d.ts.map +1 -0
  43. package/dist/piggy.d.ts.map +1 -1
  44. package/dist/piggy.js +914 -181
  45. package/dist/register/index.js +39 -86
  46. package/package.json +1 -1
  47. package/piggy/captcha/index.d.ts +35 -0
  48. package/piggy/captcha/index.ts +93 -0
  49. package/piggy/capture/index.ts +76 -0
  50. package/piggy/dialog/index.d.ts +29 -0
  51. package/piggy/dialog/index.ts +85 -0
  52. package/piggy/export/index.d.ts +117 -0
  53. package/piggy/export/index.ts +147 -0
  54. package/piggy/find/index.d.ts +79 -0
  55. package/piggy/find/index.ts +165 -0
  56. package/piggy/http/index.ts +65 -0
  57. package/piggy/human/index.ts +115 -53
  58. package/piggy/iframe/index.ts +79 -0
  59. package/piggy/interactions/index.ts +79 -0
  60. package/piggy/launch/detect.ts +19 -2
  61. package/piggy/launch/spawn.ts +1 -8
  62. package/piggy/media/index.ts +46 -0
  63. package/piggy/navigation/index.ts +52 -0
  64. package/piggy/provide/index.ts +144 -0
  65. package/piggy/proxy/index.ts +154 -0
  66. package/piggy/register/index.ts +41 -59
  67. package/piggy/router/index.ts +69 -0
  68. package/piggy/session/index.ts +76 -0
  69. package/piggy/tabs/index.ts +22 -0
  70. package/piggy/wait/index.ts +90 -0
  71. package/piggy.ts +94 -39
  72. package/dist/piggy/open/index.d.ts +0 -4
  73. package/dist/piggy/open/index.d.ts.map +0 -1
  74. package/piggy/open/index.d.ts +0 -4
  75. package/piggy/open/index.d.ts.map +0 -1
  76. package/piggy/open/index.ts +0 -5
@@ -1,8 +1,8 @@
1
1
  // piggy/register/index.ts
2
2
  import { PiggyClient } from "../client";
3
+ import { HumanClient } from "../human";
3
4
  import logger from "../logger";
4
5
  import { routeRegistry, keepAliveSites, type RouteHandler, type BeforeMiddleware, type RouteDetail } from "../server";
5
- import { randomDelay, humanTypeSequence } from "../human";
6
6
  import { buildRespondScript, buildModifyResponseScript } from "../intercept/scripts";
7
7
  import { storeRecord } from "../store";
8
8
  import { TabPool } from "../pool";
@@ -37,8 +37,6 @@ export function createSiteObject(
37
37
  let _currentUrl: string = registeredUrl;
38
38
  let _modifyRuleCounter = 0;
39
39
 
40
- // ── helpers ────────────────────────────────────────────────────────────────
41
- // If pool exists, run fn with a pool tab. Otherwise use the fixed tabId.
42
40
  function withTab<T>(fn: (t: string) => Promise<T>): Promise<T> {
43
41
  return pool ? pool.withTab(fn) : fn(tabId);
44
42
  }
@@ -69,10 +67,8 @@ export function createSiteObject(
69
67
  _tabId: tabId,
70
68
  _pool: pool ?? null,
71
69
 
72
- // ── Pool stats ────────────────────────────────────────────────────────────
73
70
  poolStats: () => pool?.stats ?? null,
74
71
 
75
- // ── Navigation ────────────────────────────────────────────────────────────
76
72
  navigate: (url?: string, opts?: { retries?: number }) => {
77
73
  const target = url ?? registeredUrl;
78
74
  return withTab(t =>
@@ -115,7 +111,6 @@ export function createSiteObject(
115
111
  waitForResponse: (pattern: string, timeout = 30000) =>
116
112
  withTab(t => client.waitForResponse(pattern, timeout, t)),
117
113
 
118
- // ── Init Script ───────────────────────────────────────────────────────────
119
114
  addInitScript: async (js: string | (() => void)) => {
120
115
  const code = typeof js === "function" ? `(${js.toString()})();` : js;
121
116
  await withTab(t => client.addInitScript(code, t));
@@ -123,7 +118,6 @@ export function createSiteObject(
123
118
  return site;
124
119
  },
125
120
 
126
- // ── Events ────────────────────────────────────────────────────────────────
127
121
  on: (event: string, handler: (data: any) => void): (() => void) => {
128
122
  if (!_eventListeners.has(event)) _eventListeners.set(event, new Set());
129
123
  _eventListeners.get(event)!.add(handler);
@@ -138,12 +132,11 @@ export function createSiteObject(
138
132
  _eventListeners.get(event)?.delete(handler);
139
133
  },
140
134
 
141
- // ── Interactions ──────────────────────────────────────────────────────────
142
135
  click: (selector: string, opts?: { retries?: number; timeout?: number }) =>
143
136
  withErrScreen(() =>
144
137
  withTab(t =>
145
138
  retry(name, async () => {
146
- if (humanMode) await randomDelay(80, 220);
139
+ if (humanMode) await new Promise(r => setTimeout(r, 80 + Math.random() * 140));
147
140
  await client.waitForSelector(selector, opts?.timeout ?? 15000, t);
148
141
  const ok = await client.click(selector, t);
149
142
  if (!ok) throw new Error(`click failed: ${selector}`);
@@ -157,7 +150,7 @@ export function createSiteObject(
157
150
  doubleClick: (selector: string) =>
158
151
  withErrScreen(() =>
159
152
  withTab(async t => {
160
- if (humanMode) await randomDelay(80, 200);
153
+ if (humanMode) await new Promise(r => setTimeout(r, 80 + Math.random() * 120));
161
154
  return client.doubleClick(selector, t);
162
155
  }),
163
156
  `dblclick(${selector})`
@@ -166,52 +159,50 @@ export function createSiteObject(
166
159
  hover: (selector: string) =>
167
160
  withErrScreen(() =>
168
161
  withTab(async t => {
169
- if (humanMode) await randomDelay(50, 150);
162
+ if (humanMode) await new Promise(r => setTimeout(r, 50 + Math.random() * 100));
170
163
  return client.hover(selector, t);
171
164
  }),
172
165
  `hover(${selector})`
173
166
  ),
174
167
 
175
- type: (selector: string, text: string, opts?: { delay?: number; retries?: number; fact?: boolean; wpm?: number }) =>
176
- withErrScreen(() =>
177
- withTab(async t => {
178
- await client.waitForSelector(selector, 30000, t);
179
- if (humanMode && !opts?.fact) {
180
- const seq = humanTypeSequence(text);
181
- let current = "";
182
- for (const action of seq) {
183
- if (action === "BACKSPACE") current = current.slice(0, -1);
184
- else current += action;
168
+ // ──────────────────────────────────────────────────────────────────────────
169
+ // type – uses HumanClient when humanMode is true, otherwise basic client.type
170
+ // ──────────────────────────────────────────────────────────────────────────
171
+ type: (selector: string, text: string, opts?: { delay?: number; retries?: number; clear?: boolean; speed?: number }) =>
172
+ withErrScreen(() =>
173
+ withTab(async t => {
174
+ await client.waitForSelector(selector, 30000, t);
175
+
176
+ if (humanMode) {
177
+ const human = new HumanClient(client);
178
+ await human.type({
179
+ selector,
180
+ text,
181
+ clear: opts?.clear ?? false,
182
+ speed: opts?.speed
183
+ }, t);
184
+ } else {
185
+ // Fallback to basic type (with optional clear)
186
+ if (opts?.clear) {
187
+ await client.evaluate(`
188
+ const el = document.querySelector('${selector.replace(/'/g, "\\'")}');
189
+ if (el) { el.value = ''; el.dispatchEvent(new Event('input', { bubbles: true })); }
190
+ `, t);
191
+ }
192
+ await client.type(selector, text, t);
193
+ }
194
+
195
+ // Fire blur to trigger reactive frameworks
185
196
  await client.evaluate(`
186
- (function() {
187
- const el = document.querySelector('${selector}');
188
- const nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
189
- nativeSetter.call(el, '${current.replace(/'/g, "\\'")}');
190
- el.dispatchEvent(new Event('input', { bubbles: true }));
191
- el.dispatchEvent(new Event('change', { bubbles: true }));
192
- })()
197
+ document.querySelector('${selector.replace(/'/g, "\\'")}')?.dispatchEvent(new Event('blur', { bubbles: true }));
193
198
  `, t);
194
- const wpm = opts?.wpm ?? 120;
195
- const msPerChar = Math.round(60000 / (wpm * 5));
196
- await randomDelay(msPerChar * 0.5, msPerChar * 1.8);
197
- }
198
- } else if (opts?.delay) {
199
- for (const ch of text) {
200
- await client.type(selector, ch, t);
201
- await new Promise(r => setTimeout(r, opts.delay));
202
- }
203
- } else {
204
- await client.type(selector, text, t);
205
- }
206
- // fire blur so Phoenix/Angular/React pick up the final value
207
- await client.evaluate(`
208
- document.querySelector('${selector}').dispatchEvent(new Event('blur', { bubbles: true }))
209
- `, t);
210
- logger.success(`[${name}] typed into: ${selector}`);
211
- return true;
212
- }),
213
- `type(${selector})`
214
- ),
199
+
200
+ logger.success(`[${name}] typed into: ${selector}`);
201
+ return true;
202
+ }),
203
+ `type(${selector})`
204
+ ),
205
+
215
206
  select: (selector: string, value: string) => withTab(t => client.select(selector, value, t)),
216
207
 
217
208
  evaluate: (js: string | (() => any), ...args: any[]) => {
@@ -240,7 +231,7 @@ export function createSiteObject(
240
231
  const chunk = px / steps;
241
232
  for (let i = 0; i < steps; i++) {
242
233
  await client.scrollBy(chunk, t);
243
- await randomDelay(30, 80);
234
+ await new Promise(r => setTimeout(r, 30 + Math.random() * 50));
244
235
  }
245
236
  } else {
246
237
  await client.scrollBy(px, t);
@@ -248,7 +239,6 @@ export function createSiteObject(
248
239
  }) as Promise<void>,
249
240
  },
250
241
 
251
- // ── Fetch ─────────────────────────────────────────────────────────────────
252
242
  fetchText: (selector: string) => withTab(t => client.fetchText(selector, t)),
253
243
 
254
244
  fetchLinks: async (selector: string) => {
@@ -268,7 +258,6 @@ export function createSiteObject(
268
258
  id: (query: string) => withTab(t => client.searchId(query, t)),
269
259
  },
270
260
 
271
- // ── Screenshot / PDF ──────────────────────────────────────────────────────
272
261
  screenshot: async (filePath?: string) => {
273
262
  const r = await withTab(t => client.screenshot(filePath, t));
274
263
  logger.success(`[${name}] screenshot → ${filePath ?? "base64"}`);
@@ -284,7 +273,6 @@ export function createSiteObject(
284
273
  blockImages: () => withTab(async t => { await client.blockImages(t); logger.info(`[${name}] images blocked`); }),
285
274
  unblockImages: () => withTab(async t => { await client.unblockImages(t); logger.info(`[${name}] images unblocked`); }),
286
275
 
287
- // ── Cookies ───────────────────────────────────────────────────────────────
288
276
  cookies: {
289
277
  set: async (cookieName: string, value: string, domain: string, path = "/") => {
290
278
  await withTab(t => client.setCookie(cookieName, value, domain, path, t));
@@ -298,7 +286,6 @@ export function createSiteObject(
298
286
  list: () => withTab(t => client.listCookies(t)),
299
287
  },
300
288
 
301
- // ── Interception ──────────────────────────────────────────────────────────
302
289
  intercept: {
303
290
  block: async (pattern: string) => {
304
291
  await withTab(t => client.addInterceptRule("block", pattern, {}, t));
@@ -399,7 +386,6 @@ export function createSiteObject(
399
386
  },
400
387
  },
401
388
 
402
- // ── Network capture ───────────────────────────────────────────────────────
403
389
  capture: {
404
390
  start: () => withTab(async t => { await client.captureStart(t); logger.info(`[${name}] capture started`); }),
405
391
  stop: () => withTab(async t => { await client.captureStop(t); logger.info(`[${name}] capture stopped`); }),
@@ -410,7 +396,6 @@ export function createSiteObject(
410
396
  clear: () => withTab(async t => { await client.captureClear(t); logger.info(`[${name}] capture cleared`); }),
411
397
  },
412
398
 
413
- // ── Session ───────────────────────────────────────────────────────────────
414
399
  session: {
415
400
  export: async () => {
416
401
  const data = await withTab(t => client.sessionExport(t));
@@ -423,7 +408,6 @@ export function createSiteObject(
423
408
  },
424
409
  },
425
410
 
426
- // ── Expose Function ───────────────────────────────────────────────────────
427
411
  exposeFunction: async (fnName: string, handler: (data: any) => Promise<any> | any) => {
428
412
  await client.exposeFunction(fnName, handler, tabId);
429
413
  logger.success(`[${name}] exposed function: ${fnName}`);
@@ -447,7 +431,6 @@ export function createSiteObject(
447
431
  return site;
448
432
  },
449
433
 
450
- // ── Store ─────────────────────────────────────────────────────────────────
451
434
  store: async (
452
435
  data: Record<string, any> | Record<string, any>[],
453
436
  schemaName?: string
@@ -458,7 +441,6 @@ export function createSiteObject(
458
441
  return result;
459
442
  },
460
443
 
461
- // ── Elysia API ────────────────────────────────────────────────────────────
462
444
  api: (
463
445
  path: string,
464
446
  handler: RouteHandler,
@@ -0,0 +1,69 @@
1
+ // piggy/router/index.ts
2
+ // Mirrors PiggyCommandRouter.cpp.
3
+ // This is the single entry point that composes all sub-clients
4
+ // into one object — the same way the C++ router dispatches to sub-handlers.
5
+
6
+ import { PiggyClient } from "../client";
7
+ import { CaptureClient, createCaptureAPI } from "../capture";
8
+ import { CaptchaClient, createCaptchaAPI } from "../captcha";
9
+ import { DialogClient, createDialogAPI } from "../dialog";
10
+ import { ExportClient, createExportAPI } from "../export";
11
+ import { FindClient, createFindAPI } from "../find";
12
+ import { HumanClient, createHumanAPI } from "../human";
13
+ import { IframeClient, createIframeAPI } from "../iframe";
14
+ import { InteractionsClient, createInteractionsAPI } from "../interactions";
15
+ import { MediaClient, createMediaAPI } from "../media";
16
+ import { NavigationClient, createNavigationAPI } from "../navigation";
17
+ import { ProvideClient, createProvideAPI } from "../provide";
18
+ import { ProxyClient, createProxyAPI } from "../proxy";
19
+ import { SessionClient, createSessionAPI } from "../session";
20
+ import { TabsClient, createTabsAPI } from "../tabs";
21
+ import { WaitClient, EvaluateClient, FetchClient, createWaitAPI, createEvaluateAPI, createFetchAPI } from "../wait";
22
+
23
+ export interface PiggyRouter {
24
+ // Core transport
25
+ client: PiggyClient;
26
+
27
+ // Sub-routers — 1:1 with C++ files
28
+ tabs: TabsClient;
29
+ navigation: NavigationClient;
30
+ interactions: InteractionsClient;
31
+ media: MediaClient;
32
+ capture: CaptureClient;
33
+ find: FindClient;
34
+ provide: ProvideClient;
35
+ wait: WaitClient;
36
+ evaluate: EvaluateClient;
37
+ fetch: FetchClient;
38
+ proxy: ProxyClient;
39
+ captcha: CaptchaClient;
40
+ dialog: DialogClient;
41
+ human: HumanClient;
42
+ iframe: IframeClient;
43
+ session: SessionClient;
44
+ export: ExportClient;
45
+ }
46
+
47
+ export function createRouter(client: PiggyClient): PiggyRouter {
48
+ return {
49
+ client,
50
+ tabs: createTabsAPI(client),
51
+ navigation: createNavigationAPI(client),
52
+ interactions: createInteractionsAPI(client),
53
+ media: createMediaAPI(client),
54
+ capture: createCaptureAPI(client),
55
+ find: createFindAPI(client),
56
+ provide: createProvideAPI(client),
57
+ wait: createWaitAPI(client),
58
+ evaluate: createEvaluateAPI(client),
59
+ fetch: createFetchAPI(client),
60
+ proxy: createProxyAPI(client),
61
+ captcha: createCaptchaAPI(client),
62
+ dialog: createDialogAPI(client),
63
+ human: createHumanAPI(client),
64
+ iframe: createIframeAPI(client),
65
+ session: createSessionAPI(client),
66
+ export: createExportAPI(client),
67
+ };
68
+ }
69
+
@@ -0,0 +1,76 @@
1
+ // piggy/session/index.ts
2
+ import { PiggyClient } from "../client";
3
+ import type { CookieSetOptions, CookieDeleteOptions } from "../export";
4
+
5
+ export interface SessionPaths {
6
+ workDir: string;
7
+ cookies: string;
8
+ profile: string;
9
+ ws: string;
10
+ pings: string;
11
+ }
12
+
13
+ export class SessionClient {
14
+ constructor(private client: PiggyClient) {}
15
+
16
+ // Session lifecycle
17
+ reload(tabId = "default"): Promise<void> {
18
+ return this.client.send("session.reload", { tabId });
19
+ }
20
+
21
+ // Paths
22
+ paths(): Promise<SessionPaths> {
23
+ return this.client.send("session.paths", {});
24
+ }
25
+
26
+ cookiesPath(): Promise<string> {
27
+ return this.client.send("session.cookies.path", {});
28
+ }
29
+
30
+ profilePath(): Promise<string> {
31
+ return this.client.send("session.profile.path", {});
32
+ }
33
+
34
+ wsPath(): Promise<string> {
35
+ return this.client.send("session.ws.path", {});
36
+ }
37
+
38
+ pingsPath(): Promise<string> {
39
+ return this.client.send("session.pings.path", {});
40
+ }
41
+
42
+ // Opt-in persistence
43
+ setWsSave(enabled: boolean): Promise<void> {
44
+ return this.client.send("session.ws.save", { enabled });
45
+ }
46
+
47
+ setPingsSave(enabled: boolean): Promise<void> {
48
+ return this.client.send("session.pings.save", { enabled });
49
+ }
50
+
51
+ // Export / import
52
+ async export(tabId = "default"): Promise<any> {
53
+ const raw = await this.client.send<string>("session.export", { tabId });
54
+ return typeof raw === "string" ? JSON.parse(raw) : raw;
55
+ }
56
+
57
+ import(data: any, tabId = "default"): Promise<void> {
58
+ return this.client.send("session.import", {
59
+ data: JSON.stringify(data),
60
+ tabId,
61
+ });
62
+ }
63
+
64
+ // Cookies (using imported types from export)
65
+ setCookie(opts: CookieSetOptions, tabId = "default"): Promise<void> {
66
+ return this.client.send("cookie.set", { ...opts, tabId });
67
+ }
68
+
69
+ deleteCookie(opts: CookieDeleteOptions, tabId = "default"): Promise<void> {
70
+ return this.client.send("cookie.delete", { ...opts, tabId });
71
+ }
72
+ }
73
+
74
+ export function createSessionAPI(client: PiggyClient): SessionClient {
75
+ return new SessionClient(client);
76
+ }
@@ -0,0 +1,22 @@
1
+ // piggy/tabs/index.ts
2
+ import { PiggyClient } from "../client";
3
+
4
+ export class TabsClient {
5
+ constructor(private client: PiggyClient) {}
6
+
7
+ new(): Promise<string> {
8
+ return this.client.send("tab.new", {});
9
+ }
10
+
11
+ close(tabId: string): Promise<void> {
12
+ return this.client.send("tab.close", { tabId });
13
+ }
14
+
15
+ list(): Promise<string[]> {
16
+ return this.client.send("tab.list", {});
17
+ }
18
+ }
19
+
20
+ export function createTabsAPI(client: PiggyClient): TabsClient {
21
+ return new TabsClient(client);
22
+ }
@@ -0,0 +1,90 @@
1
+ // piggy/wait/index.ts
2
+ import { PiggyClient } from "../client";
3
+
4
+ export type WaitSelectorState = "attached" | "detached" | "visible" | "hidden";
5
+
6
+ // ─── WaitClient ───────────────────────────────────────────────────────────────
7
+ // Maps to PiggyWait.cpp
8
+
9
+ export class WaitClient {
10
+ constructor(private client: PiggyClient) {}
11
+
12
+ // wait.function — poll every 100ms until JS expr is truthy
13
+ function(js: string, timeout = 10000, tabId = "default"): Promise<void> {
14
+ return this.client.send("wait.function", { js, timeout, tabId });
15
+ }
16
+
17
+ // wait.selector — with full state support (attached|detached|visible|hidden)
18
+ selector(
19
+ selector: string,
20
+ state: WaitSelectorState = "attached",
21
+ timeout = 10000,
22
+ tabId = "default"
23
+ ): Promise<void> {
24
+ return this.client.send("wait.selector", { selector, state, timeout, tabId });
25
+ }
26
+ }
27
+
28
+ // ─── EvaluateClient ───────────────────────────────────────────────────────────
29
+ // Maps to the evaluate command in PiggyWait.cpp (with timeout support)
30
+ // and PiggyInteractions.cpp (without timeout)
31
+
32
+ export class EvaluateClient {
33
+ constructor(private client: PiggyClient) {}
34
+
35
+ // evaluate with optional wall-clock timeout
36
+ run(js: string, timeout?: number, tabId = "default"): Promise<unknown> {
37
+ return this.client.send("evaluate", {
38
+ js,
39
+ tabId,
40
+ ...(timeout !== undefined ? { timeout } : {}),
41
+ });
42
+ }
43
+ }
44
+
45
+ // ─── FetchClient ──────────────────────────────────────────────────────────────
46
+ // Maps to fetch.textAll / fetch.attr / fetch.attrAll in PiggyWait.cpp
47
+ // and fetch.text / fetch.links / fetch.image in PiggyExport.cpp
48
+
49
+ export class FetchClient {
50
+ constructor(private client: PiggyClient) {}
51
+
52
+ // fetch.text — single element innerText
53
+ text(selector: string, tabId = "default"): Promise<string | null> {
54
+ return this.client.send("fetch.text", { query: selector, tabId });
55
+ }
56
+
57
+ // fetch.textAll — all matching elements innerText
58
+ textAll(selector: string, tabId = "default"): Promise<string[]> {
59
+ return this.client.send("fetch.textAll", { selector, tabId });
60
+ }
61
+
62
+ // fetch.attr — single attribute from first match
63
+ attr(selector: string, attr: string, tabId = "default"): Promise<string | null> {
64
+ return this.client.send("fetch.attr", { selector, attr, tabId });
65
+ }
66
+
67
+ // fetch.attrAll — attribute from all matches
68
+ attrAll(selector: string, attr: string, tabId = "default"): Promise<string[]> {
69
+ return this.client.send("fetch.attrAll", { selector, attr, tabId });
70
+ }
71
+
72
+ // fetch.links — links inside a selector
73
+ links(selector: string, tabId = "default"): Promise<string[]> {
74
+ return this.client.send("fetch.links", { query: selector, tabId });
75
+ }
76
+
77
+ // fetch.links.all — all links on page
78
+ linksAll(tabId = "default"): Promise<string[]> {
79
+ return this.client.send("fetch.links.all", { tabId });
80
+ }
81
+
82
+ // fetch.image — images inside a selector
83
+ images(selector: string, tabId = "default"): Promise<string[]> {
84
+ return this.client.send("fetch.image", { query: selector, tabId });
85
+ }
86
+ }
87
+
88
+ export function createWaitAPI(client: PiggyClient): WaitClient { return new WaitClient(client); }
89
+ export function createEvaluateAPI(client: PiggyClient): EvaluateClient { return new EvaluateClient(client); }
90
+ export function createFetchAPI(client: PiggyClient): FetchClient { return new FetchClient(client); }