@kyneta/yjs-schema 1.0.0 → 1.2.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.
- package/dist/index.d.ts +109 -147
- package/dist/index.js +321 -210
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/src/__tests__/bind-constraints.test.ts +333 -0
- package/src/__tests__/bind-yjs.test.ts +53 -55
- package/src/__tests__/create.test.ts +71 -62
- package/src/__tests__/{store-reader.test.ts → reader.test.ts} +64 -90
- package/src/__tests__/record-text-spike.test.ts +38 -31
- package/src/__tests__/structural-merge.test.ts +362 -0
- package/src/__tests__/substrate.test.ts +65 -84
- package/src/__tests__/version.test.ts +82 -16
- package/src/bind-yjs.ts +115 -64
- package/src/change-mapping.ts +60 -84
- package/src/create.ts +33 -28
- package/src/index.ts +32 -51
- package/src/populate.ts +87 -92
- package/src/{store-reader.ts → reader.ts} +7 -12
- package/src/substrate.ts +186 -42
- package/src/sync.ts +26 -26
- package/src/version.ts +57 -4
- package/src/yjs-resolve.ts +5 -21
- package/src/yjs-escape.ts +0 -100
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Version, Schema, Ref, SubstratePayload,
|
|
2
|
-
export {
|
|
1
|
+
import { Version, Schema, Ref, SubstratePayload, SubstrateNamespace, CrdtStrategy, Path, ChangeBase, Op, Reader, Substrate, ReplicaFactory, SubstrateFactory, Segment } from '@kyneta/schema';
|
|
2
|
+
export { Op, Ref, Schema, SubstratePayload, applyChanges, change, subscribe, subscribeNode } from '@kyneta/schema';
|
|
3
3
|
import * as Y from 'yjs';
|
|
4
|
-
|
|
4
|
+
export { Changeset } from '@kyneta/changefeed';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* A Version wrapping a Yjs state vector.
|
|
@@ -41,6 +41,16 @@ declare class YjsVersion implements Version {
|
|
|
41
41
|
* Throws if `other` is not a `YjsVersion`.
|
|
42
42
|
*/
|
|
43
43
|
compare(other: Version): "behind" | "equal" | "ahead" | "concurrent";
|
|
44
|
+
/**
|
|
45
|
+
* Greatest lower bound (lattice meet) of two Yjs versions.
|
|
46
|
+
*
|
|
47
|
+
* Decodes both state vectors, computes the component-wise minimum
|
|
48
|
+
* via the shared `versionVectorMeet` utility, and encodes the result
|
|
49
|
+
* back to a Yjs state vector.
|
|
50
|
+
*
|
|
51
|
+
* @throws If `other` is not a `YjsVersion`.
|
|
52
|
+
*/
|
|
53
|
+
meet(other: Version): YjsVersion;
|
|
44
54
|
/**
|
|
45
55
|
* Parse a serialized YjsVersion string back into a YjsVersion.
|
|
46
56
|
*
|
|
@@ -74,7 +84,7 @@ declare class YjsVersion implements Version {
|
|
|
74
84
|
* CRDT collaboration support.
|
|
75
85
|
*
|
|
76
86
|
* The returned ref observes **all** mutations to the underlying Y.Doc,
|
|
77
|
-
* regardless of source (local kyneta writes,
|
|
87
|
+
* regardless of source (local kyneta writes, merge, external
|
|
78
88
|
* `Y.applyUpdate()`, external raw Yjs API mutations).
|
|
79
89
|
*
|
|
80
90
|
* @param schema - The schema describing the document structure.
|
|
@@ -83,21 +93,21 @@ declare class YjsVersion implements Version {
|
|
|
83
93
|
*/
|
|
84
94
|
type CreateYjsDoc = <S extends Schema>(schema: S, doc?: Y.Doc) => Ref<S>;
|
|
85
95
|
declare const createYjsDoc: CreateYjsDoc;
|
|
86
|
-
type
|
|
96
|
+
type CreateYjsDocFromEntirety = <S extends Schema>(schema: S, payload: SubstratePayload) => Ref<S>;
|
|
87
97
|
/**
|
|
88
|
-
* Reconstruct a live Yjs-backed document from a substrate
|
|
98
|
+
* Reconstruct a live Yjs-backed document from a substrate entirety payload.
|
|
89
99
|
*
|
|
90
|
-
* The payload must have been produced by `
|
|
100
|
+
* The payload must have been produced by `exportEntirety()` on a
|
|
91
101
|
* compatible document. This is the entry point for SSR hydration
|
|
92
102
|
* and reconnection past log compaction.
|
|
93
103
|
*
|
|
94
104
|
* ```ts
|
|
95
|
-
* const payload =
|
|
96
|
-
* const docB =
|
|
105
|
+
* const payload = exportEntirety(docA)
|
|
106
|
+
* const docB = createYjsDocFromEntirety(MySchema, payload)
|
|
97
107
|
* // docB has the same state as docA at the time of export
|
|
98
108
|
* ```
|
|
99
109
|
*/
|
|
100
|
-
declare const
|
|
110
|
+
declare const createYjsDocFromEntirety: CreateYjsDocFromEntirety;
|
|
101
111
|
|
|
102
112
|
/**
|
|
103
113
|
* Current version as a `YjsVersion` (wrapping a Yjs state vector).
|
|
@@ -105,20 +115,20 @@ declare const createYjsDocFromSnapshot: CreateYjsDocFromSnapshot;
|
|
|
105
115
|
* Use `.serialize()` to get a text-safe string for embedding in HTML
|
|
106
116
|
* meta tags, URL parameters, etc.
|
|
107
117
|
*
|
|
108
|
-
* @param doc - A document created by `createYjsDoc` or `
|
|
109
|
-
* @throws If `doc` was not created by `createYjsDoc` / `
|
|
118
|
+
* @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.
|
|
119
|
+
* @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.
|
|
110
120
|
*/
|
|
111
121
|
declare function version(doc: object): YjsVersion;
|
|
112
122
|
/**
|
|
113
|
-
* Export the full substrate
|
|
114
|
-
* reconstruct an equivalent document via `
|
|
123
|
+
* Export the full substrate entirety — sufficient for a new peer to
|
|
124
|
+
* reconstruct an equivalent document via `createYjsDocFromEntirety()`.
|
|
115
125
|
*
|
|
116
126
|
* Returns a binary `SubstratePayload` (Yjs state-as-update bytes).
|
|
117
127
|
*
|
|
118
|
-
* @param doc - A document created by `createYjsDoc` or `
|
|
119
|
-
* @throws If `doc` was not created by `createYjsDoc` / `
|
|
128
|
+
* @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.
|
|
129
|
+
* @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.
|
|
120
130
|
*/
|
|
121
|
-
declare function
|
|
131
|
+
declare function exportEntirety(doc: object): SubstratePayload;
|
|
122
132
|
/**
|
|
123
133
|
* Export a delta payload containing all changes since the given version.
|
|
124
134
|
*
|
|
@@ -129,78 +139,53 @@ declare function exportSnapshot(doc: object): SubstratePayload;
|
|
|
129
139
|
* const v0 = version(docA)
|
|
130
140
|
* change(docA, d => d.title.insert(0, "Hi"))
|
|
131
141
|
* const delta = exportSince(docA, v0)
|
|
132
|
-
*
|
|
142
|
+
* merge(docB, delta!)
|
|
133
143
|
* ```
|
|
134
144
|
*
|
|
135
|
-
* @param doc - A document created by `createYjsDoc` or `
|
|
145
|
+
* @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.
|
|
136
146
|
* @param since - The version to diff from.
|
|
137
|
-
* @throws If `doc` was not created by `createYjsDoc` / `
|
|
147
|
+
* @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.
|
|
138
148
|
*/
|
|
139
149
|
declare function exportSince(doc: object, since: YjsVersion): SubstratePayload | null;
|
|
140
150
|
/**
|
|
141
151
|
* Import a delta payload into a live document.
|
|
142
152
|
*
|
|
143
153
|
* The payload must have been produced by `exportSince()` or
|
|
144
|
-
* `
|
|
154
|
+
* `exportEntirety()` on a compatible document.
|
|
145
155
|
*
|
|
146
156
|
* After import, the changefeed fires for all subscribers — the event
|
|
147
157
|
* bridge handles this automatically.
|
|
148
158
|
*
|
|
149
159
|
* ```ts
|
|
150
160
|
* const delta = exportSince(docA, sinceVersion)
|
|
151
|
-
*
|
|
161
|
+
* merge(docB, delta!, "sync")
|
|
152
162
|
* ```
|
|
153
163
|
*
|
|
154
|
-
* @param doc - A document created by `createYjsDoc` or `
|
|
155
|
-
* @param payload - The delta or
|
|
164
|
+
* @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.
|
|
165
|
+
* @param payload - The delta or entirety payload to merge.
|
|
156
166
|
* @param origin - Optional provenance tag for the changeset.
|
|
157
|
-
* @throws If `doc` was not created by `createYjsDoc` / `
|
|
167
|
+
* @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.
|
|
158
168
|
*/
|
|
159
|
-
declare function
|
|
169
|
+
declare function merge(doc: object, payload: SubstratePayload, origin?: string): void;
|
|
160
170
|
|
|
161
171
|
/**
|
|
162
|
-
*
|
|
163
|
-
* using the schema as a type witness to determine navigation at each
|
|
164
|
-
* path segment.
|
|
172
|
+
* The Yjs CRDT substrate namespace.
|
|
165
173
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
174
|
+
* - `yjs.bind(schema)` — collaborative sync (default)
|
|
175
|
+
* - `yjs.bind(schema, "ephemeral")` — ephemeral/presence broadcast
|
|
176
|
+
* - `yjs.replica()` — collaborative replication (default)
|
|
177
|
+
* - `yjs.replica("ephemeral")` — ephemeral replication
|
|
178
|
+
* - `yjs.unwrap(ref)` — access the underlying Y.Doc
|
|
169
179
|
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* @param doc - The Y.Doc to read from.
|
|
173
|
-
* @param schema - The root schema for the document.
|
|
180
|
+
* Strategy is constrained to `CrdtStrategy` (`"collaborative" | "ephemeral"`).
|
|
181
|
+
* Passing `"authoritative"` is a compile error.
|
|
174
182
|
*/
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
* - `Y.Map` → `.get(key)`
|
|
182
|
-
* - `Y.Array` → `.get(index)`
|
|
183
|
-
* - `Y.Text` → terminal (cannot step further)
|
|
184
|
-
* - Plain value → terminal (return `undefined`)
|
|
185
|
-
*
|
|
186
|
-
* @param current - The current position (a Yjs shared type or plain value)
|
|
187
|
-
* @param segment - The path segment to follow
|
|
188
|
-
*/
|
|
189
|
-
declare function stepIntoYjs(current: unknown, segment: Segment): unknown;
|
|
190
|
-
/**
|
|
191
|
-
* Resolve a Yjs shared type (or plain value) at the given path.
|
|
192
|
-
*
|
|
193
|
-
* Left-folds over path segments using `advanceSchema` for pure schema
|
|
194
|
-
* descent and `stepIntoYjs` for Yjs-specific navigation.
|
|
195
|
-
*
|
|
196
|
-
* Returns the Yjs shared type or plain value at the terminal position.
|
|
197
|
-
* For an empty path, returns the root map itself.
|
|
198
|
-
*
|
|
199
|
-
* @param rootMap - The root `Y.Map` obtained via `doc.getMap("root")`
|
|
200
|
-
* @param rootSchema - The root document schema
|
|
201
|
-
* @param path - The path to resolve
|
|
202
|
-
*/
|
|
203
|
-
declare function resolveYjsType(rootMap: Y.Map<any>, rootSchema: Schema, path: Path): unknown;
|
|
183
|
+
/** The closed set of capability tags that the Yjs substrate supports. */
|
|
184
|
+
type YjsCaps = "text" | "json";
|
|
185
|
+
declare const yjs: SubstrateNamespace<CrdtStrategy, YjsCaps> & {
|
|
186
|
+
/** Access the underlying `Y.Doc` backing a ref. */
|
|
187
|
+
unwrap(ref: object): Y.Doc;
|
|
188
|
+
};
|
|
204
189
|
|
|
205
190
|
/**
|
|
206
191
|
* Apply a kyneta Change to the Yjs shared type tree imperatively.
|
|
@@ -228,24 +213,52 @@ declare function applyChangeToYjs(rootMap: Y.Map<any>, rootSchema: Schema, path:
|
|
|
228
213
|
*
|
|
229
214
|
* @param events - The events from the `observeDeep` callback
|
|
230
215
|
*/
|
|
231
|
-
declare function eventsToOps(events: Y.YEvent<any>[]): Op[];
|
|
216
|
+
declare function eventsToOps(events: Y.YEvent<any>[], schema: Schema): Op[];
|
|
232
217
|
|
|
233
218
|
/**
|
|
234
219
|
* Ensure that a Y.Doc's root map contains the correct Yjs shared types
|
|
235
220
|
* matching the schema structure.
|
|
236
221
|
*
|
|
237
|
-
* Obtains the root map via `doc.getMap("root")`,
|
|
238
|
-
* schema, and creates empty containers for each field within a
|
|
239
|
-
* `doc.transact()` call for atomicity.
|
|
222
|
+
* Obtains the root map via `doc.getMap("root")`, reads the root product
|
|
223
|
+
* schema's fields, and creates empty containers for each field within a
|
|
224
|
+
* single `doc.transact()` call for atomicity.
|
|
225
|
+
*
|
|
226
|
+
* When `conditional` is true, fields that already exist in the root map
|
|
227
|
+
* are skipped. This is the correct mode after hydration — containers
|
|
228
|
+
* present from stored state must not be overwritten (each `rootMap.set()`
|
|
229
|
+
* is a CRDT write that advances the version vector and may conflict
|
|
230
|
+
* with stored operations).
|
|
231
|
+
*
|
|
232
|
+
* When `conditional` is false (default), all fields are created
|
|
233
|
+
* unconditionally. This is the correct mode for fresh documents.
|
|
240
234
|
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
235
|
+
* **Structural identity:** This function temporarily sets `doc.clientID`
|
|
236
|
+
* to `STRUCTURAL_YJS_CLIENT_ID` (0) for the duration of container creation,
|
|
237
|
+
* then restores the caller's clientID. This produces byte-identical
|
|
238
|
+
* structural ops across all peers, enabling Yjs deduplication on merge.
|
|
244
239
|
*
|
|
245
240
|
* @param doc - The Y.Doc to prepare
|
|
246
|
-
* @param schema - The root document schema (
|
|
241
|
+
* @param schema - The root document schema (a ProductSchema)
|
|
242
|
+
* @param conditional - If true, skip fields that already exist in the root map.
|
|
243
|
+
* Context: jj:smmulzkm (two-phase substrate construction)
|
|
244
|
+
*/
|
|
245
|
+
declare function ensureContainers(doc: Y.Doc, schema: Schema, conditional?: boolean): void;
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Creates a Reader that navigates the Yjs shared type tree live,
|
|
249
|
+
* using the schema as a type witness to determine navigation at each
|
|
250
|
+
* path segment.
|
|
251
|
+
*
|
|
252
|
+
* The reader is a live view — mutations to the underlying Y.Doc
|
|
253
|
+
* (via `doc.transact()`, or `Y.applyUpdate()`) are immediately
|
|
254
|
+
* visible through the reader.
|
|
255
|
+
*
|
|
256
|
+
* Internally obtains the root map via `doc.getMap("root")`.
|
|
257
|
+
*
|
|
258
|
+
* @param doc - The Y.Doc to read from.
|
|
259
|
+
* @param schema - The root schema for the document.
|
|
247
260
|
*/
|
|
248
|
-
declare function
|
|
261
|
+
declare function yjsReader(doc: Y.Doc, schema: Schema): Reader;
|
|
249
262
|
|
|
250
263
|
/**
|
|
251
264
|
* Creates a `Substrate<YjsVersion>` wrapping a user-provided Y.Doc.
|
|
@@ -253,11 +266,11 @@ declare function ensureContainers(doc: Y.Doc, schema: Schema): void;
|
|
|
253
266
|
* This is the "bring your own doc" entry point. The user creates and
|
|
254
267
|
* manages the Y.Doc (possibly via a Yjs provider); this function wraps
|
|
255
268
|
* it with a schema-aware overlay providing typed reads, writes,
|
|
256
|
-
* versioning, and export/
|
|
269
|
+
* versioning, and export/merge through the standard Substrate interface.
|
|
257
270
|
*
|
|
258
271
|
* **Event bridge contract:** A persistent `observeDeep` handler is
|
|
259
272
|
* registered on the root Y.Map at construction time. All non-kyneta
|
|
260
|
-
* mutations to the Y.Doc (
|
|
273
|
+
* mutations to the Y.Doc (merges, external local writes) are bridged
|
|
261
274
|
* to the kyneta changefeed. Subscribing to the kyneta doc observes all
|
|
262
275
|
* mutations regardless of source.
|
|
263
276
|
*
|
|
@@ -266,86 +279,35 @@ declare function ensureContainers(doc: Y.Doc, schema: Schema): void;
|
|
|
266
279
|
* @param schema - The root schema for the document.
|
|
267
280
|
*/
|
|
268
281
|
declare function createYjsSubstrate(doc: Y.Doc, schema: Schema): Substrate<YjsVersion>;
|
|
269
|
-
|
|
270
|
-
* Factory for constructing Yjs-backed substrates.
|
|
271
|
-
*
|
|
272
|
-
* - `create(schema)` — creates a fresh Y.Doc with empty containers
|
|
273
|
-
* matching the schema structure. No seed data — initial content
|
|
274
|
-
* should be applied via `change()` after construction.
|
|
275
|
-
* - `fromSnapshot(payload, schema)` — creates a Y.Doc from a snapshot
|
|
276
|
-
* payload, returns a substrate.
|
|
277
|
-
* - `parseVersion(serialized)` — deserializes a YjsVersion.
|
|
278
|
-
*/
|
|
282
|
+
declare const yjsReplicaFactory: ReplicaFactory<YjsVersion>;
|
|
279
283
|
declare const yjsSubstrateFactory: SubstrateFactory<YjsVersion>;
|
|
280
284
|
|
|
281
285
|
/**
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
* This is the recommended way to declare a Yjs-backed document type.
|
|
285
|
-
* The factory builder injects a deterministic numeric Yjs clientID derived
|
|
286
|
-
* from the exchange's string peerId, ensuring consistent change attribution
|
|
287
|
-
* across all documents and sessions.
|
|
286
|
+
* Navigate one step deeper into the Yjs shared type tree.
|
|
288
287
|
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
* `
|
|
292
|
-
* `
|
|
288
|
+
* Uses `instanceof` for runtime type discrimination:
|
|
289
|
+
* - `Y.Map` → `.get(key)`
|
|
290
|
+
* - `Y.Array` → `.get(index)`
|
|
291
|
+
* - `Y.Text` → terminal (cannot step further)
|
|
292
|
+
* - Plain value → terminal (return `undefined`)
|
|
293
293
|
*
|
|
294
|
-
* @
|
|
295
|
-
*
|
|
296
|
-
* import { bindYjs } from "@kyneta/yjs-schema"
|
|
297
|
-
* import { Schema } from "@kyneta/schema"
|
|
298
|
-
*
|
|
299
|
-
* const TodoDoc = bindYjs(Schema.doc({
|
|
300
|
-
* title: Schema.annotated("text"),
|
|
301
|
-
* items: Schema.list(Schema.struct({
|
|
302
|
-
* name: Schema.string(),
|
|
303
|
-
* done: Schema.boolean(),
|
|
304
|
-
* })),
|
|
305
|
-
* }))
|
|
306
|
-
*
|
|
307
|
-
* const doc = exchange.get("my-todos", TodoDoc)
|
|
308
|
-
* ```
|
|
294
|
+
* @param current - The current position (a Yjs shared type or plain value)
|
|
295
|
+
* @param segment - The path segment to follow
|
|
309
296
|
*/
|
|
310
|
-
declare function
|
|
311
|
-
|
|
297
|
+
declare function stepIntoYjs(current: unknown, segment: Segment): unknown;
|
|
312
298
|
/**
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
* This is the Yjs-specific escape hatch for accessing substrate-level
|
|
316
|
-
* capabilities: raw Yjs API, y-prosemirror/y-codemirror bindings,
|
|
317
|
-
* undo manager, awareness protocol, Yjs providers (y-websocket,
|
|
318
|
-
* y-indexeddb, y-webrtc, Hocuspocus, Liveblocks), etc.
|
|
319
|
-
*
|
|
320
|
-
* Currently supports root document refs only. Child-level resolution
|
|
321
|
-
* (e.g. `yjs(doc.title)` → `Y.Text`) is future work.
|
|
322
|
-
*
|
|
323
|
-
* @param ref - A root document ref backed by a Yjs substrate
|
|
324
|
-
* @returns The `Y.Doc` backing the ref
|
|
325
|
-
* @throws If the ref is not backed by a Yjs substrate
|
|
326
|
-
*
|
|
327
|
-
* @example
|
|
328
|
-
* ```ts
|
|
329
|
-
* import { yjs } from "@kyneta/yjs-schema"
|
|
299
|
+
* Resolve a Yjs shared type (or plain value) at the given path.
|
|
330
300
|
*
|
|
331
|
-
*
|
|
332
|
-
*
|
|
333
|
-
* console.log(yjsDoc.getMap("root").toJSON()) // raw state
|
|
334
|
-
* console.log(yjsDoc.clientID) // client ID
|
|
335
|
-
* ```
|
|
336
|
-
*/
|
|
337
|
-
declare function yjs(ref: object): Doc;
|
|
338
|
-
|
|
339
|
-
/**
|
|
340
|
-
* Collaborative text (CRDT). Produces `annotated("text")`.
|
|
301
|
+
* Left-folds over path segments using `advanceSchema` for pure schema
|
|
302
|
+
* descent and `stepIntoYjs` for Yjs-specific navigation.
|
|
341
303
|
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
* via Y.Text.
|
|
304
|
+
* Returns the Yjs shared type or plain value at the terminal position.
|
|
305
|
+
* For an empty path, returns the root map itself.
|
|
345
306
|
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
307
|
+
* @param rootMap - The root `Y.Map` obtained via `doc.getMap("root")`
|
|
308
|
+
* @param rootSchema - The root document schema
|
|
309
|
+
* @param path - The path to resolve
|
|
348
310
|
*/
|
|
349
|
-
declare function
|
|
311
|
+
declare function resolveYjsType(rootMap: Y.Map<any>, rootSchema: Schema, path: Path): unknown;
|
|
350
312
|
|
|
351
|
-
export { YjsVersion, applyChangeToYjs,
|
|
313
|
+
export { type YjsCaps, YjsVersion, applyChangeToYjs, createYjsDoc, createYjsDocFromEntirety, createYjsSubstrate, ensureContainers, eventsToOps, exportEntirety, exportSince, merge, resolveYjsType, stepIntoYjs, version, yjs, yjsReader, yjsReplicaFactory, yjsSubstrateFactory };
|