yrby-rails 0.6.1 → 0.7.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: ce4164de6e9497a1f6236581facfc1ca47bf33951fc32a977078016dd52a42d2
4
- data.tar.gz: 1353f914370ba5b0142ece86d1ab38960d0a92c07c63b1e5320b8fe155350ce6
3
+ metadata.gz: 397e3f48e611d521d0afa26ddca292826276406b1fd04fb2bcfd09858a73081d
4
+ data.tar.gz: 51e88f6856f8a2859d18823e87ff2d5cd901e678e8b7a618ff7b2b0308d80c1d
5
5
  SHA512:
6
- metadata.gz: f5e63dfbc35fd3969cd9effcb26d92f05cfa7f29efb302c7c71ecba11f51bc25b686e99ded9e979cd2c4d382069e4a8c10625a622c389f3e4b0ae3131f959ab7
7
- data.tar.gz: 120215f5109ec37e77371a59318bb9fded69fbe79338f0a50cf1b6810a784b191bfc8696400369e0ec97e01752f7fc77b0fd74fb85ae220fc2f294d67c26a6c4
6
+ metadata.gz: 3299b4149a6dea79105be6bf69e643c8079029ac751bb50f68ce4b8d49ce570541b184f5b601215214d92b7848694a8b9a9a685eaf593f885378cbc8454e0a18
7
+ data.tar.gz: 4490087a7702972c2e447abcb2f4afff938d72e77704eb7cfc04d5a5272bc6e5248d22a883b193b3840db0aa5c3e602e6a0c50b57fb1d3169839e7530a764ff4
data/CHANGELOG-rails.md CHANGED
@@ -5,6 +5,118 @@ documented here. The
5
5
  format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
6
  this project aims to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [Unreleased]
9
+
10
+ ## [0.7.0] - 2026-10-01
11
+
12
+ ### Added
13
+
14
+ - `Y::DocumentChannel.authorize_document { |record, name| ... }` runs the
15
+ application's permission check when a client subscribes, on top of the
16
+ signed grant. The block runs in channel context. If it denies access, the
17
+ channel rejects that subscription without opening a stream or serving
18
+ state, and other subscriptions on the connection keep working. Without a
19
+ block, a valid signed grant is enough.
20
+
21
+ The check runs once per subscription, so a permission you revoke
22
+ mid-session takes effect the next time that client subscribes.
23
+ - `record.collaborative_document(name)` returns a bound
24
+ `Y::Collaborative::Attribute`, which both application code and the shipped
25
+ channel use to read and write the document. `y_doc` builds a native
26
+ `Y::Doc`, and `load_state`, `append`, and `key` all use the same storage
27
+ class. `key` and `load_state` don't create a row, but the first `append`
28
+ does. Use `.document_row` for the built-in row operations.
29
+ - `Y::Document.key_for(record, name)` returns the conventional key without
30
+ creating a document row.
31
+ - `collaborative_sgid(name, expires_in:)` and
32
+ `collaborative_document_tag(record, name, expires_in:, refresh:)` let you
33
+ set the grant lifetime, which used to be GlobalID's default with no way to
34
+ shorten it. `refresh:` is a URL the element fetches when a subscription is
35
+ rejected. That action re-runs the app's authorization and renders
36
+ `{ grant: ... }`, and the client resubscribes the same session with the new
37
+ grant. The client fetches the URL once per rejection and does not poll.
38
+
39
+ - `Y::DocumentChannel` ships in the gem, so apps don't need to write a
40
+ channel. Clients subscribe with the signed grant the page rendered
41
+ (`{ grant:, name: }`). The channel finds the record from the grant, loads
42
+ the document, and stores changes in `Y::Document`. It rejects grants that
43
+ are missing, tampered with, for the wrong attribute, or for a destroyed
44
+ record.
45
+
46
+ - `collaborative_document_tag(record, name, **options)` is available in
47
+ Action View through the engine. It renders the mount element with the
48
+ signed grant, the attribute name, and the channel name as data attributes.
49
+ Anyone holding the grant can open the document, the same as with a
50
+ `turbo_stream_from` stream name, so only render the tag on pages where the
51
+ user is allowed to edit the record.
52
+
53
+ - Channels get `Y::Document` storage by default. Declare `on_load` and
54
+ `on_change` if you want a different store. Outside a yrby-rails app there
55
+ is still no default, and subscribing raises until both hooks are declared.
56
+
57
+ - `has_collaborative_document :name, encrypted: true` declares which storage
58
+ class backs an attribute, and `Y::DocumentChannel` uses that class. Every
59
+ load and append for an encrypted attribute goes through
60
+ `Y::EncryptedDocument`, so the bytes are ciphertext at rest and the plain
61
+ classes can't read them. The model decides whether an attribute is
62
+ encrypted, and a page or client can't change that. Undeclared attributes use
63
+ plain `Y::Document`.
64
+
65
+ - `Y::Collaborative` adds signed tokens for record-backed documents, and the
66
+ engine includes it into ActiveRecord::Base. A page creates a token with
67
+ `record.collaborative_sgid(:body)`, and the channel finds the record with
68
+ `Y::Collaborative.locate(params[:grant], :body)`. The token is a signed
69
+ GlobalID scoped to one attribute, so a token for one field can't open
70
+ another. This is the standard way to implement `authorized?` for
71
+ record-backed documents. lexxy-realtime already uses this flow, and now it
72
+ ships in yrby-rails.
73
+
74
+ ### Changed
75
+
76
+ - **Breaking:** channels reject every subscription until they define
77
+ `authorized?(key)`. `sync_subscribed` now calls it before opening a stream
78
+ or serving state, and the default returns `false`. Add the method to every
79
+ channel that includes `Y::ActionCable`:
80
+
81
+ ```ruby
82
+ private
83
+
84
+ def authorized?(key)
85
+ current_user&.can_edit?(key)
86
+ end
87
+ ```
88
+
89
+ Return `true` for public documents. If the default is what rejected a
90
+ subscription, the log message says so.
91
+
92
+ - `yrby:install` now creates only the storage migration, since the gem ships
93
+ `Y::DocumentChannel`. Pass `--channel` to also generate an application
94
+ channel for custom authorization or room-keyed documents. That channel
95
+ rejects every subscription until you implement `authorized?`.
96
+
97
+ ### Fixed
98
+
99
+ - Document elements now attach editors to sessions that exist independently
100
+ of any editor, so pending edits survive navigation under their original
101
+ grant and rejected deliveries can still be recovered. Turbo previews are
102
+ inert, and cached HTML contains no CRDT bytes.
103
+ - When an attribute morphs, the element releases the old editor binding and
104
+ acquires the new document. It doesn't move pending edits or authorization
105
+ over to the new document.
106
+
107
+ - Default storage now supplies the loader and recorder together, and
108
+ declaring only one custom hook raises before subscribing or acknowledging
109
+ an update. Previously, reads and writes silently went to different stores.
110
+
111
+ - Signed grants work even when the host app doesn't load Active Job.
112
+
113
+ - The `yrby:tables` migration template caps `y_documents.state` at
114
+ `1.gigabyte - 1` instead of `4.gigabytes - 1`. Postgres raises
115
+ `ArgumentError` for binary limits above 1 GB - 1, so `db:migrate` on a
116
+ fresh Postgres app failed. MySQL maps both values to `longblob`, and
117
+ SQLite ignores limits, so nothing changes there. Apps that already
118
+ migrated are unaffected.
119
+
8
120
  ## [0.6.1] - 2026-08-11
9
121
 
10
122
  ### Fixed