bidi2pdf 0.1.3 → 0.1.5

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +19 -1
  3. data/CHANGELOG.md +40 -3
  4. data/README.md +145 -55
  5. data/docker/Dockerfile.chromedriver +5 -0
  6. data/docker/entrypoint.sh +11 -1
  7. data/lib/bidi2pdf/bidi/add_headers_interceptor.rb +18 -21
  8. data/lib/bidi2pdf/bidi/auth_interceptor.rb +31 -38
  9. data/lib/bidi2pdf/bidi/browser_tab.rb +47 -53
  10. data/lib/bidi2pdf/bidi/client.rb +24 -52
  11. data/lib/bidi2pdf/bidi/command_manager.rb +50 -28
  12. data/lib/bidi2pdf/bidi/commands/add_intercept.rb +41 -0
  13. data/lib/bidi2pdf/bidi/commands/base.rb +73 -0
  14. data/lib/bidi2pdf/bidi/commands/browser_close.rb +15 -0
  15. data/lib/bidi2pdf/bidi/commands/browser_create_user_context.rb +15 -0
  16. data/lib/bidi2pdf/bidi/commands/browsing_context_close.rb +25 -0
  17. data/lib/bidi2pdf/bidi/commands/browsing_context_navigate.rb +31 -0
  18. data/lib/bidi2pdf/bidi/commands/browsing_context_print.rb +28 -0
  19. data/lib/bidi2pdf/bidi/commands/cancel_auth.rb +26 -0
  20. data/lib/bidi2pdf/bidi/commands/create_tab.rb +11 -0
  21. data/lib/bidi2pdf/bidi/commands/create_window.rb +32 -0
  22. data/lib/bidi2pdf/bidi/commands/get_user_contexts.rb +15 -0
  23. data/lib/bidi2pdf/bidi/commands/network_continue.rb +29 -0
  24. data/lib/bidi2pdf/bidi/commands/print_parameters_validator.rb +116 -0
  25. data/lib/bidi2pdf/bidi/commands/provide_credentials.rb +33 -0
  26. data/lib/bidi2pdf/bidi/commands/script_evaluate.rb +33 -0
  27. data/lib/bidi2pdf/bidi/commands/session_end.rb +15 -0
  28. data/lib/bidi2pdf/bidi/commands/session_status.rb +15 -0
  29. data/lib/bidi2pdf/bidi/commands/session_subscribe.rb +25 -0
  30. data/lib/bidi2pdf/bidi/commands/set_tab_cookie.rb +71 -0
  31. data/lib/bidi2pdf/bidi/commands/set_usercontext_cookie.rb +67 -0
  32. data/lib/bidi2pdf/bidi/commands.rb +27 -0
  33. data/lib/bidi2pdf/bidi/connection_manager.rb +16 -13
  34. data/lib/bidi2pdf/bidi/event_manager.rb +2 -0
  35. data/lib/bidi2pdf/bidi/interceptor.rb +75 -0
  36. data/lib/bidi2pdf/bidi/network_events.rb +0 -1
  37. data/lib/bidi2pdf/bidi/session.rb +139 -65
  38. data/lib/bidi2pdf/bidi/user_context.rb +25 -31
  39. data/lib/bidi2pdf/bidi/web_socket_dispatcher.rb +2 -0
  40. data/lib/bidi2pdf/chromedriver_manager.rb +2 -1
  41. data/lib/bidi2pdf/cli.rb +12 -7
  42. data/lib/bidi2pdf/dsl.rb +45 -0
  43. data/lib/bidi2pdf/launcher.rb +6 -2
  44. data/lib/bidi2pdf/session_runner.rb +9 -2
  45. data/lib/bidi2pdf/version.rb +1 -1
  46. data/lib/bidi2pdf.rb +16 -1
  47. data/sig/bidi2pdf/bidi/command_manager.rbs +41 -0
  48. data/sig/bidi2pdf/bidi/commands/add_intercept.rbs +21 -0
  49. data/sig/bidi2pdf/bidi/commands/base.rbs +27 -0
  50. data/sig/bidi2pdf/bidi/commands/browser_close.rbs +9 -0
  51. data/sig/bidi2pdf/bidi/commands/browser_create_user_context.rbs +9 -0
  52. data/sig/bidi2pdf/bidi/commands/browsing_context_close.rbs +11 -0
  53. data/sig/bidi2pdf/bidi/commands/browsing_context_navigate.rbs +15 -0
  54. data/sig/bidi2pdf/bidi/commands/browsing_context_print.rbs +14 -0
  55. data/sig/bidi2pdf/bidi/commands/cancel_auth.rbs +11 -0
  56. data/sig/bidi2pdf/bidi/commands/create_tab.rbs +9 -0
  57. data/sig/bidi2pdf/bidi/commands/create_window.rbs +19 -0
  58. data/sig/bidi2pdf/bidi/commands/get_user_contexts.rbs +9 -0
  59. data/sig/bidi2pdf/bidi/commands/network_continue.rbs +19 -0
  60. data/sig/bidi2pdf/bidi/commands/print_parameters_validator.rbs +53 -0
  61. data/sig/bidi2pdf/bidi/commands/provide_credentials.rbs +15 -0
  62. data/sig/bidi2pdf/bidi/commands/script_evaluate.rbs +17 -0
  63. data/sig/bidi2pdf/bidi/commands/session_end.rbs +9 -0
  64. data/sig/bidi2pdf/bidi/commands/session_status.rbs +9 -0
  65. data/sig/bidi2pdf/bidi/commands/session_subscribe.rbs +15 -0
  66. data/sig/bidi2pdf/bidi/commands/set_tab_cookie.rbs +31 -0
  67. data/sig/bidi2pdf/bidi/commands/set_usercontext_cookie.rbs +27 -0
  68. data/sig/bidi2pdf/bidi/commands.rbs +6 -0
  69. data/sig/bidi2pdf/bidi/connection_manager.rbs +17 -0
  70. data/sig/bidi2pdf/bidi/interceptor.rbs +31 -0
  71. data/tasks/coverage.rake +16 -0
  72. metadata +66 -11
  73. data/lib/bidi2pdf/bidi/print_parameters_validator.rb +0 -114
  74. data/sig/bidi2pdf/bidi/print_parameters_validator.rbs +0 -44
  75. data/sig/bidi2pdf/chrome/chromedriver_downloader.rbs +0 -11
  76. data/sig/bidi2pdf/chrome/downloader_helper.rbs +0 -9
  77. data/sig/bidi2pdf/chrome/finder.rbs +0 -27
  78. data/sig/bidi2pdf/chrome/platform.rbs +0 -13
  79. data/sig/bidi2pdf/chrome/version_resolver.rbs +0 -19
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class CreateTab < CreateWindow
7
+ def type = "tab"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class CreateWindow
7
+ include Base
8
+
9
+ def initialize(user_context_id: nil, reference_context: nil, background: false)
10
+ @user_context_id = user_context_id
11
+ @reference_context = reference_context
12
+ @background = background
13
+ end
14
+
15
+ def method_name
16
+ "browsingContext.create"
17
+ end
18
+
19
+ def params
20
+ {
21
+ type: type,
22
+ userContext: @user_context_id,
23
+ referenceContext: @reference_context,
24
+ background: @background
25
+ }.compact
26
+ end
27
+
28
+ def type = "window"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class GetUserContexts
7
+ include Base
8
+
9
+ def method_name
10
+ "browser.getUserContexts"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class NetworkContinue
7
+ include Base
8
+
9
+ attr_reader :request, :headers
10
+
11
+ def initialize(request:, headers:)
12
+ @headers = headers
13
+ @request = request
14
+ end
15
+
16
+ def method_name
17
+ "network.continueRequest"
18
+ end
19
+
20
+ def params
21
+ {
22
+ request: request,
23
+ headers: headers
24
+ }.compact
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ # Validates parameters for the BiDi method `browsingContext.print`.
7
+ #
8
+ # Allowed structure of the params hash:
9
+ #
10
+ # {
11
+ # background: Boolean (optional, default: false) – print background graphics,
12
+ # margin: {
13
+ # top: Float >= 0.0 (optional, default: 1.0),
14
+ # bottom: Float >= 0.0 (optional, default: 1.0),
15
+ # left: Float >= 0.0 (optional, default: 1.0),
16
+ # right: Float >= 0.0 (optional, default: 1.0)
17
+ # },
18
+ # orientation: "portrait" or "landscape" (optional, default: "portrait"),
19
+ # page: {
20
+ # width: Float >= 0.0352 (optional, default: 21.59),
21
+ # height: Float >= 0.0352 (optional, default: 27.94)
22
+ # },
23
+ # pageRanges: Array of Integers or Strings (optional),
24
+ # scale: Float between 0.1 and 2.0 (optional, default: 1.0),
25
+ # shrinkToFit: Boolean (optional, default: true)
26
+ # }
27
+ #
28
+ # This validator checks presence, types, allowed ranges, and values,
29
+ # and raises ArgumentError with a descriptive message if validation fails.
30
+ class PrintParametersValidator
31
+ def self.validate!(params)
32
+ new(params).validate!
33
+ end
34
+
35
+ def initialize(params)
36
+ @params = params
37
+ end
38
+
39
+ def validate!
40
+ raise ArgumentError, "params must be a Hash" unless @params.is_a?(Hash)
41
+
42
+ validate_boolean(:background)
43
+ validate_boolean(:shrinkToFit)
44
+ validate_orientation
45
+ validate_scale
46
+ validate_page_ranges
47
+ validate_margin
48
+ validate_page_size
49
+
50
+ true
51
+ end
52
+
53
+ private
54
+
55
+ def validate_boolean(key)
56
+ return unless @params.key?(key)
57
+ return if [true, false].include?(@params[key])
58
+
59
+ raise ArgumentError, ":#{key} must be a boolean"
60
+ end
61
+
62
+ def validate_orientation
63
+ return unless @params.key?(:orientation)
64
+ return if %w[portrait landscape].include?(@params[:orientation])
65
+
66
+ raise ArgumentError, ":orientation must be 'portrait' or 'landscape'"
67
+ end
68
+
69
+ def validate_scale
70
+ return unless @params.key?(:scale)
71
+
72
+ scale = @params[:scale]
73
+ return if scale.is_a?(Numeric) && scale >= 0.1 && scale <= 2.0
74
+
75
+ raise ArgumentError, ":scale must be a number between 0.1 and 2.0"
76
+ end
77
+
78
+ def validate_page_ranges
79
+ return unless @params.key?(:pageRanges)
80
+ unless @params[:pageRanges].is_a?(Array) &&
81
+ @params[:pageRanges].all? { |v| v.is_a?(Integer) || v.is_a?(String) }
82
+ raise ArgumentError, ":pageRanges must be an array of integers or strings"
83
+ end
84
+ end
85
+
86
+ def validate_margin
87
+ return unless @params.key?(:margin)
88
+
89
+ margin = @params[:margin]
90
+ raise ArgumentError, ":margin must be a Hash" unless margin.is_a?(Hash)
91
+
92
+ %i[top bottom left right].each do |side|
93
+ next unless margin.key?(side)
94
+
95
+ val = margin[side]
96
+ raise ArgumentError, "margin[:#{side}] must be a float >= 0.0" unless val.is_a?(Numeric) && val >= 0.0
97
+ end
98
+ end
99
+
100
+ def validate_page_size
101
+ return unless @params.key?(:page)
102
+
103
+ page = @params[:page]
104
+ raise ArgumentError, ":page must be a Hash" unless page.is_a?(Hash)
105
+
106
+ %i[width height].each do |dim|
107
+ next unless page.key?(dim)
108
+
109
+ val = page[dim]
110
+ raise ArgumentError, "page[:#{dim}] must be a float >= 0.0352" unless val.is_a?(Numeric) && val >= 0.0352
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class ProvideCredentials
7
+ include Base
8
+
9
+ def initialize(request:, username:, password:)
10
+ @request = request
11
+ @username = username
12
+ @password = password
13
+ end
14
+
15
+ def params
16
+ {
17
+ request: @request,
18
+ action: "provideCredentials",
19
+ credentials: {
20
+ type: "password",
21
+ username: @username,
22
+ password: @password
23
+ }
24
+ }
25
+ end
26
+
27
+ def method_name
28
+ "network.continueWithAuth"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class ScriptEvaluate
7
+ include Base
8
+
9
+ def initialize(expression:,
10
+ context:,
11
+ await_promise: true)
12
+ @expression = expression
13
+ @context = context
14
+ @await_promise = await_promise
15
+ end
16
+
17
+ def params
18
+ {
19
+ expression: @expression,
20
+ target: {
21
+ context: @context
22
+ },
23
+ awaitPromise: @await_promise
24
+ }
25
+ end
26
+
27
+ def method_name
28
+ "script.evaluate"
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class SessionEnd
7
+ include Base
8
+
9
+ def method_name
10
+ "session.end"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class SessionStatus
7
+ include Base
8
+
9
+ def method_name
10
+ "session.status"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class SessionSubscribe
7
+ include Base
8
+
9
+ attr_reader :events
10
+
11
+ def initialize(events:)
12
+ @events = events
13
+ end
14
+
15
+ def method_name
16
+ "session.subscribe"
17
+ end
18
+
19
+ def params
20
+ { events: events }.compact
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class SetTabCookie
7
+ include Base
8
+
9
+ class << self
10
+ attr_writer :time_provider
11
+
12
+ def time_provider
13
+ @time_provider ||= -> { Time.now }
14
+ end
15
+ end
16
+
17
+ attr_reader :name, :value, :domain, :path, :secure, :http_only, :same_site, :ttl, :browsing_context_id
18
+
19
+ def initialize(name:,
20
+ value:,
21
+ domain:,
22
+ browsing_context_id:,
23
+ path: "/",
24
+ secure: true,
25
+ http_only: false,
26
+ same_site: "strict",
27
+ ttl: 30)
28
+ @name = name
29
+ @value = value
30
+ @domain = domain
31
+ @path = path
32
+ @secure = secure
33
+ @http_only = http_only
34
+ @same_site = same_site
35
+ @ttl = ttl
36
+ @browsing_context_id = browsing_context_id
37
+ end
38
+
39
+ def expiry
40
+ self.class.time_provider.call.to_i + ttl
41
+ end
42
+
43
+ def method_name
44
+ "storage.setCookie"
45
+ end
46
+
47
+ def params
48
+ {
49
+ cookie: {
50
+ name: name,
51
+ value: {
52
+ type: "string",
53
+ value: value
54
+ },
55
+ domain: domain,
56
+ path: path,
57
+ secure: secure,
58
+ httpOnly: http_only,
59
+ sameSite: same_site,
60
+ expiry: expiry
61
+ },
62
+ partition: {
63
+ type: "context",
64
+ context: browsing_context_id
65
+ }
66
+ }.compact
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ class SetUsercontextCookie < SetTabCookie
7
+ include Base
8
+
9
+ attr_reader :user_context_id, :source_origin
10
+
11
+ def initialize(name:,
12
+ value:,
13
+ domain:,
14
+ user_context_id:,
15
+ source_origin:,
16
+ path: "/",
17
+ secure: true,
18
+ http_only: false,
19
+ same_site: "strict",
20
+ ttl: 30)
21
+ super(name: name, value: value,
22
+ domain: domain,
23
+ path: path,
24
+ secure: secure,
25
+ http_only: http_only,
26
+ same_site: same_site,
27
+ ttl: ttl,
28
+ browsing_context_id: nil)
29
+
30
+ @user_context_id = user_context_id
31
+ @source_origin = source_origin
32
+ end
33
+
34
+ def expiry
35
+ Time.now.to_i + ttl
36
+ end
37
+
38
+ def method_name
39
+ "storage.setCookie"
40
+ end
41
+
42
+ def params
43
+ {
44
+ cookie: {
45
+ name: name,
46
+ value: {
47
+ type: "string",
48
+ value: value
49
+ },
50
+ domain: domain,
51
+ path: path,
52
+ secure: secure,
53
+ httpOnly: http_only,
54
+ sameSite: same_site,
55
+ expiry: expiry
56
+ },
57
+ partition: {
58
+ type: "storageKey",
59
+ userContext: user_context_id,
60
+ sourceOrigin: source_origin
61
+ }
62
+ }.compact
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Commands
6
+ require_relative "commands/base"
7
+ require_relative "commands/create_window"
8
+ require_relative "commands/create_tab"
9
+ require_relative "commands/add_intercept"
10
+ require_relative "commands/set_tab_cookie"
11
+ require_relative "commands/set_usercontext_cookie"
12
+ require_relative "commands/session_status"
13
+ require_relative "commands/get_user_contexts"
14
+ require_relative "commands/script_evaluate"
15
+ require_relative "commands/browser_create_user_context"
16
+ require_relative "commands/browser_close"
17
+ require_relative "commands/browsing_context_close"
18
+ require_relative "commands/browsing_context_navigate"
19
+ require_relative "commands/browsing_context_print"
20
+ require_relative "commands/session_subscribe"
21
+ require_relative "commands/session_end"
22
+ require_relative "commands/cancel_auth"
23
+ require_relative "commands/network_continue"
24
+ require_relative "commands/provide_credentials"
25
+ end
26
+ end
27
+ end
@@ -6,28 +6,31 @@ module Bidi2pdf
6
6
  def initialize(logger:)
7
7
  @logger = logger
8
8
  @connected = false
9
- @mutex = Mutex.new
10
- @cv = ConditionVariable.new
9
+ @connection_queue = Thread::Queue.new
11
10
  end
12
11
 
13
12
  def mark_connected
14
- @mutex.synchronize do
15
- @connected = true
16
- @cv.broadcast
17
- end
13
+ return if @connected
14
+
15
+ @connected = true
16
+ @logger.debug "WebSocket connection is open"
17
+ @connection_queue.push(true)
18
18
  end
19
19
 
20
20
  def wait_until_open(timeout:)
21
- @mutex.synchronize do
22
- unless @connected
23
- @logger.debug "Waiting for WebSocket connection to open"
24
- @cv.wait(@mutex, timeout)
21
+ return true if @connected
22
+
23
+ @logger.debug "Waiting for WebSocket connection to open"
24
+
25
+ begin
26
+ Timeout.timeout(timeout) do
27
+ @connection_queue.pop
25
28
  end
29
+ rescue Timeout::Error
30
+ raise Bidi2pdf::WebsocketError, "WebSocket connection did not open in time #{timeout} sec."
26
31
  end
27
32
 
28
- raise "WebSocket connection did not open in time" unless @connected
29
-
30
- @logger.debug "WebSocket connection is open"
33
+ true
31
34
  end
32
35
  end
33
36
  end
@@ -39,6 +39,8 @@ module Bidi2pdf
39
39
  end
40
40
  end
41
41
 
42
+ private
43
+
42
44
  def log_msg(prefix, data)
43
45
  message = truncate_large_values(data)
44
46
  Bidi2pdf.logger.debug "#{prefix}: #{message.inspect}"
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bidi2pdf
4
+ module Bidi
5
+ module Interceptor
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ module ClassMethods
11
+ def phases = raise(NotImplementedError, "Interceptors must implement phases")
12
+
13
+ def events = raise(NotImplementedError, "Interceptors must implement events")
14
+ end
15
+
16
+ def url_patterns = raise(NotImplementedError, "Interceptors must implement url_patterns")
17
+
18
+ def context = raise(NotImplementedError, "Interceptors must implement context")
19
+
20
+ def process_interception(_event_response, _navigation_id, _network_id, _url) = raise(NotImplementedError, "Interceptors must implement process_interception")
21
+
22
+ def register_with_client(client:)
23
+ @client = client
24
+
25
+ cmd = Bidi2pdf::Bidi::Commands::AddIntercept.new context: context, phases: self.class.phases, url_patterns: url_patterns
26
+
27
+ client.send_cmd_and_wait(cmd) do |response|
28
+ @interceptor_id = response["result"]["intercept"]
29
+
30
+ Bidi2pdf.logger.debug "Interceptor added: #{@interceptor_id}"
31
+
32
+ client.on_event(*self.class.events, &method(:handle_event))
33
+
34
+ self
35
+ end
36
+ end
37
+
38
+ # rubocop: disable Metrics/AbcSize
39
+ def handle_event(response)
40
+ event_response = response["params"]
41
+
42
+ return unless event_response["intercepts"]&.include?(interceptor_id) && event_response["isBlocked"]
43
+
44
+ navigation_id = event_response["navigation"]
45
+ network_id = event_response["request"]["request"]
46
+ url = event_response["request"]["url"]
47
+
48
+ # Log the interception
49
+ Bidi2pdf.logger.debug "Interceptor #{interceptor_id} handling event: #{navigation_id}/#{network_id}/#{url}"
50
+
51
+ process_interception(event_response, navigation_id, network_id, url)
52
+ rescue StandardError => e
53
+ Bidi2pdf.logger.error "Error handling event: #{e.message}"
54
+ Bidi2pdf.logger.error e.backtrace.join("\n")
55
+ raise e
56
+ end
57
+
58
+ # rubocop: enable Metrics/AbcSize
59
+
60
+ def interceptor_id
61
+ @interceptor_id
62
+ end
63
+
64
+ def client
65
+ @client
66
+ end
67
+
68
+ def validate_phases!
69
+ valid_phases = [Phases::BEFORE_REQUEST, Phases::RESPONSE_STARTED, Phases::AUTH_REQUIRED]
70
+
71
+ raise ArgumentError, "Unsupported phase(s): #{self.class.phases}" unless self.class.phases.all? { |phase| valid_phases.include?(phase) }
72
+ end
73
+ end
74
+ end
75
+ end
@@ -68,7 +68,6 @@ module Bidi2pdf
68
68
  end
69
69
 
70
70
  if Time.now - start_time > timeout
71
- # rubocop:disable Layout/LineLength
72
71
  Bidi2pdf.logger.warn "⏰ Timeout while waiting for network events to complete. Still in progress: #{in_progress.map(&:id)}"
73
72
  # rubocop:enable Layout/LineLength
74
73
  break