nothing-browser 0.0.19 → 0.0.20

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.
@@ -1,165 +1,165 @@
1
- // piggy/launch/spawn.ts
2
- import { spawn as nodeSpawn } from "child_process";
3
- import { execSync } from "child_process";
4
- import { platform } from "os";
5
- import { detectBinary, type BinaryMode } from "./detect";
6
- import logger from "../logger";
7
-
8
- let activeProcess: any = null;
9
- const extraProcesses: any[] = [];
10
-
11
- // Runtime detection without importing "bun" at build time
12
- const isBun = typeof (globalThis as any).Bun !== 'undefined';
13
-
14
- export function killAllBrowsers(): void {
15
- try {
16
- logger.info('Cleaning up existing browser processes...');
17
-
18
- if (platform() === 'win32') {
19
- execSync('taskkill /F /IM nothing-browser-headless.exe 2>nul || true', { stdio: 'ignore' });
20
- execSync('taskkill /F /IM nothing-browser-headful.exe 2>nul || true', { stdio: 'ignore' });
21
- execSync('taskkill /F /IM QtWebEngineProcess.exe 2>nul || true', { stdio: 'ignore' });
22
- } else {
23
- execSync('pkill -f nothing-browser-headless 2>/dev/null || true', { stdio: 'ignore' });
24
- execSync('pkill -f nothing-browser-headful 2>/dev/null || true', { stdio: 'ignore' });
25
- execSync('pkill -f QtWebEngineProcess 2>/dev/null || true', { stdio: 'ignore' });
26
- execSync('rm -f /tmp/piggy', { stdio: 'ignore' });
27
- }
28
- } catch {
29
- // no processes to kill
30
- }
31
- }
32
-
33
- export async function spawnBrowser(mode: BinaryMode = 'headless'): Promise<string> {
34
- killAllBrowsers();
35
- await new Promise(resolve => setTimeout(resolve, 500));
36
-
37
- const binaryPath = detectBinary(mode);
38
- if (!binaryPath) {
39
- throw new Error(`Binary not found (${mode}). Cannot launch.`);
40
- }
41
-
42
- logger.info(`Spawning Nothing Browser (${mode}) from: ${binaryPath}`);
43
-
44
- if (isBun) {
45
- // Bun runtime - use Bun.spawn
46
- const Bun = (globalThis as any).Bun;
47
- activeProcess = Bun.spawn([binaryPath], {
48
- stdio: ['ignore', 'pipe', 'pipe'],
49
- env: process.env,
50
- });
51
-
52
- if (activeProcess.stdout) {
53
- const reader = activeProcess.stdout.getReader();
54
- const read = async () => {
55
- const { done, value } = await reader.read();
56
- if (!done) {
57
- logger.debug(`[Browser] ${new TextDecoder().decode(value)}`);
58
- read();
59
- }
60
- };
61
- read();
62
- }
63
-
64
- activeProcess.exited.then((code: number | null) => {
65
- logger.warn(`Browser process exited with code: ${code}`);
66
- activeProcess = null;
67
- });
68
- } else {
69
- // Node.js runtime
70
- activeProcess = nodeSpawn(binaryPath, [], {
71
- stdio: ['ignore', 'pipe', 'pipe'],
72
- env: process.env,
73
- });
74
-
75
- if (activeProcess.stdout) {
76
- activeProcess.stdout.on('data', (data: Buffer) => {
77
- logger.debug(`[Browser] ${data.toString()}`);
78
- });
79
- }
80
-
81
- if (activeProcess.stderr) {
82
- activeProcess.stderr.on('data', (data: Buffer) => {
83
- logger.debug(`[Browser Error] ${data.toString()}`);
84
- });
85
- }
86
-
87
- activeProcess.on('exit', (code: number | null) => {
88
- logger.warn(`Browser process exited with code: ${code}`);
89
- activeProcess = null;
90
- });
91
- }
92
-
93
- await new Promise(resolve => setTimeout(resolve, 2000));
94
-
95
- if (activeProcess) {
96
- logger.success(`Browser spawned and running (${mode})`);
97
- } else {
98
- logger.error('Browser started but exited immediately');
99
- }
100
-
101
- return binaryPath;
102
- }
103
-
104
- export async function spawnBrowserOnSocket(
105
- socketName: string,
106
- mode: BinaryMode = 'headless'
107
- ): Promise<void> {
108
- const binaryPath = detectBinary(mode);
109
- if (!binaryPath) {
110
- throw new Error(`Binary not found (${mode}). Cannot launch.`);
111
- }
112
-
113
- logger.info(`Spawning browser (${mode}) on socket: ${socketName}`);
114
-
115
- if (isBun) {
116
- // Bun runtime
117
- const Bun = (globalThis as any).Bun;
118
- const proc = Bun.spawn([binaryPath], {
119
- stdio: ['ignore', 'pipe', 'pipe'],
120
- env: { ...process.env, PIGGY_SOCKET: socketName },
121
- });
122
-
123
- extraProcesses.push(proc);
124
-
125
- proc.exited.then((code: number | null) => {
126
- logger.warn(`Browser on socket ${socketName} exited with code: ${code}`);
127
- });
128
- } else {
129
- // Node.js runtime
130
- const proc = nodeSpawn(binaryPath, [], {
131
- stdio: ['ignore', 'pipe', 'pipe'],
132
- env: { ...process.env, PIGGY_SOCKET: socketName },
133
- });
134
-
135
- extraProcesses.push(proc);
136
-
137
- proc.on('exit', (code: number | null) => {
138
- logger.warn(`Browser on socket ${socketName} exited with code: ${code}`);
139
- });
140
- }
141
-
142
- await new Promise(resolve => setTimeout(resolve, 1000));
143
- logger.success(`Browser spawned (${mode}) on socket: ${socketName}`);
144
- }
145
-
146
- export function killBrowser(): void {
147
- if (activeProcess) {
148
- logger.info('Killing browser process...');
149
- if (isBun) {
150
- activeProcess.kill();
151
- } else {
152
- activeProcess.kill('SIGTERM');
153
- }
154
- activeProcess = null;
155
- }
156
-
157
- for (const proc of extraProcesses) {
158
- if (isBun) {
159
- proc.kill();
160
- } else {
161
- proc.kill('SIGTERM');
162
- }
163
- }
164
- extraProcesses.length = 0;
1
+ // piggy/launch/spawn.ts
2
+ import { spawn as nodeSpawn } from "child_process";
3
+ import { execSync } from "child_process";
4
+ import { platform } from "os";
5
+ import { detectBinary, type BinaryMode } from "./detect";
6
+ import logger from "../logger";
7
+
8
+ let activeProcess: any = null;
9
+ const extraProcesses: any[] = [];
10
+
11
+ // Runtime detection without importing "bun" at build time
12
+ const isBun = typeof (globalThis as any).Bun !== 'undefined';
13
+
14
+ export function killAllBrowsers(): void {
15
+ try {
16
+ logger.info('Cleaning up existing browser processes...');
17
+
18
+ if (platform() === 'win32') {
19
+ execSync('taskkill /F /IM nothing-browser-headless.exe 2>nul || true', { stdio: 'ignore' });
20
+ execSync('taskkill /F /IM nothing-browser-headful.exe 2>nul || true', { stdio: 'ignore' });
21
+ execSync('taskkill /F /IM QtWebEngineProcess.exe 2>nul || true', { stdio: 'ignore' });
22
+ } else {
23
+ execSync('pkill -f nothing-browser-headless 2>/dev/null || true', { stdio: 'ignore' });
24
+ execSync('pkill -f nothing-browser-headful 2>/dev/null || true', { stdio: 'ignore' });
25
+ execSync('pkill -f QtWebEngineProcess 2>/dev/null || true', { stdio: 'ignore' });
26
+ execSync('rm -f /tmp/piggy', { stdio: 'ignore' });
27
+ }
28
+ } catch {
29
+ // no processes to kill
30
+ }
31
+ }
32
+
33
+ export async function spawnBrowser(mode: BinaryMode = 'headless'): Promise<string> {
34
+ killAllBrowsers();
35
+ await new Promise(resolve => setTimeout(resolve, 500));
36
+
37
+ const binaryPath = detectBinary(mode);
38
+ if (!binaryPath) {
39
+ throw new Error(`Binary not found (${mode}). Cannot launch.`);
40
+ }
41
+
42
+ logger.info(`Spawning Nothing Browser (${mode}) from: ${binaryPath}`);
43
+
44
+ if (isBun) {
45
+ // Bun runtime - use Bun.spawn
46
+ const Bun = (globalThis as any).Bun;
47
+ activeProcess = Bun.spawn([binaryPath], {
48
+ stdio: ['ignore', 'pipe', 'pipe'],
49
+ env: process.env,
50
+ });
51
+
52
+ if (activeProcess.stdout) {
53
+ const reader = activeProcess.stdout.getReader();
54
+ const read = async () => {
55
+ const { done, value } = await reader.read();
56
+ if (!done) {
57
+ logger.debug(`[Browser] ${new TextDecoder().decode(value)}`);
58
+ read();
59
+ }
60
+ };
61
+ read();
62
+ }
63
+
64
+ activeProcess.exited.then((code: number | null) => {
65
+ logger.warn(`Browser process exited with code: ${code}`);
66
+ activeProcess = null;
67
+ });
68
+ } else {
69
+ // Node.js runtime
70
+ activeProcess = nodeSpawn(binaryPath, [], {
71
+ stdio: ['ignore', 'pipe', 'pipe'],
72
+ env: process.env,
73
+ });
74
+
75
+ if (activeProcess.stdout) {
76
+ activeProcess.stdout.on('data', (data: Buffer) => {
77
+ logger.debug(`[Browser] ${data.toString()}`);
78
+ });
79
+ }
80
+
81
+ if (activeProcess.stderr) {
82
+ activeProcess.stderr.on('data', (data: Buffer) => {
83
+ logger.debug(`[Browser Error] ${data.toString()}`);
84
+ });
85
+ }
86
+
87
+ activeProcess.on('exit', (code: number | null) => {
88
+ logger.warn(`Browser process exited with code: ${code}`);
89
+ activeProcess = null;
90
+ });
91
+ }
92
+
93
+ await new Promise(resolve => setTimeout(resolve, 2000));
94
+
95
+ if (activeProcess) {
96
+ logger.success(`Browser spawned and running (${mode})`);
97
+ } else {
98
+ logger.error('Browser started but exited immediately');
99
+ }
100
+
101
+ return binaryPath;
102
+ }
103
+
104
+ export async function spawnBrowserOnSocket(
105
+ socketName: string,
106
+ mode: BinaryMode = 'headless'
107
+ ): Promise<void> {
108
+ const binaryPath = detectBinary(mode);
109
+ if (!binaryPath) {
110
+ throw new Error(`Binary not found (${mode}). Cannot launch.`);
111
+ }
112
+
113
+ logger.info(`Spawning browser (${mode}) on socket: ${socketName}`);
114
+
115
+ if (isBun) {
116
+ // Bun runtime
117
+ const Bun = (globalThis as any).Bun;
118
+ const proc = Bun.spawn([binaryPath], {
119
+ stdio: ['ignore', 'pipe', 'pipe'],
120
+ env: { ...process.env, PIGGY_SOCKET: socketName },
121
+ });
122
+
123
+ extraProcesses.push(proc);
124
+
125
+ proc.exited.then((code: number | null) => {
126
+ logger.warn(`Browser on socket ${socketName} exited with code: ${code}`);
127
+ });
128
+ } else {
129
+ // Node.js runtime
130
+ const proc = nodeSpawn(binaryPath, [], {
131
+ stdio: ['ignore', 'pipe', 'pipe'],
132
+ env: { ...process.env, PIGGY_SOCKET: socketName },
133
+ });
134
+
135
+ extraProcesses.push(proc);
136
+
137
+ proc.on('exit', (code: number | null) => {
138
+ logger.warn(`Browser on socket ${socketName} exited with code: ${code}`);
139
+ });
140
+ }
141
+
142
+ await new Promise(resolve => setTimeout(resolve, 1000));
143
+ logger.success(`Browser spawned (${mode}) on socket: ${socketName}`);
144
+ }
145
+
146
+ export function killBrowser(): void {
147
+ if (activeProcess) {
148
+ logger.info('Killing browser process...');
149
+ if (isBun) {
150
+ activeProcess.kill();
151
+ } else {
152
+ activeProcess.kill('SIGTERM');
153
+ }
154
+ activeProcess = null;
155
+ }
156
+
157
+ for (const proc of extraProcesses) {
158
+ if (isBun) {
159
+ proc.kill();
160
+ } else {
161
+ proc.kill('SIGTERM');
162
+ }
163
+ }
164
+ extraProcesses.length = 0;
165
165
  }
@@ -1,3 +1,3 @@
1
- declare const logger: import("ernest-logger").Logger;
2
- export default logger;
1
+ declare const logger: import("ernest-logger").Logger;
2
+ export default logger;
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,59 +1,59 @@
1
-
2
- //piggy/logger/index.ts
3
- import { createLogger } from "ernest-logger";
4
-
5
- const logger = createLogger({
6
- time: true,
7
- file: true,
8
- filePath: './piggy.log',
9
- prefix: '[PIGGY]',
10
- emoji: true,
11
- level: 'trace',
12
- customLevels: {
13
- info: {
14
- color: 'blue',
15
- emoji: 'ℹ️',
16
- priority: 2
17
- },
18
- success: {
19
- color: 'green',
20
- emoji: '✅',
21
- priority: 2
22
- },
23
- error: {
24
- color: 'red',
25
- emoji: '❌',
26
- priority: 5
27
- },
28
- warn: {
29
- color: 'yellow',
30
- emoji: '⚠️',
31
- priority: 4
32
- },
33
- debug: {
34
- color: 'magenta',
35
- emoji: '🐞',
36
- priority: 1
37
- },
38
- network: {
39
- color: 'brightCyan',
40
- emoji: '🌐',
41
- priority: 2
42
- },
43
- db: {
44
- color: 'brightMagenta',
45
- emoji: '🗄️',
46
- priority: 2
47
- },
48
- security: {
49
- color: 'brightRed',
50
- emoji: '🔒',
51
- priority: 3
52
- }
53
- }
54
- });
55
-
56
- // Now these methods exist with your custom colors
57
- logger.info("logger initialized"); // Blue with ℹ️
58
-
1
+
2
+ //piggy/logger/index.ts
3
+ import { createLogger } from "ernest-logger";
4
+
5
+ const logger = createLogger({
6
+ time: true,
7
+ file: true,
8
+ filePath: './piggy.log',
9
+ prefix: '[PIGGY]',
10
+ emoji: true,
11
+ level: 'trace',
12
+ customLevels: {
13
+ info: {
14
+ color: 'blue',
15
+ emoji: 'ℹ️',
16
+ priority: 2
17
+ },
18
+ success: {
19
+ color: 'green',
20
+ emoji: '✅',
21
+ priority: 2
22
+ },
23
+ error: {
24
+ color: 'red',
25
+ emoji: '❌',
26
+ priority: 5
27
+ },
28
+ warn: {
29
+ color: 'yellow',
30
+ emoji: '⚠️',
31
+ priority: 4
32
+ },
33
+ debug: {
34
+ color: 'magenta',
35
+ emoji: '🐞',
36
+ priority: 1
37
+ },
38
+ network: {
39
+ color: 'brightCyan',
40
+ emoji: '🌐',
41
+ priority: 2
42
+ },
43
+ db: {
44
+ color: 'brightMagenta',
45
+ emoji: '🗄️',
46
+ priority: 2
47
+ },
48
+ security: {
49
+ color: 'brightRed',
50
+ emoji: '🔒',
51
+ priority: 3
52
+ }
53
+ }
54
+ });
55
+
56
+ // Now these methods exist with your custom colors
57
+ logger.info("logger initialized"); // Blue with ℹ️
58
+
59
59
  export default logger;
@@ -1,4 +1,4 @@
1
- export default function open(): {
2
- name: string;
3
- };
1
+ export default function open(): {
2
+ name: string;
3
+ };
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
- export default function open() {
2
- return {
3
- name: 'piggy-open',
4
- };
1
+ export default function open() {
2
+ return {
3
+ name: 'piggy-open',
4
+ };
5
5
  }
@@ -1,12 +1,12 @@
1
- // piggy/pool/index.d.ts
2
- import { PiggyClient } from "../client";
3
-
4
- export declare class TabPool {
5
- constructor(client: PiggyClient, size: number, seedUrl: string, name: string);
6
- init(): Promise<void>;
7
- acquire(): Promise<string>;
8
- release(tabId: string): void;
9
- withTab<T>(fn: (tabId: string) => Promise<T>): Promise<T>;
10
- close(): Promise<void>;
11
- get stats(): { idle: number; busy: number; queued: number; total: number };
1
+ // piggy/pool/index.d.ts
2
+ import { PiggyClient } from "../client";
3
+
4
+ export declare class TabPool {
5
+ constructor(client: PiggyClient, size: number, seedUrl: string, name: string);
6
+ init(): Promise<void>;
7
+ acquire(): Promise<string>;
8
+ release(tabId: string): void;
9
+ withTab<T>(fn: (tabId: string) => Promise<T>): Promise<T>;
10
+ close(): Promise<void>;
11
+ get stats(): { idle: number; busy: number; queued: number; total: number };
12
12
  }
@@ -1,75 +1,75 @@
1
- // piggy/pool/index.ts
2
- import { PiggyClient } from "../client";
3
- import logger from "../logger";
4
-
5
- export class TabPool {
6
- private idle: string[] = [];
7
- private busy = new Set<string>();
8
- private queue: ((tabId: string) => void)[] = [];
9
-
10
- constructor(
11
- private client: PiggyClient,
12
- private size: number,
13
- private seedUrl: string,
14
- private name: string
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(): Promise<string> {
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: string) {
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<T>(fn: (tabId: string) => Promise<T>): Promise<T> {
50
- const tabId = await this.acquire();
51
- try {
52
- return await fn(tabId);
53
- } finally {
54
- this.release(tabId);
55
- }
56
- }
57
-
58
- async close() {
59
- for (const tabId of [...this.idle, ...this.busy]) {
60
- try { await this.client.closeTab(tabId); } catch {}
61
- }
62
- this.idle = [];
63
- this.busy.clear();
64
- this.queue = [];
65
- }
66
-
67
- get stats() {
68
- return {
69
- idle: this.idle.length,
70
- busy: this.busy.size,
71
- queued: this.queue.length,
72
- total: this.size,
73
- };
74
- }
1
+ // piggy/pool/index.ts
2
+ import { PiggyClient } from "../client";
3
+ import logger from "../logger";
4
+
5
+ export class TabPool {
6
+ private idle: string[] = [];
7
+ private busy = new Set<string>();
8
+ private queue: ((tabId: string) => void)[] = [];
9
+
10
+ constructor(
11
+ private client: PiggyClient,
12
+ private size: number,
13
+ private seedUrl: string,
14
+ private name: string
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(): Promise<string> {
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: string) {
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<T>(fn: (tabId: string) => Promise<T>): Promise<T> {
50
+ const tabId = await this.acquire();
51
+ try {
52
+ return await fn(tabId);
53
+ } finally {
54
+ this.release(tabId);
55
+ }
56
+ }
57
+
58
+ async close() {
59
+ for (const tabId of [...this.idle, ...this.busy]) {
60
+ try { await this.client.closeTab(tabId); } catch {}
61
+ }
62
+ this.idle = [];
63
+ this.busy.clear();
64
+ this.queue = [];
65
+ }
66
+
67
+ get stats() {
68
+ return {
69
+ idle: this.idle.length,
70
+ busy: this.busy.size,
71
+ queued: this.queue.length,
72
+ total: this.size,
73
+ };
74
+ }
75
75
  }
@@ -1,7 +1,7 @@
1
- import { PiggyClient } from "../client";
2
- export declare let humanMode: boolean;
3
- export declare function setClient(c: PiggyClient | null): void;
4
- export declare function setHumanMode(v: boolean): void;
5
- export declare function createSiteObject(name: string, registeredUrl: string, client: PiggyClient, tabId: string): any;
6
- export declare function createExposedAPI<T extends Record<string, (data: any) => any>>(site: any, apiName: string, handlers: T): Promise<void>;
1
+ import { PiggyClient } from "../client";
2
+ export declare let humanMode: boolean;
3
+ export declare function setClient(c: PiggyClient | null): void;
4
+ export declare function setHumanMode(v: boolean): void;
5
+ export declare function createSiteObject(name: string, registeredUrl: string, client: PiggyClient, tabId: string): any;
6
+ export declare function createExposedAPI<T extends Record<string, (data: any) => any>>(site: any, apiName: string, handlers: T): Promise<void>;
7
7
  //# sourceMappingURL=index.d.ts.map