bidi2pdf 0.1.5 → 0.1.7

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -1
  3. data/CHANGELOG.md +61 -1
  4. data/README.md +47 -10
  5. data/docker/Dockerfile +11 -3
  6. data/docker/Dockerfile.chromedriver +4 -2
  7. data/docker/Dockerfile.slim +75 -0
  8. data/lib/bidi2pdf/bidi/browser_console_logger.rb +92 -0
  9. data/lib/bidi2pdf/bidi/browser_tab.rb +415 -39
  10. data/lib/bidi2pdf/bidi/client.rb +85 -23
  11. data/lib/bidi2pdf/bidi/command_manager.rb +46 -48
  12. data/lib/bidi2pdf/bidi/commands/base.rb +39 -1
  13. data/lib/bidi2pdf/bidi/commands/browser_remove_user_context.rb +27 -0
  14. data/lib/bidi2pdf/bidi/commands/browsing_context_print.rb +4 -0
  15. data/lib/bidi2pdf/bidi/commands/print_parameters_validator.rb +5 -0
  16. data/lib/bidi2pdf/bidi/commands.rb +1 -0
  17. data/lib/bidi2pdf/bidi/event_manager.rb +1 -1
  18. data/lib/bidi2pdf/bidi/interceptor.rb +1 -1
  19. data/lib/bidi2pdf/bidi/js_logger_helper.rb +16 -0
  20. data/lib/bidi2pdf/bidi/logger_events.rb +66 -0
  21. data/lib/bidi2pdf/bidi/network_event.rb +40 -7
  22. data/lib/bidi2pdf/bidi/network_event_formatters/network_event_console_formatter.rb +110 -0
  23. data/lib/bidi2pdf/bidi/network_event_formatters/network_event_formatter_utils.rb +53 -0
  24. data/lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb +125 -0
  25. data/lib/bidi2pdf/bidi/network_event_formatters.rb +11 -0
  26. data/lib/bidi2pdf/bidi/network_events.rb +46 -17
  27. data/lib/bidi2pdf/bidi/session.rb +120 -13
  28. data/lib/bidi2pdf/bidi/user_context.rb +62 -0
  29. data/lib/bidi2pdf/bidi/web_socket_dispatcher.rb +7 -7
  30. data/lib/bidi2pdf/chromedriver_manager.rb +48 -21
  31. data/lib/bidi2pdf/cli.rb +27 -3
  32. data/lib/bidi2pdf/dsl.rb +33 -0
  33. data/lib/bidi2pdf/launcher.rb +34 -2
  34. data/lib/bidi2pdf/notifications/event.rb +52 -0
  35. data/lib/bidi2pdf/notifications/instrumenter.rb +65 -0
  36. data/lib/bidi2pdf/notifications/logging_subscriber.rb +136 -0
  37. data/lib/bidi2pdf/notifications.rb +78 -0
  38. data/lib/bidi2pdf/session_runner.rb +49 -7
  39. data/lib/bidi2pdf/verbose_logger.rb +79 -0
  40. data/lib/bidi2pdf/version.rb +1 -1
  41. data/lib/bidi2pdf.rb +99 -5
  42. data/sig/bidi2pdf/bidi/client.rbs +1 -1
  43. metadata +45 -4
  44. data/lib/bidi2pdf/utils.rb +0 -15
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Notifications
5
+ # for reuse within ActiveSupport::LogSubscriber
6
+ module LoggingSubscriberActions
7
+ def handle_response(event)
8
+ payload = event.payload
9
+
10
+ if payload[:error]
11
+ logger.error "Received error: #{payload[:error].inspect} for cmd: #{payload[:id] || "-"}"
12
+ elsif !payload[:handled]
13
+ Bidi2pdf.logger.warn "Unknown response: #{payload[:data].inspect}"
14
+ end
15
+ end
16
+
17
+ def send_cmd(event)
18
+ logger.debug "Sending command: #{event.payload[:cmd].method_name} id: ##{event.payload[:cmd_payload][:id]}"
19
+
20
+ logger.debug1 do
21
+ payload = redact_sensitive_fields(event.payload[:cmd_payload])
22
+ "Sending command: #{payload.inspect} (#{event.duration.round(1)}ms)"
23
+ end
24
+ end
25
+
26
+ def send_cmd_and_wait(event)
27
+ return unless event.payload[:exception]
28
+
29
+ payload = redact_sensitive_fields(event.payload[:cmd]&.params || {})
30
+ logger.error "Error sending command: #{payload} (#{event.duration.round(1)}ms) - #{event.payload[:exception].inspect}"
31
+ end
32
+
33
+ def session_close(event)
34
+ return unless event.payload[:error]
35
+
36
+ logger.error "Session close error: #{event.payload[:error].inspect}, attempt: #{event.payload[:attempt]}, retry: #{event.payload[:retry]}"
37
+ end
38
+
39
+ # rubocop: disable Metrics/AbcSize
40
+ def network_event_received(event)
41
+ return unless logger.debug2?
42
+
43
+ msg = case event.payload[:method]
44
+ when "network.beforeRequestSent"
45
+ "Request url '#{event.payload[:url]}' started"
46
+
47
+ when "network.responseStarted"
48
+ nil
49
+ when "network.responseCompleted"
50
+ "Request url '#{event.payload[:url]}' completed"
51
+ when "network.fetchError"
52
+ "Request url '#{event.payload[:url]}' error."
53
+ else
54
+ "Unknown network event: #{event.payload[:method]} for url '#{event.payload[:url]}'"
55
+ end
56
+
57
+ logger.debug2 msg if msg
58
+ end
59
+
60
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
61
+ def network_idle(event)
62
+ return unless logger.info?
63
+
64
+ requests = event.payload[:requests]
65
+ transfered = requests.map { |request| request.bytes_received || 0 }.sum
66
+ status_counts = requests
67
+ .group_by { |evt| evt.http_status_code || 0 }
68
+ .transform_keys { |code| code.zero? || code.nil? ? "pending" : code.to_s }
69
+ .transform_values(&:count)
70
+ .map { |code, count| "#{code}: #{count}" }
71
+ .join(", ")
72
+
73
+ logger.info "Network was idle after #{event.duration.round(1)}ms, #{requests.size} requests, " \
74
+ "transferred #{transfered} bytes (status codes: #{status_counts})"
75
+ end
76
+
77
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
78
+
79
+ def page_loaded(event)
80
+ logger.info "Page loaded: #{event.duration.round(1)}ms"
81
+ end
82
+
83
+ def print(event)
84
+ logger.info "Page printed: #{event.duration.round(1)}ms"
85
+ end
86
+
87
+ private
88
+
89
+ def redact_sensitive_fields(obj, sensitive_keys = %w[value token password authorization username])
90
+ case obj
91
+ when Hash
92
+ obj.transform_values.with_index do |v, idx|
93
+ k = obj.keys[idx]
94
+ sensitive_keys.include?(k.to_s.downcase) ? "[REDACTED]" : redact_sensitive_fields(v, sensitive_keys)
95
+ end
96
+ when Array
97
+ obj.map { |item| redact_sensitive_fields(item, sensitive_keys) }
98
+ else
99
+ obj
100
+ end
101
+ end
102
+ end
103
+
104
+ class LoggingSubscriber
105
+ include LoggingSubscriberActions
106
+
107
+ attr_accessor :logger
108
+
109
+ # rubocop: disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
110
+ def initialize(logger: Logger.new($stdout))
111
+ @logger = logger
112
+ Bidi2pdf.notification_service&.subscribe("handle_response.bidi2pdf", &method(:handle_response))
113
+ Bidi2pdf.notification_service&.subscribe("send_cmd.bidi2pdf", &method(:send_cmd))
114
+ Bidi2pdf.notification_service&.subscribe("send_cmd_and_wait.bidi2pdf", &method(:send_cmd_and_wait))
115
+ Bidi2pdf.notification_service&.subscribe("session_close.bidi2pdf", &method(:session_close))
116
+ Bidi2pdf.notification_service&.subscribe("network_idle.bidi2pdf", &method(:network_idle))
117
+ Bidi2pdf.notification_service&.subscribe("page_loaded.bidi2pdf", &method(:page_loaded))
118
+ Bidi2pdf.notification_service&.subscribe("network_event_received.bidi2pdf", &method(:network_event_received))
119
+ Bidi2pdf.notification_service&.subscribe("print.bidi2pdf", &method(:network_event_received))
120
+ end
121
+
122
+ def unsubscribe
123
+ Bidi2pdf.notification_service&.unsubscribe("handle_response.bidi2pdf", &method(:handle_response))
124
+ Bidi2pdf.notification_service&.unsubscribe("send_cmd.bidi2pdf", &method(:send_cmd))
125
+ Bidi2pdf.notification_service&.unsubscribe("send_cmd_and_wait.bidi2pdf", &method(:send_cmd_and_wait))
126
+ Bidi2pdf.notification_service&.unsubscribe("session_close.bidi2pdf", &method(:session_close))
127
+ Bidi2pdf.notification_service&.unsubscribe("network_idle.bidi2pdf", &method(:network_idle))
128
+ Bidi2pdf.notification_service&.unsubscribe("page_loaded.bidi2pdf", &method(:page_loaded))
129
+ Bidi2pdf.notification_service&.unsubscribe("network_event_received.bidi2pdf", &method(:network_event_received))
130
+ Bidi2pdf.notification_service&.unsubscribe("print.bidi2pdf", &method(:network_event_received))
131
+ end
132
+
133
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "chromedriver_manager"
4
+ require_relative "session_runner"
5
+ require_relative "bidi/session"
6
+ require_relative "notifications/event"
7
+ require_relative "notifications/instrumenter"
8
+
9
+ require "securerandom"
10
+
11
+ module Bidi2pdf
12
+ # This module provides a way to instrument events in the Bidi2pdf library.
13
+ # It it's heavyly inspired by ActiveSupport::Notifications.
14
+ # and thought to be used in a similar way.
15
+ # In Rails environment, ActiveSupport::Notifications should be used instead.
16
+ # via configuration: config.notification_service = ActiveSupport::Notifications
17
+
18
+ module Notifications
19
+ Thread.attr_accessor :bidi2pdf_notification_instrumenter
20
+
21
+ @subscribers = Hash.new { |h, k| h[k] = [] }
22
+
23
+ class << self
24
+ attr_reader :subscribers
25
+
26
+ def instrument(name, payload = {})
27
+ payload = payload.dup
28
+
29
+ if listening?(name)
30
+ notify(name, payload) { yield payload if block_given? }
31
+ elsif block_given?
32
+ yield payload
33
+ end
34
+ end
35
+
36
+ def subscribe(event_pattern, &block)
37
+ pattern = normalize_pattern(event_pattern)
38
+
39
+ @subscribers[pattern] << block
40
+
41
+ block
42
+ end
43
+
44
+ def unsubscribe(event_pattern, block = nil)
45
+ pattern = normalize_pattern(event_pattern)
46
+
47
+ if block
48
+ @subscribers[pattern].delete(block)
49
+ else
50
+ @subscribers[pattern].clear
51
+ end
52
+ end
53
+
54
+ # rubocop: disable Style/CaseEquality
55
+ def listening?(name)
56
+ @subscribers.any? do |pattern, blocks|
57
+ pattern === name && blocks.any?
58
+ end
59
+ end
60
+
61
+ # rubocop: enable Style/CaseEquality
62
+
63
+ private
64
+
65
+ def bidi2pdf_notification_instrumenter = Thread.current.bidi2pdf_notification_instrumenter ||= Instrumenter.new
66
+
67
+ def notify(name, payload, &) = bidi2pdf_notification_instrumenter.notify(name, payload, &)
68
+
69
+ def normalize_pattern(pat)
70
+ case pat
71
+ when String, Regexp then pat
72
+ else
73
+ raise ArgumentError, "Pattern must be String or Regexp"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -1,9 +1,42 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bidi2pdf
4
+ # Represents a runner for managing browser sessions and executing tasks
5
+ # using the Bidi2pdf library. This class handles the setup, configuration,
6
+ # and execution of browser-related workflows, including navigation, cookie
7
+ # management, and printing.
8
+ #
9
+ # @example Running a session
10
+ # session_runner = Bidi2pdf::SessionRunner.new(
11
+ # session: session,
12
+ # url: "http://example.com",
13
+ # inputfile: "input.html",
14
+ # output: "output.pdf",
15
+ # cookies: { "key" => "value" },
16
+ # headers: { "Authorization" => "Bearer token" },
17
+ # auth: { username: "user", password: "pass" },
18
+ # wait_window_loaded: true,
19
+ # wait_network_idle: true,
20
+ # print_options: { landscape: true },
21
+ # network_log_format: :json
22
+ # )
23
+ # session_runner.run
24
+ #
25
+ # @param [Object] session The browser session object to use.
26
+ # @param [String, nil] url The URL to navigate to in the browser session.
27
+ # @param [String, nil] inputfile The path to the input file to be processed if no URL is provided.
28
+ # @param [String, nil] output The path to the output file to be generated.
29
+ # @param [Hash] cookies A hash of cookies to set in the browser session. Defaults to an empty hash.
30
+ # @param [Hash] headers A hash of HTTP headers to include in the browser session. Defaults to an empty hash.
31
+ # @param [Hash, nil] auth Authentication credentials (e.g., username and password). Defaults to an empty hash.
32
+ # @param [Boolean] wait_window_loaded Whether to wait for the window to fully load. Defaults to false.
33
+ # @param [Boolean] wait_network_idle Whether to wait for the network to become idle. Defaults to false.
34
+ # @param [Hash] print_options Options for printing the page. Defaults to an empty hash.
35
+ # @param [Symbol] network_log_format The format for network logs. Defaults to :console.
4
36
  class SessionRunner
37
+ # rubocop: disable Metrics/ParameterLists
5
38
  def initialize(session:, url:, inputfile:, output:, cookies: {}, headers: {}, auth: {}, wait_window_loaded: false,
6
- wait_network_idle: false, print_options: {})
39
+ wait_network_idle: false, print_options: {}, network_log_format: :console)
7
40
  @session = session
8
41
  @url = url
9
42
  @inputfile = inputfile
@@ -14,8 +47,11 @@ module Bidi2pdf
14
47
  @wait_window_loaded = wait_window_loaded
15
48
  @wait_network_idle = wait_network_idle
16
49
  @print_options = print_options || {}
50
+ @network_log_format = network_log_format
17
51
  end
18
52
 
53
+ # rubocop: enable Metrics/ParameterLists
54
+
19
55
  def run
20
56
  @session.start
21
57
  @session.client.on_close { Bidi2pdf.logger.info "WebSocket closed" }
@@ -35,6 +71,7 @@ module Bidi2pdf
35
71
 
36
72
  @window = window
37
73
  @tab = tab
74
+ @user_context = user_context
38
75
 
39
76
  add_cookies(tab)
40
77
 
@@ -72,36 +109,41 @@ module Bidi2pdf
72
109
  )
73
110
  end
74
111
 
112
+ # rubocop: disable Metrics/AbcSize
75
113
  def run_flow
76
114
  @session.status
77
115
  @session.user_contexts
78
116
 
79
117
  if @url
80
- @tab.open_page(@url)
118
+ @tab.navigate_to(@url)
81
119
  else
82
120
  Bidi2pdf.logger.info "Loading HTML file #{@inputfile}"
83
121
  data = File.read(@inputfile)
84
- @tab.view_html_page(data)
122
+ @tab.render_html_content(data)
85
123
  end
86
124
 
87
125
  if @wait_network_idle
88
126
  Bidi2pdf.logger.info "Waiting for network idle"
89
- @tab.wait_until_all_finished
127
+ @tab.wait_until_network_idle
90
128
  end
91
129
 
130
+ log_output_file = (@output || "report").sub(/\.pdf$/, "-network.pdf") # only need, when html output
131
+ @tab.log_network_traffic format: @network_log_format, output: log_output_file
132
+
92
133
  if @wait_window_loaded
93
134
  Bidi2pdf.logger.info "Waiting for window to be loaded"
94
- @tab.execute_script <<-EOF_SCRIPT
95
- new Promise(resolve => { const check = () => window.loaded ? resolve('done') : setTimeout(check, 100); check(); });
96
- EOF_SCRIPT
135
+ @tab.wait_until_page_loaded
97
136
  end
98
137
 
99
138
  @tab.print(@output, print_options: @print_options)
100
139
  ensure
101
140
  @tab.close
102
141
  @window.close
142
+ @user_context.close
103
143
  end
104
144
 
145
+ # rubocop: enable Metrics/AbcSize
146
+
105
147
  def uri
106
148
  @uri ||= URI(@url)
107
149
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ class VerboseLogger < SimpleDelegator
5
+ VERBOSITY_LEVELS = {
6
+ none: 0,
7
+ low: 1,
8
+ medium: 2,
9
+ high: 3
10
+ }.freeze
11
+
12
+ attr_reader :logger, :verbosity
13
+
14
+ def initialize(logger, verbosity = :low)
15
+ super(logger)
16
+ self.verbosity = verbosity
17
+ @logger = logger
18
+ end
19
+
20
+ def verbosity=(verbosity)
21
+ min_verbosity = VERBOSITY_LEVELS.values.min
22
+
23
+ @verbosity = if verbosity.is_a?(Numeric)
24
+ verbosity = verbosity.to_i
25
+ max_verbosity = VERBOSITY_LEVELS.values.max
26
+
27
+ verbosity.clamp(min_verbosity, max_verbosity)
28
+ else
29
+ VERBOSITY_LEVELS.fetch verbosity.to_sym, min_verbosity
30
+ end
31
+ end
32
+
33
+ def verbosity_sym
34
+ VERBOSITY_LEVELS.find { |_, v| v == verbosity }.first
35
+ end
36
+
37
+ def debug1(progname = nil, &)
38
+ return unless debug1?
39
+
40
+ logger.debug("[D1] #{progname}", &)
41
+ end
42
+
43
+ def debug1?
44
+ verbosity >= 1
45
+ end
46
+
47
+ def debug1!
48
+ @verbosity = VERBOSITY_LEVELS[:high]
49
+ end
50
+
51
+ def debug2(progname = nil, &)
52
+ return unless debug2?
53
+
54
+ logger.debug("[D2] #{progname}", &)
55
+ end
56
+
57
+ def debug2?
58
+ verbosity >= 2
59
+ end
60
+
61
+ def debug2!
62
+ @verbosity = VERBOSITY_LEVELS[:high]
63
+ end
64
+
65
+ def debug3(progname = nil, &)
66
+ return unless debug3?
67
+
68
+ logger.debug("[D3] #{progname}", &)
69
+ end
70
+
71
+ def debug3?
72
+ verbosity >= 3
73
+ end
74
+
75
+ def debug3!
76
+ @verbosity = VERBOSITY_LEVELS[:high]
77
+ end
78
+ end
79
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bidi2pdf
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.7"
5
5
  end
data/lib/bidi2pdf.rb CHANGED
@@ -1,14 +1,30 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "bidi2pdf/utils"
4
3
  require_relative "bidi2pdf/process_tree"
5
4
  require_relative "bidi2pdf/launcher"
6
5
  require_relative "bidi2pdf/bidi/session"
7
6
  require_relative "bidi2pdf/dsl"
7
+ require_relative "bidi2pdf/notifications"
8
+ require_relative "bidi2pdf/notifications/logging_subscriber"
9
+ require_relative "bidi2pdf/verbose_logger"
8
10
 
9
11
  require "logger"
10
12
 
11
13
  module Bidi2pdf
14
+ PAPER_FORMATS_CM = {
15
+ letter: { width: 21.59, height: 27.94 },
16
+ legal: { width: 21.59, height: 35.56 },
17
+ tabloid: { width: 27.94, height: 43.18 },
18
+ ledger: { width: 43.18, height: 27.94 },
19
+ a0: { width: 84.1, height: 118.9 },
20
+ a1: { width: 59.4, height: 84.1 },
21
+ a2: { width: 42.0, height: 59.4 },
22
+ a3: { width: 29.7, height: 42.0 },
23
+ a4: { width: 21.0, height: 29.7 },
24
+ a5: { width: 14.8, height: 21.0 },
25
+ a6: { width: 10.5, height: 14.8 }
26
+ }.freeze
27
+
12
28
  class Error < StandardError; end
13
29
 
14
30
  class SessionNotStartedError < Error; end
@@ -25,17 +41,95 @@ module Bidi2pdf
25
41
 
26
42
  class PrintError < Error; end
27
43
 
28
- @logger = Logger.new($stdout)
29
- @logger.level = Logger::INFO
44
+ class ScriptInjectionError < Error; end
45
+
46
+ class StyleInjectionError < Error; end
47
+
48
+ class NotificationsError < Error
49
+ attr_reader :causes
30
50
 
31
- @default_timeout = 60
51
+ def initialize(causes)
52
+ @causes = causes
53
+ exception_class_names = causes.map { |e| e.class.name }
54
+ super("Notifications errors: #{exception_class_names.join(", ")}")
55
+ end
56
+ end
32
57
 
33
58
  class << self
34
- attr_accessor :logger, :default_timeout
59
+ attr_accessor :default_timeout, :enable_default_logging_subscriber
60
+ attr_reader :logging_subscriber, :logger, :network_events_logger, :browser_console_logger, :notification_service
35
61
 
36
62
  # Allow configuration through a block
37
63
  def configure
38
64
  yield self if block_given?
65
+
66
+ init
67
+ end
68
+
69
+ def init
70
+ self.logging_subscriber = (Notifications::LoggingSubscriber.new(logger: logger) if enable_default_logging_subscriber)
71
+ begin
72
+ require "websocket-native"
73
+
74
+ logger.debug "websocket-native available; use enhance performance."
75
+ rescue LoadError => e
76
+ raise unless e.message =~ /websocket-native/
77
+
78
+ logger.warn "websocket-native not available; installing it may enhance performance."
79
+ end
80
+ end
81
+
82
+ def translate_paper_format(format)
83
+ format = format.to_s.downcase.to_sym
84
+
85
+ dim = PAPER_FORMATS_CM[format]
86
+
87
+ raise ArgumentError, "Invalid paper format: #{format}" unless dim
88
+
89
+ width = dim[:width] || 0
90
+ height = dim[:height] || 0
91
+
92
+ { width: width, height: height }
93
+ end
94
+
95
+ def logger=(new_logger)
96
+ @logger = Bidi2pdf::VerboseLogger.new new_logger
97
+ end
98
+
99
+ def network_events_logger=(new_network_events_logger)
100
+ @network_events_logger = Bidi2pdf::VerboseLogger.new(new_network_events_logger)
101
+ end
102
+
103
+ def browser_console_logger=(new_browser_console_logger)
104
+ @browser_console_logger = Bidi2pdf::VerboseLogger.new(new_browser_console_logger)
39
105
  end
106
+
107
+ def logging_subscriber=(new_logging_subscriber)
108
+ @logging_subscriber&.unsubscribe
109
+ @logging_subscriber = new_logging_subscriber
110
+ end
111
+
112
+ def notification_service=(new_notification_service)
113
+ @logging_subscriber&.unsubscribe
114
+
115
+ @notification_service = new_notification_service
116
+ end
117
+ end
118
+
119
+ configure do |config|
120
+ config.logger = Logger.new($stdout)
121
+ config.logger.level = Logger::INFO
122
+
123
+ config.network_events_logger = Logger.new($stdout)
124
+ config.network_events_logger.level = Logger::FATAL
125
+
126
+ config.browser_console_logger = Logger.new($stdout)
127
+ config.browser_console_logger.level = Logger::WARN
128
+
129
+ config.enable_default_logging_subscriber = true
130
+
131
+ config.default_timeout = 60
132
+
133
+ config.notification_service = Notifications
40
134
  end
41
135
  end
@@ -34,7 +34,7 @@ module Bidi2pdf
34
34
 
35
35
  def wait_until_open: (?timeout: untyped) -> untyped
36
36
 
37
- def send_cmd: (untyped method, ?::Hash[untyped, untyped] params) -> untyped
37
+ def send_cmd: (Bidi2pdf::Bidi::Commands::Base cmd) -> untyped
38
38
 
39
39
  # rubocop:disable Metrics/AbcSize
40
40
  def send_cmd_and_wait: (untyped method, ?::Hash[untyped, untyped] params, ?timeout: untyped) ?{ (untyped) -> untyped } -> untyped
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bidi2pdf
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dieter S.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-04-10 00:00:00.000000000 Z
11
+ date: 2025-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
180
  version: '3.0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rspec-benchmark
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '0.6'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '0.6'
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: rubocop
183
197
  requirement: !ruby/object:Gem::Requirement
@@ -276,6 +290,20 @@ dependencies:
276
290
  - - "~>"
277
291
  - !ruby/object:Gem::Version
278
292
  version: '1.4'
293
+ - !ruby/object:Gem::Dependency
294
+ name: websocket-native
295
+ requirement: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - "~>"
298
+ - !ruby/object:Gem::Version
299
+ version: '1.0'
300
+ type: :development
301
+ prerelease: false
302
+ version_requirements: !ruby/object:Gem::Requirement
303
+ requirements:
304
+ - - "~>"
305
+ - !ruby/object:Gem::Version
306
+ version: '1.0'
279
307
  description: |
280
308
  Bidi2pdf is a powerful PDF generation tool that uses Chrome's BiDirectional Protocol
281
309
  to render web pages as high-quality PDF documents. It offers:
@@ -310,6 +338,7 @@ files:
310
338
  - cliff.toml
311
339
  - docker/Dockerfile
312
340
  - docker/Dockerfile.chromedriver
341
+ - docker/Dockerfile.slim
313
342
  - docker/docker-compose.yml
314
343
  - docker/entrypoint.sh
315
344
  - docker/nginx/default.conf
@@ -319,6 +348,7 @@ files:
319
348
  - lib/bidi2pdf/bidi/add_headers_interceptor.rb
320
349
  - lib/bidi2pdf/bidi/auth_interceptor.rb
321
350
  - lib/bidi2pdf/bidi/browser.rb
351
+ - lib/bidi2pdf/bidi/browser_console_logger.rb
322
352
  - lib/bidi2pdf/bidi/browser_tab.rb
323
353
  - lib/bidi2pdf/bidi/client.rb
324
354
  - lib/bidi2pdf/bidi/command_manager.rb
@@ -327,6 +357,7 @@ files:
327
357
  - lib/bidi2pdf/bidi/commands/base.rb
328
358
  - lib/bidi2pdf/bidi/commands/browser_close.rb
329
359
  - lib/bidi2pdf/bidi/commands/browser_create_user_context.rb
360
+ - lib/bidi2pdf/bidi/commands/browser_remove_user_context.rb
330
361
  - lib/bidi2pdf/bidi/commands/browsing_context_close.rb
331
362
  - lib/bidi2pdf/bidi/commands/browsing_context_navigate.rb
332
363
  - lib/bidi2pdf/bidi/commands/browsing_context_print.rb
@@ -346,7 +377,13 @@ files:
346
377
  - lib/bidi2pdf/bidi/connection_manager.rb
347
378
  - lib/bidi2pdf/bidi/event_manager.rb
348
379
  - lib/bidi2pdf/bidi/interceptor.rb
380
+ - lib/bidi2pdf/bidi/js_logger_helper.rb
381
+ - lib/bidi2pdf/bidi/logger_events.rb
349
382
  - lib/bidi2pdf/bidi/network_event.rb
383
+ - lib/bidi2pdf/bidi/network_event_formatters.rb
384
+ - lib/bidi2pdf/bidi/network_event_formatters/network_event_console_formatter.rb
385
+ - lib/bidi2pdf/bidi/network_event_formatters/network_event_formatter_utils.rb
386
+ - lib/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rb
350
387
  - lib/bidi2pdf/bidi/network_events.rb
351
388
  - lib/bidi2pdf/bidi/session.rb
352
389
  - lib/bidi2pdf/bidi/user_context.rb
@@ -355,9 +392,13 @@ files:
355
392
  - lib/bidi2pdf/cli.rb
356
393
  - lib/bidi2pdf/dsl.rb
357
394
  - lib/bidi2pdf/launcher.rb
395
+ - lib/bidi2pdf/notifications.rb
396
+ - lib/bidi2pdf/notifications/event.rb
397
+ - lib/bidi2pdf/notifications/instrumenter.rb
398
+ - lib/bidi2pdf/notifications/logging_subscriber.rb
358
399
  - lib/bidi2pdf/process_tree.rb
359
400
  - lib/bidi2pdf/session_runner.rb
360
- - lib/bidi2pdf/utils.rb
401
+ - lib/bidi2pdf/verbose_logger.rb
361
402
  - lib/bidi2pdf/version.rb
362
403
  - sig/bidi2pdf.rbs
363
404
  - sig/bidi2pdf/bidi/add_headers_interceptor.rbs
@@ -423,7 +464,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
423
464
  requirements:
424
465
  - - ">="
425
466
  - !ruby/object:Gem::Version
426
- version: 3.0.0
467
+ version: 3.3.0
427
468
  required_rubygems_version: !ruby/object:Gem::Requirement
428
469
  requirements:
429
470
  - - ">="
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bidi2pdf
4
- module Utils
5
- def timed(operation_name)
6
- start_time = Time.now
7
- result = yield
8
- elapsed = Time.now - start_time
9
- Bidi2pdf.logger.debug "#{operation_name} completed in #{elapsed.round(3)}s"
10
- result
11
- end
12
-
13
- module_function :timed
14
- end
15
- end