puppeteer-bidi 0.0.2 → 0.0.3.beta2

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: 7242d594b90af9ed2739ef56e234d3d26805cf19d688212e9b0b68ad823a96c2
4
- data.tar.gz: 6bca1fe1f82165851376466367cee72ec59f10fabb6b2970a70bcffde5ef42b6
3
+ metadata.gz: 72e6358ce92df4d8ecc3da2fd620b05e05bf0a2d72140dc9f2922e586ae2a356
4
+ data.tar.gz: 7c461d352c323edda5ee2c8ff83004c9ddf8be5fe2f3cb56fb00178a6d9cd3e1
5
5
  SHA512:
6
- metadata.gz: 1c7f73fa6f5351d1e1e12a87388e56c05faa11286bf724e400ec97e19092a81a6e22c6718970c028305717afdaf560ddd16dbfa6f499b5dbc28313b8b55d3347
7
- data.tar.gz: ad2cc2f58a6da1d6bff87bde5eab283536f46aa160bbc3ac197c6574f9fd5f35950ca47331c9363feea4a93eaa8af76322f38b123e31d9212206d01e01d2d9d4
6
+ metadata.gz: 4e5096f1f616ffa1f5f0a06b390a9fa79cab0a4bf5ffd38f36ee2f2af8de4ee3e8c75b6270cb6fec239164f3363ccd2c2c71bb7ccf81962201324dd4cf8c46e8
7
+ data.tar.gz: eb1fd0a20857db922cea98ec9bd8740baed85d57414ae2358adeb6d1d9dd0894f298f77ce8b8339405e52368cc7079fb44428a78f982e54bc3564a39659b0469
data/README.md CHANGED
@@ -78,9 +78,13 @@ Puppeteer::Bidi.launch(
78
78
  ### Connect to an existing browser
79
79
 
80
80
  ```ruby
81
- browser = Puppeteer::Bidi.launch_browser_instance(headless: true)
82
- ws_endpoint = browser.ws_endpoint
83
- browser.disconnect
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
 
@@ -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
@@ -92,7 +94,7 @@ module Puppeteer
92
94
  # Start transport connection in background thread with Sync reactor
93
95
  # Sync is the preferred way to run async code at the top level
94
96
  timeout_ms = ((timeout || 30) * 1000).to_i
95
- AsyncUtils.async_timeout(timeout_ms, transport.connect).wait
97
+ AsyncUtils.async_timeout(timeout_ms) { transport.connect }.wait
96
98
 
97
99
  connection = Connection.new(transport)
98
100
 
@@ -111,7 +113,7 @@ module Puppeteer
111
113
  transport = Transport.new(ws_endpoint)
112
114
  ws_endpoint = transport.url
113
115
  timeout_ms = ((timeout || 30) * 1000).to_i
114
- AsyncUtils.async_timeout(timeout_ms, transport.connect).wait
116
+ AsyncUtils.async_timeout(timeout_ms) { transport.connect }.wait
115
117
  connection = Connection.new(transport)
116
118
 
117
119
  # Verify that this endpoint speaks WebDriver BiDi (and is ready) before creating a new session.
@@ -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
 
@@ -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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Puppeteer
4
4
  module Bidi
5
- VERSION = "0.0.2"
5
+ VERSION = "0.0.3.beta2"
6
6
  end
7
7
  end
@@ -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 (if no block given)
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
- Browser.launch(
82
- executable_path: executable_path,
83
- user_data_dir: user_data_dir,
84
- headless: headless,
85
- args: args,
86
- timeout: timeout,
87
- accept_insecure_certs: accept_insecure_certs
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
- Browser.connect(ws_endpoint, timeout: timeout, accept_insecure_certs: accept_insecure_certs)
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
@@ -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
@@ -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
@@ -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 (if no block given)
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.2
4
+ version: 0.0.3.beta2
5
5
  platform: ruby
6
6
  authors:
7
7
  - YusukeIwaki
@@ -79,28 +79,6 @@ files:
79
79
  - ".rubocop.yml"
80
80
  - AGENTS.md
81
81
  - 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/rspec_pending_vs_skip.md
99
- - CLAUDE/selector_evaluation.md
100
- - CLAUDE/test_server_routes.md
101
- - CLAUDE/testing_strategy.md
102
- - CLAUDE/two_layer_architecture.md
103
- - CLAUDE/wrapped_element_click.md
104
82
  - DEVELOPMENT.md
105
83
  - LICENSE.txt
106
84
  - README.md
@@ -145,6 +123,7 @@ files:
145
123
  - lib/puppeteer/bidi/mouse.rb
146
124
  - lib/puppeteer/bidi/page.rb
147
125
  - lib/puppeteer/bidi/query_handler.rb
126
+ - lib/puppeteer/bidi/reactor_runner.rb
148
127
  - lib/puppeteer/bidi/realm.rb
149
128
  - lib/puppeteer/bidi/serializer.rb
150
129
  - lib/puppeteer/bidi/target.rb
@@ -191,6 +170,7 @@ files:
191
170
  - sig/puppeteer/bidi/mouse.rbs
192
171
  - sig/puppeteer/bidi/page.rbs
193
172
  - sig/puppeteer/bidi/query_handler.rbs
173
+ - sig/puppeteer/bidi/reactor_runner.rbs
194
174
  - sig/puppeteer/bidi/realm.rbs
195
175
  - sig/puppeteer/bidi/serializer.rbs
196
176
  - sig/puppeteer/bidi/target.rbs