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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +73 -28
- data/Cargo.lock +29 -0
- data/README.md +352 -103
- data/ext/yrby/Cargo.toml +3 -0
- data/ext/yrby/crates/html-core/Cargo.toml +15 -0
- data/ext/yrby/{src/render_rules.rs → crates/html-core/src/lib.rs} +6 -0
- data/ext/yrby/crates/lexical-html/Cargo.toml +16 -0
- data/ext/yrby/{src/lexical_html.rs → crates/lexical-html/src/lib.rs} +14 -10
- data/ext/yrby/crates/prosemirror-html/Cargo.toml +16 -0
- data/ext/yrby/{src/prosemirror_html.rs → crates/prosemirror-html/src/lib.rs} +14 -10
- data/ext/yrby/src/lib.rs +10 -10
- data/ext/yrby/src/protocol.rs +63 -0
- data/ext/yrby/src/read.rs +3 -3
- data/ext/yrby/target/debug/build/clang-sys-e3ae45bd384f74c3/out/common.rs +355 -0
- data/ext/yrby/target/debug/build/clang-sys-e3ae45bd384f74c3/out/dynamic.rs +276 -0
- data/ext/yrby/target/debug/build/clang-sys-e3ae45bd384f74c3/out/macros.rs +49 -0
- data/ext/yrby/target/debug/build/rb-sys-4407948463231c4f/out/bindings-0.9.128-mri-arm64-darwin23-3.4.7.rs +8934 -0
- data/ext/yrby/target/debug/build/rb-sys-dbeea42737529c2d/out/bindings-0.9.128-mri-arm64-darwin23-3.4.7.rs +8934 -0
- data/ext/yrby/target/debug/build/serde-58ea0ee887cc2602/out/private.rs +6 -0
- data/ext/yrby/target/debug/build/serde_core-41f407c21c1f205e/out/private.rs +5 -0
- data/ext/yrby/target/debug/build/thiserror-0f1416a82ff26f22/out/private.rs +5 -0
- data/lib/generators/yrby/install/install_generator.rb +44 -0
- data/lib/generators/yrby/install/templates/document_channel.rb +36 -0
- data/lib/generators/yrby/tables/tables_generator.rb +41 -0
- data/lib/generators/yrby/tables/templates/create_y_tables.rb +24 -0
- data/lib/y/decoder.rb +64 -0
- data/lib/y/lexxy.rb +25 -4
- data/lib/y/version.rb +1 -1
- data/lib/y.rb +1 -0
- metadata +21 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7e0200b53061315ddc075bbd337a5d9ead543f99617cac94d53180242a80bbd
|
|
4
|
+
data.tar.gz: 956ec491daabcafc73009e40df573e9c01e5b7587be9d3001d32a7b0a4092f01
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
63
|
-
the details family
|
|
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
|
|
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
|
|
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
|
|
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
|
|
114
|
-
routinely belong to *other* clients
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
192
|
-
but not stored or relayed
|
|
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
|
|
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
|
|
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"
|
|
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
|
|
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"
|