nestjs-puppeteer 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.
- package/.beads/README.md +81 -0
- package/.beads/config.yaml +56 -0
- package/.beads/hooks/applypatch-msg +3 -0
- package/.beads/hooks/commit-msg +3 -0
- package/.beads/hooks/post-applypatch +3 -0
- package/.beads/hooks/post-checkout +27 -0
- package/.beads/hooks/post-commit +3 -0
- package/.beads/hooks/post-merge +27 -0
- package/.beads/hooks/post-rewrite +3 -0
- package/.beads/hooks/pre-applypatch +3 -0
- package/.beads/hooks/pre-auto-gc +3 -0
- package/.beads/hooks/pre-commit +27 -0
- package/.beads/hooks/pre-merge-commit +3 -0
- package/.beads/hooks/pre-push +27 -0
- package/.beads/hooks/pre-rebase +3 -0
- package/.beads/hooks/prepare-commit-msg +27 -0
- package/.beads/issues.jsonl +10 -0
- package/.beads/metadata.json +7 -0
- package/.claude/settings.json +26 -0
- package/AGENTS.md +88 -0
- package/CLAUDE.md +106 -0
- package/README.md +7 -5
- package/dist/interfaces/puppeteer-options.interface.d.ts +21 -5
- package/dist/puppeteer-core.module.d.ts +1 -1
- package/dist/puppeteer-core.module.js +66 -19
- package/package.json +14 -14
package/.beads/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Beads - AI-Native Issue Tracking
|
|
2
|
+
|
|
3
|
+
Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
|
|
4
|
+
|
|
5
|
+
## What is Beads?
|
|
6
|
+
|
|
7
|
+
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
|
|
8
|
+
|
|
9
|
+
**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### Essential Commands
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Create new issues
|
|
17
|
+
bd create "Add user authentication"
|
|
18
|
+
|
|
19
|
+
# View all issues
|
|
20
|
+
bd list
|
|
21
|
+
|
|
22
|
+
# View issue details
|
|
23
|
+
bd show <issue-id>
|
|
24
|
+
|
|
25
|
+
# Update issue status
|
|
26
|
+
bd update <issue-id> --claim
|
|
27
|
+
bd update <issue-id> --status done
|
|
28
|
+
|
|
29
|
+
# Sync with Dolt remote
|
|
30
|
+
bd dolt push
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Working with Issues
|
|
34
|
+
|
|
35
|
+
Issues in Beads are:
|
|
36
|
+
- **Git-native**: Stored in Dolt database with version control and branching
|
|
37
|
+
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
|
|
38
|
+
- **Branch-aware**: Issues can follow your branch workflow
|
|
39
|
+
- **Always in sync**: Auto-syncs with your commits
|
|
40
|
+
|
|
41
|
+
## Why Beads?
|
|
42
|
+
|
|
43
|
+
✨ **AI-Native Design**
|
|
44
|
+
- Built specifically for AI-assisted development workflows
|
|
45
|
+
- CLI-first interface works seamlessly with AI coding agents
|
|
46
|
+
- No context switching to web UIs
|
|
47
|
+
|
|
48
|
+
🚀 **Developer Focused**
|
|
49
|
+
- Issues live in your repo, right next to your code
|
|
50
|
+
- Works offline, syncs when you push
|
|
51
|
+
- Fast, lightweight, and stays out of your way
|
|
52
|
+
|
|
53
|
+
🔧 **Git Integration**
|
|
54
|
+
- Automatic sync with git commits
|
|
55
|
+
- Branch-aware issue tracking
|
|
56
|
+
- Dolt-native three-way merge resolution
|
|
57
|
+
|
|
58
|
+
## Get Started with Beads
|
|
59
|
+
|
|
60
|
+
Try Beads in your own projects:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Install Beads
|
|
64
|
+
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
|
|
65
|
+
|
|
66
|
+
# Initialize in your repo
|
|
67
|
+
bd init
|
|
68
|
+
|
|
69
|
+
# Create your first issue
|
|
70
|
+
bd create "Try out Beads"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Learn More
|
|
74
|
+
|
|
75
|
+
- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
|
|
76
|
+
- **Quick Start Guide**: Run `bd quickstart`
|
|
77
|
+
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
*Beads: Issue tracking that moves at the speed of thought* ⚡
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Beads Configuration File
|
|
2
|
+
# This file configures default behavior for all bd commands in this repository
|
|
3
|
+
# All settings can also be set via environment variables (BD_* prefix)
|
|
4
|
+
# or overridden with command-line flags
|
|
5
|
+
|
|
6
|
+
# Issue prefix for this repository (used by bd init)
|
|
7
|
+
# If not set, bd init will auto-detect from directory name
|
|
8
|
+
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
|
|
9
|
+
# issue-prefix: ""
|
|
10
|
+
|
|
11
|
+
# Use no-db mode: JSONL-only, no Dolt database
|
|
12
|
+
# When true, bd will use .beads/issues.jsonl as the source of truth
|
|
13
|
+
# no-db: false
|
|
14
|
+
|
|
15
|
+
# Enable JSON output by default
|
|
16
|
+
# json: false
|
|
17
|
+
|
|
18
|
+
# Feedback title formatting for mutating commands (create/update/close/dep/edit)
|
|
19
|
+
# 0 = hide titles, N > 0 = truncate to N characters
|
|
20
|
+
# output:
|
|
21
|
+
# title-length: 255
|
|
22
|
+
|
|
23
|
+
# Default actor for audit trails (overridden by BEADS_ACTOR or --actor)
|
|
24
|
+
# actor: ""
|
|
25
|
+
|
|
26
|
+
# Export events (audit trail) to .beads/events.jsonl on each flush/sync
|
|
27
|
+
# When enabled, new events are appended incrementally using a high-water mark.
|
|
28
|
+
# Use 'bd export --events' to trigger manually regardless of this setting.
|
|
29
|
+
# events-export: false
|
|
30
|
+
|
|
31
|
+
# Multi-repo configuration (experimental - bd-307)
|
|
32
|
+
# Allows hydrating from multiple repositories and routing writes to the correct database
|
|
33
|
+
# repos:
|
|
34
|
+
# primary: "." # Primary repo (where this database lives)
|
|
35
|
+
# additional: # Additional repos to hydrate from (read-only)
|
|
36
|
+
# - ~/beads-planning # Personal planning repo
|
|
37
|
+
# - ~/work-planning # Work planning repo
|
|
38
|
+
|
|
39
|
+
# JSONL backup (periodic export for off-machine recovery)
|
|
40
|
+
# Auto-enabled when a git remote exists. Override explicitly:
|
|
41
|
+
# backup:
|
|
42
|
+
# enabled: false # Disable auto-backup entirely
|
|
43
|
+
# interval: 15m # Minimum time between auto-exports
|
|
44
|
+
# git-push: false # Disable git push (export locally only)
|
|
45
|
+
# git-repo: "" # Separate git repo for backups (default: project repo)
|
|
46
|
+
|
|
47
|
+
# Integration settings (access with 'bd config get/set')
|
|
48
|
+
# These are stored in the database, not in this file:
|
|
49
|
+
# - jira.url
|
|
50
|
+
# - jira.project
|
|
51
|
+
# - linear.url
|
|
52
|
+
# - linear.api-key
|
|
53
|
+
# - github.org
|
|
54
|
+
# - github.repo
|
|
55
|
+
|
|
56
|
+
sync.remote: "git+https://github.com/oblakstudio/nestjs-puppeteer"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Injected by beads (GH#3132): husky helper layout not mirrored into this dir.
|
|
3
|
+
export PATH="$PWD/node_modules/.bin:$PATH"
|
|
4
|
+
|
|
5
|
+
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
|
6
|
+
# This section is managed by beads. Do not remove these markers.
|
|
7
|
+
if command -v bd >/dev/null 2>&1; then
|
|
8
|
+
export BD_GIT_HOOK=1
|
|
9
|
+
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
|
10
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
11
|
+
timeout "$_bd_timeout" bd hooks run post-checkout "$@"
|
|
12
|
+
_bd_exit=$?
|
|
13
|
+
if [ $_bd_exit -eq 124 ]; then
|
|
14
|
+
echo >&2 "beads: hook 'post-checkout' timed out after ${_bd_timeout}s — continuing without beads"
|
|
15
|
+
_bd_exit=0
|
|
16
|
+
fi
|
|
17
|
+
else
|
|
18
|
+
bd hooks run post-checkout "$@"
|
|
19
|
+
_bd_exit=$?
|
|
20
|
+
fi
|
|
21
|
+
if [ $_bd_exit -eq 3 ]; then
|
|
22
|
+
echo >&2 "beads: database not initialized — skipping hook 'post-checkout'"
|
|
23
|
+
_bd_exit=0
|
|
24
|
+
fi
|
|
25
|
+
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
|
26
|
+
fi
|
|
27
|
+
# --- END BEADS INTEGRATION v1.0.2 ---
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Injected by beads (GH#3132): husky helper layout not mirrored into this dir.
|
|
3
|
+
export PATH="$PWD/node_modules/.bin:$PATH"
|
|
4
|
+
|
|
5
|
+
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
|
6
|
+
# This section is managed by beads. Do not remove these markers.
|
|
7
|
+
if command -v bd >/dev/null 2>&1; then
|
|
8
|
+
export BD_GIT_HOOK=1
|
|
9
|
+
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
|
10
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
11
|
+
timeout "$_bd_timeout" bd hooks run post-merge "$@"
|
|
12
|
+
_bd_exit=$?
|
|
13
|
+
if [ $_bd_exit -eq 124 ]; then
|
|
14
|
+
echo >&2 "beads: hook 'post-merge' timed out after ${_bd_timeout}s — continuing without beads"
|
|
15
|
+
_bd_exit=0
|
|
16
|
+
fi
|
|
17
|
+
else
|
|
18
|
+
bd hooks run post-merge "$@"
|
|
19
|
+
_bd_exit=$?
|
|
20
|
+
fi
|
|
21
|
+
if [ $_bd_exit -eq 3 ]; then
|
|
22
|
+
echo >&2 "beads: database not initialized — skipping hook 'post-merge'"
|
|
23
|
+
_bd_exit=0
|
|
24
|
+
fi
|
|
25
|
+
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
|
26
|
+
fi
|
|
27
|
+
# --- END BEADS INTEGRATION v1.0.2 ---
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Injected by beads (GH#3132): husky helper layout not mirrored into this dir.
|
|
3
|
+
export PATH="$PWD/node_modules/.bin:$PATH"
|
|
4
|
+
|
|
5
|
+
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
|
6
|
+
# This section is managed by beads. Do not remove these markers.
|
|
7
|
+
if command -v bd >/dev/null 2>&1; then
|
|
8
|
+
export BD_GIT_HOOK=1
|
|
9
|
+
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
|
10
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
11
|
+
timeout "$_bd_timeout" bd hooks run pre-commit "$@"
|
|
12
|
+
_bd_exit=$?
|
|
13
|
+
if [ $_bd_exit -eq 124 ]; then
|
|
14
|
+
echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s — continuing without beads"
|
|
15
|
+
_bd_exit=0
|
|
16
|
+
fi
|
|
17
|
+
else
|
|
18
|
+
bd hooks run pre-commit "$@"
|
|
19
|
+
_bd_exit=$?
|
|
20
|
+
fi
|
|
21
|
+
if [ $_bd_exit -eq 3 ]; then
|
|
22
|
+
echo >&2 "beads: database not initialized — skipping hook 'pre-commit'"
|
|
23
|
+
_bd_exit=0
|
|
24
|
+
fi
|
|
25
|
+
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
|
26
|
+
fi
|
|
27
|
+
# --- END BEADS INTEGRATION v1.0.2 ---
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Injected by beads (GH#3132): husky helper layout not mirrored into this dir.
|
|
3
|
+
export PATH="$PWD/node_modules/.bin:$PATH"
|
|
4
|
+
|
|
5
|
+
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
|
6
|
+
# This section is managed by beads. Do not remove these markers.
|
|
7
|
+
if command -v bd >/dev/null 2>&1; then
|
|
8
|
+
export BD_GIT_HOOK=1
|
|
9
|
+
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
|
10
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
11
|
+
timeout "$_bd_timeout" bd hooks run pre-push "$@"
|
|
12
|
+
_bd_exit=$?
|
|
13
|
+
if [ $_bd_exit -eq 124 ]; then
|
|
14
|
+
echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s — continuing without beads"
|
|
15
|
+
_bd_exit=0
|
|
16
|
+
fi
|
|
17
|
+
else
|
|
18
|
+
bd hooks run pre-push "$@"
|
|
19
|
+
_bd_exit=$?
|
|
20
|
+
fi
|
|
21
|
+
if [ $_bd_exit -eq 3 ]; then
|
|
22
|
+
echo >&2 "beads: database not initialized — skipping hook 'pre-push'"
|
|
23
|
+
_bd_exit=0
|
|
24
|
+
fi
|
|
25
|
+
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
|
26
|
+
fi
|
|
27
|
+
# --- END BEADS INTEGRATION v1.0.2 ---
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env sh
|
|
2
|
+
# Injected by beads (GH#3132): husky helper layout not mirrored into this dir.
|
|
3
|
+
export PATH="$PWD/node_modules/.bin:$PATH"
|
|
4
|
+
|
|
5
|
+
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
|
6
|
+
# This section is managed by beads. Do not remove these markers.
|
|
7
|
+
if command -v bd >/dev/null 2>&1; then
|
|
8
|
+
export BD_GIT_HOOK=1
|
|
9
|
+
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
|
10
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
11
|
+
timeout "$_bd_timeout" bd hooks run prepare-commit-msg "$@"
|
|
12
|
+
_bd_exit=$?
|
|
13
|
+
if [ $_bd_exit -eq 124 ]; then
|
|
14
|
+
echo >&2 "beads: hook 'prepare-commit-msg' timed out after ${_bd_timeout}s — continuing without beads"
|
|
15
|
+
_bd_exit=0
|
|
16
|
+
fi
|
|
17
|
+
else
|
|
18
|
+
bd hooks run prepare-commit-msg "$@"
|
|
19
|
+
_bd_exit=$?
|
|
20
|
+
fi
|
|
21
|
+
if [ $_bd_exit -eq 3 ]; then
|
|
22
|
+
echo >&2 "beads: database not initialized — skipping hook 'prepare-commit-msg'"
|
|
23
|
+
_bd_exit=0
|
|
24
|
+
fi
|
|
25
|
+
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
|
26
|
+
fi
|
|
27
|
+
# --- END BEADS INTEGRATION v1.0.2 ---
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{"id":"nestjs-puppeteer-cg9","title":"Handle puppeteer-extra global plugin state — dedupe + document caveat","description":"lib/puppeteer-core.module.ts:45 calls puppeteer.use(plugin) per module options. puppeteer-extra is a global singleton so plugins from different forRoot() calls bleed across browsers. Repeated module re-registration in tests stacks plugins. At minimum: dedupe (skip if already registered) and add a JSDoc warning on PuppeteerModuleOptions.plugins. Investigate per-browser scoping if puppeteer-extra exposes a non-singleton API.","status":"closed","priority":1,"issue_type":"bug","assignee":"Sibin Grasic","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T09:11:06Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T09:46:33Z","started_at":"2026-05-09T09:41:54Z","closed_at":"2026-05-09T09:46:33Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
2
|
+
{"id":"nestjs-puppeteer-x6e","title":"Verify and fix multi-browser shutdown closes every Browser instance","description":"lib/puppeteer-core.module.ts:103-114 onApplicationShutdown resolves 'getBrowserToken(this.options)' from its own moduleRef. With multiple PuppeteerCoreModule instances, each instance closes only its own browser. Add a multi-browser e2e spec asserting both browsers actually disconnect on app close. Fix any leak found.","notes":"Investigation: added tests/e2e/puppeteer-multi-browser.spec.ts covering both forRoot and forRootAsync with two distinct browser instances. Verified via browser.process().pid that Puppeteer spawns separate Chromium processes per instance and that browser.connected flips to false on app.close() for both. No leak found: each PuppeteerCoreModule instance receives its own scoped PUPPETEER_MODULE_OPTIONS, so getBrowserToken(this.options) in onApplicationShutdown resolves the correct per-instance browser token. Closing as verified-by-test.","status":"closed","priority":1,"issue_type":"bug","assignee":"Sibin Grasic","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T09:10:59Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T09:22:29Z","started_at":"2026-05-09T09:16:55Z","closed_at":"2026-05-09T09:22:29Z","close_reason":"Verified by new e2e spec; per-instance shutdown works correctly.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
3
|
+
{"id":"nestjs-puppeteer-5cn","title":"Tune up CI workflows: release.yml + pull_request.yml","description":"Both GitHub Actions workflows have drifted from current repo layout and modern CI practice.\n\nrelease.yml is broken at job-time:\n- runs npm test (no such script in package.json)\n- npm clean-install in test/ directory which does not exist\n- extra_plugins: @semantic-release/github duplicates plugin already in .releaserc\n- triggers on beta and *.x branches that .releaserc does not configure\n\npull_request.yml drifted:\n- Node 21 in matrix (EOL)\n- job named 'test' actually only does lint and audit; lint depends on test_matrix unnecessarily\n- push: branches: [renovate/**] trigger duplicates the pull_request trigger Renovate already fires\n- coverage re-runs full Chromium e2e on every PR just to upload Codecov\n\nDecisions (confirmed with user):\n- Trust merge queue; release runs build + publish only\n- Trim release branches to master + next\n- Node matrix: 20, 22, 24\n- Coverage only on merge_group\n\nPlan file: ~/.claude/plans/i-did-some-changes-mellow-hare.md","status":"closed","priority":2,"issue_type":"task","assignee":"Sibin Grasic","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T10:39:27Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T10:40:18Z","started_at":"2026-05-09T10:39:35Z","closed_at":"2026-05-09T10:40:18Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
4
|
+
{"id":"nestjs-puppeteer-ro3","title":"Audit major dependency bumps flagged by ncu","description":"Several deps have major-version bumps available. Each needs review of breaking changes and CI impact before applying:\n\n- @commitlint/cli, @commitlint/config-angular: 18 → 21\n- @types/jest: 29 → 30 (paired with jest 30)\n- @types/node: ^20 → ^25\n- @types/supertest: 6 → 7 (paired with supertest 7)\n- @typescript-eslint/eslint-plugin, @typescript-eslint/parser: 6 → 8\n- eslint: 8 → 10 (flat config migration required)\n- eslint-config-prettier: 9 → 10\n- jest: 29 → 30\n- lint-staged: ^15 → ^17\n- supertest: 6 → 7\n- typescript: ^5 → ^6\n\nNOTE: puppeteer ^23 → ^24 is being handled separately and is NOT in scope for this issue.\n\nApplied safe bumps in nestjs-puppeteer-qim.","status":"open","priority":2,"issue_type":"task","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T09:46:04Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T10:00:21Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
5
|
+
{"id":"nestjs-puppeteer-qim","title":"Dependency sanity check and bumps","description":"Audit dependencies, devDependencies, and peerDependencies in package.json against the current registry. Identify outdated packages (npm outdated). For each: classify as safe-bump (patch/minor) vs major (needs review). Apply safe bumps in one commit; flag majors with a short note for the user to decide. Re-run lint, build, and e2e after bumping. Do NOT bump peerDependency ranges without confirming the public-API impact.","status":"closed","priority":2,"issue_type":"task","assignee":"Sibin Grasic","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T09:11:29Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T09:46:33Z","started_at":"2026-05-09T09:43:41Z","closed_at":"2026-05-09T09:46:33Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
6
|
+
{"id":"nestjs-puppeteer-abp","title":"Validate forRootAsync requires one of useFactory/useClass/useExisting","description":"lib/puppeteer-core.module.ts:145-147 type-asserts (options.useClass || options.useExisting) as Type — if all three of useFactory/useClass/useExisting are missing, the inject array contains undefined and DI fails with a confusing error. Throw an explicit 'PuppeteerModule.forRootAsync requires one of useFactory, useClass, or useExisting' early.","status":"closed","priority":2,"issue_type":"task","assignee":"Sibin Grasic","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T09:11:19Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T09:26:38Z","started_at":"2026-05-09T09:24:56Z","closed_at":"2026-05-09T09:26:38Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
7
|
+
{"id":"nestjs-puppeteer-2a4","title":"Tighten public types — drop any from forRoot and async options","description":"lib/puppeteer-core.module.ts:35 has 'forRoot(options: any = {})' — should be 'PuppeteerModuleOptions'. lib/interfaces/puppeteer-options.interface.ts:33-34 has 'useFactory?: (...args: any[])' and 'inject?: any[]' — should match @nestjs/typeorm convention using InjectionToken[] / OptionalFactoryDependency. Pure type-tightening, no runtime change.","status":"closed","priority":2,"issue_type":"task","assignee":"Sibin Grasic","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T09:10:45Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T09:13:06Z","started_at":"2026-05-09T09:11:35Z","closed_at":"2026-05-09T09:13:06Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
8
|
+
{"id":"nestjs-puppeteer-y6o","title":"Modernize puppeteer launch-options type for v23 compatibility","description":"lib/interfaces/puppeteer-options.interface.ts uses Partial\u003cPuppeteerNodeLaunchOptions\u003e. Puppeteer v23 renamed this to LaunchOptions. Pick a type strategy that works across the peer-dep range '^21 || ^22 || ^23' without breaking consumers. Verify in a clean install against each major.","status":"closed","priority":3,"issue_type":"task","assignee":"Sibin Grasic","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T09:11:25Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T10:07:47Z","started_at":"2026-05-09T10:01:10Z","closed_at":"2026-05-09T10:07:47Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
9
|
+
{"id":"nestjs-puppeteer-az3","title":"Detect duplicate browser name registration","description":"lib/puppeteer-core.module.ts:54,84 registers a browser provider keyed by getBrowserToken(options). Two forRoot() calls with the same 'name' silently overwrite the DI token — no warning. Throw a clear error or log a warning when a duplicate name is detected (track registered names in a module-level Set).","status":"closed","priority":3,"issue_type":"task","assignee":"Sibin Grasic","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T09:11:17Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T10:07:47Z","started_at":"2026-05-09T10:01:10Z","closed_at":"2026-05-09T10:07:47Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
10
|
+
{"id":"nestjs-puppeteer-ct4","title":"Improve onApplicationShutdown error logging (preserve stack)","description":"lib/puppeteer-core.module.ts:111-113 currently logs 'e?.message' only — drops stack and breaks for non-Error throws. Pass the full error to this.logger.error so the Nest logger can format it properly.","status":"closed","priority":3,"issue_type":"task","assignee":"Sibin Grasic","owner":"sibin.grasic@oblak.studio","created_at":"2026-05-09T09:11:08Z","created_by":"Sibin Grasic","updated_at":"2026-05-09T09:30:13Z","started_at":"2026-05-09T09:29:15Z","closed_at":"2026-05-09T09:30:13Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreCompact": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"command": "bd prime",
|
|
8
|
+
"type": "command"
|
|
9
|
+
}
|
|
10
|
+
],
|
|
11
|
+
"matcher": ""
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"SessionStart": [
|
|
15
|
+
{
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"command": "bd prime",
|
|
19
|
+
"type": "command"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"matcher": ""
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
This project uses **bd** (beads) for issue tracking. Run `bd prime` for full workflow context.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bd ready # Find available work
|
|
9
|
+
bd show <id> # View issue details
|
|
10
|
+
bd update <id> --claim # Claim work atomically
|
|
11
|
+
bd close <id> # Complete work
|
|
12
|
+
bd dolt push # Push beads data to remote
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Non-Interactive Shell Commands
|
|
16
|
+
|
|
17
|
+
**ALWAYS use non-interactive flags** with file operations to avoid hanging on confirmation prompts.
|
|
18
|
+
|
|
19
|
+
Shell commands like `cp`, `mv`, and `rm` may be aliased to include `-i` (interactive) mode on some systems, causing the agent to hang indefinitely waiting for y/n input.
|
|
20
|
+
|
|
21
|
+
**Use these forms instead:**
|
|
22
|
+
```bash
|
|
23
|
+
# Force overwrite without prompting
|
|
24
|
+
cp -f source dest # NOT: cp source dest
|
|
25
|
+
mv -f source dest # NOT: mv source dest
|
|
26
|
+
rm -f file # NOT: rm file
|
|
27
|
+
|
|
28
|
+
# For recursive operations
|
|
29
|
+
rm -rf directory # NOT: rm -r directory
|
|
30
|
+
cp -rf source dest # NOT: cp -r source dest
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
**Other commands that may prompt:**
|
|
34
|
+
- `scp` - use `-o BatchMode=yes` for non-interactive
|
|
35
|
+
- `ssh` - use `-o BatchMode=yes` to fail instead of prompting
|
|
36
|
+
- `apt-get` - use `-y` flag
|
|
37
|
+
- `brew` - use `HOMEBREW_NO_AUTO_UPDATE=1` env var
|
|
38
|
+
|
|
39
|
+
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->
|
|
40
|
+
## Beads Issue Tracker
|
|
41
|
+
|
|
42
|
+
This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
|
|
43
|
+
|
|
44
|
+
### Quick Reference
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bd ready # Find available work
|
|
48
|
+
bd show <id> # View issue details
|
|
49
|
+
bd update <id> --claim # Claim work
|
|
50
|
+
bd close <id> # Complete work
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Rules
|
|
54
|
+
|
|
55
|
+
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
|
|
56
|
+
- Run `bd prime` for detailed command reference and session close protocol
|
|
57
|
+
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
|
|
58
|
+
|
|
59
|
+
## Session Completion
|
|
60
|
+
|
|
61
|
+
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
|
|
62
|
+
|
|
63
|
+
**MANDATORY WORKFLOW:**
|
|
64
|
+
|
|
65
|
+
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
|
66
|
+
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
|
67
|
+
3. **Update issue status** - Close finished work, update in-progress items
|
|
68
|
+
4. **PUSH TO REMOTE** - This is MANDATORY:
|
|
69
|
+
```bash
|
|
70
|
+
git pull --rebase
|
|
71
|
+
bd dolt push
|
|
72
|
+
git push
|
|
73
|
+
git status # MUST show "up to date with origin"
|
|
74
|
+
```
|
|
75
|
+
5. **Clean up** - Clear stashes, prune remote branches
|
|
76
|
+
6. **Verify** - All changes committed AND pushed
|
|
77
|
+
7. **Hand off** - Provide context for next session
|
|
78
|
+
|
|
79
|
+
**CRITICAL RULES:**
|
|
80
|
+
- Work is NOT complete until `git push` succeeds
|
|
81
|
+
- NEVER stop before pushing - that leaves work stranded locally
|
|
82
|
+
- NEVER say "ready to push when you are" - YOU must push
|
|
83
|
+
- If push fails, resolve and retry until it succeeds
|
|
84
|
+
<!-- END BEADS INTEGRATION -->
|
|
85
|
+
|
|
86
|
+
## Commit Authorship
|
|
87
|
+
|
|
88
|
+
**NEVER** add `Co-Authored-By: <LLM>` (or any equivalent LLM-attribution trailer) to git commits in this repo. This applies to every commit, every branch, every PR — no exceptions, even if the harness suggests it by default. Commits are authored by the human running the session.
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->
|
|
6
|
+
## Beads Issue Tracker
|
|
7
|
+
|
|
8
|
+
This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
|
|
9
|
+
|
|
10
|
+
### Quick Reference
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
bd ready # Find available work
|
|
14
|
+
bd show <id> # View issue details
|
|
15
|
+
bd update <id> --claim # Claim work
|
|
16
|
+
bd close <id> # Complete work
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Rules
|
|
20
|
+
|
|
21
|
+
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
|
|
22
|
+
- Run `bd prime` for detailed command reference and session close protocol
|
|
23
|
+
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
|
|
24
|
+
|
|
25
|
+
## Session Completion
|
|
26
|
+
|
|
27
|
+
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
|
|
28
|
+
|
|
29
|
+
**MANDATORY WORKFLOW:**
|
|
30
|
+
|
|
31
|
+
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
|
32
|
+
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
|
33
|
+
3. **Update issue status** - Close finished work, update in-progress items
|
|
34
|
+
4. **PUSH TO REMOTE** - This is MANDATORY:
|
|
35
|
+
```bash
|
|
36
|
+
git pull --rebase
|
|
37
|
+
bd dolt push
|
|
38
|
+
git push
|
|
39
|
+
git status # MUST show "up to date with origin"
|
|
40
|
+
```
|
|
41
|
+
5. **Clean up** - Clear stashes, prune remote branches
|
|
42
|
+
6. **Verify** - All changes committed AND pushed
|
|
43
|
+
7. **Hand off** - Provide context for next session
|
|
44
|
+
|
|
45
|
+
**CRITICAL RULES:**
|
|
46
|
+
- Work is NOT complete until `git push` succeeds
|
|
47
|
+
- NEVER stop before pushing - that leaves work stranded locally
|
|
48
|
+
- NEVER say "ready to push when you are" - YOU must push
|
|
49
|
+
- If push fails, resolve and retry until it succeeds
|
|
50
|
+
<!-- END BEADS INTEGRATION -->
|
|
51
|
+
|
|
52
|
+
## Commit Authorship
|
|
53
|
+
|
|
54
|
+
**NEVER** add `Co-Authored-By: <LLM>` (or any equivalent LLM-attribution trailer) to git commits in this repo. This applies to every commit, every branch, every PR — no exceptions, even if the harness suggests it by default. Commits are authored by the human running the session.
|
|
55
|
+
|
|
56
|
+
## Build & Test
|
|
57
|
+
|
|
58
|
+
Node.js >= 20 (see `.nvmrc`). Package is published as `nestjs-puppeteer`; source lives in `lib/` and compiles to `dist/`. Peer deps span `@nestjs/common`/`@nestjs/core` `^10 || ^11` and `puppeteer` `^21 || ^22 || ^23` — public API and types must stay compatible across all of these.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm install
|
|
62
|
+
npm run build # rm -rf dist && tsc -p tsconfig.json
|
|
63
|
+
npm run watch # incremental tsc
|
|
64
|
+
npm run lint # eslint 'lib/**/*.ts' --fix
|
|
65
|
+
npm run format # prettier --write "**/*.ts"
|
|
66
|
+
npm run test:e2e # jest --config ./tests/jest-e2e.json --runInBand
|
|
67
|
+
npm run test:e2e:cov # with coverage
|
|
68
|
+
npm run test:e2e:dev # watch mode
|
|
69
|
+
|
|
70
|
+
# Run a single e2e spec
|
|
71
|
+
npx jest --config ./tests/jest-e2e.json --runInBand tests/e2e/puppeteer.spec.ts
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
There are no unit tests — all tests are e2e specs in `tests/e2e/*.spec.ts` that boot a NestJS app via `@nestjs/testing` and drive it with `supertest`. They launch real Chromium, so they need a working Puppeteer install. CI runs them under AppArmor (`aa-exec --profile=chrome`) on Ubuntu — locally that prefix is not needed.
|
|
75
|
+
|
|
76
|
+
The `stealth-check` test in `tests/e2e/puppeteer-with-plugin.spec.ts` hits `https://arh.antoinevastel.com/bots/areyouheadless` and requires network access.
|
|
77
|
+
|
|
78
|
+
## Architecture Overview
|
|
79
|
+
|
|
80
|
+
This is a thin NestJS wrapper around `puppeteer-extra` that exposes a `Browser` (and named `Page`s) through Nest's DI container. The whole library is ~6 small files in `lib/`.
|
|
81
|
+
|
|
82
|
+
**Two-module pattern (mirrors `@nestjs/typeorm`, `@nestjs/mongoose`, etc.):**
|
|
83
|
+
|
|
84
|
+
- `PuppeteerModule` (`lib/puppeteer.module.ts`) — public façade with three statics: `forRoot`, `forRootAsync`, `forFeature`. Holds no state; delegates to the core module.
|
|
85
|
+
- `PuppeteerCoreModule` (`lib/puppeteer-core.module.ts`) — `@Global()`, owns the singleton `Browser` and the plugin registration. Implements `OnApplicationShutdown` to call `browser.close()` cleanly. The async branch (`forRootAsync`) supports `useFactory` / `useClass` / `useExisting` via `PuppeteerOptionsFactory.createPuppeteerOptions(name?)`.
|
|
86
|
+
|
|
87
|
+
**DI token scheme (`lib/common/puppeteer.utils.ts`):**
|
|
88
|
+
|
|
89
|
+
- Default (unnamed) browser → injected as the `Browser` class itself: `@InjectBrowser()`.
|
|
90
|
+
- Named browser → string token `\`${name}Browser\``: `@InjectBrowser('foo')`.
|
|
91
|
+
- Page token: `\`${browserPrefix}${page}Page\``, where `browserPrefix` is `''` for the default browser and `\`${name}_\`` otherwise. Built by `getPageToken` and consumed by `@InjectPage(page, browser?)`.
|
|
92
|
+
|
|
93
|
+
This is why `forFeature(pages, browser?)` must receive the *same* browser identifier (string name or options object) used at `forRoot` — the tokens are derived from it, not looked up. Mismatched names produce DI resolution errors, not runtime warnings.
|
|
94
|
+
|
|
95
|
+
**Plugin handling:** `options.plugins` is an array of `PuppeteerExtraPlugin`s. The core module calls `puppeteer.use(plugin)` for each before launching. Because `puppeteer-extra` mutates global state, plugins registered on one browser affect any subsequent `puppeteer.launch()` in the same process — keep this in mind when adding multi-browser tests.
|
|
96
|
+
|
|
97
|
+
> Per README: most `puppeteer-extra` plugins are **not compatible** with Chrome's "new headless" mode. When using plugins (e.g. stealth), pass `headless: true`, not `headless: 'new'`.
|
|
98
|
+
|
|
99
|
+
**Public API surface** (re-exported from `lib/index.ts`): `PuppeteerModule`, `PuppeteerCoreModule`, decorators (`InjectBrowser`, `InjectPage`), token helpers (`getBrowserToken`, `getPageToken`, `getBrowserPrefix`), constants, and the `PuppeteerModuleOptions` / `PuppeteerModuleAsyncOptions` / `PuppeteerOptionsFactory` interfaces. Treat anything exported here as a breaking-change surface.
|
|
100
|
+
|
|
101
|
+
## Conventions & Patterns
|
|
102
|
+
|
|
103
|
+
- **Commits:** Angular conventional commits enforced by commitlint + Husky (`.commitlintrc.json`). Allowed types: `build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test, sample`. Subject case is permissive (sentence/start/pascal/upper/lower) but a type prefix is required. Releases are fully automated by `semantic-release` on `master` and `next` — version bumps and CHANGELOG come from commit messages, so be deliberate about `feat:` vs `fix:` vs `chore:`.
|
|
104
|
+
- **TypeScript:** `strict: true`, `target: ES2019`, `module: commonjs`, decorators + `emitDecoratorMetadata` on. `rootDir` is `lib/`; tests live outside it and are excluded from the build.
|
|
105
|
+
- **Tests are intentionally outside `rootDir`** — they import the library via relative paths (`'../../lib'`), not via the package name. Don't add a path alias just to make this prettier; it would break `tsc` output.
|
|
106
|
+
- **Non-interactive shell commands:** `cp`, `mv`, `rm` may be aliased to `-i` mode. Use `-f` (or `-rf`) so commands don't hang waiting on y/n. See `AGENTS.md` for the full list.
|
package/README.md
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
# nestjs-puppeteer
|
|
4
4
|
Puppeteer module for Nest framework (node.js)
|
|
5
5
|
|
|
6
|
-
[](https://npmjs/package/nestjs-puppeteer)
|
|
6
|
+
[](https://npmjs.com/package/nestjs-puppeteer)
|
|
7
|
+
[](https://www.npmjs.com/package/@nestjs/core)
|
|
8
|
+
[](https://www.npmjs.com/package/puppeteer)
|
|
7
9
|

|
|
8
|
-
](https://github.com/oblakstudio/nestjs-puppeteer/blob/master/LICENSE)
|
|
11
|
+

|
|
12
|
+
[](https://github.com/semantic-release/semantic-release)
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
</div>
|
|
14
16
|
|
|
@@ -1,20 +1,35 @@
|
|
|
1
|
-
import { ModuleMetadata, Type } from '@nestjs/common';
|
|
2
|
-
import {
|
|
1
|
+
import { InjectionToken, ModuleMetadata, OptionalFactoryDependency, Type } from '@nestjs/common';
|
|
2
|
+
import { PuppeteerNode } from 'puppeteer';
|
|
3
3
|
import { PuppeteerExtraPlugin } from 'puppeteer-extra';
|
|
4
|
+
/**
|
|
5
|
+
* Launch options accepted by the installed puppeteer's `launch()`. Resolved
|
|
6
|
+
* structurally so the same type works across the supported peer-dep range
|
|
7
|
+
* (^21 || ^22 || ^23), where the canonical name has been renamed/deprecated
|
|
8
|
+
* (`PuppeteerLaunchOptions` → `PuppeteerNodeLaunchOptions` → `LaunchOptions`).
|
|
9
|
+
*/
|
|
10
|
+
type LaunchOptions = NonNullable<Parameters<PuppeteerNode['launch']>[0]>;
|
|
4
11
|
export type PuppeteerModuleOptions = {
|
|
5
12
|
/**
|
|
6
13
|
* Browser name
|
|
7
14
|
*/
|
|
8
15
|
name?: string;
|
|
9
16
|
/**
|
|
10
|
-
* Array of puppeteer-extra plugins
|
|
17
|
+
* Array of puppeteer-extra plugins.
|
|
18
|
+
*
|
|
19
|
+
* NOTE: puppeteer-extra registers plugins on a process-global singleton, so
|
|
20
|
+
* any plugin you list here affects every subsequent `puppeteer.launch()` in
|
|
21
|
+
* the same process — including launches by other PuppeteerModule instances
|
|
22
|
+
* that did not opt into the plugin. The module dedupes by plugin name to
|
|
23
|
+
* avoid stacking on repeated module re-registration, but it cannot scope a
|
|
24
|
+
* plugin to a single browser. Most plugins (e.g. stealth) are also
|
|
25
|
+
* incompatible with Chrome's "new headless" mode; pass `headless: true`.
|
|
11
26
|
*/
|
|
12
27
|
plugins?: PuppeteerExtraPlugin[];
|
|
13
28
|
/**
|
|
14
29
|
* Is the module global
|
|
15
30
|
*/
|
|
16
31
|
isGlobal?: boolean;
|
|
17
|
-
} & Partial<
|
|
32
|
+
} & Partial<LaunchOptions>;
|
|
18
33
|
export interface PuppeteerOptionsFactory {
|
|
19
34
|
createPuppeteerOptions(browserName?: string): Promise<PuppeteerModuleOptions> | PuppeteerModuleOptions;
|
|
20
35
|
}
|
|
@@ -24,5 +39,6 @@ export interface PuppeteerModuleAsyncOptions extends Pick<ModuleMetadata, 'impor
|
|
|
24
39
|
useExisting?: Type<PuppeteerOptionsFactory>;
|
|
25
40
|
useClass?: Type<PuppeteerOptionsFactory>;
|
|
26
41
|
useFactory?: (...args: any[]) => Promise<PuppeteerModuleOptions> | PuppeteerModuleOptions;
|
|
27
|
-
inject?:
|
|
42
|
+
inject?: Array<InjectionToken | OptionalFactoryDependency>;
|
|
28
43
|
}
|
|
44
|
+
export {};
|
|
@@ -6,7 +6,7 @@ export declare class PuppeteerCoreModule implements OnApplicationShutdown {
|
|
|
6
6
|
private readonly moduleRef;
|
|
7
7
|
private readonly logger;
|
|
8
8
|
constructor(options: PuppeteerModuleOptions, moduleRef: ModuleRef);
|
|
9
|
-
static forRoot(options?:
|
|
9
|
+
static forRoot(options?: PuppeteerModuleOptions): DynamicModule;
|
|
10
10
|
static forRootAsync(options: PuppeteerModuleAsyncOptions): DynamicModule;
|
|
11
11
|
onApplicationShutdown(): Promise<void>;
|
|
12
12
|
private static createAsyncProviders;
|
|
@@ -22,6 +22,53 @@ const core_1 = require("@nestjs/core");
|
|
|
22
22
|
const puppeteer_extra_1 = __importDefault(require("puppeteer-extra"));
|
|
23
23
|
const puppeteer_constants_1 = require("./puppeteer.constants");
|
|
24
24
|
const common_2 = require("./common");
|
|
25
|
+
const pluginRegistrationLogger = new common_1.Logger('PuppeteerModule');
|
|
26
|
+
const registeredBrowserNames = new Set();
|
|
27
|
+
function resolveBrowserKey(name) {
|
|
28
|
+
return name && name !== puppeteer_constants_1.DEFAULT_BROWSER_NAME ? name : puppeteer_constants_1.DEFAULT_BROWSER_NAME;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Reserve a browser name on the process-level registry. Two PuppeteerModule
|
|
32
|
+
* registrations sharing the same `name` (or both omitting it) would otherwise
|
|
33
|
+
* silently overwrite each other's DI token, leaving one Browser unreachable
|
|
34
|
+
* and un-shutdown. Throw immediately so the misconfiguration is loud.
|
|
35
|
+
*/
|
|
36
|
+
function claimBrowserName(name) {
|
|
37
|
+
const key = resolveBrowserKey(name);
|
|
38
|
+
if (registeredBrowserNames.has(key)) {
|
|
39
|
+
const label = key === puppeteer_constants_1.DEFAULT_BROWSER_NAME ? 'default (unnamed)' : `"${key}"`;
|
|
40
|
+
throw new Error(`PuppeteerModule: a browser with name ${label} is already registered. ` +
|
|
41
|
+
`Each PuppeteerModule.forRoot()/forRootAsync() call must use a unique \`name\`.`);
|
|
42
|
+
}
|
|
43
|
+
registeredBrowserNames.add(key);
|
|
44
|
+
}
|
|
45
|
+
function releaseBrowserName(name) {
|
|
46
|
+
registeredBrowserNames.delete(resolveBrowserKey(name));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Register plugins on the global puppeteer-extra singleton, skipping any whose
|
|
50
|
+
* name is already registered. puppeteer-extra holds plugin state in module
|
|
51
|
+
* scope, so naive re-registration (e.g. multiple forRoot calls in tests) would
|
|
52
|
+
* stack duplicate plugins on every subsequent launch.
|
|
53
|
+
*/
|
|
54
|
+
function registerPlugins(plugins) {
|
|
55
|
+
if (!(plugins === null || plugins === void 0 ? void 0 : plugins.length)) {
|
|
56
|
+
return [];
|
|
57
|
+
}
|
|
58
|
+
const registered = new Set(puppeteer_extra_1.default.pluginNames);
|
|
59
|
+
for (const plugin of plugins) {
|
|
60
|
+
const name = plugin === null || plugin === void 0 ? void 0 : plugin.name;
|
|
61
|
+
if (typeof name === 'string' && registered.has(name)) {
|
|
62
|
+
pluginRegistrationLogger.warn(`Skipping duplicate puppeteer-extra plugin "${name}" — already registered on the global instance.`);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
puppeteer_extra_1.default.use(plugin);
|
|
66
|
+
if (typeof name === 'string') {
|
|
67
|
+
registered.add(name);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return plugins;
|
|
71
|
+
}
|
|
25
72
|
let PuppeteerCoreModule = PuppeteerCoreModule_1 = class PuppeteerCoreModule {
|
|
26
73
|
constructor(options, moduleRef) {
|
|
27
74
|
this.options = options;
|
|
@@ -29,19 +76,14 @@ let PuppeteerCoreModule = PuppeteerCoreModule_1 = class PuppeteerCoreModule {
|
|
|
29
76
|
this.logger = new common_1.Logger('PuppeteerModule');
|
|
30
77
|
}
|
|
31
78
|
static forRoot(options = {}) {
|
|
79
|
+
claimBrowserName(options.name);
|
|
32
80
|
const puppeteerModuleOptions = {
|
|
33
81
|
provide: puppeteer_constants_1.PUPPETEER_MODULE_OPTIONS,
|
|
34
82
|
useValue: options,
|
|
35
83
|
};
|
|
36
84
|
const pluginProvider = {
|
|
37
85
|
provide: puppeteer_constants_1.PUPPETEER_BROWSER_PLUGINS,
|
|
38
|
-
useFactory: async (options) =>
|
|
39
|
-
if (options.plugins) {
|
|
40
|
-
options.plugins.forEach((plugin) => puppeteer_extra_1.default.use(plugin));
|
|
41
|
-
return options.plugins;
|
|
42
|
-
}
|
|
43
|
-
return [];
|
|
44
|
-
},
|
|
86
|
+
useFactory: async (options) => registerPlugins(options.plugins),
|
|
45
87
|
inject: [puppeteer_constants_1.PUPPETEER_MODULE_OPTIONS],
|
|
46
88
|
};
|
|
47
89
|
const browserProvider = {
|
|
@@ -60,15 +102,13 @@ let PuppeteerCoreModule = PuppeteerCoreModule_1 = class PuppeteerCoreModule {
|
|
|
60
102
|
};
|
|
61
103
|
}
|
|
62
104
|
static forRootAsync(options) {
|
|
105
|
+
if (!options.useFactory && !options.useClass && !options.useExisting) {
|
|
106
|
+
throw new Error('PuppeteerModule.forRootAsync requires one of useFactory, useClass, or useExisting');
|
|
107
|
+
}
|
|
108
|
+
claimBrowserName(options.name);
|
|
63
109
|
const pluginProvider = {
|
|
64
110
|
provide: puppeteer_constants_1.PUPPETEER_BROWSER_PLUGINS,
|
|
65
|
-
useFactory: async (options) =>
|
|
66
|
-
if (options.plugins) {
|
|
67
|
-
options.plugins.forEach((plugin) => puppeteer_extra_1.default.use(plugin));
|
|
68
|
-
return options.plugins;
|
|
69
|
-
}
|
|
70
|
-
return [];
|
|
71
|
-
},
|
|
111
|
+
useFactory: async (options) => registerPlugins(options.plugins),
|
|
72
112
|
inject: [puppeteer_constants_1.PUPPETEER_MODULE_OPTIONS],
|
|
73
113
|
};
|
|
74
114
|
const browserProvider = {
|
|
@@ -96,7 +136,15 @@ let PuppeteerCoreModule = PuppeteerCoreModule_1 = class PuppeteerCoreModule {
|
|
|
96
136
|
}
|
|
97
137
|
}
|
|
98
138
|
catch (e) {
|
|
99
|
-
|
|
139
|
+
if (e instanceof Error) {
|
|
140
|
+
this.logger.error(`Failed to close browser: ${e.message}`, e.stack);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
this.logger.error(`Failed to close browser: ${String(e)}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
releaseBrowserName(this.options.name);
|
|
100
148
|
}
|
|
101
149
|
}
|
|
102
150
|
static createAsyncProviders(options) {
|
|
@@ -113,6 +161,7 @@ let PuppeteerCoreModule = PuppeteerCoreModule_1 = class PuppeteerCoreModule {
|
|
|
113
161
|
];
|
|
114
162
|
}
|
|
115
163
|
static createAsyncOptionsProvider(options) {
|
|
164
|
+
var _a;
|
|
116
165
|
if (options.useFactory) {
|
|
117
166
|
return {
|
|
118
167
|
provide: puppeteer_constants_1.PUPPETEER_MODULE_OPTIONS,
|
|
@@ -120,10 +169,8 @@ let PuppeteerCoreModule = PuppeteerCoreModule_1 = class PuppeteerCoreModule {
|
|
|
120
169
|
inject: options.inject || [],
|
|
121
170
|
};
|
|
122
171
|
}
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
options.useExisting),
|
|
126
|
-
];
|
|
172
|
+
const factory = ((_a = options.useClass) !== null && _a !== void 0 ? _a : options.useExisting);
|
|
173
|
+
const inject = [factory];
|
|
127
174
|
return {
|
|
128
175
|
provide: puppeteer_constants_1.PUPPETEER_MODULE_OPTIONS,
|
|
129
176
|
useFactory: async (optionsFactory) => await optionsFactory.createPuppeteerOptions(options.name),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nestjs-puppeteer",
|
|
3
3
|
"description": "Puppeteer module for NestJS",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.3.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Sibin Grasic",
|
|
@@ -25,30 +25,30 @@
|
|
|
25
25
|
"main": "dist/index.js",
|
|
26
26
|
"types": "dist/index.d.ts",
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@commitlint/cli": "18.6.
|
|
29
|
-
"@commitlint/config-angular": "18.6.
|
|
28
|
+
"@commitlint/cli": "18.6.1",
|
|
29
|
+
"@commitlint/config-angular": "18.6.1",
|
|
30
30
|
"@nestjs/common": "^11",
|
|
31
31
|
"@nestjs/core": "^11",
|
|
32
32
|
"@nestjs/platform-express": "^11",
|
|
33
33
|
"@nestjs/testing": "^11",
|
|
34
|
-
"@types/jest": "29.5.
|
|
34
|
+
"@types/jest": "29.5.14",
|
|
35
35
|
"@types/node": "^20",
|
|
36
|
-
"@types/supertest": "6.0.
|
|
37
|
-
"@typescript-eslint/eslint-plugin": "6.
|
|
38
|
-
"@typescript-eslint/parser": "6.
|
|
39
|
-
"eslint": "8.
|
|
40
|
-
"eslint-config-prettier": "9.1.
|
|
41
|
-
"eslint-plugin-import": "2.
|
|
42
|
-
"husky": "^9.
|
|
36
|
+
"@types/supertest": "6.0.3",
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "6.21.0",
|
|
38
|
+
"@typescript-eslint/parser": "6.21.0",
|
|
39
|
+
"eslint": "8.57.1",
|
|
40
|
+
"eslint-config-prettier": "9.1.2",
|
|
41
|
+
"eslint-plugin-import": "2.32.0",
|
|
42
|
+
"husky": "^9.1.7",
|
|
43
43
|
"jest": "29.7.0",
|
|
44
|
-
"lint-staged": "^15.2
|
|
44
|
+
"lint-staged": "^15.5.2",
|
|
45
45
|
"puppeteer": "^23",
|
|
46
46
|
"puppeteer-extra": "^3.3.6",
|
|
47
47
|
"puppeteer-extra-plugin-stealth": "^2.11.2",
|
|
48
48
|
"reflect-metadata": "^0.2",
|
|
49
|
-
"rxjs": "^7.8.
|
|
49
|
+
"rxjs": "^7.8.2",
|
|
50
50
|
"supertest": "6.3.4",
|
|
51
|
-
"ts-jest": "29.
|
|
51
|
+
"ts-jest": "29.4.9",
|
|
52
52
|
"typescript": "^5"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|