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.
- package/dist/client/index.js +24 -6
- package/dist/piggy/client/index.d.ts +3 -11
- package/dist/piggy/client/index.d.ts.map +1 -1
- package/dist/piggy/expose/index.d.ts +6 -0
- package/dist/piggy/expose/index.d.ts.map +1 -0
- package/dist/piggy/find/index.d.ts +11 -49
- package/dist/piggy/find/index.d.ts.map +1 -1
- package/dist/piggy/provide/index.d.ts +36 -19
- package/dist/piggy/provide/index.d.ts.map +1 -1
- package/dist/piggy/register/index.d.ts.map +1 -1
- package/dist/piggy/tabs/index.d.ts +3 -1
- package/dist/piggy/tabs/index.d.ts.map +1 -1
- package/dist/piggy.d.ts.map +1 -1
- package/dist/piggy.js +657 -558
- package/dist/register/index.js +428 -35
- package/package.json +1 -1
- package/piggy/client/index.ts +48 -126
- package/piggy/expose/index.ts +42 -0
- package/piggy/find/index.ts +30 -77
- package/piggy/provide/index.ts +58 -32
- package/piggy/register/index.ts +115 -40
- package/piggy/tabs/index.ts +2 -1
- package/piggy.ts +3 -1
- package/piggy/cache/memory.d.ts +0 -7
- package/piggy/cache/memory.d.ts.map +0 -1
- package/piggy/captcha/index.d.ts +0 -35
- package/piggy/client/index.d.ts +0 -79
- package/piggy/client/index.d.ts.map +0 -1
- package/piggy/dialog/index.d.ts +0 -29
- package/piggy/find/index.d.ts +0 -79
- package/piggy/human/index.d.ts +0 -7
- package/piggy/human/index.d.ts.map +0 -1
- package/piggy/register/index.d.ts +0 -7
- package/piggy/register/index.d.ts.map +0 -1
- package/piggy/server/index.d.ts +0 -58
- package/piggy/server/index.d.ts.map +0 -1
package/piggy/client/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
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
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
async
|
|
416
|
-
|
|
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
|
|
490
|
-
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
async
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
async
|
|
517
|
-
|
|
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
|
+
}
|
package/piggy/find/index.ts
CHANGED
|
@@ -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:
|
|
9
|
-
id:
|
|
10
|
-
cls:
|
|
8
|
+
tag: string;
|
|
9
|
+
id: string;
|
|
10
|
+
cls: string;
|
|
11
11
|
/** First 400 chars of innerText */
|
|
12
|
-
text:
|
|
12
|
+
text: string;
|
|
13
13
|
/** First 800 chars of innerHTML */
|
|
14
|
-
html:
|
|
15
|
-
href:
|
|
16
|
-
src:
|
|
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
|
|
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()
|
|
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
|
|
81
|
-
byText(
|
|
82
|
-
return this.client.send("find.byText", {
|
|
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(
|
|
87
|
-
return this.client.send("find.byAttr", {
|
|
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
|
|
101
|
-
byRole(
|
|
102
|
-
return this.client.send("find.byRole", {
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
114
|
+
// ─── Factory helper ───────────────────────────────────────────────────────────
|
|
162
115
|
|
|
163
116
|
export function createFindAPI(client: PiggyClient): FindClient {
|
|
164
117
|
return new FindClient(client);
|
package/piggy/provide/index.ts
CHANGED
|
@@ -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(
|
|
63
|
-
return this.client.send("provide.text", {
|
|
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(
|
|
68
|
-
return this.client.send("provide.textAll", {
|
|
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(
|
|
73
|
-
return this.client.send("provide.attr", {
|
|
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(
|
|
78
|
-
return this.client.send("provide.attrAll", {
|
|
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(
|
|
83
|
-
return this.client.send("provide.html", {
|
|
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(
|
|
88
|
-
return this.client.send("provide.table", {
|
|
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
|
|
92
|
-
list(
|
|
93
|
-
return this.client.send("provide.list", {
|
|
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
|
|
97
|
-
links(
|
|
98
|
-
return this.client.send("provide.links", {
|
|
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
|
|
102
|
-
images(
|
|
103
|
-
return this.client.send("provide.images", {
|
|
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(
|
|
108
|
-
return this.client.send("provide.form", {
|
|
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(
|
|
118
|
-
return this.client.send("provide.div", {
|
|
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(
|
|
128
|
-
return this.client.send("provide.select", {
|
|
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(
|
|
136
|
-
return this.client.send("provide.json", {
|
|
161
|
+
json(opts?: Partial<ProvideOptions>, tabId = "default"): Promise<unknown> {
|
|
162
|
+
return this.client.send("provide.json", { ...opts, tabId });
|
|
137
163
|
}
|
|
138
164
|
}
|
|
139
165
|
|