nothing-browser 0.1.2 → 0.1.4

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 (121) hide show
  1. package/package.json +14 -26
  2. package/piggy/cache/{memory.ts → memory.js} +5 -10
  3. package/piggy/captcha/{index.ts → index.js} +19 -28
  4. package/piggy/capture/index.js +40 -0
  5. package/piggy/client/{index.ts → index.js} +133 -154
  6. package/piggy/dialog/{index.ts → index.js} +15 -31
  7. package/piggy/export/index.js +105 -0
  8. package/piggy/expose/index.js +24 -0
  9. package/piggy/find/{index.ts → index.js} +24 -39
  10. package/piggy/http/{index.ts → index.js} +7 -16
  11. package/piggy/human/{index.ts → index.js} +18 -49
  12. package/piggy/iframe/index.js +56 -0
  13. package/piggy/interactions/{index.ts → index.js} +20 -23
  14. package/piggy/intercept/{scripts.ts → scripts.js} +4 -13
  15. package/piggy/launch/{detect.ts → detect.js} +4 -6
  16. package/piggy/launch/{spawn.ts → spawn.js} +17 -22
  17. package/piggy/media/{index.ts → index.js} +13 -11
  18. package/piggy/navigation/{index.ts → index.js} +16 -14
  19. package/piggy/pool/index.js +76 -0
  20. package/piggy/provide/index.js +97 -0
  21. package/piggy/proxy/index.js +95 -0
  22. package/piggy/register/index.js +543 -0
  23. package/piggy/router/index.js +44 -0
  24. package/piggy/server/{index.ts → index.js} +16 -64
  25. package/piggy/session/index.js +69 -0
  26. package/piggy/store/{index.ts → index.js} +22 -62
  27. package/piggy/tabs/index.js +24 -0
  28. package/piggy/wait/index.js +77 -0
  29. package/piggy.js +316 -0
  30. package/dist/cache/memory.js +0 -35
  31. package/dist/client/index.js +0 -1284
  32. package/dist/human/index.js +0 -82
  33. package/dist/launch/detect.js +0 -782
  34. package/dist/launch/spawn.js +0 -915
  35. package/dist/logger/index.js +0 -733
  36. package/dist/piggy/cache/memory.d.ts +0 -7
  37. package/dist/piggy/cache/memory.d.ts.map +0 -1
  38. package/dist/piggy/captcha/index.d.ts +0 -39
  39. package/dist/piggy/captcha/index.d.ts.map +0 -1
  40. package/dist/piggy/capture/index.d.ts +0 -48
  41. package/dist/piggy/capture/index.d.ts.map +0 -1
  42. package/dist/piggy/client/index.d.ts +0 -146
  43. package/dist/piggy/client/index.d.ts.map +0 -1
  44. package/dist/piggy/dialog/index.d.ts +0 -28
  45. package/dist/piggy/dialog/index.d.ts.map +0 -1
  46. package/dist/piggy/export/index.d.ts +0 -62
  47. package/dist/piggy/export/index.d.ts.map +0 -1
  48. package/dist/piggy/expose/index.d.ts +0 -6
  49. package/dist/piggy/expose/index.d.ts.map +0 -1
  50. package/dist/piggy/find/index.d.ts +0 -52
  51. package/dist/piggy/find/index.d.ts.map +0 -1
  52. package/dist/piggy/http/index.d.ts +0 -14
  53. package/dist/piggy/http/index.d.ts.map +0 -1
  54. package/dist/piggy/human/index.d.ts +0 -39
  55. package/dist/piggy/human/index.d.ts.map +0 -1
  56. package/dist/piggy/iframe/index.d.ts +0 -53
  57. package/dist/piggy/iframe/index.d.ts.map +0 -1
  58. package/dist/piggy/interactions/index.d.ts +0 -24
  59. package/dist/piggy/interactions/index.d.ts.map +0 -1
  60. package/dist/piggy/intercept/scripts.d.ts +0 -13
  61. package/dist/piggy/intercept/scripts.d.ts.map +0 -1
  62. package/dist/piggy/launch/detect.d.ts +0 -3
  63. package/dist/piggy/launch/detect.d.ts.map +0 -1
  64. package/dist/piggy/launch/spawn.d.ts +0 -6
  65. package/dist/piggy/launch/spawn.d.ts.map +0 -1
  66. package/dist/piggy/logger/index.d.ts +0 -3
  67. package/dist/piggy/logger/index.d.ts.map +0 -1
  68. package/dist/piggy/media/index.d.ts +0 -11
  69. package/dist/piggy/media/index.d.ts.map +0 -1
  70. package/dist/piggy/navigation/index.d.ts +0 -16
  71. package/dist/piggy/navigation/index.d.ts.map +0 -1
  72. package/dist/piggy/pool/index.d.ts +0 -23
  73. package/dist/piggy/pool/index.d.ts.map +0 -1
  74. package/dist/piggy/provide/index.d.ts +0 -98
  75. package/dist/piggy/provide/index.d.ts.map +0 -1
  76. package/dist/piggy/proxy/index.d.ts +0 -94
  77. package/dist/piggy/proxy/index.d.ts.map +0 -1
  78. package/dist/piggy/register/index.d.ts +0 -9
  79. package/dist/piggy/register/index.d.ts.map +0 -1
  80. package/dist/piggy/router/index.d.ts +0 -38
  81. package/dist/piggy/router/index.d.ts.map +0 -1
  82. package/dist/piggy/server/index.d.ts +0 -42
  83. package/dist/piggy/server/index.d.ts.map +0 -1
  84. package/dist/piggy/session/index.d.ts +0 -27
  85. package/dist/piggy/session/index.d.ts.map +0 -1
  86. package/dist/piggy/store/index.d.ts +0 -22
  87. package/dist/piggy/store/index.d.ts.map +0 -1
  88. package/dist/piggy/tabs/index.d.ts +0 -12
  89. package/dist/piggy/tabs/index.d.ts.map +0 -1
  90. package/dist/piggy/wait/index.d.ts +0 -28
  91. package/dist/piggy/wait/index.d.ts.map +0 -1
  92. package/dist/piggy.d.ts +0 -10
  93. package/dist/piggy.d.ts.map +0 -1
  94. package/dist/piggy.js +0 -30186
  95. package/dist/register/index.js +0 -23710
  96. package/dist/server/index.js +0 -26831
  97. package/piggy/capture/index.ts +0 -76
  98. package/piggy/export/index.d.ts +0 -117
  99. package/piggy/export/index.ts +0 -147
  100. package/piggy/expose/index.ts +0 -42
  101. package/piggy/iframe/index.ts +0 -79
  102. package/piggy/intercept/scripts.d.ts +0 -13
  103. package/piggy/intercept/scripts.d.ts.map +0 -1
  104. package/piggy/launch/detect.d.ts +0 -3
  105. package/piggy/launch/detect.d.ts.map +0 -1
  106. package/piggy/launch/spawn.d.ts +0 -6
  107. package/piggy/launch/spawn.d.ts.map +0 -1
  108. package/piggy/logger/index.d.ts +0 -3
  109. package/piggy/logger/index.d.ts.map +0 -1
  110. package/piggy/pool/index.d.ts +0 -12
  111. package/piggy/pool/index.ts +0 -75
  112. package/piggy/provide/index.ts +0 -170
  113. package/piggy/proxy/index.ts +0 -154
  114. package/piggy/register/index.ts +0 -575
  115. package/piggy/router/index.ts +0 -69
  116. package/piggy/session/index.ts +0 -76
  117. package/piggy/store/index.d.ts +0 -26
  118. package/piggy/tabs/index.ts +0 -23
  119. package/piggy/wait/index.ts +0 -90
  120. package/piggy.ts +0 -274
  121. /package/piggy/logger/{index.ts → index.js} +0 -0
@@ -1,15 +1,14 @@
1
+
1
2
  import { existsSync } from 'fs';
2
3
  import { join } from 'path';
3
- import logger from '../logger';
4
-
5
- export type BinaryMode = 'headless' | 'headful' | (string & {});
4
+ import logger from '../logger.js';
6
5
 
7
- const BINARY_NAMES: Record<string, string> = {
6
+ const BINARY_NAMES = {
8
7
  headless: 'nothing-browser-headless',
9
8
  headful: 'nothing-browser-headful',
10
9
  };
11
10
 
12
- export function detectBinary(mode: BinaryMode = 'headless'): string | null {
11
+ export function detectBinary(mode = 'headless') {
13
12
  const cwd = process.cwd();
14
13
 
15
14
  // Custom path passed directly
@@ -18,7 +17,6 @@ export function detectBinary(mode: BinaryMode = 'headless'): string | null {
18
17
  logger.success(`Binary found (custom path): ${mode}`);
19
18
  return mode;
20
19
  }
21
- // try relative to cwd
22
20
  const abs = join(cwd, mode);
23
21
  if (existsSync(abs)) {
24
22
  logger.success(`Binary found (custom path): ${abs}`);
@@ -1,15 +1,15 @@
1
1
  import { spawn as nodeSpawn } from "child_process";
2
2
  import { execSync } from "child_process";
3
3
  import { platform } from "os";
4
- import { detectBinary, type BinaryMode } from "./detect";
5
- import logger from "../logger";
4
+ import { detectBinary } from "./detect.js";
5
+ import logger from "../logger.js";
6
6
 
7
- let activeProcess: any = null;
8
- const extraProcesses: any[] = [];
7
+ let activeProcess = null;
8
+ const extraProcesses = [];
9
9
 
10
- const isBun = typeof (globalThis as any).Bun !== 'undefined';
10
+ const isBun = typeof globalThis.Bun !== 'undefined';
11
11
 
12
- export function killAllBrowsers(): void {
12
+ export function killAllBrowsers() {
13
13
  try {
14
14
  logger.info('Cleaning up existing browser processes...');
15
15
  if (platform() === 'win32') {
@@ -27,7 +27,7 @@ export function killAllBrowsers(): void {
27
27
  }
28
28
  }
29
29
 
30
- export async function spawnBrowser(mode: BinaryMode = 'headless'): Promise<string> {
30
+ export async function spawnBrowser(mode = 'headless') {
31
31
  killAllBrowsers();
32
32
  await new Promise(resolve => setTimeout(resolve, 500));
33
33
 
@@ -39,8 +39,7 @@ export async function spawnBrowser(mode: BinaryMode = 'headless'): Promise<strin
39
39
  logger.info(`Spawning Nothing Browser (${mode}) from: ${binaryPath}`);
40
40
 
41
41
  if (isBun) {
42
- const Bun = (globalThis as any).Bun;
43
- activeProcess = Bun.spawn([binaryPath], {
42
+ activeProcess = globalThis.Bun.spawn([binaryPath], {
44
43
  stdio: ['ignore', 'pipe', 'pipe'],
45
44
  env: process.env,
46
45
  });
@@ -57,7 +56,7 @@ export async function spawnBrowser(mode: BinaryMode = 'headless'): Promise<strin
57
56
  read();
58
57
  }
59
58
 
60
- activeProcess.exited.then((code: number | null) => {
59
+ activeProcess.exited.then((code) => {
61
60
  logger.warn(`Browser process exited with code: ${code}`);
62
61
  activeProcess = null;
63
62
  });
@@ -68,18 +67,18 @@ export async function spawnBrowser(mode: BinaryMode = 'headless'): Promise<strin
68
67
  });
69
68
 
70
69
  if (activeProcess.stdout) {
71
- activeProcess.stdout.on('data', (data: Buffer) => {
70
+ activeProcess.stdout.on('data', (data) => {
72
71
  logger.debug(`[Browser] ${data.toString()}`);
73
72
  });
74
73
  }
75
74
 
76
75
  if (activeProcess.stderr) {
77
- activeProcess.stderr.on('data', (data: Buffer) => {
76
+ activeProcess.stderr.on('data', (data) => {
78
77
  logger.debug(`[Browser Error] ${data.toString()}`);
79
78
  });
80
79
  }
81
80
 
82
- activeProcess.on('exit', (code: number | null) => {
81
+ activeProcess.on('exit', (code) => {
83
82
  logger.warn(`Browser process exited with code: ${code}`);
84
83
  activeProcess = null;
85
84
  });
@@ -96,10 +95,7 @@ export async function spawnBrowser(mode: BinaryMode = 'headless'): Promise<strin
96
95
  return binaryPath;
97
96
  }
98
97
 
99
- export async function spawnBrowserOnSocket(
100
- socketName: string,
101
- mode: BinaryMode = 'headless'
102
- ): Promise<void> {
98
+ export async function spawnBrowserOnSocket(socketName, mode = 'headless') {
103
99
  const binaryPath = detectBinary(mode);
104
100
  if (!binaryPath) {
105
101
  throw new Error(`Binary not found (${mode}). Cannot launch.`);
@@ -108,15 +104,14 @@ export async function spawnBrowserOnSocket(
108
104
  logger.info(`Spawning browser (${mode}) on socket: ${socketName}`);
109
105
 
110
106
  if (isBun) {
111
- const Bun = (globalThis as any).Bun;
112
- const proc = Bun.spawn([binaryPath], {
107
+ const proc = globalThis.Bun.spawn([binaryPath], {
113
108
  stdio: ['ignore', 'pipe', 'pipe'],
114
109
  env: { ...process.env, PIGGY_SOCKET: socketName },
115
110
  });
116
111
 
117
112
  extraProcesses.push(proc);
118
113
 
119
- proc.exited.then((code: number | null) => {
114
+ proc.exited.then((code) => {
120
115
  logger.warn(`Browser on socket ${socketName} exited with code: ${code}`);
121
116
  });
122
117
  } else {
@@ -127,7 +122,7 @@ export async function spawnBrowserOnSocket(
127
122
 
128
123
  extraProcesses.push(proc);
129
124
 
130
- proc.on('exit', (code: number | null) => {
125
+ proc.on('exit', (code) => {
131
126
  logger.warn(`Browser on socket ${socketName} exited with code: ${code}`);
132
127
  });
133
128
  }
@@ -136,7 +131,7 @@ export async function spawnBrowserOnSocket(
136
131
  logger.success(`Browser spawned (${mode}) on socket: ${socketName}`);
137
132
  }
138
133
 
139
- export function killBrowser(): void {
134
+ export function killBrowser() {
140
135
  if (activeProcess) {
141
136
  logger.info('Killing browser process...');
142
137
  if (isBun) {
@@ -1,15 +1,17 @@
1
- // piggy/media/index.ts
2
- import { PiggyClient } from "../client";
1
+ // piggy/media/index.js
2
+ import { PiggyClient } from "../client.js";
3
3
  import { writeFileSync, mkdirSync } from "fs";
4
4
  import { dirname } from "path";
5
5
 
6
6
  export class MediaClient {
7
- constructor(private client: PiggyClient) {}
7
+ constructor(client) {
8
+ this.client = client;
9
+ }
8
10
 
9
11
  // ── Screenshot ────────────────────────────────────────────────────────────
10
12
 
11
- async screenshot(filePath?: string, tabId = "default"): Promise<string> {
12
- const b64 = await this.client.send<string>("screenshot", { tabId });
13
+ async screenshot(filePath, tabId = "default") {
14
+ const b64 = await this.client.send("screenshot", { tabId });
13
15
  if (filePath) {
14
16
  mkdirSync(dirname(filePath), { recursive: true });
15
17
  writeFileSync(filePath, Buffer.from(b64, "base64"));
@@ -20,8 +22,8 @@ export class MediaClient {
20
22
 
21
23
  // ── PDF ───────────────────────────────────────────────────────────────────
22
24
 
23
- async pdf(filePath?: string, tabId = "default"): Promise<string> {
24
- const b64 = await this.client.send<string>("pdf", { tabId });
25
+ async pdf(filePath, tabId = "default") {
26
+ const b64 = await this.client.send("pdf", { tabId });
25
27
  if (filePath) {
26
28
  mkdirSync(dirname(filePath), { recursive: true });
27
29
  writeFileSync(filePath, Buffer.from(b64, "base64"));
@@ -32,15 +34,15 @@ export class MediaClient {
32
34
 
33
35
  // ── Image blocking ────────────────────────────────────────────────────────
34
36
 
35
- blockImages(tabId = "default"): Promise<void> {
37
+ blockImages(tabId = "default") {
36
38
  return this.client.send("intercept.block.images", { tabId });
37
39
  }
38
40
 
39
- unblockImages(tabId = "default"): Promise<void> {
41
+ unblockImages(tabId = "default") {
40
42
  return this.client.send("intercept.unblock.images", { tabId });
41
43
  }
42
44
  }
43
45
 
44
- export function createMediaAPI(client: PiggyClient): MediaClient {
46
+ export function createMediaAPI(client) {
45
47
  return new MediaClient(client);
46
- }
48
+ }
@@ -1,52 +1,54 @@
1
- // piggy/navigation/index.ts
2
- import { PiggyClient } from "../client";
1
+ // piggy/navigation/index.js
2
+ import { PiggyClient } from "../client.js";
3
3
 
4
4
  export class NavigationClient {
5
- constructor(private client: PiggyClient) {}
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
6
8
 
7
9
  // ── Navigation ────────────────────────────────────────────────────────────
8
10
 
9
- navigate(url: string, tabId = "default"): Promise<void> {
11
+ navigate(url, tabId = "default") {
10
12
  return this.client.send("navigate", { url, tabId });
11
13
  }
12
14
 
13
- reload(tabId = "default"): Promise<void> {
15
+ reload(tabId = "default") {
14
16
  return this.client.send("reload", { tabId });
15
17
  }
16
18
 
17
- goBack(tabId = "default"): Promise<void> {
19
+ goBack(tabId = "default") {
18
20
  return this.client.send("go.back", { tabId });
19
21
  }
20
22
 
21
- goForward(tabId = "default"): Promise<void> {
23
+ goForward(tabId = "default") {
22
24
  return this.client.send("go.forward", { tabId });
23
25
  }
24
26
 
25
27
  // ── Page info ─────────────────────────────────────────────────────────────
26
28
 
27
- url(tabId = "default"): Promise<string> {
29
+ url(tabId = "default") {
28
30
  return this.client.send("page.url", { tabId });
29
31
  }
30
32
 
31
- title(tabId = "default"): Promise<string> {
33
+ title(tabId = "default") {
32
34
  return this.client.send("page.title", { tabId });
33
35
  }
34
36
 
35
- content(tabId = "default"): Promise<string> {
37
+ content(tabId = "default") {
36
38
  return this.client.send("page.content", { tabId });
37
39
  }
38
40
 
39
41
  // ── Wait ──────────────────────────────────────────────────────────────────
40
42
 
41
- waitForNavigation(tabId = "default"): Promise<void> {
43
+ waitForNavigation(tabId = "default") {
42
44
  return this.client.send("wait.navigation", { tabId });
43
45
  }
44
46
 
45
- waitForSelector(selector: string, timeout = 10000, tabId = "default"): Promise<void> {
47
+ waitForSelector(selector, timeout = 10000, tabId = "default") {
46
48
  return this.client.send("wait.selector", { selector, timeout, tabId });
47
49
  }
48
50
  }
49
51
 
50
- export function createNavigationAPI(client: PiggyClient): NavigationClient {
52
+ export function createNavigationAPI(client) {
51
53
  return new NavigationClient(client);
52
- }
54
+ }
@@ -0,0 +1,76 @@
1
+ // piggy/pool/index.js
2
+ import { PiggyClient } from "../client.js";
3
+ import logger from "../logger.js";
4
+
5
+ export class TabPool {
6
+ #idle = [];
7
+ #busy = new Set();
8
+ #queue = [];
9
+
10
+ constructor(client, size, seedUrl, name) {
11
+ this.client = client;
12
+ this.size = size;
13
+ this.seedUrl = seedUrl;
14
+ this.name = name;
15
+ }
16
+
17
+ async init() {
18
+ for (let i = 0; i < this.size; i++) {
19
+ const tabId = await this.client.newTab();
20
+ await this.client.navigate(this.seedUrl, tabId);
21
+ this.#idle.push(tabId);
22
+ logger.success(`[${this.name}] pool tab ${i + 1}/${this.size} ready: ${tabId}`);
23
+ }
24
+ }
25
+
26
+ acquire() {
27
+ return new Promise(resolve => {
28
+ const tabId = this.#idle.pop();
29
+ if (tabId) {
30
+ this.#busy.add(tabId);
31
+ resolve(tabId);
32
+ } else {
33
+ this.#queue.push(resolve);
34
+ }
35
+ });
36
+ }
37
+
38
+ release(tabId) {
39
+ this.#busy.delete(tabId);
40
+ const next = this.#queue.shift();
41
+ if (next) {
42
+ this.#busy.add(tabId);
43
+ next(tabId);
44
+ } else {
45
+ this.#idle.push(tabId);
46
+ }
47
+ }
48
+
49
+ async withTab(fn) {
50
+ logger.warn("[TabPool] withTab() is deprecated and will be removed in a later version — use the new tab API instead.");
51
+ const tabId = await this.acquire();
52
+ try {
53
+ return await fn(tabId);
54
+ } finally {
55
+ this.release(tabId);
56
+ }
57
+ }
58
+
59
+ async close() {
60
+ for (const tabId of [...this.#idle, ...this.#busy]) {
61
+ try { await this.client.closeTab(tabId); } catch {}
62
+ }
63
+ this.#idle = [];
64
+ this.#busy.clear();
65
+ this.#queue = [];
66
+ }
67
+
68
+ get stats() {
69
+ return {
70
+ idle: this.#idle.length,
71
+ busy: this.#busy.size,
72
+ queued: this.#queue.length,
73
+ total: this.size,
74
+ };
75
+ }
76
+ }
@@ -0,0 +1,97 @@
1
+ // piggy/provide/index.js
2
+ import { PiggyClient } from "../client.js";
3
+
4
+ // ─── ProvideClient ────────────────────────────────────────────────────────────
5
+ // provide answers ONE question: "give me the actual value from this element."
6
+ // All methods take an options object { selector, parent? } so scoping is consistent.
7
+ // If you just want to know if something exists, use find instead.
8
+
9
+ export class ProvideClient {
10
+ constructor(client) {
11
+ this.client = client;
12
+ }
13
+
14
+ /** innerText of the first matched element. */
15
+ text(opts, tabId = "default") {
16
+ return this.client.send("provide.text", { ...opts, tabId });
17
+ }
18
+
19
+ /** innerText of all matched elements. */
20
+ textAll(opts, tabId = "default") {
21
+ return this.client.send("provide.textAll", { ...opts, tabId });
22
+ }
23
+
24
+ /** Single attribute value from the first matched element. */
25
+ attr(opts, tabId = "default") {
26
+ return this.client.send("provide.attr", { ...opts, tabId });
27
+ }
28
+
29
+ /** Attribute value from all matched elements. */
30
+ attrAll(opts, tabId = "default") {
31
+ return this.client.send("provide.attrAll", { ...opts, tabId });
32
+ }
33
+
34
+ /** innerHTML of the first matched element. */
35
+ html(opts, tabId = "default") {
36
+ return this.client.send("provide.html", { ...opts, tabId });
37
+ }
38
+
39
+ /** Extract a table into headers + rows. */
40
+ table(opts, tabId = "default") {
41
+ return this.client.send("provide.table", { ...opts, tabId });
42
+ }
43
+
44
+ /** Extract a list of text items, optionally scoped to child items. */
45
+ list(opts, tabId = "default") {
46
+ return this.client.send("provide.list", { ...opts, tabId });
47
+ }
48
+
49
+ /** All links inside the matched selector. */
50
+ links(opts, tabId = "default") {
51
+ return this.client.send("provide.links", { ...opts, tabId });
52
+ }
53
+
54
+ /** All images inside the matched selector. */
55
+ images(opts, tabId = "default") {
56
+ return this.client.send("provide.images", { ...opts, tabId });
57
+ }
58
+
59
+ /** Form field name→value map. */
60
+ form(opts, tabId = "default") {
61
+ return this.client.send("provide.form", { ...opts, tabId });
62
+ }
63
+
64
+ /** Full page info: title, url, html, text. No selector needed. */
65
+ page(tabId = "default") {
66
+ return this.client.send("provide.page", { tabId });
67
+ }
68
+
69
+ /** Structured div tree: tag, id, cls, text, html, children[]. */
70
+ div(opts, tabId = "default") {
71
+ return this.client.send("provide.div", { ...opts, tabId });
72
+ }
73
+
74
+ /** All <meta> name→content pairs. No selector needed. */
75
+ meta(tabId = "default") {
76
+ return this.client.send("provide.meta", { tabId });
77
+ }
78
+
79
+ /** <select> current value + all options. */
80
+ select(opts, tabId = "default") {
81
+ return this.client.send("provide.select", { ...opts, tabId });
82
+ }
83
+
84
+ /**
85
+ * Parse JSON from element innerText or script[type=application/json].
86
+ * selector is optional — defaults to the first matching JSON script tag.
87
+ */
88
+ json(opts, tabId = "default") {
89
+ return this.client.send("provide.json", { ...opts, tabId });
90
+ }
91
+ }
92
+
93
+ // ─── Factory helper ───────────────────────────────────────────────────────────
94
+
95
+ export function createProvideAPI(client) {
96
+ return new ProvideClient(client);
97
+ }
@@ -0,0 +1,95 @@
1
+ // piggy/proxy/index.js
2
+ import { PiggyClient } from "../client.js";
3
+
4
+ export class ProxyClient {
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+
9
+ /** Load proxies from a local file path. */
10
+ load(path) {
11
+ return this.client.send("proxy.load", { path });
12
+ }
13
+
14
+ /** Fetch proxies from a URL. Result comes via proxy:loaded / proxy:fetch:failed events. */
15
+ fetch(url) {
16
+ return this.client.send("proxy.fetch", { url });
17
+ }
18
+
19
+ /** Load an .ovpn config file. */
20
+ ovpn(path) {
21
+ return this.client.send("proxy.ovpn", { path });
22
+ }
23
+
24
+ /** Set a single proxy inline. */
25
+ set(opts) {
26
+ return this.client.send("proxy.set", opts);
27
+ }
28
+
29
+ /** Health-check all loaded proxies. Results come via events. */
30
+ test() {
31
+ return this.client.send("proxy.test", {});
32
+ }
33
+
34
+ /** Abort an in-progress health check. */
35
+ testStop() {
36
+ return this.client.send("proxy.test.stop", {});
37
+ }
38
+
39
+ /** Rotate to the next proxy in the pool. */
40
+ next() {
41
+ return this.client.send("proxy.next", {});
42
+ }
43
+
44
+ /** Alias for next(). */
45
+ rotate() {
46
+ return this.client.send("proxy.rotate", {});
47
+ }
48
+
49
+ /** Disable the proxy — use the real IP. */
50
+ disable() {
51
+ return this.client.send("proxy.disable", {});
52
+ }
53
+
54
+ /** Re-enable the current proxy. */
55
+ enable() {
56
+ return this.client.send("proxy.enable", {});
57
+ }
58
+
59
+ /** Get the current active proxy details. */
60
+ current() {
61
+ return this.client.send("proxy.current", {});
62
+ }
63
+
64
+ /** Get pool stats: total, alive, dead, index, active, checking. */
65
+ stats() {
66
+ return this.client.send("proxy.stats", {});
67
+ }
68
+
69
+ /** List all proxies with health info. limit defaults to 500. */
70
+ list(limit) {
71
+ return this.client.send("proxy.list", { limit });
72
+ }
73
+
74
+ /** Set rotation mode and interval (seconds, only used for "timed"). */
75
+ rotation(mode, interval) {
76
+ return this.client.send("proxy.rotation", { mode, interval });
77
+ }
78
+
79
+ /** Set skipDead / autoCheck flags. */
80
+ config(opts) {
81
+ return this.client.send("proxy.config", opts);
82
+ }
83
+
84
+ /**
85
+ * Save the current proxy list to a file.
86
+ * filter: "all" | "alive" | "dead" — defaults to "all".
87
+ */
88
+ save(path, filter) {
89
+ return this.client.send("proxy.save", { path, filter });
90
+ }
91
+ }
92
+
93
+ export function createProxyAPI(client) {
94
+ return new ProxyClient(client);
95
+ }