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/piggy.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // piggy.ts
2
- import { detectBinary, type BinaryMode } from "./piggy/launch/detect";
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
- // ── Lifecycle ─────────────────────────────────────────────────────────────
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; // number of tabs to pool — enables concurrent requests
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
- // ── Elysia server ─────────────────────────────────────────────────────────
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
  };