pgbus 0.6.5 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d43bc708b048e8b81cae4c4954686fcd0f227fd8bc2498c599ab231f262ef607
4
- data.tar.gz: 465eba8207a366a8f639f110e8131d064b46267161ffbf969b86aa470de56bfa
3
+ metadata.gz: e747f3644d06463ea9cd87dc79fbe19e1787ba7d4e9df0ba7512f729b6189224
4
+ data.tar.gz: 1b2f8a1eb8f2c10c32fa3f2c0eab5a59b2de1382e07537ee349d958393061d96
5
5
  SHA512:
6
- metadata.gz: cbc395ea4f504b5295b7a90c50981aef8cce7b7a060a8488db2b8a3a73d9d08ae8a571a067626e1e269a501e87790f230231018d527096ebba89a6a743a390ac
7
- data.tar.gz: 12ff4342c5fb3b1b30c328c7f373dcfcfd2db72d87d4a079e0538ba6857ca89b952078a6150ae495b560815bb637f396768397ccb250b98371096e146cb37b23
6
+ metadata.gz: b60516dfce1ba05b77afd21ab8e6ad3d227a2dde4edefc662221217533e22d1fa810f5056cc0f7d4470dbc220a7ae920408b785b088f8155c6a0bcb802283243
7
+ data.tar.gz: b386c96b0b493a00b196926fc014797e807538a6039fd772a4b0bafefd2b019d9a4f2f25136b264c03aa6a88a1533899e156341e57b81afdda0c9bf88de6a477
@@ -35,7 +35,8 @@
35
35
  // automatically based on the last id: we observed. The native
36
36
  // client is more battle-tested for reconnection backoff.
37
37
 
38
- import { connectStreamSource, disconnectStreamSource } from "@hotwired/turbo"
38
+ import { Turbo } from "@hotwired/turbo-rails"
39
+ const { connectStreamSource, disconnectStreamSource } = Turbo
39
40
 
40
41
  class PgbusStreamSourceElement extends HTMLElement {
41
42
  static get observedAttributes() {
@@ -51,6 +52,7 @@ class PgbusStreamSourceElement extends HTMLElement {
51
52
  }
52
53
 
53
54
  connectedCallback() {
55
+ this.closed = false
54
56
  connectStreamSource(this)
55
57
  const sinceId = this.getAttribute("since-id")
56
58
  this.lastEventId = sinceId && sinceId !== "" ? sinceId : null
@@ -106,7 +108,11 @@ class PgbusStreamSourceElement extends HTMLElement {
106
108
 
107
109
  while (!this.closed) {
108
110
  const { value, done } = await reader.read()
109
- if (done) break
111
+ if (done) {
112
+ this.removeAttribute("connected")
113
+ this.switchToEventSource()
114
+ return
115
+ }
110
116
 
111
117
  buffer += decoder.decode(value, { stream: true })
112
118
  const events = buffer.split("\n\n")
@@ -131,7 +137,7 @@ class PgbusStreamSourceElement extends HTMLElement {
131
137
  switchToEventSource() {
132
138
  if (this.closed) return
133
139
 
134
- const url = this.buildUrl({ includeSince: false })
140
+ const url = this.buildUrl({ includeSince: true })
135
141
  this.eventSource = new EventSource(url, { withCredentials: true })
136
142
 
137
143
  this.eventSource.addEventListener("open", () => {
@@ -46,7 +46,11 @@ module Pgbus
46
46
  "channel" => "Turbo::StreamsChannel"
47
47
  }.merge(html_attributes.transform_keys(&:to_s))
48
48
 
49
- render_tag("pgbus-stream-source", attributes)
49
+ element = render_tag("pgbus-stream-source", attributes)
50
+ script = pgbus_stream_source_script_tag
51
+ return element unless script
52
+
53
+ safe_concat(script, element)
50
54
  end
51
55
 
52
56
  private
@@ -118,6 +122,34 @@ module Pgbus
118
122
  cache[stream_name] ||= Pgbus.stream(stream_name).current_msg_id
119
123
  end
120
124
 
125
+ # Emits a <script type="module"> tag that imports the custom element
126
+ # definition exactly once per request. Without this, <pgbus-stream-source>
127
+ # is an inert unknown element — no SSE connection opens. Uses a
128
+ # thread-local flag cleared by the WatermarkCacheMiddleware.
129
+ def pgbus_stream_source_script_tag
130
+ cache = Thread.current[:pgbus_streams_watermark_cache] ||= {}
131
+ return nil if cache[:script_emitted]
132
+
133
+ cache[:script_emitted] = true
134
+ script = '<script type="module">import "pgbus/stream_source_element"</script>'
135
+ script.respond_to?(:html_safe) ? script.html_safe : script
136
+ end
137
+
138
+ # Concatenates two HTML-safe strings without losing the safety flag.
139
+ # ActiveSupport::SafeBuffer#+ preserves safety when both operands
140
+ # are safe. Plain string interpolation ("#{a}#{b}") creates a new
141
+ # String, dropping html_safe — which causes Phlex and safe_join to
142
+ # HTML-escape the output.
143
+ def safe_concat(*parts)
144
+ if defined?(ActiveSupport::SafeBuffer)
145
+ buf = ActiveSupport::SafeBuffer.new
146
+ parts.each { |p| buf.safe_concat(p) }
147
+ buf
148
+ else
149
+ parts.join
150
+ end
151
+ end
152
+
121
153
  def render_tag(name, attributes)
122
154
  attr_string = attributes.map { |k, v| %(#{k}="#{CGI.escape_html(v.to_s)}") }.join(" ")
123
155
  html = "<#{name} #{attr_string}></#{name}>"
data/lib/pgbus/engine.rb CHANGED
@@ -66,6 +66,29 @@ module Pgbus
66
66
  app.middleware.use Pgbus::Streams::WatermarkCacheMiddleware if Pgbus.configuration.streams_enabled
67
67
  end
68
68
 
69
+ # Make stream_source_element.js available to the host app's asset
70
+ # pipeline (Propshaft or Sprockets) so it can be included via
71
+ # `javascript_include_tag "pgbus/stream_source_element"` or pinned
72
+ # in importmap. When importmap-rails is loaded, auto-pin it so
73
+ # host apps get it without manual configuration.
74
+ initializer "pgbus.streams.assets" do |app|
75
+ if Pgbus.configuration.streams_enabled && app.config.respond_to?(:assets)
76
+ app.config.assets.precompile += %w[pgbus/stream_source_element.js]
77
+ end
78
+ end
79
+
80
+ initializer "pgbus.streams.importmap" do
81
+ if Pgbus.configuration.streams_enabled && defined?(::Importmap::Map)
82
+ ActiveSupport.on_load(:after_initialize) do
83
+ next unless Rails.application.respond_to?(:importmap)
84
+
85
+ Rails.application.importmap.pin(
86
+ "pgbus/stream_source_element", to: "pgbus/stream_source_element.js"
87
+ )
88
+ end
89
+ end
90
+ end
91
+
69
92
  # Install the Turbo::StreamsChannel patch after turbo-rails has been
70
93
  # loaded. The patch redirects broadcast_stream_to through Pgbus.stream
71
94
  # instead of ActionCable. When turbo-rails is not loaded, this is a
@@ -84,6 +107,15 @@ module Pgbus
84
107
  # `_` keeps RuboCop's Lint/Void from deleting the line.
85
108
  _autoload_trigger = Pgbus::Streams::TurboBroadcastable
86
109
  Pgbus::Streams.install_turbo_broadcastable_patch!
110
+
111
+ # Subscribe-side patch: override turbo_stream_from to render
112
+ # <pgbus-stream-source> (SSE) instead of <turbo-cable-stream-source>
113
+ # (ActionCable). Without this, third-party gems like
114
+ # hotwire-livereload that call turbo_stream_from in their views
115
+ # subscribe via ActionCable while the broadcast (patched above)
116
+ # goes through PGMQ — the message never arrives.
117
+ _autoload_trigger_override = Pgbus::Streams::TurboStreamOverride
118
+ Pgbus::Streams.install_turbo_stream_override!
87
119
  end
88
120
  end
89
121
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pgbus
4
+ module Streams
5
+ # Runtime patch that redirects `turbo_stream_from` (the view helper)
6
+ # through `pgbus_stream_from` when pgbus streams are enabled. This is
7
+ # the subscribe-side counterpart to `TurboBroadcastable` (which patches
8
+ # the publish-side `broadcast_stream_to`).
9
+ #
10
+ # Without this patch, third-party gems like hotwire-livereload call
11
+ # `turbo_stream_from "hotwire-livereload"` in their views, which
12
+ # renders a `<turbo-cable-stream-source>` element connected to
13
+ # ActionCable. Meanwhile, the `TurboBroadcastable` patch routes the
14
+ # broadcast through PGMQ/SSE. Publisher and subscriber end up on
15
+ # different transports — the message never arrives.
16
+ #
17
+ # After this patch, `turbo_stream_from` renders a `<pgbus-stream-source>`
18
+ # element instead, so both sides use PGMQ/SSE. When `streams_enabled`
19
+ # is false, the original turbo-rails behavior is preserved via `super`.
20
+ #
21
+ # The `include Pgbus::StreamsHelper` is required because some callers
22
+ # invoke `turbo_stream_from` from a Rack middleware context (e.g.
23
+ # hotwire-livereload's Middleware uses `ActionController::Base.helpers`)
24
+ # where `Turbo::StreamsHelper` is available but `Pgbus::StreamsHelper`
25
+ # is not — the engine's `isolate_namespace` scopes helpers to its own
26
+ # views. Including it here ensures `pgbus_stream_from` is always
27
+ # reachable on the receiver.
28
+ module TurboStreamOverride
29
+ include Pgbus::StreamsHelper
30
+
31
+ def turbo_stream_from(*streamables, **attributes)
32
+ if Pgbus.configuration.streams_enabled
33
+ pgbus_stream_from(*streamables, **attributes)
34
+ else
35
+ super
36
+ end
37
+ end
38
+ end
39
+
40
+ # Apply the patch to Turbo::StreamsHelper. Idempotent: prepending the
41
+ # same module twice is a no-op. Called from Pgbus::Engine's initializer
42
+ # when both turbo-rails and pgbus streams are enabled.
43
+ def self.install_turbo_stream_override!
44
+ return unless defined?(::Turbo::StreamsHelper)
45
+ return if ::Turbo::StreamsHelper.ancestors.include?(TurboStreamOverride)
46
+
47
+ ::Turbo::StreamsHelper.prepend(TurboStreamOverride)
48
+ end
49
+ end
50
+ end
data/lib/pgbus/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pgbus
4
- VERSION = "0.6.5"
4
+ VERSION = "0.6.7"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pgbus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 0.6.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikael Henriksson
@@ -121,6 +121,7 @@ files:
121
121
  - LICENSE.txt
122
122
  - README.md
123
123
  - Rakefile
124
+ - app/assets/javascripts/pgbus/stream_source_element.js
124
125
  - app/controllers/pgbus/api/insights_controller.rb
125
126
  - app/controllers/pgbus/api/metrics_controller.rb
126
127
  - app/controllers/pgbus/api/stats_controller.rb
@@ -145,7 +146,6 @@ files:
145
146
  - app/frontend/pgbus/vendor/turbo.js
146
147
  - app/helpers/pgbus/application_helper.rb
147
148
  - app/helpers/pgbus/streams_helper.rb
148
- - app/javascript/pgbus/stream_source_element.js
149
149
  - app/models/pgbus/application_record.rb
150
150
  - app/models/pgbus/batch_entry.rb
151
151
  - app/models/pgbus/blocked_execution.rb
@@ -297,6 +297,7 @@ files:
297
297
  - lib/pgbus/streams/presence.rb
298
298
  - lib/pgbus/streams/signed_name.rb
299
299
  - lib/pgbus/streams/turbo_broadcastable.rb
300
+ - lib/pgbus/streams/turbo_stream_override.rb
300
301
  - lib/pgbus/streams/watermark_cache_middleware.rb
301
302
  - lib/pgbus/uniqueness.rb
302
303
  - lib/pgbus/version.rb