puppeteer-bidi 0.0.2 → 0.0.3.beta1
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 +4 -4
- data/CLAUDE/reactor_runner.md +111 -0
- data/CLAUDE.md +1 -0
- data/README.md +7 -3
- data/Rakefile +7 -0
- data/lib/puppeteer/bidi/browser.rb +2 -2
- data/lib/puppeteer/bidi/reactor_runner.rb +183 -0
- data/lib/puppeteer/bidi/version.rb +1 -1
- data/lib/puppeteer/bidi.rb +57 -10
- data/sig/_external.rbs +15 -0
- data/sig/puppeteer/bidi/reactor_runner.rbs +65 -0
- data/sig/puppeteer/bidi.rbs +4 -1
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 56fae0e1e155b3fe5bcce854ac7ff615c86af3815b0b9b186bf97fa905421a00
|
|
4
|
+
data.tar.gz: 3b13efc226d2a7d84da91056679707ff3d0b3a5d511f2211c9cfc4da1c0fef23
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: abd414f479202d7f642c6f3b4b0ce3db308a18c5116bd4545483b88cf03193cdfb49e41931e596b3cb99c7699ac8f6773ba4ffff7bf160dd8f0a165745f6d0cc
|
|
7
|
+
data.tar.gz: 62c840fafb7844a586286eb5776b58763e2d7d3547194233e74e75d997aeb239d875fa263b20dbdfe0a326bc2608748c5c4cc479148299096a6b11f43d116cca
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# ReactorRunner - Using Browser Outside Sync Blocks
|
|
2
|
+
|
|
3
|
+
## Problem
|
|
4
|
+
|
|
5
|
+
The socketry/async library requires all async operations to run inside a `Sync do ... end` block. However, some use cases cannot wrap their entire code in a Sync block:
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
# This pattern doesn't work with plain async:
|
|
9
|
+
browser = Puppeteer::Bidi.launch_browser_instance(headless: true)
|
|
10
|
+
at_exit { browser.close } # Called outside any Sync block!
|
|
11
|
+
|
|
12
|
+
Sync do
|
|
13
|
+
page = browser.new_page
|
|
14
|
+
page.goto("https://example.com")
|
|
15
|
+
end
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The `at_exit` hook runs after the Sync block has finished, so `browser.close` would fail.
|
|
19
|
+
|
|
20
|
+
## Solution: ReactorRunner
|
|
21
|
+
|
|
22
|
+
`ReactorRunner` creates a dedicated Async reactor in a background thread and provides a way to execute code within that reactor from any thread.
|
|
23
|
+
|
|
24
|
+
### How It Works
|
|
25
|
+
|
|
26
|
+
1. **Background Thread with Reactor**: ReactorRunner spawns a new thread that runs `Sync do ... end` with an `Async::Queue` for receiving jobs
|
|
27
|
+
2. **Proxy Pattern**: Returns a `Proxy` object that wraps the real Browser and forwards all method calls through the ReactorRunner
|
|
28
|
+
3. **Automatic Detection**: `launch_browser_instance` and `connect_to_browser_instance` check `Async::Task.current` to decide whether to use ReactorRunner
|
|
29
|
+
|
|
30
|
+
### Architecture
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Main Thread Background Thread (ReactorRunner)
|
|
34
|
+
│ │
|
|
35
|
+
│ launch_browser_instance() │
|
|
36
|
+
│ ─────────────────────────────────>│ Sync do
|
|
37
|
+
│ │ Browser.launch()
|
|
38
|
+
│ <─────────────────────────────────│ (browser created)
|
|
39
|
+
│ returns Proxy │
|
|
40
|
+
│ │
|
|
41
|
+
│ proxy.new_page() │
|
|
42
|
+
│ ─────────────────────────────────>│ browser.new_page()
|
|
43
|
+
│ <─────────────────────────────────│ (returns page)
|
|
44
|
+
│ │
|
|
45
|
+
│ at_exit { proxy.close } │
|
|
46
|
+
│ ─────────────────────────────────>│ browser.close()
|
|
47
|
+
│ │ end
|
|
48
|
+
│ │
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Key Components
|
|
52
|
+
|
|
53
|
+
#### ReactorRunner
|
|
54
|
+
|
|
55
|
+
- Creates background thread with `Sync` reactor
|
|
56
|
+
- Uses `Async::Queue` to receive jobs from other threads
|
|
57
|
+
- `sync(&block)` method executes block in reactor and returns result
|
|
58
|
+
- Handles proper cleanup when closed
|
|
59
|
+
|
|
60
|
+
#### ReactorRunner::Proxy
|
|
61
|
+
|
|
62
|
+
- Extends `SimpleDelegator` for transparent method forwarding
|
|
63
|
+
- Wraps/unwraps return values (e.g., Page becomes Proxy too)
|
|
64
|
+
- `owns_runner: true` means closing browser also closes the ReactorRunner
|
|
65
|
+
- Handles edge cases like calling `close` after runner is already closed
|
|
66
|
+
|
|
67
|
+
### Usage Patterns
|
|
68
|
+
|
|
69
|
+
#### Pattern 1: Block-based (Recommended)
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
Puppeteer::Bidi.launch do |browser|
|
|
73
|
+
page = browser.new_page
|
|
74
|
+
# ... use browser
|
|
75
|
+
end # automatically closed
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### Pattern 2: Instance with at_exit
|
|
79
|
+
|
|
80
|
+
```ruby
|
|
81
|
+
browser = Puppeteer::Bidi.launch_browser_instance(headless: true)
|
|
82
|
+
at_exit { browser.close }
|
|
83
|
+
|
|
84
|
+
Sync do
|
|
85
|
+
page = browser.new_page
|
|
86
|
+
page.goto("https://example.com")
|
|
87
|
+
end
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### Pattern 3: Inside existing Async context
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
Sync do
|
|
94
|
+
# No ReactorRunner used - browser is returned directly
|
|
95
|
+
browser = Puppeteer::Bidi.launch_browser_instance(headless: true)
|
|
96
|
+
page = browser.new_page
|
|
97
|
+
# ...
|
|
98
|
+
browser.close
|
|
99
|
+
end
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Implementation Notes
|
|
103
|
+
|
|
104
|
+
1. **Thread Safety**: `Async::Queue` handles cross-thread communication safely
|
|
105
|
+
2. **Proxyable Check**: Only `Puppeteer::Bidi::*` objects (excluding Core layer) are wrapped in Proxy
|
|
106
|
+
3. **Error Handling**: Errors in reactor are propagated back to calling thread via `Async::Promise`
|
|
107
|
+
4. **Type Annotations**: Return type is `Browser` (Proxy is an internal detail)
|
|
108
|
+
|
|
109
|
+
### Reference
|
|
110
|
+
|
|
111
|
+
This pattern is inspired by [async-webdriver](https://github.com/socketry/async-webdriver) by Samuel Williams (author of socketry/async).
|
data/CLAUDE.md
CHANGED
|
@@ -186,6 +186,7 @@ See the [CLAUDE/](CLAUDE/) directory for detailed implementation guides:
|
|
|
186
186
|
|
|
187
187
|
- **[Two-Layer Architecture](CLAUDE/two_layer_architecture.md)** - Core vs Upper layer, async patterns
|
|
188
188
|
- **[Async Programming](CLAUDE/async_programming.md)** - Fiber-based concurrency with socketry/async
|
|
189
|
+
- **[ReactorRunner](CLAUDE/reactor_runner.md)** - Using browser outside Sync blocks (at_exit, etc.)
|
|
189
190
|
- **[Porting Puppeteer](CLAUDE/porting_puppeteer.md)** - Best practices for implementing features
|
|
190
191
|
- **[Core Layer Gotchas](CLAUDE/core_layer_gotchas.md)** - EventEmitter/Disposable pitfalls, @disposed conflicts
|
|
191
192
|
|
data/README.md
CHANGED
|
@@ -78,9 +78,13 @@ Puppeteer::Bidi.launch(
|
|
|
78
78
|
### Connect to an existing browser
|
|
79
79
|
|
|
80
80
|
```ruby
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
ws_endpoint = nil
|
|
82
|
+
|
|
83
|
+
Sync do
|
|
84
|
+
browser = Puppeteer::Bidi.launch_browser_instance(headless: true)
|
|
85
|
+
ws_endpoint = browser.ws_endpoint
|
|
86
|
+
browser.disconnect
|
|
87
|
+
end
|
|
84
88
|
|
|
85
89
|
Puppeteer::Bidi.connect(ws_endpoint) do |session|
|
|
86
90
|
puts "Reconnected to browser"
|
data/Rakefile
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
4
|
require "rspec/core/rake_task"
|
|
5
|
+
require 'rake/testtask'
|
|
6
|
+
|
|
7
|
+
Rake::TestTask.new(:test) do |t|
|
|
8
|
+
t.libs << "test"
|
|
9
|
+
t.test_files = FileList['test/**/*_test.rb']
|
|
10
|
+
t.verbose = true
|
|
11
|
+
end
|
|
5
12
|
|
|
6
13
|
RSpec::Core::RakeTask.new(:spec)
|
|
7
14
|
|
|
@@ -92,7 +92,7 @@ module Puppeteer
|
|
|
92
92
|
# Start transport connection in background thread with Sync reactor
|
|
93
93
|
# Sync is the preferred way to run async code at the top level
|
|
94
94
|
timeout_ms = ((timeout || 30) * 1000).to_i
|
|
95
|
-
AsyncUtils.async_timeout(timeout_ms
|
|
95
|
+
AsyncUtils.async_timeout(timeout_ms) { transport.connect }.wait
|
|
96
96
|
|
|
97
97
|
connection = Connection.new(transport)
|
|
98
98
|
|
|
@@ -111,7 +111,7 @@ module Puppeteer
|
|
|
111
111
|
transport = Transport.new(ws_endpoint)
|
|
112
112
|
ws_endpoint = transport.url
|
|
113
113
|
timeout_ms = ((timeout || 30) * 1000).to_i
|
|
114
|
-
AsyncUtils.async_timeout(timeout_ms
|
|
114
|
+
AsyncUtils.async_timeout(timeout_ms) { transport.connect }.wait
|
|
115
115
|
connection = Connection.new(transport)
|
|
116
116
|
|
|
117
117
|
# Verify that this endpoint speaks WebDriver BiDi (and is ready) before creating a new session.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# rbs_inline: enabled
|
|
3
|
+
|
|
4
|
+
require "async"
|
|
5
|
+
require "async/promise"
|
|
6
|
+
require "async/queue"
|
|
7
|
+
require "delegate"
|
|
8
|
+
require "thread"
|
|
9
|
+
|
|
10
|
+
module Puppeteer
|
|
11
|
+
module Bidi
|
|
12
|
+
# Runs a dedicated Async reactor in a background thread and proxies calls into it.
|
|
13
|
+
class ReactorRunner
|
|
14
|
+
class Proxy < SimpleDelegator
|
|
15
|
+
# @rbs runner: ReactorRunner -- Reactor runner
|
|
16
|
+
# @rbs target: untyped -- Target object to proxy
|
|
17
|
+
# @rbs owns_runner: bool -- Whether to close runner on close/disconnect
|
|
18
|
+
# @rbs return: void
|
|
19
|
+
def initialize(runner, target, owns_runner: false)
|
|
20
|
+
super(target)
|
|
21
|
+
@runner = runner
|
|
22
|
+
@owns_runner = owns_runner
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def method_missing(name, *args, **kwargs, &block)
|
|
26
|
+
if @owns_runner && @runner.closed? && close_like?(name)
|
|
27
|
+
return nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
@runner.sync do
|
|
32
|
+
args = args.map { |arg| @runner.unwrap(arg) }
|
|
33
|
+
kwargs = kwargs.transform_values { |value| @runner.unwrap(value) }
|
|
34
|
+
result = __getobj__.public_send(name, *args, **kwargs, &block)
|
|
35
|
+
@runner.wrap(result)
|
|
36
|
+
end
|
|
37
|
+
ensure
|
|
38
|
+
@runner.close if @owns_runner && close_like?(name)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def respond_to_missing?(name, include_private = false)
|
|
43
|
+
__getobj__.respond_to?(name, include_private) || super
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def class
|
|
47
|
+
__getobj__.class
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def is_a?(klass)
|
|
51
|
+
__getobj__.is_a?(klass)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
alias kind_of? is_a?
|
|
55
|
+
|
|
56
|
+
def instance_of?(klass)
|
|
57
|
+
__getobj__.instance_of?(klass)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def ==(other)
|
|
61
|
+
__getobj__ == @runner.unwrap(other)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def eql?(other)
|
|
65
|
+
__getobj__.eql?(@runner.unwrap(other))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def hash
|
|
69
|
+
__getobj__.hash
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def close_like?(name)
|
|
75
|
+
name == :close || name == :disconnect
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @rbs return: void
|
|
80
|
+
def initialize
|
|
81
|
+
@queue = Async::Queue.new
|
|
82
|
+
@ready = Queue.new
|
|
83
|
+
@closed = false
|
|
84
|
+
@thread = Thread.new do
|
|
85
|
+
Sync do |task|
|
|
86
|
+
@ready << true
|
|
87
|
+
@queue.async(parent: task) do |_async_task, job|
|
|
88
|
+
job.call
|
|
89
|
+
end
|
|
90
|
+
ensure
|
|
91
|
+
@closed = true
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
@ready.pop
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# @rbs &block: () -> untyped
|
|
99
|
+
# @rbs return: untyped
|
|
100
|
+
def sync(&block)
|
|
101
|
+
return block.call if runner_thread?
|
|
102
|
+
raise Error, "ReactorRunner is closed" if closed?
|
|
103
|
+
|
|
104
|
+
promise = Async::Promise.new
|
|
105
|
+
job = lambda do
|
|
106
|
+
begin
|
|
107
|
+
promise.resolve(block.call)
|
|
108
|
+
rescue => e
|
|
109
|
+
promise.reject(e)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
begin
|
|
114
|
+
@queue << job
|
|
115
|
+
rescue Async::Queue::ClosedError
|
|
116
|
+
raise Error, "ReactorRunner is closed"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
promise.wait
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @rbs return: void
|
|
123
|
+
def close
|
|
124
|
+
return if closed?
|
|
125
|
+
|
|
126
|
+
@closed = true
|
|
127
|
+
@queue.close
|
|
128
|
+
@thread.join unless runner_thread?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# @rbs return: bool
|
|
132
|
+
def closed?
|
|
133
|
+
@closed
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# @rbs value: untyped
|
|
137
|
+
# @rbs return: untyped
|
|
138
|
+
def wrap(value)
|
|
139
|
+
return value if value.nil? || value.is_a?(Proxy)
|
|
140
|
+
|
|
141
|
+
if value.is_a?(Array)
|
|
142
|
+
return value.map { |item| wrap(item) }
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
return Proxy.new(self, value) if proxyable?(value)
|
|
146
|
+
|
|
147
|
+
value
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# @rbs value: untyped
|
|
151
|
+
# @rbs return: untyped
|
|
152
|
+
def unwrap(value)
|
|
153
|
+
case value
|
|
154
|
+
when Proxy
|
|
155
|
+
value.__getobj__
|
|
156
|
+
when Array
|
|
157
|
+
value.map { |item| unwrap(item) }
|
|
158
|
+
when Hash
|
|
159
|
+
value.transform_values { |item| unwrap(item) }
|
|
160
|
+
else
|
|
161
|
+
value
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private
|
|
166
|
+
|
|
167
|
+
def runner_thread?
|
|
168
|
+
Thread.current == @thread
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def proxyable?(value)
|
|
172
|
+
return false if value.is_a?(Module) || value.is_a?(Class)
|
|
173
|
+
|
|
174
|
+
name = value.class.name
|
|
175
|
+
return false unless name&.start_with?("Puppeteer::Bidi")
|
|
176
|
+
return false if name.start_with?("Puppeteer::Bidi::Core")
|
|
177
|
+
return false if value.is_a?(ReactorRunner) || value.is_a?(Proxy)
|
|
178
|
+
|
|
179
|
+
true
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
data/lib/puppeteer/bidi.rb
CHANGED
|
@@ -5,6 +5,7 @@ require "puppeteer/bidi/version"
|
|
|
5
5
|
require "puppeteer/bidi/errors"
|
|
6
6
|
|
|
7
7
|
require "puppeteer/bidi/async_utils"
|
|
8
|
+
require "puppeteer/bidi/reactor_runner"
|
|
8
9
|
require "puppeteer/bidi/timeout_settings"
|
|
9
10
|
require "puppeteer/bidi/task_manager"
|
|
10
11
|
require "puppeteer/bidi/serializer"
|
|
@@ -75,17 +76,39 @@ module Puppeteer
|
|
|
75
76
|
# @rbs args: Array[String]? -- Additional browser arguments
|
|
76
77
|
# @rbs timeout: Numeric? -- Launch timeout in seconds
|
|
77
78
|
# @rbs accept_insecure_certs: bool -- Accept insecure certificates
|
|
78
|
-
# @rbs return: Browser -- Browser instance
|
|
79
|
+
# @rbs return: Browser -- Browser instance
|
|
79
80
|
def self.launch_browser_instance(executable_path: nil, user_data_dir: nil, headless: true, args: nil, timeout: nil,
|
|
80
81
|
accept_insecure_certs: false)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
82
|
+
if async_context?
|
|
83
|
+
Browser.launch(
|
|
84
|
+
executable_path: executable_path,
|
|
85
|
+
user_data_dir: user_data_dir,
|
|
86
|
+
headless: headless,
|
|
87
|
+
args: args,
|
|
88
|
+
timeout: timeout,
|
|
89
|
+
accept_insecure_certs: accept_insecure_certs
|
|
90
|
+
)
|
|
91
|
+
else
|
|
92
|
+
runner = ReactorRunner.new
|
|
93
|
+
begin
|
|
94
|
+
browser = runner.sync do
|
|
95
|
+
Browser.launch(
|
|
96
|
+
executable_path: executable_path,
|
|
97
|
+
user_data_dir: user_data_dir,
|
|
98
|
+
headless: headless,
|
|
99
|
+
args: args,
|
|
100
|
+
timeout: timeout,
|
|
101
|
+
accept_insecure_certs: accept_insecure_certs
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
rescue StandardError
|
|
105
|
+
runner.close
|
|
106
|
+
raise
|
|
107
|
+
end
|
|
108
|
+
# @type var proxy: Browser
|
|
109
|
+
proxy = ReactorRunner::Proxy.new(runner, browser, owns_runner: true)
|
|
110
|
+
proxy
|
|
111
|
+
end
|
|
89
112
|
end
|
|
90
113
|
|
|
91
114
|
# Connect to an existing browser instance
|
|
@@ -116,7 +139,31 @@ module Puppeteer
|
|
|
116
139
|
# @rbs accept_insecure_certs: bool -- Accept insecure certificates
|
|
117
140
|
# @rbs return: Browser -- Browser instance
|
|
118
141
|
def self.connect_to_browser_instance(ws_endpoint, timeout: nil, accept_insecure_certs: false)
|
|
119
|
-
|
|
142
|
+
if async_context?
|
|
143
|
+
Browser.connect(ws_endpoint, timeout: timeout, accept_insecure_certs: accept_insecure_certs)
|
|
144
|
+
else
|
|
145
|
+
runner = ReactorRunner.new
|
|
146
|
+
begin
|
|
147
|
+
browser = runner.sync do
|
|
148
|
+
Browser.connect(ws_endpoint, timeout: timeout, accept_insecure_certs: accept_insecure_certs)
|
|
149
|
+
end
|
|
150
|
+
rescue StandardError
|
|
151
|
+
runner.close
|
|
152
|
+
raise
|
|
153
|
+
end
|
|
154
|
+
# @type var proxy: Browser
|
|
155
|
+
proxy = ReactorRunner::Proxy.new(runner, browser, owns_runner: true)
|
|
156
|
+
proxy
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# @rbs return: bool -- Whether we're inside an Async task
|
|
161
|
+
def self.async_context?
|
|
162
|
+
task = Async::Task.current
|
|
163
|
+
!task.nil?
|
|
164
|
+
rescue RuntimeError, NoMethodError
|
|
165
|
+
false
|
|
120
166
|
end
|
|
167
|
+
private_class_method :async_context?
|
|
121
168
|
end
|
|
122
169
|
end
|
data/sig/_external.rbs
CHANGED
|
@@ -11,12 +11,22 @@ module Async
|
|
|
11
11
|
def self.call: [T] () { () -> T } -> Task[T]
|
|
12
12
|
|
|
13
13
|
class Task[T]
|
|
14
|
+
def self.current: () -> Task[untyped]?
|
|
14
15
|
def wait: () -> T
|
|
15
16
|
def sleep: (Numeric) -> void
|
|
16
17
|
def stop: () -> void
|
|
17
18
|
def with_timeout: [U] (Numeric) { () -> U } -> U
|
|
18
19
|
end
|
|
19
20
|
|
|
21
|
+
class Queue[T]
|
|
22
|
+
class ClosedError < StandardError
|
|
23
|
+
end
|
|
24
|
+
def initialize: () -> void
|
|
25
|
+
def <<: (T) -> void
|
|
26
|
+
def close: () -> void
|
|
27
|
+
def async: [U] (?parent: Task[untyped]?) { (Task[untyped], T) -> U } -> void
|
|
28
|
+
end
|
|
29
|
+
|
|
20
30
|
class Promise[T]
|
|
21
31
|
def initialize: () -> void
|
|
22
32
|
def wait: () -> T
|
|
@@ -126,3 +136,8 @@ class DateTime
|
|
|
126
136
|
def to_time: () -> Time
|
|
127
137
|
end
|
|
128
138
|
|
|
139
|
+
# Delegate library
|
|
140
|
+
class SimpleDelegator
|
|
141
|
+
def __getobj__: () -> untyped
|
|
142
|
+
def __setobj__: (untyped) -> untyped
|
|
143
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Generated from lib/puppeteer/bidi/reactor_runner.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module Puppeteer
|
|
4
|
+
module Bidi
|
|
5
|
+
# Runs a dedicated Async reactor in a background thread and proxies calls into it.
|
|
6
|
+
class ReactorRunner
|
|
7
|
+
class Proxy < SimpleDelegator
|
|
8
|
+
# @rbs runner: ReactorRunner -- Reactor runner
|
|
9
|
+
# @rbs target: untyped -- Target object to proxy
|
|
10
|
+
# @rbs owns_runner: bool -- Whether to close runner on close/disconnect
|
|
11
|
+
# @rbs return: void
|
|
12
|
+
def initialize: (ReactorRunner runner, untyped target, ?owns_runner: bool) -> void
|
|
13
|
+
|
|
14
|
+
def method_missing: (untyped name, *untyped args, **untyped kwargs) ?{ (?) -> untyped } -> untyped
|
|
15
|
+
|
|
16
|
+
def respond_to_missing?: (untyped name, ?untyped include_private) -> untyped
|
|
17
|
+
|
|
18
|
+
def class: () -> untyped
|
|
19
|
+
|
|
20
|
+
def is_a?: (untyped klass) -> untyped
|
|
21
|
+
|
|
22
|
+
alias kind_of? is_a?
|
|
23
|
+
|
|
24
|
+
def instance_of?: (untyped klass) -> untyped
|
|
25
|
+
|
|
26
|
+
def ==: (untyped other) -> untyped
|
|
27
|
+
|
|
28
|
+
def eql?: (untyped other) -> untyped
|
|
29
|
+
|
|
30
|
+
def hash: () -> untyped
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def close_like?: (untyped name) -> untyped
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @rbs return: void
|
|
38
|
+
def initialize: () -> void
|
|
39
|
+
|
|
40
|
+
# @rbs &block: () -> untyped
|
|
41
|
+
# @rbs return: untyped
|
|
42
|
+
def sync: () { () -> untyped } -> untyped
|
|
43
|
+
|
|
44
|
+
# @rbs return: void
|
|
45
|
+
def close: () -> void
|
|
46
|
+
|
|
47
|
+
# @rbs return: bool
|
|
48
|
+
def closed?: () -> bool
|
|
49
|
+
|
|
50
|
+
# @rbs value: untyped
|
|
51
|
+
# @rbs return: untyped
|
|
52
|
+
def wrap: (untyped value) -> untyped
|
|
53
|
+
|
|
54
|
+
# @rbs value: untyped
|
|
55
|
+
# @rbs return: untyped
|
|
56
|
+
def unwrap: (untyped value) -> untyped
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def runner_thread?: () -> untyped
|
|
61
|
+
|
|
62
|
+
def proxyable?: (untyped value) -> untyped
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/sig/puppeteer/bidi.rbs
CHANGED
|
@@ -20,7 +20,7 @@ module Puppeteer
|
|
|
20
20
|
# @rbs args: Array[String]? -- Additional browser arguments
|
|
21
21
|
# @rbs timeout: Numeric? -- Launch timeout in seconds
|
|
22
22
|
# @rbs accept_insecure_certs: bool -- Accept insecure certificates
|
|
23
|
-
# @rbs return: Browser -- Browser instance
|
|
23
|
+
# @rbs return: Browser -- Browser instance
|
|
24
24
|
def self.launch_browser_instance: (?executable_path: String?, ?user_data_dir: String?, ?headless: bool, ?args: Array[String]?, ?timeout: Numeric?, ?accept_insecure_certs: bool) -> Browser
|
|
25
25
|
|
|
26
26
|
# Connect to an existing browser instance
|
|
@@ -37,5 +37,8 @@ module Puppeteer
|
|
|
37
37
|
# @rbs accept_insecure_certs: bool -- Accept insecure certificates
|
|
38
38
|
# @rbs return: Browser -- Browser instance
|
|
39
39
|
def self.connect_to_browser_instance: (String ws_endpoint, ?timeout: Numeric?, ?accept_insecure_certs: bool) -> Browser
|
|
40
|
+
|
|
41
|
+
# @rbs return: bool -- Whether we're inside an Async task
|
|
42
|
+
def self.async_context?: () -> bool
|
|
40
43
|
end
|
|
41
44
|
end
|
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.
|
|
4
|
+
version: 0.0.3.beta1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- YusukeIwaki
|
|
@@ -95,6 +95,7 @@ files:
|
|
|
95
95
|
- CLAUDE/navigation_waiting.md
|
|
96
96
|
- CLAUDE/porting_puppeteer.md
|
|
97
97
|
- CLAUDE/query_handler.md
|
|
98
|
+
- CLAUDE/reactor_runner.md
|
|
98
99
|
- CLAUDE/rspec_pending_vs_skip.md
|
|
99
100
|
- CLAUDE/selector_evaluation.md
|
|
100
101
|
- CLAUDE/test_server_routes.md
|
|
@@ -145,6 +146,7 @@ files:
|
|
|
145
146
|
- lib/puppeteer/bidi/mouse.rb
|
|
146
147
|
- lib/puppeteer/bidi/page.rb
|
|
147
148
|
- lib/puppeteer/bidi/query_handler.rb
|
|
149
|
+
- lib/puppeteer/bidi/reactor_runner.rb
|
|
148
150
|
- lib/puppeteer/bidi/realm.rb
|
|
149
151
|
- lib/puppeteer/bidi/serializer.rb
|
|
150
152
|
- lib/puppeteer/bidi/target.rb
|
|
@@ -191,6 +193,7 @@ files:
|
|
|
191
193
|
- sig/puppeteer/bidi/mouse.rbs
|
|
192
194
|
- sig/puppeteer/bidi/page.rbs
|
|
193
195
|
- sig/puppeteer/bidi/query_handler.rbs
|
|
196
|
+
- sig/puppeteer/bidi/reactor_runner.rbs
|
|
194
197
|
- sig/puppeteer/bidi/realm.rbs
|
|
195
198
|
- sig/puppeteer/bidi/serializer.rbs
|
|
196
199
|
- sig/puppeteer/bidi/target.rbs
|