yrb-lite 0.1.0.beta7-aarch64-mingw-ucrt → 0.1.0.beta9-aarch64-mingw-ucrt

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: fbf634b759425b1eac44a737a4eeee7de4b5339405ee1994f504882d33745003
4
- data.tar.gz: c1d5756f78a505d2a2b12b5df4a32c7e2eaf3688859baa67feab3138236aa271
3
+ metadata.gz: 5c65659e7f4abd5578f7775314d9410ec41656c2226b6291d65302d56c0cccf6
4
+ data.tar.gz: f848f63fcf096be692b100fce8080609a1500c97a47f7d73e171cbf94c89a41f
5
5
  SHA512:
6
- metadata.gz: a91dfeafd0dd74edb5cc8de4dcab10f39a92c7487140b10b8cd2900298a00c9c8de0e762be77546275be613ccb8db947d63776e486032b906c3cc44984ebfc1c
7
- data.tar.gz: ba620b483797e49021b42447ae3cb53ba9b5f13e3bf3b5607219a7c7ad841e4e559859198224570f66e29f00100c6db71a0a718a3944879e49d69bf09599411c
6
+ metadata.gz: 7577c5170055d9a41d535ee694adfcab716432541d836fa3a7066eda6798191db9a5e41835c54a772545e394977de2cec4bbfc7831be1400e2cb62a4a58f53cd
7
+ data.tar.gz: a8b1b143dc96f97771bb817b1ce8687b338d7533fdf353b2cb373b54de07d5f3a4c9572e2a32e06b5a7a1f6d01d4e0e5e132521e2b68c7793963fde4c543141b
data/CHANGELOG.md CHANGED
@@ -5,135 +5,3 @@ All notable changes to this project are documented here. The format is based on
5
5
  to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
-
9
- ## [0.1.0.beta6] - 2026-06-22
10
-
11
- (yrb-lite core gem. The `yrb-lite-client` npm package ships these client changes as 0.1.2.)
12
-
13
- ### Added
14
-
15
- - `yrb-lite-client`, the TypeScript client package for the yrb-lite
16
- ActionCable/AnyCable protocol. It provides `ActionCableProvider`,
17
- `YProtocolSession`, and the standalone `ReliableSync` delivery core.
18
-
19
- ### Changed
20
-
21
- - Document delivery is ack-tracked by default in `yrb-lite-client`: document
22
- frames use `{ update, id }`, acknowledgements use `{ ack }`, and pending
23
- document updates stay queued until acked.
24
- - The ActionCable protocol surface uses a single canonical document envelope:
25
- `{ "update" => "<base64 frame>" }`.
26
- - AnyCable awareness/presence uses an awareness-only whisper envelope,
27
- `{ awareness: "<base64 awareness frame>" }`, while document frames stay on
28
- the server persistence/ack path.
29
-
30
- ### Fixed
31
-
32
- - Incoming protocol frames are validated before mutating documents or awareness
33
- state, including trailing-byte rejection on the TypeScript client.
34
- - Native/Rust protocol entry points reject wire client IDs that are unsafe for
35
- JavaScript clients.
36
- - `lib0` is declared as a direct runtime dependency of `yrb-lite-client`.
37
-
38
- ## [0.1.0.beta5] - 2026-06-18
39
-
40
- ### Changed
41
-
42
- - **Breaking:** the ActionCable integration has been extracted into a separate
43
- gem, [`yrb-lite-actioncable`](https://rubygems.org/gems/yrb-lite-actioncable).
44
- `yrb-lite` is now a standalone y-crdt wrapper: CRDT documents, awareness, and
45
- the y-websocket sync protocol primitives, with no Rails/ActionCable coupling
46
- (mirrors the `y-rb` / `yrb-actioncable` split). The `base64` runtime
47
- dependency moved with it.
48
-
49
- ### Migration
50
-
51
- - Using `YrbLite::Sync`? Add `gem "yrb-lite-actioncable"` and change
52
- `include YrbLite::Sync` to `include YrbLite::ActionCable::Sync`. The concern's
53
- API is otherwise unchanged. If you only use `YrbLite::Doc`/`YrbLite::Awareness`,
54
- nothing changes.
55
-
56
- ## [0.1.0.beta4] - 2026-06-18
57
-
58
- ### Changed
59
-
60
- - `on_change` block recorders now run in the **channel instance's context**
61
- (via `instance_exec`), so a recorder can call the channel's own methods --
62
- `current_user`, `params`, request/connection-scoped accessors -- directly,
63
- instead of plumbing them in through a thread-local. A non-Proc callable (an
64
- object responding to `#call`) is still invoked with `#call` and its own
65
- context. Existing block recorders that use
66
- only the `(key, update)` arguments and lexically-scoped constants are
67
- unaffected; the only behavioral change is `self` inside the block.
68
-
69
- ## [0.1.0.beta3] - 2026-06-18
70
-
71
- ### Changed
72
-
73
- - Upgraded the bundled `yrs` (y-crdt) from 0.21 to 0.27.2. No change to the
74
- `YrbLite::Doc`, `YrbLite::Awareness`, or `YrbLite::Sync` public API; existing
75
- code and the wire protocol are unaffected.
76
- - Thread-safety is preserved across the upgrade. yrs 0.27 dropped `Awareness`'s
77
- internal locking (its mutating methods now take `&mut self`, and `Awareness`
78
- is no longer `Sync`), so `YrbLite::Awareness` now serializes access through an
79
- internal `Mutex`. The lock is taken only while the GVL is released and is
80
- never held across the GVL boundary, so concurrent access from multiple Ruby
81
- threads stays safe and deadlock-free, and document reads still run in parallel
82
- (they operate on a cheaply-cloned, `Arc`-backed `Doc` handle, not under the
83
- presence lock).
84
-
85
- ### Build
86
-
87
- - Building the gem from source now requires **Rust 1.94 or newer** (yrs 0.27.2
88
- uses `let`-chains). The precompiled platform gems are unaffected -- they need
89
- no Rust toolchain to install.
90
-
91
- ## [0.1.0.beta2] - 2026-06-16
92
-
93
- ### Added
94
-
95
- - Reliable delivery (opt-in, client-driven). A client may tag a document update
96
- with an `"id"`; the server replies `{ "ack": <id> }` once the update has been
97
- durably recorded. This lets an
98
- ack-aware client retain and retransmit an update until delivery is confirmed,
99
- so an edit can't be silently lost on a flaky connection. Clients that omit
100
- `"id"` are still accepted, but their delivery is not ack-tracked.
101
- - Demo coverage for reliable delivery with "sync-since-last-ack" framing (the
102
- unacknowledged tail is sent as one merged, causally-complete delta), plus a
103
- minimal reference client and an intensive message-loss stress test.
104
-
105
- ### Fixed
106
-
107
- - Causal-gap protection. The authoritative, fast, and store paths now reject a
108
- document update that isn't causally ready -- one whose dependencies are
109
- missing because an earlier update was lost in transit or its durable record
110
- failed -- and ask the client to resync, instead of recording or relaying an
111
- un-integrable update that would leave the log permanently pending. Adds native
112
- `Doc#update_ready?`/`#pending?` (cheap, read-only checks) used to gate the
113
- record-before-distribute path.
114
-
115
- ## [0.1.0.beta1]
116
-
117
- ### Added
118
-
119
- - Thread-safe `YrbLite::Doc` and `YrbLite::Awareness` over `yrs` (magnus/rb-sys
120
- native extension). The GVL is released during CRDT work so docs can run in
121
- parallel on MRI.
122
- - `YrbLite::Sync` ActionCable channel concern implementing the y-websocket
123
- protocol (document sync plus awareness/presence).
124
- - A "record-before-distribute" mode via an `on_change` hook, so every change is
125
- recorded durably before it's applied or relayed.
126
- - Presence cleanup on disconnect, and idle-document eviction.
127
- - Store-backed ActionCable delivery for AnyCable and multi-process use.
128
- - Hardening against bad input: malformed or multi-message frames are dropped
129
- before processing or relay, and native panics are contained at the FFI
130
- boundary.
131
- - Precompiled native gems for common platforms (no Rust toolchain needed to
132
- install) via the cross-gem workflow.
133
-
134
- [Unreleased]: https://github.com/jpcamara/yrb-lite/compare/v0.1.0.beta5...main
135
- [0.1.0.beta5]: https://github.com/jpcamara/yrb-lite/compare/v0.1.0.beta4...v0.1.0.beta5
136
- [0.1.0.beta4]: https://github.com/jpcamara/yrb-lite/compare/v0.1.0.beta3...v0.1.0.beta4
137
- [0.1.0.beta3]: https://github.com/jpcamara/yrb-lite/compare/v0.1.0.beta2...v0.1.0.beta3
138
- [0.1.0.beta2]: https://github.com/jpcamara/yrb-lite/compare/v0.1.0.beta1...v0.1.0.beta2
139
- [0.1.0.beta1]: https://github.com/jpcamara/yrb-lite/releases/tag/v0.1.0.beta1
data/README.md CHANGED
@@ -19,12 +19,12 @@ end
19
19
 
20
20
  On the browser, use the `yrb-lite-client` `ActionCableProvider`. Tiptap,
21
21
  ProseMirror, and BlockNote all sync through the `Y.Doc` you pass in and the
22
- provider's Awareness instance, unless you supply your own.
22
+ provider's Awareness instance.
23
23
 
24
24
  ## What you get
25
25
 
26
- - Thread-safe Ruby wrappers for `Doc` and `Awareness`. You can share them
27
- across Puma threads; native CRDT work runs with the GVL released.
26
+ - A thread-safe Ruby `Doc` you can share across Puma threads; native CRDT work
27
+ runs with the GVL released.
28
28
  - The y-websocket protocol (document sync plus awareness/presence) as a
29
29
  one-include ActionCable concern.
30
30
  - Store-backed ActionCable/AnyCable delivery for multi-process deployments.
@@ -86,11 +86,7 @@ require "yrb_lite"
86
86
 
87
87
  # Create docs
88
88
  doc = YrbLite::Doc.new # random client ID
89
- doc = YrbLite::Doc.new(12345) # specific client ID
90
-
91
- # Get document info
92
- doc.client_id # => unique client identifier
93
- doc.guid # => document GUID
89
+ doc = YrbLite::Doc.new(12345) # specific client ID (used for CRDT identity)
94
90
 
95
91
  # Encoding
96
92
  doc.encode_state_vector # => current state vector
@@ -100,36 +96,23 @@ doc.encode_state_as_update(sv) # => update diff against state vector
100
96
  # Applying updates
101
97
  doc.apply_update(update_bytes) # apply raw V1 update
102
98
 
103
- # Sync protocol messages
104
- doc.sync_step1 # => SyncStep1 message (contains state vector)
105
- doc.sync_step2(state_vector) # => SyncStep2 message (contains update)
106
- doc.handle_sync_message(data) # => [msg_type, sync_type, response]
107
- doc.encode_update_message(update) # => wrap update as sync Update message
99
+ # Sync protocol
100
+ doc.sync_step1 # => SyncStep1 message (this doc's state vector)
101
+ doc.handle_sync_message(data) # => [msg_type, sync_type, response]; answers a
102
+ # peer's SyncStep1 with a SyncStep2
108
103
  ```
109
104
 
110
- ### Awareness (Document + Presence)
111
-
112
- ```ruby
113
- # Create awareness instances (each contains a Doc)
114
- awareness = YrbLite::Awareness.new # random client ID
115
- awareness = YrbLite::Awareness.new(12345) # specific client ID
116
-
117
- # Get document info
118
- awareness.client_id # => unique client identifier
119
- awareness.guid # => document GUID
120
- ```
105
+ ### Protocol codec (module functions)
121
106
 
122
- ### Handling Sync Messages
107
+ Classifying and unwrapping wire frames is stateless, so it's exposed as
108
+ `YrbLite` module functions rather than a class. The server never holds presence
109
+ or document state to route a frame — presence lives in the browser clients, and
110
+ the server only relays awareness frames opaquely.
123
111
 
124
112
  ```ruby
125
- # When connection opens, send initial sync messages
126
- initial_message = awareness.start
127
- # Send initial_message to peer via WebSocket
128
-
129
- # When receiving messages from peer
130
- response = awareness.handle(incoming_data)
131
- # Send response back to peer if not empty
132
- send_to_peer(response) unless response.empty?
113
+ YrbLite.message_kind(frame) # => 0 drop / 1 step1 / 2 update / 3 awareness / 4 query
114
+ YrbLite.update_from_message(frame) # => the document delta carried by a frame, or nil
115
+ YrbLite.wrap_update(update_bytes) # => wrap a raw doc update as a sync Update frame
133
116
  ```
134
117
 
135
118
  ### ActionCable Integration
@@ -178,6 +161,34 @@ oversized, or unknown frames are dropped. A bad frame can't crash the process: a
178
161
  Rust panic is caught at the FFI boundary and re-raised as a Ruby exception. And
179
162
  no single client can relay garbage that breaks the others in a room.
180
163
 
164
+ #### Delivery guarantees
165
+
166
+ The contract is the same at every scale — one process, or hundreds across many
167
+ servers:
168
+
169
+ - **The document always converges.** CRDT updates are commutative and
170
+ idempotent, so out-of-order, duplicate, or concurrent delivery all converge to
171
+ the same correct document. This needs no coordination and holds everywhere.
172
+ - **The durable log never goes gappy.** An update is recorded only once its
173
+ causal dependencies are already in the store (checked against `on_load`); a
174
+ causally-incomplete update triggers a resync instead, so the log always
175
+ rebuilds cleanly.
176
+ - **`on_change` is at-least-once, and the durable guarantee is that replaying the
177
+ log reconstructs the document.** Every change is recorded before it's acked or
178
+ broadcast (record-before-distribute). Entry count is not 1:1 with edits: a
179
+ best-effort check skips most lost-ack retries but isn't cross-process exact (a
180
+ retry on another process can record the same update twice), and a resync can
181
+ coalesce a client's un-acked tail into a single record. So **make `on_change`
182
+ idempotent** if duplicate side effects would matter (a webhook, a counter) — a
183
+ raw append-only delta log is naturally fine, since it replays to the same
184
+ document either way.
185
+
186
+ There is deliberately no in-gem cross-process lock. One that only spanned a
187
+ single process would give exactly-once at small scale and silently degrade as
188
+ you scale out, so the guarantee is uniform instead. If you need exactly-once
189
+ *side effects*, enforce it in your store (a unique key on the update) or with
190
+ your own distributed lock — the gem stays storage-agnostic and assumes neither.
191
+
181
192
  #### Multi-process deployments
182
193
 
183
194
  Most Rails apps run several processes (Puma workers, multiple dynos), and any of
@@ -295,43 +306,15 @@ one `{ ack: id }` cumulatively confirms everything up to it. Because CRDT apply
295
306
  is idempotent, a resend that already landed is a harmless no-op that just
296
307
  re-acks. Awareness stays ephemeral and is not acked.
297
308
 
298
- ### User Awareness/Presence
299
-
300
- ```ruby
301
- # Set local user state (cursor position, name, etc.)
302
- awareness.set_local_state('{"user": {"name": "Alice", "color": "#ff0000"}}')
303
-
304
- # Get local state
305
- awareness.local_state # => '{"user": {"name": "Alice", "color": "#ff0000"}}'
306
-
307
- # Clear local state (e.g., when disconnecting)
308
- awareness.clear_local_state
309
-
310
- # Encode awareness update for broadcasting
311
- update = awareness.encode_awareness_update
312
- ```
313
-
314
- ### Low-Level Access
315
-
316
- ```ruby
317
- # Get state vector for manual sync
318
- sv = awareness.encode_state_vector
319
-
320
- # Get update diffed against a state vector
321
- update = awareness.encode_state_as_update(remote_state_vector)
322
-
323
- # Apply raw update to the document
324
- awareness.apply_update(update_bytes)
325
-
326
- # Wrap raw update data in a sync message
327
- message = awareness.encode_update(update_bytes)
328
- ```
309
+ Presence (cursors, selections) is owned by the browser clients — the server
310
+ never sets or holds presence state, it only relays awareness frames opaquely.
311
+ See `yrb-lite-client` for the client-side awareness API.
329
312
 
330
313
  ## Thread Safety
331
314
 
332
- `Doc` and `Awareness` are safe to share across Ruby threads. A `Doc` or
333
- `Awareness` can be used concurrently from Puma workers, ActionCable connection
334
- threads, or background jobs without external locking.
315
+ A `Doc` is safe to share across Ruby threads used concurrently from Puma
316
+ workers, ActionCable connection threads, or background jobs without external
317
+ locking.
335
318
 
336
319
  That comes from how the underlying types work, not from locking on top:
337
320
 
Binary file
Binary file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module YrbLite
4
- VERSION = "0.1.0.beta7"
4
+ VERSION = "0.1.0.beta9"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yrb-lite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.beta7
4
+ version: 0.1.0.beta9
5
5
  platform: aarch64-mingw-ucrt
6
6
  authors:
7
7
  - JP Camara
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-23 00:00:00.000000000 Z
11
+ date: 2026-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest