yrby 0.6.0-aarch64-linux → 0.6.1-aarch64-linux
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 +12 -0
- data/README.md +64 -14
- 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/3.4/yrby.so +0 -0
- data/lib/y/4.0/yrby.so +0 -0
- data/lib/y/lexxy.rb +25 -4
- data/lib/y/version.rb +1 -1
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b673ec3d72751c36ecb0b8edc586c15f393e1136d5a1589f52bc3199bc872030
|
|
4
|
+
data.tar.gz: 1ff196e6cedb72da559c3c61a20cd54254c43ef08203a4d476819caf46af5384
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 81949023eb7db2147856515dadb883919a95fac2bb85bd3bc774ba64b37e1065cea942608a554f4a390cbb700a2df2992a378d98a039147329e6f58c1232201c
|
|
7
|
+
data.tar.gz: 5055fb7f8f94d381cb7ad1a12ed204059064a6dbced1ce1693e574491feb1a66d1a1fc124ca01b890af14f04a01ed04a35c5e0dedb03ab073c2c165da9697d5a
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ 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.6.1] - 2026-08-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `Y::Lexxy` emits the attachment tag the node was created with instead of
|
|
12
|
+
a hardcoded `<action-text-attachment>`. Lexxy makes the tag configurable
|
|
13
|
+
(`Lexxy.configure`'s `attachmentTagName`, paired with
|
|
14
|
+
`ActionText::Attachment.tag_name` in Rails), and each attachment node
|
|
15
|
+
stores its tag, so a custom-tag app's rendered HTML now matches its
|
|
16
|
+
editor. A stored value that doesn't look like a tag name falls back to
|
|
17
|
+
the default; documents from before the tag was stored render unchanged.
|
|
18
|
+
|
|
7
19
|
## [0.6.0] - 2026-07-11
|
|
8
20
|
|
|
9
21
|
### Added
|
data/README.md
CHANGED
|
@@ -5,14 +5,16 @@
|
|
|
5
5
|
Collaborative editing for Rails, backed by [y-crdt](https://github.com/y-crdt/y-crdt)
|
|
6
6
|
(the Rust library behind Y.js). Your Rails server speaks the y-websocket sync
|
|
7
7
|
protocol directly, so there's no separate Node process hosting the Y.js
|
|
8
|
-
documents.
|
|
8
|
+
documents. Pronounced "yer-bee".
|
|
9
|
+
|
|
10
|
+

|
|
9
11
|
|
|
10
12
|
```ruby
|
|
11
13
|
class DocumentChannel < ApplicationCable::Channel
|
|
12
|
-
include Y::ActionCable
|
|
14
|
+
include Y::ActionCable
|
|
13
15
|
|
|
14
|
-
on_load { |key|
|
|
15
|
-
on_change { |key, update|
|
|
16
|
+
on_load { |key| Y::Document.load_state(key) }
|
|
17
|
+
on_change { |key, update| Y::Document.append(key, update) }
|
|
16
18
|
|
|
17
19
|
def subscribed = sync_subscribed(params[:id])
|
|
18
20
|
def receive(data) = sync_receive(data, params[:id])
|
|
@@ -59,7 +61,7 @@ and `Doc#read_map` reconstruct it server-side, in Ruby.
|
|
|
59
61
|
The surface is intentionally small, but the focus is durability, resiliency, delivery
|
|
60
62
|
guarantees, correctness, and thread safety.
|
|
61
63
|
|
|
62
|
-
Towards that goal, `yrby` adds
|
|
64
|
+
Towards that goal, `yrby` adds opinionated defaults on top of normal Yjs syncing:
|
|
63
65
|
|
|
64
66
|
- Built-in update acknowledgement: the `ActionCableProvider` in `yrby-client` will continue to
|
|
65
67
|
send updates until an ack is received from the server. [`yrby-actioncable`](https://rubygems.org/gems/yrby-actioncable)
|
|
@@ -101,8 +103,9 @@ Issues and PRs are welcome.
|
|
|
101
103
|
# Core CRDT + protocol primitives:
|
|
102
104
|
gem "yrby"
|
|
103
105
|
|
|
104
|
-
# For the Rails
|
|
105
|
-
|
|
106
|
+
# For the Rails side (the sync channel, document models, the generator).
|
|
107
|
+
# Formerly yrby-actioncable; that name stops at 0.3.1.
|
|
108
|
+
gem "yrby-rails"
|
|
106
109
|
```
|
|
107
110
|
|
|
108
111
|
Requires Ruby 3.4 or newer. The release workflow builds precompiled gems for
|
|
@@ -457,25 +460,72 @@ Y.wrap_update(update_bytes) # => wrap a raw doc update as a sync Update frame
|
|
|
457
460
|
|
|
458
461
|
### ActionCable Integration
|
|
459
462
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
+
In a Rails app, one generator creates the channel and the migration:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
bin/rails generate yrby:install
|
|
467
|
+
bin/rails db:migrate
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
The models ship in the gem, the way Action Text owns
|
|
471
|
+
`ActionText::RichText`:
|
|
472
|
+
|
|
473
|
+
- **`Y::Document`** — one row per document, addressed two ways: by `key`
|
|
474
|
+
(what a channel addresses — one opaque, unique string, sometimes
|
|
475
|
+
app-supplied, never parsed) and, optionally, by polymorphic `record` +
|
|
476
|
+
`name` (which model attribute it backs; `name` is the attribute name,
|
|
477
|
+
`"body"` — one document per attribute per record, the
|
|
478
|
+
ActionText::RichText scheme). Key-only documents leave the binding nil.
|
|
479
|
+
Either side can arrive first: `Y::Document.for(record, name)` finds or
|
|
480
|
+
creates the binding, derives a readable key (`post/1/body`), and adopts
|
|
481
|
+
a key-only row already holding that key, so a channel writing first and
|
|
482
|
+
a binding created later converge on one document. The row also holds
|
|
483
|
+
the merged `state` snapshot — CRDT state only; derived data (rendered
|
|
484
|
+
HTML, search text) is the application's job, typically in the channel's
|
|
485
|
+
on_change. `.load_state(key)` / `.append(key, update)` are the store
|
|
486
|
+
calls the generated channel uses.
|
|
487
|
+
- **`Y::DocumentUpdate`** — the uncompacted tail: one delta per row,
|
|
488
|
+
compacted into `state` and deleted once the tail reaches `compact_every`
|
|
489
|
+
(default 64). Loading reads the snapshot plus the current tail; an
|
|
490
|
+
empty tail returns `state` directly. Compaction serializes on a
|
|
491
|
+
per-document row lock and skips causally-gapped rows — they're
|
|
492
|
+
quarantined until they heal rather than compacted into state or
|
|
493
|
+
deleted. Destroying a document deletes its updates with it.
|
|
494
|
+
|
|
495
|
+
The migration creates `y_documents` and `y_document_updates`. To rename
|
|
496
|
+
them, edit the generated migration and point `Y::Document.table_name` /
|
|
497
|
+
`Y::DocumentUpdate.table_name` at the new names in an initializer.
|
|
498
|
+
|
|
499
|
+
Storage is swappable: the channel only needs `on_load` and `on_change`
|
|
500
|
+
answered, and they can point at anything.
|
|
501
|
+
|
|
502
|
+
`include Y::ActionCable` (from the `yrby-rails` gem) is the channel
|
|
503
|
+
integration: the y-websocket protocol (document sync +
|
|
504
|
+
awareness/presence) over ActionCable. (`include Y::ActionCable::Sync`
|
|
505
|
+
keeps working and has the same effect.)
|
|
463
506
|
|
|
464
507
|
```ruby
|
|
465
508
|
# app/channels/document_channel.rb
|
|
466
509
|
class DocumentChannel < ApplicationCable::Channel
|
|
467
|
-
include Y::ActionCable
|
|
510
|
+
include Y::ActionCable
|
|
468
511
|
|
|
469
|
-
on_load { |key|
|
|
470
|
-
on_change { |key, update|
|
|
512
|
+
on_load { |key| Y::Document.load_state(key) } # rebuild from storage
|
|
513
|
+
on_change { |key, update| Y::Document.append(key, update) } # record, then broadcast
|
|
471
514
|
|
|
472
515
|
def subscribed
|
|
516
|
+
return reject unless authorized?(params[:id])
|
|
517
|
+
|
|
473
518
|
sync_subscribed params[:id]
|
|
474
519
|
end
|
|
475
520
|
|
|
476
521
|
def receive(data)
|
|
477
522
|
sync_receive(data, params[:id])
|
|
478
523
|
end
|
|
524
|
+
|
|
525
|
+
private
|
|
526
|
+
|
|
527
|
+
# Everyone is denied until you wire this to your app's auth.
|
|
528
|
+
def authorized?(_document_key) = false
|
|
479
529
|
end
|
|
480
530
|
```
|
|
481
531
|
|
|
@@ -571,7 +621,7 @@ It is up to you to durably record it:
|
|
|
571
621
|
|
|
572
622
|
```ruby
|
|
573
623
|
class DocumentChannel < ApplicationCable::Channel
|
|
574
|
-
include Y::ActionCable
|
|
624
|
+
include Y::ActionCable
|
|
575
625
|
|
|
576
626
|
# ...
|
|
577
627
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "generators/yrby/tables/tables_generator"
|
|
5
|
+
|
|
6
|
+
module Yrby
|
|
7
|
+
module Generators
|
|
8
|
+
# `bin/rails generate yrby:install` — a DocumentChannel speaking the
|
|
9
|
+
# y-websocket protocol over the gem's document storage, plus the storage
|
|
10
|
+
# migration (via yrby:tables). The models ship in the gem; only the
|
|
11
|
+
# migration lands in the app.
|
|
12
|
+
class InstallGenerator < ::Rails::Generators::Base
|
|
13
|
+
source_root File.expand_path("templates", __dir__)
|
|
14
|
+
|
|
15
|
+
def create_channel
|
|
16
|
+
template "document_channel.rb", "app/channels/document_channel.rb"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def create_tables
|
|
20
|
+
invoke "yrby:tables"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def show_next_steps
|
|
24
|
+
say <<~NEXT
|
|
25
|
+
|
|
26
|
+
Next steps:
|
|
27
|
+
|
|
28
|
+
1. Authorize document access: implement `authorized?` in
|
|
29
|
+
app/channels/document_channel.rb (it denies everyone until you do).
|
|
30
|
+
2. bin/rails db:migrate
|
|
31
|
+
3. Install the yrby-client npm package and connect an editor:
|
|
32
|
+
|
|
33
|
+
import { ActionCableProvider } from "yrby-client"
|
|
34
|
+
const provider = new ActionCableProvider(doc, consumer,
|
|
35
|
+
"DocumentChannel", { id: documentId })
|
|
36
|
+
provider.connect()
|
|
37
|
+
|
|
38
|
+
The README's Editors section links working integrations for
|
|
39
|
+
Tiptap, Lexxy, Rhino Editor, and CodeMirror.
|
|
40
|
+
NEXT
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Collaborative documents over Action Cable: one channel speaking the
|
|
4
|
+
# y-websocket protocol (sync plus presence). Storage is Y::Document +
|
|
5
|
+
# Y::DocumentUpdate; a document is created on its first change and its
|
|
6
|
+
# history goes with it when it is destroyed. Point on_load/on_change
|
|
7
|
+
# elsewhere to swap storage.
|
|
8
|
+
class DocumentChannel < ApplicationCable::Channel
|
|
9
|
+
include Y::ActionCable
|
|
10
|
+
|
|
11
|
+
# Rebuild a document from durable storage (nil means a brand-new document).
|
|
12
|
+
on_load { |key| Y::Document.load_state(key) }
|
|
13
|
+
|
|
14
|
+
# Record each CRDT delta durably. Runs before the change is acknowledged
|
|
15
|
+
# or broadcast; if this raises, the change is neither acked nor relayed,
|
|
16
|
+
# and yrby-client retries it.
|
|
17
|
+
on_change { |key, update| Y::Document.append(key, update) }
|
|
18
|
+
|
|
19
|
+
def subscribed
|
|
20
|
+
return reject unless authorized?(params[:id])
|
|
21
|
+
|
|
22
|
+
sync_subscribed(params[:id])
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def receive(data) = sync_receive(data, params[:id])
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
# Everyone is denied until you fill this in. Wire it to your app's auth:
|
|
30
|
+
# identify current_user on the cable connection, then check they may read
|
|
31
|
+
# and write this document. Don't lean on on_change raising for access
|
|
32
|
+
# control — that path exists for store failures.
|
|
33
|
+
def authorized?(_document_key)
|
|
34
|
+
false
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module Yrby
|
|
7
|
+
module Generators
|
|
8
|
+
# `bin/rails generate yrby:tables` — the migration for the gem-owned
|
|
9
|
+
# document models (Y::Document + Y::DocumentUpdate). Invoked by
|
|
10
|
+
# yrby:install, and by other gems building on the same storage.
|
|
11
|
+
#
|
|
12
|
+
# Template notes (kept here, not in the emitted migration): state is
|
|
13
|
+
# 4.gigabytes - 1 (longblob on MySQL — a compacted snapshot is the whole
|
|
14
|
+
# document; a 16 MB cap would break compaction) and payload is
|
|
15
|
+
# 16.megabytes - 1 (one update can carry a big paste or a client's
|
|
16
|
+
# accumulated offline edits — the 64 KB default blob is too small).
|
|
17
|
+
# The partial unique index's WHERE only keeps
|
|
18
|
+
# key-only rows out of the index: uniqueness holds without it, since
|
|
19
|
+
# unique indexes treat NULLs as distinct on every supported database,
|
|
20
|
+
# and MySQL drops the predicate harmlessly. y_document_updates indexes
|
|
21
|
+
# (document_id, pending) instead of bare document_id: the prefix
|
|
22
|
+
# serves the tail and foreign-key lookups, and the pair serves the
|
|
23
|
+
# clean-row count every append runs.
|
|
24
|
+
class TablesGenerator < ::Rails::Generators::Base
|
|
25
|
+
include ActiveRecord::Generators::Migration
|
|
26
|
+
|
|
27
|
+
source_root File.expand_path("templates", __dir__)
|
|
28
|
+
|
|
29
|
+
def create_migration_file
|
|
30
|
+
migration_template "create_y_tables.rb",
|
|
31
|
+
File.join(db_migrate_path, "create_y_tables.rb")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def migration_version
|
|
37
|
+
"[#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}]"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateYTables < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
def change
|
|
5
|
+
create_table :y_documents do |t|
|
|
6
|
+
t.string :key, null: false, index: { unique: true }
|
|
7
|
+
t.references :record, polymorphic: true, null: true, index: false
|
|
8
|
+
t.string :name
|
|
9
|
+
t.binary :state, limit: 4.gigabytes - 1
|
|
10
|
+
t.timestamps
|
|
11
|
+
t.index %i[record_type record_id name], unique: true,
|
|
12
|
+
where: "record_type IS NOT NULL",
|
|
13
|
+
name: "index_y_documents_on_record_and_name"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
create_table :y_document_updates do |t|
|
|
17
|
+
t.references :document, null: false, foreign_key: { to_table: :y_documents }, index: false
|
|
18
|
+
t.binary :payload, null: false, limit: 16.megabytes - 1
|
|
19
|
+
t.boolean :pending, null: false, default: false
|
|
20
|
+
t.datetime :created_at, null: false
|
|
21
|
+
t.index %i[document_id pending]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/y/3.4/yrby.so
CHANGED
|
Binary file
|
data/lib/y/4.0/yrby.so
CHANGED
|
Binary file
|
data/lib/y/lexxy.rb
CHANGED
|
@@ -52,24 +52,44 @@ module Y
|
|
|
52
52
|
# and presence mirror Lexxy's exportDOM (nulls omitted, `previewable`
|
|
53
53
|
# only when true, `presentation="gallery"` always).
|
|
54
54
|
def self.upload(node)
|
|
55
|
-
|
|
55
|
+
tag = attachment_tag(node)
|
|
56
|
+
out = "<#{tag}"
|
|
56
57
|
out << attachment_attr(node, "sgid", "sgid")
|
|
57
58
|
out << %( previewable="true") if node.attrs["previewable"] == true
|
|
58
59
|
[%w[url src], %w[alt altText], %w[caption caption],
|
|
59
60
|
%w[content-type contentType], %w[filename fileName],
|
|
60
61
|
%w[filesize fileSize], %w[width width], %w[height height]]
|
|
61
62
|
.each { |html_name, stored| out << attachment_attr(node, html_name, stored) }
|
|
62
|
-
%(#{out} presentation="gallery"
|
|
63
|
+
%(#{out} presentation="gallery"></#{tag}>)
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
# A content attachment (mention, embed): `content` carries the escaped
|
|
66
67
|
# inner HTML; `plainText` is not exported.
|
|
67
68
|
def self.mention(node)
|
|
68
|
-
|
|
69
|
+
tag = attachment_tag(node)
|
|
70
|
+
out = "<#{tag}"
|
|
69
71
|
out << attachment_attr(node, "sgid", "sgid")
|
|
70
72
|
out << attachment_attr(node, "content", "innerHtml")
|
|
71
73
|
out << attachment_attr(node, "content-type", "contentType")
|
|
72
|
-
"#{out}
|
|
74
|
+
"#{out}></#{tag}>"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Lexxy's attachment tag is configurable (`Lexxy.configure`'s
|
|
78
|
+
# attachmentTagName, paired with ActionText::Attachment.tag_name on the
|
|
79
|
+
# Rails side), and each attachment node stores the tag it was created
|
|
80
|
+
# with. Emit the stored tag. The value is stored document data, not
|
|
81
|
+
# markup, so anything that doesn't look like a tag name falls back to
|
|
82
|
+
# ActionText's default.
|
|
83
|
+
def self.attachment_tag(node)
|
|
84
|
+
tag = node.attrs["tagName"].to_s
|
|
85
|
+
tag.match?(/\A[a-zA-Z][a-zA-Z0-9-]*\z/) ? tag : "action-text-attachment"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# An in-flight upload placeholder. Only the uploader's browser can
|
|
89
|
+
# complete it, so it is never finished content -- materialized output
|
|
90
|
+
# (Action Text bodies, search text) must not carry it.
|
|
91
|
+
def self.pending_upload(_node)
|
|
92
|
+
""
|
|
73
93
|
end
|
|
74
94
|
|
|
75
95
|
# A stored nil (unset) is skipped; a stored empty string still emits.
|
|
@@ -90,6 +110,7 @@ module Y
|
|
|
90
110
|
"tablecell" => { contains: :blocks, render: method(:table_cell) },
|
|
91
111
|
"listitem" => { contains: :blocks, render: method(:list_item) },
|
|
92
112
|
"action_text_attachment" => method(:upload),
|
|
113
|
+
"action_text_attachment_upload" => method(:pending_upload),
|
|
93
114
|
"custom_action_text_attachment" => method(:mention)
|
|
94
115
|
}.freeze
|
|
95
116
|
|
data/lib/y/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yrby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: aarch64-linux
|
|
6
6
|
authors:
|
|
7
7
|
- JP Camara
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-08-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|
|
@@ -55,7 +55,7 @@ dependencies:
|
|
|
55
55
|
description: 'yrby is a thread-safe Ruby binding over the Rust y-crdt (yrs) library:
|
|
56
56
|
CRDT documents, awareness/presence, and the y-websocket sync protocol primitives,
|
|
57
57
|
with the GVL released during native work so documents sync in parallel. The ActionCable/Rails
|
|
58
|
-
integration lives in the companion yrby-
|
|
58
|
+
integration lives in the companion yrby-rails gem.'
|
|
59
59
|
email:
|
|
60
60
|
- johnpcamara@gmail.com
|
|
61
61
|
executables: []
|
|
@@ -65,6 +65,10 @@ files:
|
|
|
65
65
|
- CHANGELOG.md
|
|
66
66
|
- LICENSE
|
|
67
67
|
- README.md
|
|
68
|
+
- lib/generators/yrby/install/install_generator.rb
|
|
69
|
+
- lib/generators/yrby/install/templates/document_channel.rb
|
|
70
|
+
- lib/generators/yrby/tables/tables_generator.rb
|
|
71
|
+
- lib/generators/yrby/tables/templates/create_y_tables.rb
|
|
68
72
|
- lib/y.rb
|
|
69
73
|
- lib/y/3.4/yrby.so
|
|
70
74
|
- lib/y/4.0/yrby.so
|