@push.rocks/smartpuppeteer 2.1.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.
@@ -1,6 +1,7 @@
1
1
  import * as plugins from './smartpuppeteer.plugins.js';
2
2
  export interface IEnvAwareOptions {
3
3
  forceNoSandbox?: boolean;
4
+ requireSandbox?: boolean;
4
5
  usePipe?: boolean;
5
6
  launchOptions?: plugins.puppeteer.LaunchOptions;
6
7
  }
@@ -1,4 +1,10 @@
1
1
  import * as plugins from './smartpuppeteer.plugins.js';
2
+ const sandboxDisablingArguments = [
3
+ '--disable-gpu-sandbox',
4
+ '--disable-seccomp-filter-sandbox',
5
+ '--disable-setuid-sandbox',
6
+ '--no-sandbox',
7
+ ];
2
8
  export const resolveBrowserExecutablePath = (candidateNamesArg = [
3
9
  'google-chrome',
4
10
  'chromium',
@@ -17,13 +23,26 @@ export const resolveBrowserExecutablePath = (candidateNamesArg = [
17
23
  export const getEnvAwareBrowserInstance = async (optionsArg = {}) => {
18
24
  const options = {
19
25
  forceNoSandbox: false,
26
+ requireSandbox: false,
20
27
  ...optionsArg,
21
28
  };
29
+ if (options.forceNoSandbox && options.requireSandbox) {
30
+ throw new Error('forceNoSandbox and requireSandbox are mutually exclusive');
31
+ }
22
32
  const launchOptions = options.launchOptions ?? {};
23
33
  let chromeArgs = [...(launchOptions.args ?? [])];
24
- if (process.env.CI ||
25
- options.forceNoSandbox ||
26
- plugins.os.userInfo().username === 'root') {
34
+ if (options.requireSandbox) {
35
+ const forbiddenArgument = chromeArgs.find((argument) => (sandboxDisablingArguments.includes(argument.split('=', 1)[0])));
36
+ if (forbiddenArgument) {
37
+ throw new Error(`Sandbox-required browser launch rejects argument: ${forbiddenArgument}`);
38
+ }
39
+ if (process.platform === 'linux' && process.getuid?.() === 0) {
40
+ throw new Error('Sandbox-required Chromium cannot be launched as root');
41
+ }
42
+ }
43
+ else if (process.env.CI
44
+ || options.forceNoSandbox
45
+ || plugins.os.userInfo().username === 'root') {
27
46
  for (const sandboxArg of ['--no-sandbox', '--disable-setuid-sandbox']) {
28
47
  if (!chromeArgs.includes(sandboxArg)) {
29
48
  chromeArgs.push(sandboxArg);
@@ -59,4 +78,4 @@ export const getEnvAwareBrowserInstance = async (optionsArg = {}) => {
59
78
  });
60
79
  return headlessBrowser;
61
80
  };
62
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIuY2xhc3Nlcy5zbWFydHB1cHBldGVlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0cHVwcGV0ZWVyLmNsYXNzZXMuc21hcnRwdXBwZXRlZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSw2QkFBNkIsQ0FBQztBQVF2RCxNQUFNLENBQUMsTUFBTSw0QkFBNEIsR0FBRyxDQUMxQyxvQkFBOEI7SUFDNUIsZUFBZTtJQUNmLFVBQVU7SUFDVixrQkFBa0I7Q0FDbkIsRUFDbUIsRUFBRTtJQUN0QixLQUFLLE1BQU0sYUFBYSxJQUFJLGlCQUFpQixFQUFFLENBQUM7UUFDOUMsTUFBTSxjQUFjLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRTtZQUNsRSxPQUFPLEVBQUUsSUFBSTtTQUNkLENBQUMsQ0FBQztRQUNILElBQUksY0FBYyxFQUFFLENBQUM7WUFDbkIsT0FBTyxjQUFjLENBQUM7UUFDeEIsQ0FBQztJQUNILENBQUM7SUFDRCxPQUFPLFNBQVMsQ0FBQztBQUNuQixDQUFDLENBQUM7QUFFRixNQUFNLENBQUMsTUFBTSwwQkFBMEIsR0FBRyxLQUFLLEVBQzdDLGFBQStCLEVBQUUsRUFDRyxFQUFFO0lBQ3RDLE1BQU0sT0FBTyxHQUFxQjtRQUNoQyxjQUFjLEVBQUUsS0FBSztRQUNyQixHQUFHLFVBQVU7S0FDZCxDQUFDO0lBRUYsTUFBTSxhQUFhLEdBQUcsT0FBTyxDQUFDLGFBQWEsSUFBSSxFQUFFLENBQUM7SUFDbEQsSUFBSSxVQUFVLEdBQWEsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLElBQUksSUFBSSxFQUFFLENBQUMsQ0FBQyxDQUFDO0lBQzNELElBQ0UsT0FBTyxDQUFDLEdBQUcsQ0FBQyxFQUFFO1FBQ2QsT0FBTyxDQUFDLGNBQWM7UUFDdEIsT0FBTyxDQUFDLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEtBQUssTUFBTSxFQUN6QyxDQUFDO1FBQ0QsS0FBSyxNQUFNLFVBQVUsSUFBSSxDQUFDLGNBQWMsRUFBRSwwQkFBMEIsQ0FBQyxFQUFFLENBQUM7WUFDdEUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLEVBQUUsQ0FBQztnQkFDckMsVUFBVSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUM5QixDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sQ0FBQyxJQUFJLENBQUMsMERBQTBELENBQUMsQ0FBQztRQUN6RSxPQUFPLENBQUMsSUFBSSxDQUFDLG1FQUFtRSxDQUFDLENBQUM7UUFDbEYsT0FBTyxDQUFDLElBQUksQ0FBQywwREFBMEQsQ0FBQyxDQUFDO0lBQzNFLENBQUM7SUFFRCw4RUFBOEU7SUFDOUUsTUFBTSxxQkFBcUIsR0FDekIsYUFBYSxDQUFDLE9BQU8sS0FBSyxTQUFTO1dBQ2hDLGFBQWEsQ0FBQyxPQUFPLEtBQUssU0FBUztXQUNuQyxhQUFhLENBQUMsY0FBYyxLQUFLLFNBQVMsQ0FBQztJQUNoRCxNQUFNLFFBQVEsR0FBRyxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyw0QkFBNEIsRUFBRSxDQUFDO0lBRXBGLE1BQU0scUJBQXFCLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxFQUFFLGNBQWMsRUFBRSxRQUFRLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBRTNFLE9BQU8sQ0FBQyxHQUFHLENBQUMsNkNBQTZDLENBQUMsQ0FBQztJQUMzRCxPQUFPLENBQUMsR0FBRyxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ3hCLElBQUksUUFBUSxFQUFFLENBQUM7UUFDYixPQUFPLENBQUMsR0FBRyxDQUFDLHFCQUFxQixRQUFRLEVBQUUsQ0FBQyxDQUFDO0lBQy9DLENBQUM7U0FBTSxJQUFJLHFCQUFxQixFQUFFLENBQUM7UUFDakMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxxREFBcUQsQ0FBQyxDQUFDO0lBQ3JFLENBQUM7U0FBTSxDQUFDO1FBQ04sT0FBTyxDQUFDLEdBQUcsQ0FBQywwRUFBMEUsQ0FBQyxDQUFDO0lBQzFGLENBQUM7SUFFRCxNQUFNLGVBQWUsR0FBRyxNQUFNLE9BQU8sQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDO1FBQ3JELFFBQVEsRUFBRSxJQUFJO1FBQ2QsR0FBRyxhQUFhO1FBQ2hCLElBQUksRUFBRSxVQUFVO1FBQ2hCLElBQUksRUFBRSxPQUFPLENBQUMsT0FBTyxJQUFJLGFBQWEsQ0FBQyxJQUFJLElBQUksSUFBSTtRQUNuRCxHQUFHLHFCQUFxQjtLQUN6QixDQUFDLENBQUM7SUFFSCxPQUFPLGVBQWUsQ0FBQztBQUN6QixDQUFDLENBQUMifQ==
81
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRwdXBwZXRlZXIuY2xhc3Nlcy5zbWFydHB1cHBldGVlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3NtYXJ0cHVwcGV0ZWVyLmNsYXNzZXMuc21hcnRwdXBwZXRlZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSw2QkFBNkIsQ0FBQztBQVN2RCxNQUFNLHlCQUF5QixHQUFHO0lBQ2hDLHVCQUF1QjtJQUN2QixrQ0FBa0M7SUFDbEMsMEJBQTBCO0lBQzFCLGNBQWM7Q0FDZixDQUFDO0FBRUYsTUFBTSxDQUFDLE1BQU0sNEJBQTRCLEdBQUcsQ0FDMUMsb0JBQThCO0lBQzVCLGVBQWU7SUFDZixVQUFVO0lBQ1Ysa0JBQWtCO0NBQ25CLEVBQ21CLEVBQUU7SUFDdEIsS0FBSyxNQUFNLGFBQWEsSUFBSSxpQkFBaUIsRUFBRSxDQUFDO1FBQzlDLE1BQU0sY0FBYyxHQUFHLE9BQU8sQ0FBQyxVQUFVLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUU7WUFDbEUsT0FBTyxFQUFFLElBQUk7U0FDZCxDQUFDLENBQUM7UUFDSCxJQUFJLGNBQWMsRUFBRSxDQUFDO1lBQ25CLE9BQU8sY0FBYyxDQUFDO1FBQ3hCLENBQUM7SUFDSCxDQUFDO0lBQ0QsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQyxDQUFDO0FBRUYsTUFBTSxDQUFDLE1BQU0sMEJBQTBCLEdBQUcsS0FBSyxFQUM3QyxhQUErQixFQUFFLEVBQ0csRUFBRTtJQUN0QyxNQUFNLE9BQU8sR0FBcUI7UUFDaEMsY0FBYyxFQUFFLEtBQUs7UUFDckIsY0FBYyxFQUFFLEtBQUs7UUFDckIsR0FBRyxVQUFVO0tBQ2QsQ0FBQztJQUVGLElBQUksT0FBTyxDQUFDLGNBQWMsSUFBSSxPQUFPLENBQUMsY0FBYyxFQUFFLENBQUM7UUFDckQsTUFBTSxJQUFJLEtBQUssQ0FBQywwREFBMEQsQ0FBQyxDQUFDO0lBQzlFLENBQUM7SUFFRCxN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
@@ -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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
@@ -15,11 +15,58 @@ export interface ILiveBrowserScreencastOptions {
15
15
  maxHeight?: number;
16
16
  everyNthFrame?: number;
17
17
  }
18
+ export interface ILiveBrowserSecurityOptions {
19
+ denyDownloads?: boolean;
20
+ denyFileChoosers?: boolean;
21
+ denyPermissions?: boolean;
22
+ httpNavigationOnly?: boolean;
23
+ proxyCredentials?: ILiveBrowserProxyCredentials;
24
+ }
25
+ export interface ILiveBrowserProxyCredentials {
26
+ username: string;
27
+ password: string;
28
+ }
29
+ export interface ILiveBrowserOperationOptions {
30
+ signal?: AbortSignal;
31
+ }
32
+ export type TLiveBrowserJsonValue = null | boolean | number | string | TLiveBrowserJsonValue[] | {
33
+ [key: string]: TLiveBrowserJsonValue;
34
+ };
35
+ export interface ILiveBrowserEvaluateOptions {
36
+ tabId?: string;
37
+ timeoutMs?: number;
38
+ maxOutputBytes?: number;
39
+ maxDepth?: number;
40
+ maxNodes?: number;
41
+ maxStringBytes?: number;
42
+ maxArrayLength?: number;
43
+ maxObjectKeys?: number;
44
+ }
18
45
  export type TLiveBrowserLaunchOptions = Omit<NonNullable<IEnvAwareOptions['launchOptions']>, 'signal'>;
19
46
  export interface ILiveBrowserSessionOptions extends Omit<IEnvAwareOptions, 'launchOptions'> {
20
47
  launchOptions?: TLiveBrowserLaunchOptions;
21
48
  viewport?: ILiveBrowserViewport;
22
49
  screencast?: ILiveBrowserScreencastOptions;
50
+ security?: ILiveBrowserSecurityOptions;
51
+ allowEvaluation?: boolean;
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[];
23
70
  }
24
71
  export interface ILiveBrowserTabState {
25
72
  id: 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.1.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",
@@ -15,12 +15,12 @@
15
15
  "@git.zone/tsbuild": "^4.4.2",
16
16
  "@git.zone/tsrun": "^2.0.6",
17
17
  "@git.zone/tstest": "^4.0.0",
18
- "@types/node": "^26.1.2"
18
+ "@types/node": "^26.2.0"
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
@@ -44,6 +44,8 @@ const initializeBrowser = async () => {
44
44
 
45
45
  Caller `launchOptions` are passed to Puppeteer. Caller arguments are retained when the environment requires `--no-sandbox` and `--disable-setuid-sandbox`, and the required arguments are added without duplication. This no-sandbox behavior remains limited to root users, CI environments, or callers that explicitly set `forceNoSandbox`; disabling Chromium's sandbox reduces process isolation. The existing pipe transport remains enabled by default; set `usePipe: false` to request Puppeteer's WebSocket transport. Executable discovery is skipped when `launchOptions.browser`, `launchOptions.channel`, or `launchOptions.executablePath` is present.
46
46
 
47
+ Set `requireSandbox: true` when a caller must fail closed instead of accepting the environment-aware no-sandbox behavior. This rejects `forceNoSandbox`, known sandbox-disabling Chromium arguments, and Linux launches as root. It also prevents `CI` from adding no-sandbox arguments. This option prevents SmartPuppeteer from disabling Chromium's sandbox; a successful process launch is still not an independent verification of Chromium's internal sandbox state.
48
+
47
49
  Use `resolveBrowserExecutablePath()` directly when you need to inspect the selected executable or provide your own ordered candidate list:
48
50
 
49
51
  ```typescript
@@ -170,15 +172,63 @@ Only the active tab is streamed. Frames carry a session-monotonic sequence, tab/
170
172
 
171
173
  The live API includes:
172
174
 
173
- - Lifecycle and state: `start()`, `stop()`, `onEvent()`, and `getState()`
175
+ - Lifecycle and state: `start()`, `stop()`, `terminate()`, `getProcessState()`, `onEvent()`, and `getState()`
174
176
  - Tabs and navigation: `createTab()`, `activateTab()`, `closeTab()`, `navigate()`, `back()`, `forward()`, and `reload()`
175
177
  - Viewport and raw input: `setViewport()`, `dispatchMouse()`, `dispatchWheel()`, `dispatchKey()`, and `insertText()`
176
178
  - Agent-oriented actions: `click()`, `fill()`, and `press()` with bounded selectors and timeouts
177
179
  - Capture and observation: `captureSnapshot()` returns viewport-only JPEG or PNG bytes; `observe()` returns bounded URL, title, tab state, and textual accessibility content without image bytes
180
+ - Optional evaluation: `evaluate()` returns bounded JSON values when the session explicitly sets `allowEvaluation: true`
178
181
 
179
182
  Coordinate, keyboard, text, and semantic input messages include `tabId`, `generation`, and `viewportRevision`. This rejects input derived from an old stream generation, resize, or tab state. Snapshot, observation, and semantic operations are serialized with lifecycle mutations; inactive tabs receive the current session viewport before use. `viewport` takes precedence over `launchOptions.defaultViewport`; `null` falls back to 800x600. The runtime canonicalizes every page to a desktop, non-touch viewport because mobile emulation flags are outside the public viewport contract. Viewport dimensions, device scale factor, and physical pixel area are bounded, and full-page snapshots are intentionally unsupported. CDP sessions and CDP frame identifiers remain private implementation details. `LiveBrowserSession` owns launch cancellation, so callers cannot supply `launchOptions.signal`. It supports only Chromium over CDP and rejects Firefox or WebDriver BiDi launch selections.
180
183
 
181
- `LiveBrowserSession` is a browser runtime, not a security policy layer. It does not authenticate callers, authorize actions, restrict URLs, or enforce network/egress policy. Adapters must apply those controls before invoking it. `stop()` rejects active and queued operations and aborts Chromium independently of Puppeteer operation timeouts. Closing the final usable tab also stops the session. Browser-wide loss stops the runtime without automatic relaunch. Page or CDP loss is tab-scoped: another usable tab becomes active when possible, otherwise the runtime stops. Popup registration queue saturation emits `popup_registration_capacity_exceeded` and stops the session rather than leaving an untracked page.
184
+ `start()`, tab and navigation methods, `setViewport()`, `captureSnapshot()`, `observe()`, semantic actions, and `evaluate()` accept a trailing `{ signal }` operation argument. A pre-aborted operation is never admitted. An operation aborted while queued is removed immediately. An active operation receives cancellation when its Puppeteer or CDP primitive supports it; otherwise its promise rejects only after the underlying work settles, and it continues to occupy the serialized queue until then. Cancellation therefore does not promise that an already-started browser side effect did not occur. `stop()`, frame acknowledgement, event/state access, and direct raw input are intentionally not caller-cancellable.
185
+
186
+ Optional browser guards can be enabled when composing a higher-level runtime:
187
+
188
+ ```typescript
189
+ const guardedSession = new LiveBrowserSession({
190
+ requireSandbox: true,
191
+ launchOptions: {
192
+ args: [
193
+ '--proxy-server=http://127.0.0.1:8080',
194
+ '--proxy-bypass-list=<-loopback>',
195
+ ],
196
+ },
197
+ security: {
198
+ denyDownloads: true,
199
+ denyFileChoosers: true,
200
+ denyPermissions: true,
201
+ httpNavigationOnly: true,
202
+ proxyCredentials: {
203
+ username: 'proxy-user',
204
+ password: 'proxy-password',
205
+ },
206
+ },
207
+ });
208
+ ```
209
+
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.
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
+
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:
217
+
218
+ ```typescript
219
+ const evaluationSession = new LiveBrowserSession({ allowEvaluation: true });
220
+ await evaluationSession.start();
221
+ const result = await evaluationSession.evaluate(
222
+ `({ title: document.title, links: document.links.length })`,
223
+ { timeoutMs: 2000, maxOutputBytes: 65536 },
224
+ );
225
+ ```
226
+
227
+ The default evaluation limits are a 5-second timeout, 256 KiB transferred output, depth 16, 10,000 total nodes, 64 KiB per string or key, 1,000 array entries, and 1,000 object keys. Hard ceilings are 30 seconds, 1 MiB output, depth 32, 50,000 nodes, 256 KiB per string or key, and 10,000 array entries or object keys. Expressions are limited to 256 KiB of UTF-8 source. Results reject non-finite numbers, `undefined`, bigint, symbols, functions, sparse or extended arrays, accessors, non-plain objects, cycles, and repeated object references. Output is normalized and measured inside the renderer before bounded JSON text is transferred, then measured again before host parsing.
228
+
229
+ `evaluate()` is trusted-caller code execution, not a JavaScript sandbox. An expression can mutate or navigate the page, initiate network activity, consume renderer resources, or crash the renderer. User-started asynchronous work can outlive a returned result, timeout, or caller cancellation; a higher-level runtime that requires strict quiescence must stop or quarantine the browser. Keep evaluation disabled unless a higher-level policy explicitly authorizes it.
230
+
231
+ `LiveBrowserSession` remains a browser runtime, not a complete security policy layer. The optional guards do not authenticate callers, authorize actions, enforce network egress, own profile-directory cleanup, isolate operating-system resources, or contain a compromised Chromium process. Adapters must apply those controls before invoking it. `stop()` rejects active and queued operations and aborts Chromium independently of Puppeteer operation timeouts. Closing the final usable tab also stops the session. Browser-wide loss stops the runtime without automatic relaunch. Page or CDP loss is tab-scoped: another usable tab becomes active when possible, otherwise the runtime stops. Popup registration queue saturation emits `popup_registration_capacity_exceeded` and stops the session rather than leaving an untracked page.
182
232
 
183
233
  ### Handling Browser Events
184
234
  It's important to handle browser events, such as disconnections, which might occur due to various reasons:
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartpuppeteer',
6
- version: '2.1.0',
6
+ version: '2.3.0',
7
7
  description: 'Provides simplified access to Puppeteer for automation and testing purposes.'
8
8
  }