puppeteer-bidi 0.0.3.beta1 → 0.0.3.beta3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56fae0e1e155b3fe5bcce854ac7ff615c86af3815b0b9b186bf97fa905421a00
4
- data.tar.gz: 3b13efc226d2a7d84da91056679707ff3d0b3a5d511f2211c9cfc4da1c0fef23
3
+ metadata.gz: eb435eba199162f3270ff09d0efeaf728935717f0c690a0549f176f1be06e072
4
+ data.tar.gz: 7a39e4c65471997c4d37f4558459121a9370769dbf341b7f45b6d2441175b60e
5
5
  SHA512:
6
- metadata.gz: abd414f479202d7f642c6f3b4b0ce3db308a18c5116bd4545483b88cf03193cdfb49e41931e596b3cb99c7699ac8f6773ba4ffff7bf160dd8f0a165745f6d0cc
7
- data.tar.gz: 62c840fafb7844a586286eb5776b58763e2d7d3547194233e74e75d997aeb239d875fa263b20dbdfe0a326bc2608748c5c4cc479148299096a6b11f43d116cca
6
+ metadata.gz: 1d9b44c59dda746bae25de8ad9e37c9576867fd5f39b434e39e0ea2cb1ce30d05e943496d787e9cc254d5747af1ce8514818dce67c5778861345617788c2b6af
7
+ data.tar.gz: 5d80cc021677292638eb0103cf7aca8f9c87309fcb858425360b1ac6f937f2d5d1e5386c5b30e738589fc787ab7f609a271af18d682ac9452b95ae5fec25d765
@@ -64,6 +64,8 @@ module Puppeteer
64
64
  @browser_contexts = {
65
65
  default_user_context.id => @default_browser_context
66
66
  }
67
+
68
+ register_exit_cleanup if @launcher
67
69
  end
68
70
 
69
71
  # Launch a new Firefox browser instance
@@ -342,6 +344,19 @@ module Puppeteer
342
344
 
343
345
  private
344
346
 
347
+ # @rbs return: void
348
+ def register_exit_cleanup
349
+ at_exit do
350
+ next if @closed || @disconnected
351
+
352
+ begin
353
+ @launcher&.kill
354
+ rescue StandardError => e
355
+ debug_error(e)
356
+ end
357
+ end
358
+ end
359
+
345
360
  def debug_error(error)
346
361
  return unless ENV['DEBUG_BIDI_COMMAND']
347
362
 
@@ -116,7 +116,18 @@ module Puppeteer
116
116
  raise Error, "ReactorRunner is closed"
117
117
  end
118
118
 
119
- promise.wait
119
+ # Ruby 4.0: Async::Promise#wait can wake spuriously; retry until resolved.
120
+ loop do
121
+ begin
122
+ return promise.wait
123
+ rescue TypeError => e
124
+ raise unless e.message == "exception class/object expected"
125
+ next unless promise.resolved?
126
+ value = promise.value
127
+ raise value if value.is_a?(Exception)
128
+ return value
129
+ end
130
+ end
120
131
  end
121
132
 
122
133
  # @rbs return: void
@@ -108,8 +108,11 @@ module Puppeteer
108
108
  warn "Failed to parse BiDi message: #{e.message}"
109
109
  end
110
110
  end
111
+ rescue IOError, Errno::ECONNRESET, Errno::EPIPE
112
+ # Connection closed - this is expected during shutdown, no need to warn
111
113
  rescue => e
112
- warn "Transport receive error: #{e.message}"
114
+ # Only warn for unexpected errors if we weren't intentionally closed
115
+ warn "Transport receive error: #{e.message}" unless @closed
113
116
  ensure
114
117
  close unless @closed
115
118
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Puppeteer
4
4
  module Bidi
5
- VERSION = "0.0.3.beta1"
5
+ VERSION = "0.0.3.beta3"
6
6
  end
7
7
  end
data/sig/_external.rbs CHANGED
@@ -33,6 +33,7 @@ module Async
33
33
  def resolve: (T) -> void
34
34
  def reject: (Exception) -> void
35
35
  def resolved?: () -> bool
36
+ def value: () -> T?
36
37
  end
37
38
 
38
39
  class TimeoutError < StandardError
@@ -118,6 +118,9 @@ module Puppeteer
118
118
 
119
119
  private
120
120
 
121
+ # @rbs return: void
122
+ def register_exit_cleanup: () -> void
123
+
121
124
  def debug_error: (untyped error) -> untyped
122
125
 
123
126
  # @rbs &block: (BrowserTarget | PageTarget | FrameTarget) -> void -- Block to yield each target to
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppeteer-bidi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3.beta1
4
+ version: 0.0.3.beta3
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
@@ -77,38 +77,12 @@ extra_rdoc_files: []
77
77
  files:
78
78
  - ".rspec"
79
79
  - ".rubocop.yml"
80
- - AGENTS.md
81
80
  - API_COVERAGE.md
82
- - CLAUDE.md
83
- - CLAUDE/README.md
84
- - CLAUDE/async_programming.md
85
- - CLAUDE/click_implementation.md
86
- - CLAUDE/core_layer_gotchas.md
87
- - CLAUDE/error_handling.md
88
- - CLAUDE/expose_function_implementation.md
89
- - CLAUDE/file_chooser.md
90
- - CLAUDE/frame_architecture.md
91
- - CLAUDE/javascript_evaluation.md
92
- - CLAUDE/jshandle_implementation.md
93
- - CLAUDE/keyboard_implementation.md
94
- - CLAUDE/mouse_implementation.md
95
- - CLAUDE/navigation_waiting.md
96
- - CLAUDE/porting_puppeteer.md
97
- - CLAUDE/query_handler.md
98
- - CLAUDE/reactor_runner.md
99
- - CLAUDE/rspec_pending_vs_skip.md
100
- - CLAUDE/selector_evaluation.md
101
- - CLAUDE/test_server_routes.md
102
- - CLAUDE/testing_strategy.md
103
- - CLAUDE/two_layer_architecture.md
104
- - CLAUDE/wrapped_element_click.md
105
81
  - DEVELOPMENT.md
106
82
  - LICENSE.txt
107
83
  - README.md
108
84
  - Rakefile
109
85
  - Steepfile
110
- - development/generate_api_coverage.rb
111
- - development/puppeteer_revision.txt
112
86
  - lib/puppeteer/bidi.rb
113
87
  - lib/puppeteer/bidi/async_utils.rb
114
88
  - lib/puppeteer/bidi/browser.rb
data/AGENTS.md DELETED
@@ -1,44 +0,0 @@
1
- # Repository Guidelines
2
-
3
- ## Start Here (Project-Specific Guidance)
4
-
5
- - Read `CLAUDE.md` and `CLAUDE/` first; they define the core async architecture, porting workflow, and testing strategy.
6
- - Ruby: requires `>= 3.2` (CI covers 3.2–3.4). Prefer a version manager (`rbenv`, `asdf`, etc.) over system Ruby.
7
-
8
- ## Project Structure & Module Organization
9
-
10
- - `lib/puppeteer/bidi/`: user-facing API (sync, calls `.wait`).
11
- - `lib/puppeteer/bidi/core/`: low-level BiDi core (async, returns `Async::Task`).
12
- - `spec/integration/`: browser-driven specs; fixtures in `spec/assets/`.
13
-
14
- ## Build, Test, and Development Commands
15
-
16
- - See `DEVELOPMENT.md` for the full command list and environment variables.
17
- - Run RSpec via `rbenv exec bundle exec rspec ...` when using rbenv.
18
-
19
- ## Coding Style & Naming Conventions
20
-
21
- - Ruby: 2-space indent, double-quoted strings, ~120 char lines; follow RuboCop (`.rubocop.yml`).
22
- - BiDi-only: do not introduce CDP-related ports.
23
- - Async: core returns `Async::Task`; upper layer must call `.wait` on every core call (see `CLAUDE/two_layer_architecture.md`).
24
- - Reviews to watch: WS messages can be handled out-of-order; “wait for event” code must not hang (register listeners before commands, handle “already happened”, cancel on errors).
25
-
26
- ## Testing Guidelines
27
-
28
- - Prefer `spec/integration/` and the shared-browser `with_test_state` pattern (see `CLAUDE/testing_strategy.md`).
29
- - Use `pending` for browser limitations vs `skip` for unimplemented features.
30
-
31
- ## Commit & Pull Request Guidelines
32
-
33
- - See `DEVELOPMENT.md` for commit, PR, and release conventions.
34
-
35
- ## Agent Notes (Porting/Review)
36
-
37
- - When porting from upstream TS, mirror optional vs default fields: defaults are for validation, and optional keys should be omitted from payloads unless explicitly provided.
38
- - Match upstream error messages as closely as possible (including interpolated values) so tests align with Puppeteer.
39
- - In core layer option checks, use key presence (`options.key?`) when upstream uses `'in'` to distinguish "missing" from `nil`.
40
- - During reviews, compare both implementation (`packages/puppeteer-core/src/bidi/*.ts`) and tests (`test/src/page.spec.ts`) to catch behavior parity gaps.
41
-
42
- ## Security & Configuration Tips
43
-
44
- - Do not commit credentials; review network-fetched changes (e.g., `scripts/update_injected_source.rb` → `lib/puppeteer/bidi/injected.js`) carefully.
data/CLAUDE/README.md DELETED
@@ -1,158 +0,0 @@
1
- # Detailed Implementation Documentation
2
-
3
- This directory contains detailed documentation for specific implementation topics in puppeteer-bidi.
4
-
5
- ## Quick Reference
6
-
7
- | Document | Topic | Key Takeaway |
8
- |----------|-------|--------------|
9
- | [async_programming.md](async_programming.md) | Fiber-based async | Use Async, NOT concurrent-ruby |
10
- | [two_layer_architecture.md](two_layer_architecture.md) | Core vs Upper layer | Always call `.wait` on Core methods |
11
- | [porting_puppeteer.md](porting_puppeteer.md) | Implementing features | Study TypeScript first, port tests |
12
- | [query_handler.md](query_handler.md) | Selector handling | Override script methods, use PuppeteerUtil |
13
- | [javascript_evaluation.md](javascript_evaluation.md) | JS evaluation | IIFE detection is critical |
14
- | [jshandle_implementation.md](jshandle_implementation.md) | Handle management | resultOwnership must be 'root' |
15
- | [selector_evaluation.md](selector_evaluation.md) | Selector methods | Use `eval_on_selector` not `$eval` |
16
- | [error_handling.md](error_handling.md) | Custom exceptions | Type-safe error handling |
17
- | [click_implementation.md](click_implementation.md) | Click & mouse | session.subscribe is mandatory |
18
- | [wrapped_element_click.md](wrapped_element_click.md) | Wrapped elements | Use getClientRects() |
19
- | [navigation_waiting.md](navigation_waiting.md) | waitForNavigation | Event-driven with Async::Promise |
20
- | [testing_strategy.md](testing_strategy.md) | Test optimization | Browser reuse = 19x faster |
21
- | [frame_architecture.md](frame_architecture.md) | Frame hierarchy | `(parent, browsing_context)` |
22
- | [rspec_pending_vs_skip.md](rspec_pending_vs_skip.md) | Test documentation | Use `pending` for Firefox |
23
- | [test_server_routes.md](test_server_routes.md) | Dynamic routes | `server.set_route` for tests |
24
-
25
- ## Architecture & Patterns
26
-
27
- ### [Async Programming](async_programming.md)
28
-
29
- Guide to Fiber-based async programming with socketry/async.
30
-
31
- **Key concepts:**
32
- - Use Async (Fiber-based), NOT concurrent-ruby (Thread-based)
33
- - No Mutex needed - cooperative multitasking
34
- - WebSocket messages must use `Async do` for non-blocking processing
35
-
36
- ### [Two-Layer Architecture](two_layer_architecture.md)
37
-
38
- Core vs Upper layer separation for async complexity management.
39
-
40
- **Key concepts:**
41
- - Core layer returns `Async::Task`
42
- - Upper layer calls `.wait` on all Core methods
43
- - User-facing API is synchronous
44
-
45
- ### [Porting Puppeteer](porting_puppeteer.md)
46
-
47
- Best practices for implementing Puppeteer features in Ruby.
48
-
49
- **Key concepts:**
50
- - Study TypeScript implementation first
51
- - Port corresponding test cases
52
- - Use official test assets without modification
53
-
54
- ## Implementation Details
55
-
56
- ### [QueryHandler](query_handler.md)
57
-
58
- Extensible selector handling for CSS, XPath, text selectors.
59
-
60
- **Key concepts:**
61
- - Override `query_one_script` and `query_all_script`
62
- - TextQueryHandler uses PuppeteerUtil directly (closure dependency)
63
- - Handle adoption pattern for navigation
64
-
65
- ### [JavaScript Evaluation](javascript_evaluation.md)
66
-
67
- Implementation of `evaluate()` and `evaluate_handle()`.
68
-
69
- **Key concepts:**
70
- - IIFE must be detected and treated as expressions
71
- - Always deserialize BiDi results before returning
72
-
73
- ### [JSHandle and ElementHandle](jshandle_implementation.md)
74
-
75
- Handle management and BiDi protocol parameters.
76
-
77
- **Key concepts:**
78
- - Set `resultOwnership: 'root'` to get handles
79
- - Handle parameters must be arrays
80
-
81
- ### [Selector Evaluation](selector_evaluation.md)
82
-
83
- Implementation of `eval_on_selector` methods.
84
-
85
- **Key concepts:**
86
- - Delegation pattern: Page → Frame → ElementHandle
87
- - Always dispose handles in ensure blocks
88
-
89
- ### [Click Implementation](click_implementation.md)
90
-
91
- Mouse input and click functionality.
92
-
93
- **Key concepts:**
94
- - Must call `session.subscribe` for events
95
- - URL updates happen via events
96
-
97
- ### [Wrapped Element Click](wrapped_element_click.md)
98
-
99
- Clicking wrapped/multi-line text elements.
100
-
101
- **Key concepts:**
102
- - Use getClientRects() for wrapped elements
103
- - Viewport clipping ensures clicks stay visible
104
-
105
- ### [Navigation Waiting](navigation_waiting.md)
106
-
107
- waitForNavigation patterns.
108
-
109
- **Key concepts:**
110
- - Event-driven with Async::Promise
111
- - Attach to existing navigations before block execution
112
-
113
- ### [Frame Architecture](frame_architecture.md)
114
-
115
- Parent-based frame hierarchy.
116
-
117
- **Key concepts:**
118
- - First parameter is `parent` (Page or Frame)
119
- - Enables future iframe support
120
-
121
- ### [Error Handling](error_handling.md)
122
-
123
- Custom exception types.
124
-
125
- **Key concepts:**
126
- - Use custom exceptions for type-safe rescue
127
- - Include contextual data in exceptions
128
-
129
- ## Testing
130
-
131
- ### [Testing Strategy](testing_strategy.md)
132
-
133
- Test organization and optimization.
134
-
135
- **Key concepts:**
136
- - Reuse browser across tests for 19x speedup
137
- - Use official Puppeteer test assets
138
-
139
- ### [RSpec: pending vs skip](rspec_pending_vs_skip.md)
140
-
141
- Documenting browser limitations.
142
-
143
- **Key concepts:**
144
- - Use `pending` for Firefox BiDi limitations
145
- - Use `skip` for unimplemented features
146
-
147
- ### [Test Server Routes](test_server_routes.md)
148
-
149
- Dynamic route handling for tests.
150
-
151
- **Key concepts:**
152
- - `server.set_route(path)` for intercepting requests
153
- - `server.wait_for_request(path)` for synchronization
154
-
155
- ## Related Documentation
156
-
157
- - **Main guide**: [CLAUDE.md](../CLAUDE.md) - High-level development guide
158
- - **Core layer**: [lib/puppeteer/bidi/core/README.md](../lib/puppeteer/bidi/core/README.md) - Core BiDi abstraction layer
@@ -1,158 +0,0 @@
1
- # Async Programming with socketry/async
2
-
3
- This project uses the [socketry/async](https://github.com/socketry/async) library for asynchronous operations.
4
-
5
- ## Why Async Instead of concurrent-ruby?
6
-
7
- **IMPORTANT**: This project uses `Async` (Fiber-based), **NOT** `concurrent-ruby` (Thread-based).
8
-
9
- | Feature | Async (Fiber-based) | concurrent-ruby (Thread-based) |
10
- | --------------------- | ------------------------------------------------------ | -------------------------------------- |
11
- | **Concurrency Model** | Cooperative multitasking (like JavaScript async/await) | Preemptive multitasking |
12
- | **Race Conditions** | Not possible within a Fiber | Requires Mutex, locks, etc. |
13
- | **Synchronization** | Not needed (cooperative) | Required (Mutex, Semaphore) |
14
- | **Mental Model** | Similar to JavaScript async/await | Traditional thread programming |
15
- | **Bug Risk** | Lower (no race conditions) | Higher (race conditions, deadlocks) |
16
-
17
- **Key advantages:**
18
-
19
- - **No race conditions**: Fibers yield control cooperatively, so no concurrent access to shared state
20
- - **No Mutex needed**: Since there are no race conditions, no synchronization primitives required
21
- - **Similar to JavaScript**: If you understand `async/await` in JavaScript, you understand Async in Ruby
22
- - **Easier to reason about**: Code executes sequentially within a Fiber until it explicitly yields
23
-
24
- **Example:**
25
-
26
- ```ruby
27
- # DON'T: Use concurrent-ruby (Thread-based, requires Mutex)
28
- require 'concurrent'
29
- @pending = Concurrent::Map.new # Thread-safe map
30
- promise = Concurrent::Promises.resolvable_future
31
- promise.fulfill(value)
32
-
33
- # DO: Use Async (Fiber-based, no synchronization needed)
34
- require 'async/promise'
35
- @pending = {} # Plain Hash is safe with Fibers
36
- promise = Async::Promise.new
37
- promise.resolve(value)
38
- ```
39
-
40
- ## Best Practices
41
-
42
- 1. **Use `Sync` at top level**: When running async code at the top level of a thread or application, use `Sync { }` instead of `Async { }`
43
-
44
- ```ruby
45
- Thread.new do
46
- Sync do
47
- # async operations here
48
- end
49
- end
50
- ```
51
-
52
- 2. **Reactor lifecycle**: The reactor is automatically managed by `Sync { }`. No need to create explicit `Async::Reactor` instances in application code.
53
-
54
- 3. **Background operations**: For long-running background tasks (like WebSocket connections), wrap `Sync { }` in a Thread:
55
-
56
- ```ruby
57
- connection_task = Thread.new do
58
- Sync do
59
- transport.connect # Async operation that blocks until connection closes
60
- end
61
- end
62
- ```
63
-
64
- 4. **Promise usage**: Use `Async::Promise` for async coordination:
65
-
66
- ```ruby
67
- promise = Async::Promise.new
68
-
69
- # Resolve the promise
70
- promise.resolve(value)
71
-
72
- # Wait for the promise with timeout
73
- Async do |task|
74
- task.with_timeout(5) do
75
- result = promise.wait
76
- end
77
- end.wait
78
- ```
79
-
80
- 5. **No Mutex needed**: Since Async is Fiber-based, you don't need Mutex for shared state within the same event loop
81
-
82
- ## AsyncUtils: Promise.all and Promise.race
83
-
84
- The `lib/puppeteer/bidi/async_utils.rb` module provides JavaScript-like Promise utilities:
85
-
86
- ```ruby
87
- # Promise.all - Wait for all tasks to complete
88
- results = AsyncUtils.promise_all(
89
- -> { sleep 0.1; 'first' },
90
- -> { sleep 0.2; 'second' },
91
- -> { sleep 0.05; 'third' }
92
- )
93
- # => ['first', 'second', 'third'] (in order, runs in parallel)
94
-
95
- # Promise.race - Return the first to complete
96
- result = AsyncUtils.promise_race(
97
- -> { sleep 0.3; 'slow' },
98
- -> { sleep 0.1; 'fast' },
99
- -> { sleep 0.2; 'medium' }
100
- )
101
- # => 'fast' (cancels remaining tasks)
102
- ```
103
-
104
- **When to use AsyncUtils:**
105
-
106
- - **Parallel task execution**: Running multiple independent async operations
107
- - **Racing timeouts**: First of multiple operations to complete
108
- - **NOT for event-driven waiting**: Use `Async::Promise` directly for event listeners
109
-
110
- ## WebSocket Message Handling Pattern
111
-
112
- **CRITICAL**: BiDi message handling must use `Async do` to process messages asynchronously:
113
-
114
- ```ruby
115
- # lib/puppeteer/bidi/transport.rb
116
- while (message = connection.read)
117
- next if message.nil?
118
-
119
- # DO: Use Async do for non-blocking message processing
120
- Async do
121
- data = JSON.parse(message)
122
- debug_print_receive(data)
123
- @on_message&.call(data)
124
- rescue StandardError => e
125
- # Handle errors
126
- end
127
- end
128
- ```
129
-
130
- **Why this matters:**
131
-
132
- - **Without `Async do`**: Message processing blocks the message loop, preventing other messages from being read
133
- - **With `Async do`**: Each message is processed in a separate fiber, allowing concurrent message handling
134
- - **Prevents deadlocks**: When multiple operations are waiting for responses, they can all be processed concurrently
135
-
136
- **Example of the problem this solves:**
137
-
138
- ```ruby
139
- # Without Async do:
140
- # 1. Message A arrives and starts processing
141
- # 2. Processing A calls wait_for_navigation which waits for Message B
142
- # 3. Message B arrives but can't be read because Message A is still being processed
143
- # 4. DEADLOCK
144
-
145
- # With Async do:
146
- # 1. Message A arrives and starts processing in Fiber 1
147
- # 2. Fiber 1 yields when calling wait (cooperative multitasking)
148
- # 3. Message B can now be read and processed in Fiber 2
149
- # 4. Both messages complete successfully
150
- ```
151
-
152
- This pattern is essential for the BiDi protocol's bidirectional communication model.
153
-
154
- ## References
155
-
156
- - [Async Best Practices](https://socketry.github.io/async/guides/best-practices/)
157
- - [Async Documentation](https://socketry.github.io/async/)
158
- - [Async::Barrier Guide](https://socketry.github.io/async/guides/tasks/index.html)