hotsock-turbo 0.2.0 → 0.3.0

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: 9b34d411b6586100a7331fb9a42bcdbe06410cffc9d0657fed5834a973a30324
4
- data.tar.gz: 1ce7d77b5026c991d42d4d8f4f6e0c3928d6df72b3ac906feefd12624f0a8043
3
+ metadata.gz: 855667dd61bba20964a93972d108e1ae299df990c157670b6005f979ac736c2a
4
+ data.tar.gz: cde85dbe0e22fa654230f283efcbfa460541daa87f6efa0992cf6c94063df7b2
5
5
  SHA512:
6
- metadata.gz: 045abe5e27e4ee970f3655702ba4d4c6f0b9abdd3573d75ed85dab4cf081847334f09e41b8ed6d40f296f5bbaabb979fc774cdd27d2b399eac03a234fc126758
7
- data.tar.gz: 8c119eb0cedfd340810fb279817126234baf8b01800afb6943fa1631b6e3880ce9fcbfc3f30a18b93cff3d94873c48c198fc63ab1f1ee984456c8c840d74f762
6
+ metadata.gz: 61e34a6b641bb36ea43fab13fadde47c034b9080dd9ebaec35686742526c8115e0cde07cfe5569333b6759e25473e88b83580d139590ac5dc65acf36cf0faf50
7
+ data.tar.gz: efcd9736e449b9043f96449918f4145040bd9c8649117881c5f5ae4ab025e8d050e9590bb5e870e4af7506830fb9c930b9e189be76a6b73719c7e2902ce4e9a4
data/Gemfile CHANGED
@@ -11,6 +11,6 @@ group :development, :test do
11
11
  end
12
12
 
13
13
  group :test do
14
- gem "maxitest"
14
+ gem "maxitest", "< 7"
15
15
  gem "ostruct"
16
16
  end
data/README.md CHANGED
@@ -64,14 +64,26 @@ import "@hotsock/hotsock-js"
64
64
  import "hotsock-turbo"
65
65
  ```
66
66
 
67
+ ### Mount the Engine
68
+
69
+ Add to your `config/routes.rb`:
70
+
71
+ ```ruby
72
+ mount Hotsock::Turbo::Engine => "/hotsock"
73
+ ```
74
+
75
+ This provides a `POST /hotsock/connect` endpoint that returns connection tokens for the WebSocket client.
76
+
67
77
  ## Configuration
68
78
 
69
79
  Create an initializer at `config/initializers/hotsock_turbo.rb`:
70
80
 
71
81
  ```ruby
72
82
  Hotsock::Turbo.configure do |config|
73
- # Required: Path to an endpoint that returns Hotsock connection tokens
74
- config.connect_token_path = "/hotsock/connect_token"
83
+ # Required: Path to the engine's connect endpoint (or your own custom
84
+ # endpoint). This path must accept a POST request and return a JSON object
85
+ # with a `token` field that contains a connect-scoped JWT. {"token":"ey..."}
86
+ config.connect_token_path = "/hotsock/connect"
75
87
 
76
88
  # Required: Your Hotsock WebSocket URL
77
89
  config.wss_url = "wss://your-hotsock-instance.example.com"
@@ -32,10 +32,19 @@ function createHotsockClient() {
32
32
  const hotsockClient = window.Hotsock || createHotsockClient()
33
33
  window.Hotsock = hotsockClient
34
34
 
35
- // Track active subscriptions by channel
36
35
  const subscriptions = new Map() // channel -> { binding, elements: Set, unsubscribeTimer }
36
+ const lastMessageIds = new Map() // channel -> lastMessageId (ULID)
37
37
  const UNSUBSCRIBE_DELAY_MS = 250
38
38
 
39
+ function isReplaceOrUpdateAction(html) {
40
+ return /<turbo-stream[^>]+action=["'](replace|update)["']/.test(html)
41
+ }
42
+
43
+ function isNewerMessage(newId, lastId) {
44
+ if (!lastId) return true
45
+ return newId > lastId
46
+ }
47
+
39
48
  class HotsockTurboStreamSourceElement extends HTMLElement {
40
49
  #channel = null
41
50
 
@@ -75,8 +84,17 @@ class HotsockTurboStreamSourceElement extends HTMLElement {
75
84
  const { Turbo } = window
76
85
  const binding = hotsockClient.bind(
77
86
  "turbo_stream",
78
- ({ data }) => {
87
+ ({ id, data }) => {
79
88
  if (!data?.html) return
89
+
90
+ if (isReplaceOrUpdateAction(data.html)) {
91
+ const lastId = lastMessageIds.get(channel)
92
+ if (!isNewerMessage(id, lastId)) {
93
+ return
94
+ }
95
+ lastMessageIds.set(channel, id)
96
+ }
97
+
80
98
  try {
81
99
  Turbo.renderStreamMessage(data.html)
82
100
  } catch (error) {
@@ -115,6 +133,7 @@ class HotsockTurboStreamSourceElement extends HTMLElement {
115
133
  if (sub.elements.size === 0) {
116
134
  sub.binding.unbind()
117
135
  subscriptions.delete(channel)
136
+ lastMessageIds.delete(channel)
118
137
  }
119
138
  }, UNSUBSCRIBE_DELAY_MS)
120
139
  }
@@ -27,7 +27,7 @@ module Hotsock
27
27
  def create_subscription_token(channel_name)
28
28
  Hotsock.issue_token(
29
29
  scope: "subscribe",
30
- channels: {channel_name => {omitSubCount: true, subscribe: true}},
30
+ channels: {channel_name => {omitFromSubCount: true, subscribe: true}},
31
31
  uid:,
32
32
  exp: 1.week.from_now.to_i
33
33
  )
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Hotsock
4
4
  module Turbo
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotsock-turbo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - James Miller