nothing-browser 0.0.2 → 0.0.3

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/README.md CHANGED
@@ -7,24 +7,34 @@
7
7
 
8
8
  <p align="center">
9
9
  <a href="https://www.npmjs.com/package/nothing-browser"><img src="https://img.shields.io/npm/v/nothing-browser" alt="npm version"/></a>
10
- <a href="LICENSE"><img src="https://img.shields.io/github/license/ernest-tech-house-co-operation/nothing-browser" alt="license"/></a>
11
- <a href="https://github.com/BunElysiaReact/nothing-browser/releases"><img src="https://img.shields.io/github/v/release/BunElysiaReact/nothing-browser" alt="binary releases"/></a>
10
+ <a href="LICENSE"><img src="https://img.shields.io/github/license/BunElysiaReact/nothing-browser" alt="license"/></a>
11
+ <a href="https://github.com/BunElysiaReact/nothing-browser/releases"><img src="https://img.shields.io/github/v/release/BunElysiaReact/nothing-browser" alt="releases"/></a>
12
12
  </p>
13
13
 
14
14
  ---
15
15
 
16
16
  A scraper-first headless browser library powered by the Nothing Browser Qt6/Chromium engine. Control real browser tabs, intercept network traffic, spoof fingerprints, capture WebSockets — all from Bun + TypeScript.
17
17
 
18
- > **Two repos:**
19
- > - This package (npm lib) → [ernest-tech-house-co-operation/nothing-browser](https://github.com/ernest-tech-house-co-operation/nothing-browser)
20
- > - Headless binary → [BunElysiaReact/nothing-browser](https://github.com/BunElysiaReact/nothing-browser/releases)
21
-
22
18
  ---
23
19
 
24
20
  ## Requirements
25
21
 
26
22
  - [Bun](https://bun.sh) ≥ 1.0
27
- - Nothing Browser headless binary (see below)
23
+ - A Nothing Browser binary placed in your **project root** (see below)
24
+
25
+ ---
26
+
27
+ ## Binaries
28
+
29
+ There are three binaries. All are downloaded from the same place — [GitHub Releases](https://github.com/BunElysiaReact/nothing-browser/releases).
30
+
31
+ | Binary | What it is | Where it goes |
32
+ |--------|-----------|---------------|
33
+ | `nothing-browser` | Full UI browser app — DevTools, YouTube tab, Plugins, etc. | Install system-wide |
34
+ | `nothing-browser-headless` | No window, no GPU. Runs as a background daemon for the scraping lib. | **Your project root** |
35
+ | `nothing-browser-headful` | Visible browser window, script-controlled. Useful when a site needs a real display. | **Your project root** |
36
+
37
+ The `nothingbrowser` npm/Bun lib talks to whichever binary is in your project root over a local socket. You pick headless or headful depending on your use case.
28
38
 
29
39
  ---
30
40
 
@@ -34,17 +44,38 @@ A scraper-first headless browser library powered by the Nothing Browser Qt6/Chro
34
44
  bun add nothing-browser
35
45
  ```
36
46
 
37
- Then download the **headless binary** for your platform from [BunElysiaReact/nothing-browser releases](https://github.com/BunElysiaReact/nothing-browser/releases) and place it in your project root.
47
+ Then download the binary for your platform from [GitHub Releases](https://github.com/BunElysiaReact/nothing-browser/releases) and place it in your project root.
48
+
49
+ ### Linux
38
50
 
39
- **Linux**
51
+ **Headless** (no visible window — most common for scraping)
40
52
  ```bash
41
53
  tar -xzf nothing-browser-headless-*-linux-x86_64.tar.gz
42
54
  chmod +x nothing-browser-headless
43
55
  ```
44
56
 
45
- **Windows** extract the `.zip`, place `nothing-browser-headless.exe` in project root.
57
+ **Headful** (visible window, script-controlled)
58
+ ```bash
59
+ tar -xzf nothing-browser-headful-*-linux-x86_64.tar.gz
60
+ chmod +x nothing-browser-headful
61
+ ```
62
+
63
+ **Full browser** (system-wide install, for using the UI)
64
+ ```bash
65
+ sudo dpkg -i nothing-browser_*_amd64.deb
66
+ # or
67
+ tar -xzf nothing-browser-*-linux-x86_64.tar.gz
68
+ cd nothing-browser-*-linux-x86_64
69
+ ./nothing-browser
70
+ ```
71
+
72
+ ### Windows
73
+
74
+ Download the `.zip` for your chosen binary → extract → place `nothing-browser-headless.exe` or `nothing-browser-headful.exe` in your project root. The JRE is bundled in the full browser zip.
75
+
76
+ ### macOS
46
77
 
47
- **macOS** — extract the `.tar.gz`, place `nothing-browser-headless` in project root.
78
+ Download the `.tar.gz` for your chosen binary → extract → place the binary in your project root.
48
79
 
49
80
  ---
50
81
 
@@ -90,6 +121,24 @@ await piggy.launch({ mode: "process" });
90
121
 
91
122
  ---
92
123
 
124
+ ## Headless vs Headful
125
+
126
+ **Headless** — no display needed, runs anywhere including CI. Use this by default.
127
+
128
+ ```
129
+ nothing-browser-headless ← in your project root
130
+ ```
131
+
132
+ **Headful** — opens a real visible Chromium window that your script drives. Use this when a site detects headless mode or requires a real display (canvas fingerprinting, certain login flows, etc).
133
+
134
+ ```
135
+ nothing-browser-headful ← in your project root
136
+ ```
137
+
138
+ Both binaries expose the exact same socket API. Switching is just swapping which binary is in your project root.
139
+
140
+ ---
141
+
93
142
  ## Examples
94
143
 
95
144
  ### Scrape a site and expose it as an API
@@ -130,8 +179,6 @@ piggy.books.api("/list", async (_params, query) => {
130
179
 
131
180
  piggy.books.noclose();
132
181
  await piggy.serve(3000);
133
- // GET http://localhost:3000/books/list
134
- // GET http://localhost:3000/books/list?page=2
135
182
  ```
136
183
 
137
184
  ---
@@ -170,8 +217,6 @@ piggy.books.api("/search", async (_params, query) => {
170
217
 
171
218
  return { query: query.q, count: books.length, books };
172
219
  }, { ttl: 120_000, before: [logMiddleware, authMiddleware] });
173
-
174
- // curl -H 'x-api-key: piggy-secret' 'http://localhost:3000/books/search?q=light'
175
220
  ```
176
221
 
177
222
  ---
@@ -181,11 +226,11 @@ piggy.books.api("/search", async (_params, query) => {
181
226
  ```ts
182
227
  await piggy.books.capture.clear();
183
228
  await piggy.books.capture.start();
184
- await piggy.books.wait(300); // ensure capture is active before nav
229
+ await piggy.books.wait(300);
185
230
 
186
231
  await piggy.books.navigate("https://books.toscrape.com");
187
232
  await piggy.books.waitForSelector("body", 10000);
188
- await piggy.books.wait(2000); // let async XHR/fetch calls settle
233
+ await piggy.books.wait(2000);
189
234
 
190
235
  await piggy.books.capture.stop();
191
236
 
@@ -206,13 +251,11 @@ import { existsSync, readFileSync, writeFileSync } from "fs";
206
251
 
207
252
  const SESSION_FILE = "./session.json";
208
253
 
209
- // Restore on startup
210
254
  if (existsSync(SESSION_FILE)) {
211
255
  const saved = JSON.parse(readFileSync(SESSION_FILE, "utf8"));
212
256
  await piggy.books.session.import(saved);
213
257
  }
214
258
 
215
- // Save on shutdown — always BEFORE piggy.close()
216
259
  process.on("SIGINT", async () => {
217
260
  const session = await piggy.books.session.export();
218
261
  writeFileSync(SESSION_FILE, JSON.stringify(session, null, 2));
@@ -233,8 +276,6 @@ await piggy.books.type("#search", "mystery novels");
233
276
  await piggy.books.scroll.by(400);
234
277
  ```
235
278
 
236
- Affects: `click`, `type`, `hover`, `scroll.by`, `wait`.
237
-
238
279
  ---
239
280
 
240
281
  ### Screenshot / PDF
@@ -243,7 +284,6 @@ Affects: `click`, `type`, `hover`, `scroll.by`, `wait`.
243
284
  await piggy.books.screenshot("./out/page.png");
244
285
  await piggy.books.pdf("./out/page.pdf");
245
286
 
246
- // or base64
247
287
  const b64 = await piggy.books.screenshot();
248
288
  ```
249
289
 
@@ -256,8 +296,7 @@ await piggy.register("site1", "https://example.com");
256
296
  await piggy.register("site2", "https://example.org");
257
297
 
258
298
  const titles = await piggy.all([piggy.site1, piggy.site2]).title();
259
-
260
- const h1s = await piggy.diff([piggy.site1, piggy.site2]).fetchText("h1");
299
+ const h1s = await piggy.diff([piggy.site1, piggy.site2]).fetchText("h1");
261
300
  // → { site1: "...", site2: "..." }
262
301
  ```
263
302
 
@@ -272,7 +311,7 @@ const h1s = await piggy.diff([piggy.site1, piggy.site2]).fetchText("h1");
272
311
  | `mode` | `"tab" \| "process"` | `"tab"` |
273
312
 
274
313
  ### `piggy.register(name, url)`
275
- Registers a site. Accessible as `piggy.<n>` after registration.
314
+ Registers a site. Accessible as `piggy.<name>` after registration.
276
315
 
277
316
  ### `piggy.actHuman(enable)`
278
317
  Toggles human-like interaction timing globally.
@@ -357,17 +396,17 @@ site.screenshot(filePath?) / site.pdf(filePath?)
357
396
 
358
397
  ## Binary download
359
398
 
360
- | Platform | File |
361
- |----------|------|
362
- | Linux x86_64 (deb) | `nothing-browser-headless_*_amd64.deb` |
363
- | Linux x86_64 (tar.gz) | `nothing-browser-headless-*-linux-x86_64.tar.gz` |
364
- | Windows x64 | `nothing-browser-headless-*.zip` |
365
- | macOS | `nothing-browser-headless-*.tar.gz` |
399
+ | Platform | Headless | Headful | Full Browser |
400
+ |----------|----------|---------|--------------|
401
+ | Linux x86_64 (deb) | `nothing-browser-headless_*_amd64.deb` | `nothing-browser-headful_*_amd64.deb` | `nothing-browser_*_amd64.deb` |
402
+ | Linux x86_64 (tar.gz) | `nothing-browser-headless-*-linux-x86_64.tar.gz` | `nothing-browser-headful-*-linux-x86_64.tar.gz` | `nothing-browser-*-linux-x86_64.tar.gz` |
403
+ | Windows x64 | `nothing-browser-headless-*-windows-x64.zip` | `nothing-browser-headful-*-windows-x64.zip` | `nothing-browser-*-windows-x64.zip` |
404
+ | macOS | `nothing-browser-headless-*-macos.tar.gz` | `nothing-browser-headful-*-macos.tar.gz` | `nothing-browser-*-macos.dmg` |
366
405
 
367
- → [BunElysiaReact/nothing-browser releases](https://github.com/BunElysiaReact/nothing-browser/releases)
406
+ → [All releases](https://github.com/BunElysiaReact/nothing-browser/releases)
368
407
 
369
408
  ---
370
409
 
371
410
  ## License
372
411
 
373
- MIT © [Ernest Tech House](https://github.com/ernest-tech-house-co-operation)
412
+ MIT © [Ernest Tech House](https://github.com/BunElysiaReact/nothing-browser)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothing-browser",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "description": "Scraper-first headless browser library — control real tabs, intercept network traffic, capture WebSockets, spoof fingerprints. Powered by Qt6/Chromium.",
5
5
  "module": "piggy.ts",
6
6
  "main": "piggy.ts",
@@ -2,32 +2,42 @@ import { existsSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import logger from '../logger';
4
4
 
5
- export function detectBinary(): string | null {
6
- const cwd = process.cwd();
7
-
8
- // Windows
9
- const windowsPath = join(cwd, 'nothing-browser-headless.exe');
10
- if (process.platform === 'win32' && existsSync(windowsPath)) {
11
- logger.success(`Binary found! Platform: Windows`);
12
- return windowsPath;
13
- }
14
-
15
- // Linux / macOS
16
- const unixPath = join(cwd, 'nothing-browser-headless');
17
- if (existsSync(unixPath)) {
18
- logger.success(`Binary found at: ${unixPath}`);
19
- return unixPath;
20
- }
21
-
22
- logger.error("❌ Binary not found in project root");
23
- logger.error("");
24
- logger.error("Download from:");
25
- logger.error(" https://github.com/BunElysiaReact/nothing-browser/releases/");
26
- logger.error("");
27
- logger.error(`Place in: ${cwd}/nothing-browser-headless${process.platform === 'win32' ? '.exe' : ''}`);
28
- if (process.platform !== 'win32') {
29
- logger.error("Then run: chmod +x nothing-browser-headless");
5
+ export type BinaryMode = 'headless' | 'headful';
6
+
7
+ const BINARY_NAMES: Record<BinaryMode, string> = {
8
+ headless: 'nothing-browser-headless',
9
+ headful: 'nothing-browser-headful',
10
+ };
11
+
12
+ export function detectBinary(mode: BinaryMode = 'headless'): string | null {
13
+ const cwd = process.cwd();
14
+ const name = BINARY_NAMES[mode];
15
+
16
+ // Windows
17
+ if (process.platform === 'win32') {
18
+ const p = join(cwd, `${name}.exe`);
19
+ if (existsSync(p)) {
20
+ logger.success(`Binary found (${mode}): ${p}`);
21
+ return p;
30
22
  }
31
-
32
- return null;
23
+ }
24
+
25
+ // Linux / macOS
26
+ const p = join(cwd, name);
27
+ if (existsSync(p)) {
28
+ logger.success(`Binary found (${mode}): ${p}`);
29
+ return p;
30
+ }
31
+
32
+ logger.error(`❌ Binary not found in project root: ${name}`);
33
+ logger.error('');
34
+ logger.error('Download from:');
35
+ logger.error(' https://github.com/BunElysiaReact/nothing-browser/releases/');
36
+ logger.error('');
37
+ logger.error(`Place in: ${cwd}/${name}${process.platform === 'win32' ? '.exe' : ''}`);
38
+ if (process.platform !== 'win32') {
39
+ logger.error(`Then run: chmod +x ${name}`);
40
+ }
41
+
42
+ return null;
33
43
  }
@@ -1,101 +1,99 @@
1
1
  import { spawn } from 'bun';
2
2
  import { execSync } from 'child_process';
3
- import { detectBinary } from './detect';
3
+ import { detectBinary, type BinaryMode } from './detect';
4
4
  import logger from '../logger';
5
5
 
6
6
  let activeProcess: any = null;
7
7
  const extraProcesses: any[] = [];
8
8
 
9
9
  export function killAllBrowsers(): void {
10
- try {
11
- logger.info("Cleaning up existing browser processes...");
12
- execSync('pkill -f nothing-browser-headless 2>/dev/null || true', { stdio: 'ignore' });
13
- execSync('pkill -f QtWebEngineProcess 2>/dev/null || true', { stdio: 'ignore' });
14
- execSync('rm -f /tmp/piggy', { stdio: 'ignore' });
15
- } catch {
16
- // Ignore errors - no processes to kill
17
- }
10
+ try {
11
+ logger.info('Cleaning up existing browser processes...');
12
+ execSync('pkill -f nothing-browser-headless 2>/dev/null || true', { stdio: 'ignore' });
13
+ execSync('pkill -f nothing-browser-headful 2>/dev/null || true', { stdio: 'ignore' });
14
+ execSync('pkill -f QtWebEngineProcess 2>/dev/null || true', { stdio: 'ignore' });
15
+ execSync('rm -f /tmp/piggy', { stdio: 'ignore' });
16
+ } catch {
17
+ // no processes to kill
18
+ }
18
19
  }
19
20
 
20
- export async function spawnBrowser(): Promise<string> {
21
- killAllBrowsers();
22
-
23
- // Give OS time to release the socket
24
- await new Promise(resolve => setTimeout(resolve, 500));
25
-
26
- const binaryPath = detectBinary();
27
- if (!binaryPath) {
28
- throw new Error("Binary not found. Cannot launch.");
29
- }
30
-
31
- logger.info(`Spawning Nothing Browser from: ${binaryPath}`);
32
-
33
- activeProcess = spawn([binaryPath], {
34
- stdio: ['ignore', 'pipe', 'pipe'],
35
- env: process.env
36
- });
37
-
38
- if (activeProcess.stdout) {
39
- const reader = activeProcess.stdout.getReader();
40
- const read = async () => {
41
- const { done, value } = await reader.read();
42
- if (!done) {
43
- const output = new TextDecoder().decode(value);
44
- logger.debug(`[Browser] ${output}`);
45
- read();
46
- }
47
- };
21
+ export async function spawnBrowser(mode: BinaryMode = 'headless'): Promise<string> {
22
+ killAllBrowsers();
23
+ await new Promise(resolve => setTimeout(resolve, 500));
24
+
25
+ const binaryPath = detectBinary(mode);
26
+ if (!binaryPath) {
27
+ throw new Error(`Binary not found (${mode}). Cannot launch.`);
28
+ }
29
+
30
+ logger.info(`Spawning Nothing Browser (${mode}) from: ${binaryPath}`);
31
+
32
+ activeProcess = spawn([binaryPath], {
33
+ stdio: ['ignore', 'pipe', 'pipe'],
34
+ env: process.env,
35
+ });
36
+
37
+ if (activeProcess.stdout) {
38
+ const reader = activeProcess.stdout.getReader();
39
+ const read = async () => {
40
+ const { done, value } = await reader.read();
41
+ if (!done) {
42
+ logger.debug(`[Browser] ${new TextDecoder().decode(value)}`);
48
43
  read();
49
- }
44
+ }
45
+ };
46
+ read();
47
+ }
50
48
 
51
- activeProcess.exited.then((code: number | null) => {
52
- logger.warn(`Browser process exited with code: ${code}`);
53
- activeProcess = null;
54
- });
49
+ activeProcess.exited.then((code: number | null) => {
50
+ logger.warn(`Browser process exited with code: ${code}`);
51
+ activeProcess = null;
52
+ });
55
53
 
56
- await new Promise(resolve => setTimeout(resolve, 2000));
54
+ await new Promise(resolve => setTimeout(resolve, 2000));
57
55
 
58
- if (activeProcess) {
59
- logger.success("Browser spawned and running");
60
- } else {
61
- logger.error("Browser started but exited immediately");
62
- }
56
+ if (activeProcess) {
57
+ logger.success(`Browser spawned and running (${mode})`);
58
+ } else {
59
+ logger.error('Browser started but exited immediately');
60
+ }
63
61
 
64
- return binaryPath;
62
+ return binaryPath;
65
63
  }
66
64
 
67
- export async function spawnBrowserOnSocket(socketName: string): Promise<void> {
68
- const binaryPath = detectBinary();
69
- if (!binaryPath) {
70
- throw new Error("Binary not found. Cannot launch.");
71
- }
65
+ export async function spawnBrowserOnSocket(
66
+ socketName: string,
67
+ mode: BinaryMode = 'headless'
68
+ ): Promise<void> {
69
+ const binaryPath = detectBinary(mode);
70
+ if (!binaryPath) {
71
+ throw new Error(`Binary not found (${mode}). Cannot launch.`);
72
+ }
72
73
 
73
- logger.info(`Spawning browser on socket: ${socketName}`);
74
+ logger.info(`Spawning browser (${mode}) on socket: ${socketName}`);
74
75
 
75
- const proc = spawn([binaryPath], {
76
- stdio: ['ignore', 'pipe', 'pipe'],
77
- env: { ...process.env, PIGGY_SOCKET: socketName }
78
- });
76
+ const proc = spawn([binaryPath], {
77
+ stdio: ['ignore', 'pipe', 'pipe'],
78
+ env: { ...process.env, PIGGY_SOCKET: socketName },
79
+ });
79
80
 
80
- extraProcesses.push(proc);
81
+ extraProcesses.push(proc);
81
82
 
82
- proc.exited.then((code: number | null) => {
83
- logger.warn(`Browser on socket ${socketName} exited with code: ${code}`);
84
- });
83
+ proc.exited.then((code: number | null) => {
84
+ logger.warn(`Browser on socket ${socketName} exited with code: ${code}`);
85
+ });
85
86
 
86
- await new Promise(resolve => setTimeout(resolve, 1000));
87
- logger.success(`Browser spawned on socket: ${socketName}`);
87
+ await new Promise(resolve => setTimeout(resolve, 1000));
88
+ logger.success(`Browser spawned (${mode}) on socket: ${socketName}`);
88
89
  }
89
90
 
90
91
  export function killBrowser(): void {
91
- if (activeProcess) {
92
- logger.info("Killing browser process...");
93
- activeProcess.kill();
94
- activeProcess = null;
95
- }
96
-
97
- for (const proc of extraProcesses) {
98
- proc.kill();
99
- }
100
- extraProcesses.length = 0;
92
+ if (activeProcess) {
93
+ logger.info('Killing browser process...');
94
+ activeProcess.kill();
95
+ activeProcess = null;
96
+ }
97
+ for (const proc of extraProcesses) proc.kill();
98
+ extraProcesses.length = 0;
101
99
  }
package/piggy.ts CHANGED
@@ -1,45 +1,47 @@
1
1
  // piggy.ts
2
- import { detectBinary } from "./piggy/launch/detect";
2
+ import { detectBinary, type BinaryMode } 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 } from "./piggy/register";
6
6
  import { routeRegistry, keepAliveSites, startServer, stopServer } from "./piggy/server";
7
7
  import logger from "./piggy/logger";
8
8
 
9
- type BrowserMode = "tab" | "process";
9
+ type TabMode = "tab" | "process";
10
10
  type SiteObject = ReturnType<typeof createSiteObject>;
11
11
 
12
12
  let _client: PiggyClient | null = null;
13
- let _mode: BrowserMode = "tab";
13
+ let _tabMode: TabMode = "tab";
14
14
  const _extraProcs: { socket: string; client: PiggyClient }[] = [];
15
15
  const _sites: Record<string, SiteObject> = {};
16
16
 
17
17
  const piggy: any = {
18
18
  // ── Lifecycle ───────────────────────────────────────────────────────────────
19
19
 
20
- launch: async (opts?: { mode?: BrowserMode }) => {
21
- _mode = opts?.mode ?? "tab";
22
- await spawnBrowser();
20
+ launch: async (opts?: { mode?: TabMode; binary?: BinaryMode }) => {
21
+ _tabMode = opts?.mode ?? "tab";
22
+ const binaryMode: BinaryMode = opts?.binary ?? "headless";
23
+ await spawnBrowser(binaryMode);
23
24
  await new Promise(r => setTimeout(r, 500));
24
25
  _client = new PiggyClient();
25
26
  await _client.connect();
26
27
  setClient(_client);
27
- logger.info(`[piggy] launched in "${_mode}" mode`);
28
+ logger.info(`[piggy] launched tab mode: "${_tabMode}", binary: "${binaryMode}"`);
28
29
  return piggy;
29
30
  },
30
31
 
31
- register: async (name: string, url: string, opts?: any) => {
32
+ register: async (name: string, url: string, opts?: { binary?: BinaryMode }) => {
32
33
  if (!url?.trim()) throw new Error(`No URL for site "${name}"`);
34
+ const binaryMode: BinaryMode = opts?.binary ?? "headless";
33
35
 
34
36
  let tabId = "default";
35
- if (_mode === "tab") {
37
+ if (_tabMode === "tab") {
36
38
  tabId = await _client!.newTab();
37
39
  _sites[name] = createSiteObject(name, url, _client!, tabId);
38
40
  piggy[name] = _sites[name];
39
41
  logger.success(`[${name}] registered as tab ${tabId}`);
40
42
  } else {
41
43
  const socketName = `piggy_${name}`;
42
- await spawnBrowserOnSocket(socketName);
44
+ await spawnBrowserOnSocket(socketName, binaryMode);
43
45
  await new Promise(r => setTimeout(r, 500));
44
46
  const c = new PiggyClient(socketName);
45
47
  await c.connect();
@@ -49,7 +51,6 @@ const piggy: any = {
49
51
  logger.success(`[${name}] registered as process on "${socketName}"`);
50
52
  }
51
53
 
52
- if (opts?.mode) logger.info(`[${name}] mode: ${opts.mode}`);
53
54
  return piggy;
54
55
  },
55
56
 
@@ -61,7 +62,7 @@ const piggy: any = {
61
62
  return piggy;
62
63
  },
63
64
 
64
- mode: (m: BrowserMode) => { _mode = m; return piggy; },
65
+ mode: (m: TabMode) => { _tabMode = m; return piggy; },
65
66
 
66
67
  // ── Elysia server ────────────────────────────────────────────────────────────
67
68