@push.rocks/smartpuppeteer 2.5.0 → 2.7.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.
@@ -19,6 +19,8 @@ export interface ILiveBrowserScreencastOptions {
19
19
  maxOutstandingFrames?: number;
20
20
  firstFrameTimeoutMs?: number;
21
21
  }
22
+ export interface ILiveBrowserScreencastUpdateOptions extends Pick<ILiveBrowserScreencastOptions, 'quality' | 'maxWidth' | 'maxHeight' | 'everyNthFrame'> {
23
+ }
22
24
  export interface ILiveBrowserSecurityOptions {
23
25
  denyDownloads?: boolean;
24
26
  denyFileChoosers?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@push.rocks/smartpuppeteer",
3
- "version": "2.5.0",
3
+ "version": "2.7.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,7 +15,7 @@
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.2.0"
18
+ "@types/node": "^26.4.1"
19
19
  },
20
20
  "dependencies": {
21
21
  "@push.rocks/smartdelay": "^3.0.1",
package/readme.hints.md CHANGED
@@ -5,6 +5,8 @@
5
5
  - CDP is private to the live runtime. Public contracts contain transport-neutral values and `Uint8Array` image data, never `CDPSession`, raw CDP frame IDs, or base64 image strings.
6
6
  - Every published screencast frame has one private sequence-to-CDP acknowledgement entry. Public acknowledgement requires matching tab ID, sequence, generation, and viewport revision. `screencast.maxOutstandingFrames` bounds those entries independently of any application transport window; overflow retires oldest-first. Drops and all stream invalidation paths must issue each CDP acknowledgement at most once and await in-flight acknowledgements before detaching the CDP session.
7
7
  - `refreshScreencast()` installs its exact-generation waiter before starting CDP, retires the previous stream, and resolves only after the first validated frame is published. Pre-aborted and queued calls reject without changing the stream. Active caller cancellation settles only after restorative restart; shutdown, tab replacement, crash, or closure revokes restart authority and must never resurrect the stream.
8
+ - A refresh timeout or restart failure on a tab that is still restorable is not runtime-fatal. Revoke the restart authority, wait for the in-flight `startScreencast()` to unwind, stop whatever it established, and emit non-fatal `screencast_refresh_failed` with the tab left open, non-streaming, and invalidated. `refreshScreencast()` therefore also accepts a non-streaming active tab; `updateScreencastOptions()` shares that restart core after storing validated options. Browser loss stays fatal and is normally reported by the disconnect listener first.
9
+ - Raw input is dispatched through a per-tab queue. Chromium does not keep a pipelined mouse or key command behind an earlier `mouseWheel` command, so anything queued behind wheel input waits until that wheel CDP call settles. Wheel input arriving while a wheel dispatch is in flight merges into the trailing wheel entry for the same CDP session, generation, and viewport revision; a non-wheel entry ends that batch.
8
10
  - Activation, viewport changes, navigation, tab closure, snapshots, observations, semantic actions, and shutdown share one bounded operation scheduler. Raw input and frame acknowledgement remain direct, but must validate active tab, generation, and viewport revision. Repeated internal navigation/load state updates are coalesced per tab, and shutdown cancels queued work.
9
11
  - Retain the scheduler-owned launch `AbortController` for the full browser lifetime. Shutdown aborts both the active operation and Chromium itself so a non-signal-aware Puppeteer command or disabled protocol timeout cannot retain the browser ahead of queued cleanup.
10
12
  - Viewport revision starts at 1 and advances only after `Page.setViewport()` succeeds. Stop and flush the active screencast before applying a viewport or navigation mutation, then restart it with a new generation.
@@ -13,4 +15,6 @@
13
15
  - New-page registration and navigation are transactional: validate before creating a page where possible and restore page, listener, tab-map, active-tab, viewport, and screencast ownership on failure. Keep listeners attached during `page.close()` so a failed close remains observed and recoverable.
14
16
  - Browser disconnect is runtime-fatal. Page crash or current CDP-session loss is tab-scoped: activate another usable tab, trying all candidates, or stop cleanly when none remains.
15
17
  - Every popup is either registered, closed, or escalated to browser-wide shutdown. Queue saturation and startup-time popup events must never leave an untracked live page.
18
+ - Authenticated proxy coverage owns a filtered browser-target auto-attach scope for service workers. Configure Fetch and Network before resuming each owned worker, use the hidden page-target lifecycle observer to release stopped or request-drained redundant workers, bound every security command and detach wait, and keep Puppeteer's target sessions independent.
19
+ - A navigation metadata refresh failure must not consume screencast restoration ownership. Restore the active stream first, preserve newer navigation revisions, and report the metadata failure separately.
16
20
  - Verify Puppeteer behavior against the installed Puppeteer 25 declarations and implementation. Do not add a direct `devtools-protocol` dependency; Puppeteer's public protocol typing is sufficient for private CDP calls.
package/readme.md CHANGED
@@ -173,20 +173,37 @@ try {
173
173
 
174
174
  Only the active tab is streamed. Frames carry a session-monotonic sequence, tab/CDP generation, viewport revision, viewport, MIME type, encoded dimensions, screencast metadata, and binary `Uint8Array` data. Every delivered frame must be acknowledged with all four identity fields. A delivered frame remains pending until it is acknowledged, dropped, or retired by the runtime; mismatched, duplicate, stale, retired, or operationally failed acknowledgements return `{ accepted: false }`. Operational acknowledgement failures also emit an `error` event whose code is `frame_acknowledgement_failed`. `screencast.maxOutstandingFrames` is an integer from 1 through `liveBrowserMaxOutstandingFrames` (64) and defaults to `liveBrowserDefaultMaxOutstandingFrames` (3). At capacity, SmartPuppeteer retires and CDP-acknowledges the oldest pending frame before publishing the next one. Tab switches, navigation, resize, page cleanup, disconnect, and shutdown also retire pending frames and await their single CDP acknowledgement attempt before detaching the screencast session.
175
175
 
176
- The SmartPuppeteer outstanding-frame bound covers only the private frame-to-`Page.screencastFrameAck` lifecycle. It is not a binary transport window, and `acknowledgeFrame()` is not an application transport acknowledgement. A higher-level runtime must maintain and bound its application frame window separately. `refreshScreencast()` retires the active stream and resolves with an `ILiveBrowserFrameIdentity` for the exact first validated frame from a new generation. Its `tabId`, `sequence`, `generation`, and `viewportRevision` fields can be passed directly to `acknowledgeFrame()`. `screencast.firstFrameTimeoutMs` bounds both the CDP restart and first-frame arrival, accepts integers from 100 through 60,000, and defaults to 5,000; timeout or another refresh failure emits fatal `screencast_refresh_failed` and shuts the session down. Screencast `format` accepts `jpeg` or `png`, `quality` accepts integers from 0 through 100, `maxWidth` and `maxHeight` accept integers from 1 through 4096 subject to an 8,294,400-pixel combined ceiling, and `everyNthFrame` accepts integers from 1 through 100.
176
+ The SmartPuppeteer outstanding-frame bound covers only the private frame-to-`Page.screencastFrameAck` lifecycle. It is not a binary transport window, and `acknowledgeFrame()` is not an application transport acknowledgement. A higher-level runtime must maintain and bound its application frame window separately. `refreshScreencast()` retires the active stream and resolves with an `ILiveBrowserFrameIdentity` for the exact first validated frame from a new generation. Its `tabId`, `sequence`, `generation`, and `viewportRevision` fields can be passed directly to `acknowledgeFrame()`. `screencast.firstFrameTimeoutMs` bounds both the CDP restart and first-frame arrival, accepts integers from 100 through 60,000, and defaults to 5,000. Screencast `format` accepts `jpeg` or `png`, `quality` accepts integers from 0 through 100, `maxWidth` and `maxHeight` accept integers from 1 through 4096 subject to an 8,294,400-pixel combined ceiling, and `everyNthFrame` accepts integers from 1 through 100.
177
+
178
+ A refresh timeout or restart failure on an otherwise healthy tab is recoverable. The `refreshScreencast()` promise rejects, an `error` event with code `screencast_refresh_failed` and `fatal: false` is emitted, and the tab stays open with `streaming: false` while the session keeps running; raw input for that tab is rejected until a new generation starts. Any of `refreshScreencast()`, `setViewport()`, or `activateTab()` on that tab then starts a new screencast generation. Only browser loss keeps the failure fatal: a disconnected Chromium reports `browser_disconnected` (or a fatal `screencast_refresh_failed` if the refresh observes the loss first) and shuts the session down, and page or CDP-session loss remains tab-scoped as described below.
179
+
180
+ `updateScreencastOptions({ quality, maxWidth, maxHeight, everyNthFrame })` changes the encoder settings of a session at runtime. Each supplied field is validated with the same bounds as the constructor, the merged result must still satisfy the pixel-area ceiling, and unknown keys such as `format` are rejected. Accepted values are stored for every later screencast start. When the active tab is streaming, the call restarts the screencast with a new generation exactly like `refreshScreencast()` and resolves with the same `ILiveBrowserFrameIdentity`; otherwise it stores the values and resolves with `null`.
181
+
182
+ ```typescript
183
+ const identity = await session.updateScreencastOptions({
184
+ quality: 45,
185
+ maxWidth: 640,
186
+ maxHeight: 360,
187
+ });
188
+ if (identity) {
189
+ await session.acknowledgeFrame(identity);
190
+ }
191
+ ```
177
192
 
178
193
  The live API includes:
179
194
 
180
- - Lifecycle and state: `start()`, `stop()`, `terminate()`, `refreshScreencast()`, `getProcessState()`, `onEvent()`, and `getState()`
195
+ - Lifecycle and state: `start()`, `stop()`, `terminate()`, `refreshScreencast()`, `updateScreencastOptions()`, `getProcessState()`, `onEvent()`, and `getState()`
181
196
  - Tabs and navigation: `createTab()`, `activateTab()`, `closeTab()`, `navigate()`, `back()`, `forward()`, and `reload()`
182
197
  - Viewport and raw input: `setViewport()`, `dispatchMouse()`, `dispatchWheel()`, `dispatchKey()`, and `insertText()`
183
198
  - Agent-oriented actions: `click()`, `fill()`, and `press()` with bounded selectors and timeouts
184
199
  - 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
185
200
  - Optional evaluation: `evaluate()` returns bounded JSON values when the session explicitly sets `allowEvaluation: true`
186
201
 
187
- 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.
202
+ 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.
203
+
204
+ `dispatchWheel()` merges bursts. While one `mouseWheel` CDP dispatch for a tab is in flight, further `dispatchWheel()` calls for the same tab and stream accumulate: `deltaX` and `deltaY` are summed and clamped to ±1,000,000, and the latest `x`, `y`, and `modifiers` win. The accumulated input is sent as one CDP event once the in-flight dispatch settles, and every caller's promise resolves when its deltas have been sent or rejects if that merged send fails. Ordering with other raw input on the same tab is preserved: a `dispatchMouse()`, `dispatchKey()`, or `insertText()` call issued after wheel input is sent only after the preceding wheel dispatch has been acknowledged by Chromium, and wheel input issued after such a call starts a new batch behind it. `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.
188
205
 
189
- `start()`, `refreshScreencast()`, 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. An active `refreshScreencast()` completes the restorative restart before settling caller cancellation, unless session shutdown or page invalidation revokes that restart. 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.
206
+ `start()`, `refreshScreencast()`, `updateScreencastOptions()`, 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. An active `refreshScreencast()` or `updateScreencastOptions()` completes the restorative restart before settling caller cancellation, unless session shutdown or page invalidation revokes that restart. 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.
190
207
 
191
208
  Optional browser guards can be enabled when composing a higher-level runtime:
192
209
 
@@ -214,7 +231,7 @@ const guardedSession = new LiveBrowserSession({
214
231
 
215
232
  `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.
216
233
 
217
- `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.
234
+ `proxyCredentials` handles authenticated-proxy challenges for existing and future page, dedicated-worker, and service-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. Page and dedicated-worker setup is queued from Puppeteer's public CDP session-attachment event. Service workers use an independent, filtered browser-target attachment scope that enables Fetch and Network before resuming each worker. A hidden lifecycle target releases stopped workers and redundant workers after their tracked requests drain so Chromium can destroy them normally; later restarts are paused and protected again before execution. 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.
218
235
 
219
236
  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.
220
237
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@push.rocks/smartpuppeteer',
6
- version: '2.5.0',
6
+ version: '2.7.0',
7
7
  description: 'Provides simplified access to Puppeteer for automation and testing purposes.'
8
8
  }