nothing-browser 0.0.17 → 0.0.19
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 +224 -40
- package/dist/launch/detect.js +8 -0
- package/dist/launch/spawn.js +8 -0
- package/dist/piggy/client/index.d.ts +77 -2
- package/dist/piggy/client/index.d.ts.map +1 -1
- package/dist/piggy/launch/detect.d.ts +1 -1
- package/dist/piggy/launch/detect.d.ts.map +1 -1
- package/dist/piggy.d.ts +1 -0
- package/dist/piggy.d.ts.map +1 -1
- package/dist/piggy.js +329 -48
- package/package.json +1 -1
- package/piggy/client/index.ts +325 -54
- package/piggy/launch/detect.ts +13 -4
- package/piggy.ts +46 -22
package/piggy.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// piggy.ts
|
|
2
|
-
import { detectBinary
|
|
2
|
+
import { detectBinary } from "./piggy/launch/detect";
|
|
3
3
|
import { spawnBrowser, killBrowser, spawnBrowserOnSocket } from "./piggy/launch/spawn";
|
|
4
4
|
import { PiggyClient } from "./piggy/client";
|
|
5
5
|
import { setClient, setHumanMode, createSiteObject, type SiteObject } from "./piggy/register";
|
|
@@ -9,14 +9,21 @@ import logger from "./piggy/logger";
|
|
|
9
9
|
|
|
10
10
|
type TabMode = "tab" | "process";
|
|
11
11
|
|
|
12
|
+
// "headless" and "headfull" are the built-in modes.
|
|
13
|
+
// Any other string is treated as a raw path to a binary —
|
|
14
|
+
// e.g. "../my-browser/browser", "C:\\browsers\\mybrowser.exe",
|
|
15
|
+
// "/opt/weird-browser/bin/wb", etc.
|
|
16
|
+
// The (string & {}) trick keeps autocomplete for the two literals
|
|
17
|
+
// while still accepting any arbitrary string.
|
|
18
|
+
export type BinaryMode = "headless" | "headfull" | (string & {});
|
|
19
|
+
|
|
12
20
|
let _client: PiggyClient | null = null;
|
|
13
21
|
let _tabMode: TabMode = "tab";
|
|
14
22
|
const _extraProcs: { socket: string; client: PiggyClient }[] = [];
|
|
15
23
|
const _sites: Record<string, SiteObject> = [];
|
|
16
24
|
|
|
17
25
|
const piggy: any = {
|
|
18
|
-
// ──
|
|
19
|
-
|
|
26
|
+
// ── Local launch (socket) ─────────────────────────────────────────────────
|
|
20
27
|
launch: async (opts?: { mode?: TabMode; binary?: BinaryMode }) => {
|
|
21
28
|
_tabMode = opts?.mode ?? "tab";
|
|
22
29
|
const binaryMode: BinaryMode = opts?.binary ?? "headless";
|
|
@@ -29,12 +36,23 @@ const piggy: any = {
|
|
|
29
36
|
return piggy;
|
|
30
37
|
},
|
|
31
38
|
|
|
39
|
+
// ── Remote connect (HTTP) ─────────────────────────────────────────────────
|
|
40
|
+
connect: async (opts: { host: string; key: string }) => {
|
|
41
|
+
_tabMode = "tab";
|
|
42
|
+
_client = new PiggyClient({ host: opts.host, key: opts.key });
|
|
43
|
+
await _client.connect();
|
|
44
|
+
setClient(_client);
|
|
45
|
+
logger.info(`[piggy] connected (HTTP) → ${opts.host}`);
|
|
46
|
+
return piggy;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// ── Register ──────────────────────────────────────────────────────────────
|
|
32
50
|
register: async (
|
|
33
51
|
name: string,
|
|
34
52
|
url: string,
|
|
35
53
|
opts?: {
|
|
36
54
|
binary?: BinaryMode;
|
|
37
|
-
pool?: number;
|
|
55
|
+
pool?: number;
|
|
38
56
|
}
|
|
39
57
|
) => {
|
|
40
58
|
if (!url?.trim()) throw new Error(`No URL for site "${name}"`);
|
|
@@ -42,21 +60,16 @@ const piggy: any = {
|
|
|
42
60
|
const poolSize = opts?.pool ?? 0;
|
|
43
61
|
|
|
44
62
|
if (_tabMode === "tab") {
|
|
45
|
-
if (!_client) throw new Error("No client. Call piggy.launch() first.");
|
|
63
|
+
if (!_client) throw new Error("No client. Call piggy.launch() or piggy.connect() first.");
|
|
46
64
|
|
|
47
65
|
if (poolSize > 1) {
|
|
48
|
-
// Pool mode — create N tabs, wrap in TabPool
|
|
49
66
|
const pool = new TabPool(_client, poolSize, url, name);
|
|
50
67
|
await pool.init();
|
|
51
|
-
|
|
52
|
-
// Use first tab as the "default" tabId for event subscriptions
|
|
53
|
-
const firstTabId = "default";
|
|
54
|
-
const siteObj = createSiteObject(name, url, _client, firstTabId, pool);
|
|
68
|
+
const siteObj = createSiteObject(name, url, _client, "default", pool);
|
|
55
69
|
_sites[name] = siteObj;
|
|
56
70
|
piggy[name] = siteObj;
|
|
57
71
|
logger.success(`[${name}] registered with pool of ${poolSize} tabs`);
|
|
58
72
|
} else {
|
|
59
|
-
// Single tab mode
|
|
60
73
|
const tabId = await _client.newTab();
|
|
61
74
|
const siteObj = createSiteObject(name, url, _client, tabId);
|
|
62
75
|
_sites[name] = siteObj;
|
|
@@ -80,7 +93,6 @@ const piggy: any = {
|
|
|
80
93
|
},
|
|
81
94
|
|
|
82
95
|
// ── Global controls ───────────────────────────────────────────────────────
|
|
83
|
-
|
|
84
96
|
actHuman: (enable: boolean) => {
|
|
85
97
|
setHumanMode(enable);
|
|
86
98
|
logger.info(`[piggy] actHuman: ${enable}`);
|
|
@@ -90,23 +102,41 @@ const piggy: any = {
|
|
|
90
102
|
mode: (m: TabMode) => { _tabMode = m; return piggy; },
|
|
91
103
|
|
|
92
104
|
// ── Expose Function ───────────────────────────────────────────────────────
|
|
93
|
-
|
|
94
105
|
expose: async (name: string, handler: (data: any) => Promise<any> | any, tabId = "default") => {
|
|
95
|
-
if (!_client) throw new Error("No client. Call piggy.launch() first.");
|
|
106
|
+
if (!_client) throw new Error("No client. Call piggy.launch() or piggy.connect() first.");
|
|
96
107
|
await _client.exposeFunction(name, handler, tabId);
|
|
97
108
|
logger.success(`[piggy] exposed global function: ${name}`);
|
|
98
109
|
return piggy;
|
|
99
110
|
},
|
|
100
111
|
|
|
101
112
|
unexpose: async (name: string, tabId = "default") => {
|
|
102
|
-
if (!_client) throw new Error("No client. Call piggy.launch() first.");
|
|
113
|
+
if (!_client) throw new Error("No client. Call piggy.launch() or piggy.connect() first.");
|
|
103
114
|
await _client.unexposeFunction(name, tabId);
|
|
104
115
|
logger.info(`[piggy] unexposed function: ${name}`);
|
|
105
116
|
return piggy;
|
|
106
117
|
},
|
|
107
118
|
|
|
108
|
-
// ──
|
|
119
|
+
// ── Proxy ─────────────────────────────────────────────────────────────────
|
|
120
|
+
proxy: {
|
|
121
|
+
load: (path: string) => { if (!_client) throw new Error("No client"); return _client.proxyLoad(path); },
|
|
122
|
+
fetch: (url: string) => { if (!_client) throw new Error("No client"); return _client.proxyFetch(url); },
|
|
123
|
+
ovpn: (path: string) => { if (!_client) throw new Error("No client"); return _client.proxyOvpn(path); },
|
|
124
|
+
set: (opts: Parameters<PiggyClient["proxySet"]>[0]) => { if (!_client) throw new Error("No client"); return _client.proxySet(opts); },
|
|
125
|
+
test: () => { if (!_client) throw new Error("No client"); return _client.proxyTest(); },
|
|
126
|
+
testStop: () => { if (!_client) throw new Error("No client"); return _client.proxyTestStop(); },
|
|
127
|
+
next: () => { if (!_client) throw new Error("No client"); return _client.proxyNext(); },
|
|
128
|
+
disable: () => { if (!_client) throw new Error("No client"); return _client.proxyDisable(); },
|
|
129
|
+
enable: () => { if (!_client) throw new Error("No client"); return _client.proxyEnable(); },
|
|
130
|
+
current: () => { if (!_client) throw new Error("No client"); return _client.proxyCurrent(); },
|
|
131
|
+
stats: () => { if (!_client) throw new Error("No client"); return _client.proxyStats(); },
|
|
132
|
+
list: (limit?: number) => { if (!_client) throw new Error("No client"); return _client.proxyList(limit); },
|
|
133
|
+
rotation: (mode: "none" | "timed" | "perrequest", interval?: number) => { if (!_client) throw new Error("No client"); return _client.proxyRotation(mode, interval); },
|
|
134
|
+
config: (opts: { skipDead?: boolean; autoCheck?: boolean }) => { if (!_client) throw new Error("No client"); return _client.proxyConfig(opts); },
|
|
135
|
+
save: (path: string, filter?: "alive" | "dead" | "all") => { if (!_client) throw new Error("No client"); return _client.proxySave(path, filter); },
|
|
136
|
+
on: (event: string, handler: (data: any) => void) => { if (!_client) throw new Error("No client"); return _client.onProxyEvent(event, handler); },
|
|
137
|
+
},
|
|
109
138
|
|
|
139
|
+
// ── Elysia server ─────────────────────────────────────────────────────────
|
|
110
140
|
serve: (
|
|
111
141
|
port: number,
|
|
112
142
|
opts?: {
|
|
@@ -121,7 +151,6 @@ const piggy: any = {
|
|
|
121
151
|
stopServer,
|
|
122
152
|
|
|
123
153
|
// ── Route listing ─────────────────────────────────────────────────────────
|
|
124
|
-
|
|
125
154
|
routes: () =>
|
|
126
155
|
Array.from(routeRegistry.entries()).map(([key, cfg]) => {
|
|
127
156
|
const [site] = key.split(":");
|
|
@@ -135,7 +164,6 @@ const piggy: any = {
|
|
|
135
164
|
}),
|
|
136
165
|
|
|
137
166
|
// ── Multi-site ────────────────────────────────────────────────────────────
|
|
138
|
-
|
|
139
167
|
all: (sites: SiteObject[]) =>
|
|
140
168
|
new Proxy({} as any, {
|
|
141
169
|
get: (_, method: string) =>
|
|
@@ -152,7 +180,6 @@ const piggy: any = {
|
|
|
152
180
|
}),
|
|
153
181
|
|
|
154
182
|
// ── Shutdown ──────────────────────────────────────────────────────────────
|
|
155
|
-
|
|
156
183
|
close: async (opts?: { force?: boolean }) => {
|
|
157
184
|
stopServer();
|
|
158
185
|
if (opts?.force) {
|
|
@@ -182,9 +209,6 @@ const piggy: any = {
|
|
|
182
209
|
};
|
|
183
210
|
|
|
184
211
|
// ── usePiggy ──────────────────────────────────────────────────────────────────
|
|
185
|
-
// Typed accessor — call AFTER register() so sites exist on piggy.
|
|
186
|
-
// const { amazon, ebay } = usePiggy<"amazon" | "ebay">()
|
|
187
|
-
|
|
188
212
|
type TypedPiggy<Sites extends string> = typeof piggy & {
|
|
189
213
|
[K in Sites]: SiteObject;
|
|
190
214
|
};
|