yrby 0.6.0 → 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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +73 -28
  3. data/Cargo.lock +29 -0
  4. data/README.md +352 -103
  5. data/ext/yrby/Cargo.toml +3 -0
  6. data/ext/yrby/crates/html-core/Cargo.toml +15 -0
  7. data/ext/yrby/{src/render_rules.rs → crates/html-core/src/lib.rs} +6 -0
  8. data/ext/yrby/crates/lexical-html/Cargo.toml +16 -0
  9. data/ext/yrby/{src/lexical_html.rs → crates/lexical-html/src/lib.rs} +14 -10
  10. data/ext/yrby/crates/prosemirror-html/Cargo.toml +16 -0
  11. data/ext/yrby/{src/prosemirror_html.rs → crates/prosemirror-html/src/lib.rs} +14 -10
  12. data/ext/yrby/src/lib.rs +10 -10
  13. data/ext/yrby/src/protocol.rs +63 -0
  14. data/ext/yrby/src/read.rs +3 -3
  15. data/ext/yrby/target/debug/build/clang-sys-e3ae45bd384f74c3/out/common.rs +355 -0
  16. data/ext/yrby/target/debug/build/clang-sys-e3ae45bd384f74c3/out/dynamic.rs +276 -0
  17. data/ext/yrby/target/debug/build/clang-sys-e3ae45bd384f74c3/out/macros.rs +49 -0
  18. data/ext/yrby/target/debug/build/rb-sys-4407948463231c4f/out/bindings-0.9.128-mri-arm64-darwin23-3.4.7.rs +8934 -0
  19. data/ext/yrby/target/debug/build/rb-sys-dbeea42737529c2d/out/bindings-0.9.128-mri-arm64-darwin23-3.4.7.rs +8934 -0
  20. data/ext/yrby/target/debug/build/serde-58ea0ee887cc2602/out/private.rs +6 -0
  21. data/ext/yrby/target/debug/build/serde_core-41f407c21c1f205e/out/private.rs +5 -0
  22. data/ext/yrby/target/debug/build/thiserror-0f1416a82ff26f22/out/private.rs +5 -0
  23. data/lib/generators/yrby/install/install_generator.rb +44 -0
  24. data/lib/generators/yrby/install/templates/document_channel.rb +36 -0
  25. data/lib/generators/yrby/tables/tables_generator.rb +41 -0
  26. data/lib/generators/yrby/tables/templates/create_y_tables.rb +24 -0
  27. data/lib/y/decoder.rb +64 -0
  28. data/lib/y/lexxy.rb +25 -4
  29. data/lib/y/version.rb +1 -1
  30. data/lib/y.rb +1 -0
  31. metadata +21 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93704595e7dd617d4eccd0645a3dbc99bc798e11ec2591c86f47f0da08381d2c
4
- data.tar.gz: 3f4fb67f5fc0368fe31a74cdae2104b9cea8bc44e349b398efc20f84fca7061a
3
+ metadata.gz: c7e0200b53061315ddc075bbd337a5d9ead543f99617cac94d53180242a80bbd
4
+ data.tar.gz: 956ec491daabcafc73009e40df573e9c01e5b7587be9d3001d32a7b0a4092f01
5
5
  SHA512:
6
- metadata.gz: fbe8957a8298efdb2991fa2de86a184e3395cb4315f7e467c2ee1dc1c37e750404468e9a2e71bf55967a44ca281424e4310fc4d985503171ca50a10bb3341e90
7
- data.tar.gz: a29fd67645ef0b5312e34213eaf13c42e0eb8628a4e1da1d6af08ff6a68b77673f0f6e32c74f52da5797a3b802573ca78559e4a94a0c36da5b22331102a0ade6
6
+ metadata.gz: afae1336b4e184f1ecf1d878202023123cee10fa7ea47642c7f6f2b7d48eee7201f2b2198aed217b08434228904178bddaf3d46058e6da7b5b27ad7450a0ef87
7
+ data.tar.gz: 48a7bd917201e4df0858a08ce0d1eab132fc160a507dc88bfcd1c390cae9a0a8b11a9132894e5f91889f946f69e4366455fb8257e0165c9f1d82da4c367547ed
data/CHANGELOG.md CHANGED
@@ -4,21 +4,66 @@ All notable changes to this project are documented here. The format is based on
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project aims
5
5
  to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.7.0] - 2026-08-11
8
+
9
+ ### Added
10
+
11
+ - `Y::Decoder` ships in the core gem and loads with `require "y"`. It was
12
+ scaffolded as a separate `yrby-decoder` gem, but it is 66 lines of pure
13
+ Ruby over `Doc#read_text` / `read_xml`, requires the native core either
14
+ way, and the separate gem was never published, so the split left the
15
+ module in no gem at all. The `yrby-decoder` name is retired unused.
16
+
17
+ ### Changed
18
+
19
+ - `Doc#handle_sync_message` answers a SyncStep1 with the doc's full state,
20
+ pending included, matching Y.js's `encodeStateAsUpdate`. It previously
21
+ served integrated-only state. A peer parks a served pending struct the
22
+ same way the doc did and heals it when the missing dependency arrives
23
+ from its sender's ack-driven retransmit. `compacted_state_update` still
24
+ excludes pending, so compaction cannot freeze a gap into a snapshot.
25
+
26
+ ### Fixed
27
+
28
+ - **`Doc#update_advances?` no longer misreads a gappy merged update carrying
29
+ novel content as a no-op.** A crafted frame can hide an internal gap behind
30
+ a Skip block while its post-gap blocks still integrate (yrs plants a Skip
31
+ hole in the store). That moves neither the doc's public state vector nor
32
+ pending, so the probe comparison reported genuinely novel content as
33
+ "doesn't advance"; `update_ready?` accepted the frame and the doc applied
34
+ it, but it was never recorded to the durable log or broadcast. Any
35
+ insertion past the update's own (Skip-capped) `state_vector()` now
36
+ conservatively reports as advancing. Not reachable through standard Yjs
37
+ providers (a client's own updates and diffs are gap-free), so this closes
38
+ a hostile-input hole, not a real-world regression.
39
+
40
+ ## [0.6.1] - 2026-08-04
41
+
42
+ ### Fixed
43
+
44
+ - `Y::Lexxy` emits the attachment tag the node was created with instead of
45
+ a hardcoded `<action-text-attachment>`. Lexxy makes the tag configurable
46
+ (`Lexxy.configure`'s `attachmentTagName`, paired with
47
+ `ActionText::Attachment.tag_name` in Rails), and each attachment node
48
+ stores its tag, so a custom-tag app's rendered HTML now matches its
49
+ editor. A stored value that doesn't look like a tag name falls back to
50
+ the default; documents from before the tag was stored render unchanged.
51
+
7
52
  ## [0.6.0] - 2026-07-11
8
53
 
9
54
  ### Added
10
55
 
11
56
  - **Custom render rules for `Y::Lexical` and `Y::ProseMirror`.** Both
12
- renderers now take a block registering rules per node type
57
+ renderers now take a block registering rules per node type:
13
58
  `rules.node "callout", tag: "aside"` for markup-as-data, a Ruby block for
14
- logic plus `nodes:`/`marks:` keywords as the equivalent data form, to
59
+ logic, plus `nodes:`/`marks:` keywords as the equivalent data form, to
15
60
  render node types the pinned schemas don't know or to override how a
16
61
  built-in renders.
17
62
  Declarative rules (`tag`/`attrs`/`text`/`contains`, with templates mixing
18
63
  literals and attribute references) render natively at full speed. Callback
19
64
  rules run a Ruby block per node, receiving its type, attributes,
20
65
  already-rendered children, and `child_types` (its element/block children by
21
- type the structural facts behind gallery counts and nested-list classes).
66
+ type, the structural facts behind gallery counts and nested-list classes).
22
67
  The block runs after the document read has finished, never while the doc is
23
68
  locked, so it can safely read or write the same doc. Blocks are proven
24
69
  sufficient for whole schemas: the gem's own editor schemas (`Y::Lexxy`,
@@ -26,13 +71,13 @@ to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
26
71
  output byte-identical to a live editor's. With no callback rules the
27
72
  render path is unchanged, byte for byte. See "Custom nodes and marks" in
28
73
  the README.
29
- - **`Y::Lexical#node_types` / `Y::ProseMirror#node_types` schema
74
+ - **`Y::Lexical#node_types` / `Y::ProseMirror#node_types`: schema
30
75
  discovery.** Ask a real document which node types it holds and what they
31
76
  look like: counts, attribute names as stored, child types, whether text
32
77
  runs appear, and whether a builtin or one of your rules already handles
33
78
  each ("handled" nil marks what still needs a rule). Editors store names
34
79
  you'd never guess; this is how you find them.
35
- - `Y::RenderRules.escape_text` / `escape_attr` the exact escaping the
80
+ - `Y::RenderRules.escape_text` / `escape_attr`: the exact escaping the
36
81
  native renderers use, for blocks that build markup from stored values
37
82
  (ERB's `html_escape` also rewrites apostrophes, which breaks byte parity
38
83
  with editor output).
@@ -42,15 +87,15 @@ to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
42
87
  - **Lexical rendering is now two classes: `Y::Lexical` (core Lexical) and
43
88
  `Y::Lexxy` (core plus the Lexxy schema as render rules).** Stock Lexical
44
89
  has no canonical serializer, so the editor-specific class carries the
45
- editor's name `Y::Lexxy.new(doc).to_html` is the byte-parity call for
90
+ editor's name; `Y::Lexxy.new(doc).to_html` is the byte-parity call for
46
91
  Lexxy/Rails apps, and `Y::Lexical` is the base any other Lexical editor
47
92
  extends with its own rules. The
48
- native side renders core structure paragraphs, headings, quotes, code,
93
+ native side renders core structure (paragraphs, headings, quotes, code,
49
94
  lists, tables, links, the full text-format model. Lexxy's own node types
50
95
  (attachments, galleries, `early_escape_code`, `horizontal_divider`) and its
51
96
  decorations of core nodes (the table figure wrapper, header-cell styling,
52
97
  the nested-list-item class) are rules applied beneath the app's, on the
53
- same extension API the gem's Lexxy support is the API's first consumer.
98
+ same extension API; the gem's Lexxy support is the API's first consumer.
54
99
  Output is unchanged: the fixture tests still hold `to_html` byte-identical
55
100
  to a live editor's serialized value, now through the extension path. An
56
101
  unknown Lexical container also degrades better: its block children render
@@ -59,8 +104,8 @@ to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
59
104
  ProseMirror) and `Y::Tiptap` (core plus Tiptap's extension nodes as render
60
105
  rules).** `Y::Tiptap.new(doc).to_html` is the byte-parity call for Tiptap
61
106
  apps. The native side renders prosemirror-schema-basic plus the
62
- prosemirror-tables family; Tiptap's extension nodes task lists, mentions,
63
- the details family are `Y::Tiptap::NODES` rules. Marks stay native in the
107
+ prosemirror-tables family; Tiptap's extension nodes (task lists, mentions,
108
+ the details family) are `Y::Tiptap::NODES` rules. Marks stay native in the
64
109
  base class: mark rendering (nesting order, `textStyle` CSS, `code`
65
110
  exclusivity) runs through text-run machinery node rules don't reach, so
66
111
  `Y::ProseMirror` still renders Tiptap's full mark set and `rules.mark`
@@ -71,7 +116,7 @@ to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
71
116
 
72
117
  ### Added
73
118
 
74
- - **`Y::Lexical` render Lexical/Lexxy documents to HTML.**
119
+ - **`Y::Lexical`: render Lexical/Lexxy documents to HTML.**
75
120
  `Y::Lexical.new(doc).to_html` turns a Lexxy document into HTML on the server,
76
121
  with no Node process or headless editor. The output is identical to the HTML
77
122
  a `lexxy-editor` submits to Rails; the tests check it byte-for-byte against a
@@ -89,12 +134,12 @@ to follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
89
134
 
90
135
  ### Added
91
136
 
92
- - **`Y::ProseMirror` render ProseMirror/Tiptap documents to HTML.**
137
+ - **`Y::ProseMirror`: render ProseMirror/Tiptap documents to HTML.**
93
138
  `Y::ProseMirror.new(doc).to_html` turns a Tiptap document into HTML on the
94
139
  server, with no Node process or headless editor. The output matches Tiptap's
95
140
  own `getHTML()`; the tests check it byte-for-byte against a document captured
96
141
  from a real editor. It follows `ueberdosis/tiptap-php` and reads both name
97
- styles editors use Tiptap's `bulletList`/`bold` and prosemirror-schema-basic's
142
+ styles editors use: Tiptap's `bulletList`/`bold` and prosemirror-schema-basic's
98
143
  `bullet_list`/`strong`. Covers paragraphs, headings, blockquotes,
99
144
  bullet/ordered/task lists, code blocks, links, images, mentions, details,
100
145
  hard breaks, horizontal rules, tables, text styles (color, font family), and
@@ -110,8 +155,8 @@ Fixes from a full source review.
110
155
 
111
156
  - **`Doc#update_ready?` is now exact.** It previously checked only the
112
157
  per-client clock lower bound, but yrs's real integration gate also requires
113
- every block referenced by an item's origin / right-origin / parent which
114
- routinely belong to *other* clients and post-Skip blocks in a merged update
158
+ every block referenced by an item's origin / right-origin / parent (which
159
+ routinely belong to *other* clients) and post-Skip blocks in a merged update
115
160
  sit above the lower bound. An update could pass the clock check yet park as
116
161
  pending; downstream, `update_advances?` then misread the parked update as an
117
162
  already-applied retry (pending doesn't move a state vector) and the sync
@@ -135,7 +180,7 @@ Fixes from a full source review.
135
180
  ### Changed
136
181
 
137
182
  - `update_advances?` skips its full-document probe when the update carries
138
- blocks beyond the doc's state vector (a novel update trivially advances)
183
+ blocks beyond the doc's state vector (a novel update trivially advances);
139
184
  the common case no longer pays O(doc) per frame.
140
185
  - The gem no longer packages the `yrby-decoder` gem's files (they ship in that
141
186
  gem; the duplicate copy could shadow a newer standalone release), and now
@@ -146,7 +191,7 @@ Fixes from a full source review.
146
191
  ### Fixed
147
192
 
148
193
  - **Sync no longer serves un-integrable pending structs.** When a doc holds a
149
- *pending* struct (a gappy update whose causally-prior update is missing e.g.
194
+ *pending* struct (a gappy update whose causally-prior update is missing, e.g.
150
195
  legacy data recorded before the `update_ready?` gate existed), its integrated
151
196
  state vector is empty but `encode_state_as_update` merges the pending bytes back
152
197
  in. Answering a peer's `SyncStep1` with that state handed the peer content it
@@ -154,18 +199,18 @@ Fixes from a full source review.
154
199
  non-empty-content mismatch drove endless resync traffic (observed as a browser
155
200
  re-sending frames several times a second). `handle_sync_message` now answers
156
201
  `SyncStep1` with **integrated-only** state, so a server never serves a struct it
157
- can't integrate itself. Neutralizes existing poisoned server state on deploy
202
+ can't integrate itself. Neutralizes existing poisoned server state on deploy;
158
203
  no migration needed. The server's own pending is untouched and still heals if
159
204
  the missing dependency later arrives (only then does the content become
160
205
  visible in sync). Live delta relay (`Update` frames) is unchanged.
161
206
 
162
207
  ### Added
163
208
 
164
- - `Doc#pending?` true if the doc holds un-integrable pending structs or a
209
+ - `Doc#pending?`: true if the doc holds un-integrable pending structs or a
165
210
  pending delete set (content waiting on a missing causally-prior update).
166
- - `Doc#compacted_state_update` like `encode_state_as_update` (full state) but
211
+ - `Doc#compacted_state_update`: like `encode_state_as_update` (full state) but
167
212
  **gap-free**: excludes pending structs/delete set. Use it when persisting or
168
- serving state other peers will apply. Non-destructive the doc keeps its
213
+ serving state other peers will apply. Non-destructive; the doc keeps its
169
214
  pending (so it can still heal), and `encode_state_as_update` stays lossless for
170
215
  raw-update recovery.
171
216
 
@@ -183,13 +228,13 @@ Fixes from a full source review.
183
228
  before vs. after a trial apply on an isolated probe: a genuinely new deletion
184
229
  changes it (`true`); an already-applied retry re-encodes identically (`false`).
185
230
  Insert/format-only updates keep the cheaper state-vector path, so only
186
- delete-bearing frames a minority pay for the exact comparison. The exactly-
231
+ delete-bearing frames (a minority) pay for the exact comparison. The exactly-
187
232
  once guarantee is unchanged in the safe direction: a real deletion is never
188
233
  dropped.
189
234
 
190
235
  This lets `yrby-actioncable` (and any caller gating `on_change` on
191
- `update_advances?`) settle a duplicate pure-delete frame as `:applied` acked,
192
- but not stored or relayed so apps no longer need an app-level
236
+ `update_advances?`) settle a duplicate pure-delete frame as `:applied` (acked,
237
+ but not stored or relayed), so apps no longer need an app-level
193
238
  encode-and-compare guard around their durable writes.
194
239
 
195
240
  ## [0.2.2] - 2026-06-30
@@ -200,7 +245,7 @@ Fixes from a full source review.
200
245
  embeds child blocks (list items, table cells, nested lists) as `Y.XmlText`
201
246
  embeds that `get_string` silently drops, so lists and tables previously came
202
247
  back empty. `read_xml` now walks the embeds: text runs build a line, inline
203
- children (links) join it, and nested block children flush and recurse so a
248
+ children (links) join it, and nested block children flush and recurse, so a
204
249
  document with headings, formatted text, links, bullet/numbered/check/nested
205
250
  lists, blockquotes, code blocks and tables extracts every piece of text.
206
251
  Lexical decorator elements (horizontal rule, image) are skipped instead of
@@ -211,17 +256,17 @@ Fixes from a full source review.
211
256
 
212
257
  ### Changed
213
258
  - **Internal:** renamed the native extension crate `y_ruby` → `yrby` (now loads
214
- from `lib/y/yrby.bundle`). No public API change `require "y"` and `Y::Doc`
259
+ from `lib/y/yrby.bundle`). No public API change: `require "y"` and `Y::Doc`
215
260
  are unchanged.
216
261
 
217
262
  ## [0.2.0] - 2026-06-28
218
263
 
219
264
  First release. The public Ruby interface is the top-level module **`Y`**
220
- (`Y::Doc`, `Y::Error`, `Y::VERSION`), loaded with `require "y"` mirroring the
265
+ (`Y::Doc`, `Y::Error`, `Y::VERSION`), loaded with `require "y"`, mirroring the
221
266
  `y-rb` gem's `Y::Doc` interface.
222
267
 
223
268
  ### Added
224
- - Native `Doc#read_text` and `Doc#read_map` readers reconstruct plain text and
269
+ - Native `Doc#read_text` and `Doc#read_map` readers: reconstruct plain text and
225
270
  a JSON map from the stored CRDT state in-process, server-side, with no Node or
226
271
  subprocess.
227
272
 
data/Cargo.lock CHANGED
@@ -617,6 +617,9 @@ dependencies = [
617
617
  "rb-sys-env 0.1.2",
618
618
  "serde_json",
619
619
  "yrs",
620
+ "yrs-html-core",
621
+ "yrs-lexical-html",
622
+ "yrs-prosemirror-html",
620
623
  ]
621
624
 
622
625
  [[package]]
@@ -637,6 +640,32 @@ dependencies = [
637
640
  "thiserror",
638
641
  ]
639
642
 
643
+ [[package]]
644
+ name = "yrs-html-core"
645
+ version = "0.1.0"
646
+ dependencies = [
647
+ "serde_json",
648
+ "yrs",
649
+ ]
650
+
651
+ [[package]]
652
+ name = "yrs-lexical-html"
653
+ version = "0.1.0"
654
+ dependencies = [
655
+ "serde_json",
656
+ "yrs",
657
+ "yrs-html-core",
658
+ ]
659
+
660
+ [[package]]
661
+ name = "yrs-prosemirror-html"
662
+ version = "0.1.0"
663
+ dependencies = [
664
+ "serde_json",
665
+ "yrs",
666
+ "yrs-html-core",
667
+ ]
668
+
640
669
  [[package]]
641
670
  name = "zmij"
642
671
  version = "1.0.14"