@push.rocks/smartpuppeteer 2.2.0 → 2.3.0

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.
@@ -0,0 +1,14 @@
1
+ export interface IOwnedProcessIdentity {
2
+ pid: number;
3
+ parentPid: number;
4
+ processGroupId: number;
5
+ sessionId: number;
6
+ startTime: string;
7
+ state: string;
8
+ }
9
+ export declare const readOwnedProcessIdentity: (pid: number) => Promise<IOwnedProcessIdentity | undefined>;
10
+ export declare const listOwnedProcessGroupMembers: (rootIdentity: IOwnedProcessIdentity) => Promise<IOwnedProcessIdentity[]>;
11
+ export declare const killFrozenOwnedProcessGroup: (rootIdentity: IOwnedProcessIdentity) => boolean;
12
+ export declare const signalOwnedProcessGroup: (rootIdentity: IOwnedProcessIdentity, signal: NodeJS.Signals) => Promise<boolean>;
13
+ export declare const signalOwnedProcessIdentity: (identity: IOwnedProcessIdentity, signal: NodeJS.Signals) => Promise<boolean>;
14
+ export declare const delay: (milliseconds: number) => Promise<void>;
@@ -0,0 +1,132 @@
1
+ import * as plugins from './smartpuppeteer.plugins.js';
2
+ const maxOwnedGroupProcesses = 4096;
3
+ const maxVisibleProcesses = 65536;
4
+ const processReadBatchSize = 64;
5
+ const parseProcessStat = (pid, stat) => {
6
+ const commandEnd = stat.lastIndexOf(')');
7
+ if (commandEnd < 0) {
8
+ throw new Error(`Unable to parse process identity for PID ${pid}`);
9
+ }
10
+ const fields = stat.slice(commandEnd + 2).trim().split(/\s+/);
11
+ const state = fields[0];
12
+ const parentPid = Number(fields[1]);
13
+ const processGroupId = Number(fields[2]);
14
+ const sessionId = Number(fields[3]);
15
+ const startTime = fields[19];
16
+ if (typeof state !== 'string'
17
+ || state.length !== 1
18
+ || !Number.isInteger(parentPid)
19
+ || parentPid < 0
20
+ || !Number.isInteger(processGroupId)
21
+ || processGroupId < 0
22
+ || !Number.isInteger(sessionId)
23
+ || sessionId < 0
24
+ || typeof startTime !== 'string'
25
+ || !/^[0-9]+$/.test(startTime)) {
26
+ throw new Error(`Unable to parse process identity for PID ${pid}`);
27
+ }
28
+ return { pid, parentPid, processGroupId, sessionId, startTime, state };
29
+ };
30
+ export const readOwnedProcessIdentity = async (pid) => {
31
+ if (process.platform !== 'linux') {
32
+ throw new Error('Confirmed browser process ownership is supported on Linux only');
33
+ }
34
+ try {
35
+ const stat = await plugins.fs.promises.readFile(`/proc/${pid}/stat`, 'utf8');
36
+ return parseProcessStat(pid, stat);
37
+ }
38
+ catch (error) {
39
+ if (error.code === 'ENOENT'
40
+ || error.code === 'ESRCH') {
41
+ return undefined;
42
+ }
43
+ throw error;
44
+ }
45
+ };
46
+ const listProcessIdentities = async () => {
47
+ const entries = await plugins.fs.promises.readdir('/proc', { withFileTypes: true });
48
+ const pids = entries
49
+ .filter((entry) => entry.isDirectory() && /^[0-9]+$/.test(entry.name))
50
+ .map((entry) => Number(entry.name));
51
+ if (pids.length > maxVisibleProcesses) {
52
+ throw new Error(`Refusing to inspect more than ${maxVisibleProcesses} processes`);
53
+ }
54
+ const identities = [];
55
+ for (let index = 0; index < pids.length; index += processReadBatchSize) {
56
+ identities.push(...await Promise.all(pids.slice(index, index + processReadBatchSize).map((pid) => readOwnedProcessIdentity(pid))));
57
+ }
58
+ return identities.filter((identity) => Boolean(identity));
59
+ };
60
+ export const listOwnedProcessGroupMembers = async (rootIdentity) => {
61
+ const currentRoot = await readOwnedProcessIdentity(rootIdentity.pid);
62
+ if (currentRoot && currentRoot.startTime !== rootIdentity.startTime) {
63
+ throw new Error('The owned browser PID was reused by another process');
64
+ }
65
+ if (currentRoot
66
+ && (currentRoot.processGroupId !== rootIdentity.processGroupId
67
+ || currentRoot.sessionId !== rootIdentity.sessionId)) {
68
+ throw new Error('The owned browser process changed its process-group identity');
69
+ }
70
+ const members = (await listProcessIdentities()).filter((identity) => (identity.processGroupId === rootIdentity.processGroupId
71
+ && identity.sessionId === rootIdentity.sessionId));
72
+ if (members.length > maxOwnedGroupProcesses) {
73
+ throw new Error(`Owned browser process group exceeded ${maxOwnedGroupProcesses} processes`);
74
+ }
75
+ return members;
76
+ };
77
+ export const killFrozenOwnedProcessGroup = (rootIdentity) => {
78
+ try {
79
+ process.kill(-rootIdentity.processGroupId, 'SIGKILL');
80
+ return true;
81
+ }
82
+ catch (error) {
83
+ if (error.code === 'ESRCH') {
84
+ return false;
85
+ }
86
+ throw error;
87
+ }
88
+ };
89
+ export const signalOwnedProcessGroup = async (rootIdentity, signal) => {
90
+ const currentRoot = await readOwnedProcessIdentity(rootIdentity.pid);
91
+ if (!currentRoot) {
92
+ return false;
93
+ }
94
+ if (currentRoot.startTime !== rootIdentity.startTime
95
+ || currentRoot.processGroupId !== rootIdentity.processGroupId
96
+ || currentRoot.sessionId !== rootIdentity.sessionId) {
97
+ throw new Error('Refusing to signal a reused browser process-group leader');
98
+ }
99
+ try {
100
+ process.kill(-rootIdentity.processGroupId, signal);
101
+ return true;
102
+ }
103
+ catch (error) {
104
+ if (error.code === 'ESRCH') {
105
+ return false;
106
+ }
107
+ throw error;
108
+ }
109
+ };
110
+ export const signalOwnedProcessIdentity = async (identity, signal) => {
111
+ const currentIdentity = await readOwnedProcessIdentity(identity.pid);
112
+ if (!currentIdentity) {
113
+ return false;
114
+ }
115
+ if (currentIdentity.startTime !== identity.startTime) {
116
+ throw new Error(`Refusing to signal reused PID ${identity.pid}`);
117
+ }
118
+ try {
119
+ process.kill(identity.pid, signal);
120
+ return true;
121
+ }
122
+ catch (error) {
123
+ if (error.code === 'ESRCH') {
124
+ return false;
125
+ }
126
+ throw error;
127
+ }
128
+ };
129
+ export const delay = async (milliseconds) => {
130
+ await new Promise((resolve) => setTimeout(resolve, milliseconds));
131
+ };
132
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIuaGVscGVycy5wcm9jZXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvc21hcnRwdXBwZXRlZXIuaGVscGVycy5wcm9jZXNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sNkJBQTZCLENBQUM7QUFXdkQsTUFBTSxzQkFBc0IsR0FBRyxJQUFJLENBQUM7QUFDcEMsTUFBTSxtQkFBbUIsR0FBRyxLQUFLLENBQUM7QUFDbEMsTUFBTSxvQkFBb0IsR0FBRyxFQUFFLENBQUM7QUFFaEMsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLEdBQVcsRUFBRSxJQUFZLEVBQXlCLEVBQUU7SUFDNUUsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUN6QyxJQUFJLFVBQVUsR0FBRyxDQUFDLEVBQUUsQ0FBQztRQUNuQixNQUFNLElBQUksS0FBSyxDQUFDLDRDQUE0QyxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7SUFDRCxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDOUQsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3hCLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNwQyxNQUFNLGNBQWMsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDekMsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ3BDLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxFQUFFLENBQUMsQ0FBQztJQUM3QixJQUNFLE9BQU8sS0FBSyxLQUFL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
@@ -20,6 +20,11 @@ export interface ILiveBrowserSecurityOptions {
20
20
  denyFileChoosers?: boolean;
21
21
  denyPermissions?: boolean;
22
22
  httpNavigationOnly?: boolean;
23
+ proxyCredentials?: ILiveBrowserProxyCredentials;
24
+ }
25
+ export interface ILiveBrowserProxyCredentials {
26
+ username: string;
27
+ password: string;
23
28
  }
24
29
  export interface ILiveBrowserOperationOptions {
25
30
  signal?: AbortSignal;
@@ -45,6 +50,24 @@ export interface ILiveBrowserSessionOptions extends Omit<IEnvAwareOptions, 'laun
45
50
  security?: ILiveBrowserSecurityOptions;
46
51
  allowEvaluation?: boolean;
47
52
  }
53
+ export interface ILiveBrowserProcessState {
54
+ generation: number;
55
+ pid: number | null;
56
+ processGroupId: number | null;
57
+ running: boolean;
58
+ exitCode: number | null;
59
+ signalCode: NodeJS.Signals | null;
60
+ }
61
+ export interface ILiveBrowserTerminationOptions {
62
+ gracefulTimeoutMs?: number;
63
+ forceTimeoutMs?: number;
64
+ }
65
+ export interface ILiveBrowserTerminationResult extends ILiveBrowserProcessState {
66
+ forced: boolean;
67
+ shutdownComplete: boolean;
68
+ confirmedDead: boolean;
69
+ errors: string[];
70
+ }
48
71
  export interface ILiveBrowserTabState {
49
72
  id: string;
50
73
  url: string;
@@ -1,6 +1,8 @@
1
1
  import { Buffer } from 'node:buffer';
2
- import * as os from 'os';
3
- export { Buffer, os };
2
+ import * as fs from 'node:fs';
3
+ import * as http from 'node:http';
4
+ import * as os from 'node:os';
5
+ export { Buffer, fs, http, os };
4
6
  import * as smartdelay from '@push.rocks/smartdelay';
5
7
  import * as smartshell from '@push.rocks/smartshell';
6
8
  export { smartdelay, smartshell };
@@ -1,7 +1,9 @@
1
1
  // node native scope
2
2
  import { Buffer } from 'node:buffer';
3
- import * as os from 'os';
4
- export { Buffer, os };
3
+ import * as fs from 'node:fs';
4
+ import * as http from 'node:http';
5
+ import * as os from 'node:os';
6
+ export { Buffer, fs, http, os };
5
7
  // @pushrocks scope
6
8
  import * as smartdelay from '@push.rocks/smartdelay';
7
9
  import * as smartshell from '@push.rocks/smartshell';
@@ -10,4 +12,4 @@ export { smartdelay, smartshell };
10
12
  import * as puppeteer from 'puppeteer';
11
13
  import treeKill from 'tree-kill';
12
14
  export { puppeteer, treeKill };
13
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0cHVwcGV0ZWVyLnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDckMsT0FBTyxLQUFLLEVBQUUsTUFBTSxJQUFJLENBQUM7QUFFekIsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUUsQ0FBQztBQUV0QixtQkFBbUI7QUFDbkIsT0FBTyxLQUFLLFVBQVUsTUFBTSx3QkFBd0IsQ0FBQztBQUNyRCxPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBRXJELE9BQU8sRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLENBQUM7QUFFbEMsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxTQUFTLE1BQU0sV0FBVyxDQUFDO0FBQ3ZDLE9BQU8sUUFBUSxNQUFNLFdBQVcsQ0FBQztBQUVqQyxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsRUFBRSxDQUFDIn0=
15
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIucGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0cHVwcGV0ZWVyLnBsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsb0JBQW9CO0FBQ3BCLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDckMsT0FBTyxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDOUIsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFFOUIsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFFLEVBQUUsRUFBRSxDQUFDO0FBRWhDLG1CQUFtQjtBQUNuQixPQUFPLEtBQUssVUFBVSxNQUFNLHdCQUF3QixDQUFDO0FBQ3JELE9BQU8sS0FBSyxVQUFVLE1BQU0sd0JBQXdCLENBQUM7QUFFckQsT0FBTyxFQUFFLFVBQVUsRUFBRSxVQUFVLEVBQUUsQ0FBQztBQUVsQyxvQkFBb0I7QUFDcEIsT0FBTyxLQUFLLFNBQVMsTUFBTSxXQUFXLENBQUM7QUFDdkMsT0FBTyxRQUFRLE1BQU0sV0FBVyxDQUFDO0FBRWpDLE9BQU8sRUFBRSxTQUFTLEVBQUUsUUFBUSxFQUFFLENBQUMifQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartpuppeteer",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "private": false,
5
5
  "description": "Provides simplified access to Puppeteer for automation and testing purposes.",
6
6
  "main": "dist_ts/index.js",
@@ -19,8 +19,8 @@
19
19
  },
20
20
  "dependencies": {
21
21
  "@push.rocks/smartdelay": "^3.0.1",
22
- "@push.rocks/smartshell": "^3.5.0",
23
- "puppeteer": "^25.4.0",
22
+ "@push.rocks/smartshell": "^4.1.0",
23
+ "puppeteer": "^25.5.0",
24
24
  "tree-kill": "^1.2.2"
25
25
  },
26
26
  "files": [
package/readme.md CHANGED
@@ -172,7 +172,7 @@ Only the active tab is streamed. Frames carry a session-monotonic sequence, tab/
172
172
 
173
173
  The live API includes:
174
174
 
175
- - Lifecycle and state: `start()`, `stop()`, `onEvent()`, and `getState()`
175
+ - Lifecycle and state: `start()`, `stop()`, `terminate()`, `getProcessState()`, `onEvent()`, and `getState()`
176
176
  - Tabs and navigation: `createTab()`, `activateTab()`, `closeTab()`, `navigate()`, `back()`, `forward()`, and `reload()`
177
177
  - Viewport and raw input: `setViewport()`, `dispatchMouse()`, `dispatchWheel()`, `dispatchKey()`, and `insertText()`
178
178
  - Agent-oriented actions: `click()`, `fill()`, and `press()` with bounded selectors and timeouts
@@ -188,17 +188,31 @@ Optional browser guards can be enabled when composing a higher-level runtime:
188
188
  ```typescript
189
189
  const guardedSession = new LiveBrowserSession({
190
190
  requireSandbox: true,
191
+ launchOptions: {
192
+ args: [
193
+ '--proxy-server=http://127.0.0.1:8080',
194
+ '--proxy-bypass-list=<-loopback>',
195
+ ],
196
+ },
191
197
  security: {
192
198
  denyDownloads: true,
193
199
  denyFileChoosers: true,
194
200
  denyPermissions: true,
195
201
  httpNavigationOnly: true,
202
+ proxyCredentials: {
203
+ username: 'proxy-user',
204
+ password: 'proxy-password',
205
+ },
196
206
  },
197
207
  });
198
208
  ```
199
209
 
200
210
  `denyDownloads` installs a default-context download denial at launch. `denyPermissions` applies an empty browser-wide permission grant before the first page is exposed, causing unlisted permissions to be denied. `denyFileChoosers` installs persistent CDP cancellation on each registered page. `httpNavigationOnly` limits URLs passed to `createTab()` and `navigate()` to `http:` and `https:`; it does not inspect or rewrite renderer-initiated navigation.
201
211
 
212
+ `proxyCredentials` handles authenticated-proxy challenges for existing and future page and dedicated-worker traffic. SmartPuppeteer supplies credentials only when CDP identifies the challenge source as `Proxy`; origin-server and unknown challenges are cancelled without credentials, and a repeated challenge for the same request is cancelled. Setup commands are queued from Puppeteer's public CDP session-attachment event before Puppeteer resumes a new target. Setup failure rejects `start()` and closes the browser. During a running session, an unexpected browser security-session detach, a live target without a protected replacement session, or a proxy protocol failure is fatal and requests termination; Linux termination is confirmed only when `terminate()` resolves. The caller still owns proxy selection, Chromium proxy arguments, bypass rules, DNS behavior, egress policy, and proxy trust. Credentials remain in process memory for the session lifetime.
213
+
214
+ On Linux, Puppeteer launches Chromium as a dedicated process-group and session leader. `getProcessState()` reports the generation, root PID, process-group ID, and Node.js exit state. `terminate({ gracefulTimeoutMs, forceTimeoutMs })` is idempotent for concurrent callers, first requests normal shutdown, then freezes and kills the owned process group if the graceful deadline expires. It resolves with `confirmedDead: true` only after `/proc` contains no member of that owned process group, the Node.js child exit is observed, and session shutdown settles. PID reuse, permission failures, surviving members, or an unsettled shutdown reject confirmation and keep restart blocked. Confirmed process-group termination is Linux-only; use `stop()` for portable best-effort lifecycle cleanup.
215
+
202
216
  Evaluation is disabled by default. Once enabled, it accepts a JavaScript expression, runs it in a dedicated main-frame isolated world, awaits its result, and returns only JSON-compatible values:
203
217
 
204
218
  ```typescript
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartpuppeteer',
6
- version: '2.2.0',
6
+ version: '2.3.0',
7
7
  description: 'Provides simplified access to Puppeteer for automation and testing purposes.'
8
8
  }