@kyneta/yjs-schema 1.2.0 → 1.3.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/README.md +3 -3
- package/dist/index.d.ts +82 -170
- package/dist/index.js +176 -260
- package/dist/index.js.map +1 -1
- package/package.json +5 -5
- package/src/__tests__/bind-constraints.test.ts +5 -13
- package/src/__tests__/bind-yjs.test.ts +57 -46
- package/src/__tests__/create.test.ts +80 -56
- package/src/__tests__/reader.test.ts +3 -14
- package/src/__tests__/record-text-spike.test.ts +38 -36
- package/src/__tests__/substrate.test.ts +47 -40
- package/src/bind-yjs.ts +9 -40
- package/src/change-mapping.ts +7 -2
- package/src/index.ts +24 -26
- package/src/native-map.ts +37 -0
- package/src/populate.ts +1 -1
- package/src/substrate.ts +19 -4
- package/src/version.ts +14 -67
- package/src/yjs-resolve.ts +1 -1
- package/src/create.ts +0 -177
- package/src/sync.ts +0 -107
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "@kyneta/yjs-schema"
|
|
21
21
|
|
|
22
22
|
// Define a schema
|
|
23
|
-
const TodoDoc = Schema.
|
|
23
|
+
const TodoDoc = Schema.struct({
|
|
24
24
|
title: text(),
|
|
25
25
|
items: Schema.list(
|
|
26
26
|
Schema.struct({
|
|
@@ -105,7 +105,7 @@ importDelta(docB, delta!)
|
|
|
105
105
|
import { bindYjs } from "@kyneta/yjs-schema"
|
|
106
106
|
import { Schema, text } from "@kyneta/yjs-schema"
|
|
107
107
|
|
|
108
|
-
const TodoDoc = bindYjs(Schema.
|
|
108
|
+
const TodoDoc = bindYjs(Schema.struct({
|
|
109
109
|
title: text(),
|
|
110
110
|
items: Schema.list(Schema.struct({
|
|
111
111
|
name: Schema.string(),
|
|
@@ -179,4 +179,4 @@ Because `yjs(doc)` returns a standard `Y.Doc`, the entire Yjs provider ecosystem
|
|
|
179
179
|
|
|
180
180
|
## License
|
|
181
181
|
|
|
182
|
-
MIT
|
|
182
|
+
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,173 +1,38 @@
|
|
|
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
|
-
import * as Y from 'yjs';
|
|
4
1
|
export { Changeset } from '@kyneta/changefeed';
|
|
2
|
+
import { NativeMap, SubstrateNamespace, CrdtStrategy, Schema, Path, ChangeBase, Op, Reader, Version, Substrate, ReplicaFactory, SubstrateFactory, Segment } from '@kyneta/schema';
|
|
3
|
+
export { DocRef, NATIVE, Op, Ref, Schema, SubstratePayload, applyChanges, change, createDoc, createRef, exportEntirety, exportSince, merge, subscribe, subscribeNode, unwrap, version } from '@kyneta/schema';
|
|
4
|
+
import * as Y from 'yjs';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* `
|
|
15
|
-
* `
|
|
16
|
-
*
|
|
7
|
+
* NativeMap for the Yjs CRDT substrate.
|
|
8
|
+
*
|
|
9
|
+
* Maps each schema kind to the corresponding Yjs shared type:
|
|
10
|
+
* - `root → Y.Doc` (the document itself)
|
|
11
|
+
* - `text → Y.Text`
|
|
12
|
+
* - `counter → undefined` (Yjs has no counter type)
|
|
13
|
+
* - `list → Y.Array<unknown>`
|
|
14
|
+
* - `movableList → undefined` (Yjs has no movable list)
|
|
15
|
+
* - `struct → Y.Map<unknown>` (Yjs uses maps for struct fields)
|
|
16
|
+
* - `map → Y.Map<unknown>`
|
|
17
|
+
* - `tree → undefined` (Yjs has no tree type)
|
|
18
|
+
* - `set → undefined` (not yet supported)
|
|
19
|
+
* - `scalar → undefined` (no container; stored in parent map)
|
|
20
|
+
* - `sum → undefined` (no container; stored in parent map)
|
|
17
21
|
*/
|
|
18
|
-
|
|
19
|
-
readonly
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
*
|
|
31
|
-
* Decodes both state vectors via `Y.decodeStateVector()` to get
|
|
32
|
-
* `Map<number, number>` (clientID → clock), then compares:
|
|
33
|
-
*
|
|
34
|
-
* - Collect the union of all client IDs from both maps.
|
|
35
|
-
* - For each client, compare clocks (missing client = clock 0).
|
|
36
|
-
* - If all clocks in `this` ≤ `other` and at least one strictly less → `"behind"`
|
|
37
|
-
* - If all clocks in `this` ≥ `other` and at least one strictly greater → `"ahead"`
|
|
38
|
-
* - If all clocks equal → `"equal"`
|
|
39
|
-
* - Otherwise → `"concurrent"`
|
|
40
|
-
*
|
|
41
|
-
* Throws if `other` is not a `YjsVersion`.
|
|
42
|
-
*/
|
|
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;
|
|
54
|
-
/**
|
|
55
|
-
* Parse a serialized YjsVersion string back into a YjsVersion.
|
|
56
|
-
*
|
|
57
|
-
* The inverse of `serialize()`: base64 → `Uint8Array`.
|
|
58
|
-
*/
|
|
59
|
-
static parse(serialized: string): YjsVersion;
|
|
22
|
+
interface YjsNativeMap extends NativeMap {
|
|
23
|
+
readonly root: Y.Doc;
|
|
24
|
+
readonly text: Y.Text;
|
|
25
|
+
readonly counter: undefined;
|
|
26
|
+
readonly list: Y.Array<unknown>;
|
|
27
|
+
readonly movableList: undefined;
|
|
28
|
+
readonly struct: Y.Map<unknown>;
|
|
29
|
+
readonly map: Y.Map<unknown>;
|
|
30
|
+
readonly tree: undefined;
|
|
31
|
+
readonly set: undefined;
|
|
32
|
+
readonly scalar: undefined;
|
|
33
|
+
readonly sum: undefined;
|
|
60
34
|
}
|
|
61
35
|
|
|
62
|
-
/**
|
|
63
|
-
* Create a live Yjs-backed document.
|
|
64
|
-
*
|
|
65
|
-
* **Form 1 — bring your own doc:**
|
|
66
|
-
* ```ts
|
|
67
|
-
* const yjsDoc = new Y.Doc()
|
|
68
|
-
* const doc = createYjsDoc(mySchema, yjsDoc)
|
|
69
|
-
* ```
|
|
70
|
-
*
|
|
71
|
-
* **Form 2 — fresh empty doc:**
|
|
72
|
-
* ```ts
|
|
73
|
-
* const doc = createYjsDoc(mySchema)
|
|
74
|
-
*
|
|
75
|
-
* // Apply initial content via change():
|
|
76
|
-
* change(doc, d => {
|
|
77
|
-
* d.title.insert(0, "Hello")
|
|
78
|
-
* d.items.push({ name: "First item" })
|
|
79
|
-
* })
|
|
80
|
-
* ```
|
|
81
|
-
*
|
|
82
|
-
* Returns a full-stack `Ref<S>` — callable, navigable, writable,
|
|
83
|
-
* transactable, and observable. Backed by a `YjsSubstrate` with
|
|
84
|
-
* CRDT collaboration support.
|
|
85
|
-
*
|
|
86
|
-
* The returned ref observes **all** mutations to the underlying Y.Doc,
|
|
87
|
-
* regardless of source (local kyneta writes, merge, external
|
|
88
|
-
* `Y.applyUpdate()`, external raw Yjs API mutations).
|
|
89
|
-
*
|
|
90
|
-
* @param schema - The schema describing the document structure.
|
|
91
|
-
* @param doc - Optional `Y.Doc` instance to wrap. If omitted, a fresh
|
|
92
|
-
* empty Y.Doc is created with containers matching the schema.
|
|
93
|
-
*/
|
|
94
|
-
type CreateYjsDoc = <S extends Schema>(schema: S, doc?: Y.Doc) => Ref<S>;
|
|
95
|
-
declare const createYjsDoc: CreateYjsDoc;
|
|
96
|
-
type CreateYjsDocFromEntirety = <S extends Schema>(schema: S, payload: SubstratePayload) => Ref<S>;
|
|
97
|
-
/**
|
|
98
|
-
* Reconstruct a live Yjs-backed document from a substrate entirety payload.
|
|
99
|
-
*
|
|
100
|
-
* The payload must have been produced by `exportEntirety()` on a
|
|
101
|
-
* compatible document. This is the entry point for SSR hydration
|
|
102
|
-
* and reconnection past log compaction.
|
|
103
|
-
*
|
|
104
|
-
* ```ts
|
|
105
|
-
* const payload = exportEntirety(docA)
|
|
106
|
-
* const docB = createYjsDocFromEntirety(MySchema, payload)
|
|
107
|
-
* // docB has the same state as docA at the time of export
|
|
108
|
-
* ```
|
|
109
|
-
*/
|
|
110
|
-
declare const createYjsDocFromEntirety: CreateYjsDocFromEntirety;
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Current version as a `YjsVersion` (wrapping a Yjs state vector).
|
|
114
|
-
*
|
|
115
|
-
* Use `.serialize()` to get a text-safe string for embedding in HTML
|
|
116
|
-
* meta tags, URL parameters, etc.
|
|
117
|
-
*
|
|
118
|
-
* @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.
|
|
119
|
-
* @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.
|
|
120
|
-
*/
|
|
121
|
-
declare function version(doc: object): YjsVersion;
|
|
122
|
-
/**
|
|
123
|
-
* Export the full substrate entirety — sufficient for a new peer to
|
|
124
|
-
* reconstruct an equivalent document via `createYjsDocFromEntirety()`.
|
|
125
|
-
*
|
|
126
|
-
* Returns a binary `SubstratePayload` (Yjs state-as-update bytes).
|
|
127
|
-
*
|
|
128
|
-
* @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.
|
|
129
|
-
* @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.
|
|
130
|
-
*/
|
|
131
|
-
declare function exportEntirety(doc: object): SubstratePayload;
|
|
132
|
-
/**
|
|
133
|
-
* Export a delta payload containing all changes since the given version.
|
|
134
|
-
*
|
|
135
|
-
* Returns a binary `SubstratePayload` (Yjs update bytes), or `null`
|
|
136
|
-
* if the delta cannot be computed.
|
|
137
|
-
*
|
|
138
|
-
* ```ts
|
|
139
|
-
* const v0 = version(docA)
|
|
140
|
-
* change(docA, d => d.title.insert(0, "Hi"))
|
|
141
|
-
* const delta = exportSince(docA, v0)
|
|
142
|
-
* merge(docB, delta!)
|
|
143
|
-
* ```
|
|
144
|
-
*
|
|
145
|
-
* @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.
|
|
146
|
-
* @param since - The version to diff from.
|
|
147
|
-
* @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.
|
|
148
|
-
*/
|
|
149
|
-
declare function exportSince(doc: object, since: YjsVersion): SubstratePayload | null;
|
|
150
|
-
/**
|
|
151
|
-
* Import a delta payload into a live document.
|
|
152
|
-
*
|
|
153
|
-
* The payload must have been produced by `exportSince()` or
|
|
154
|
-
* `exportEntirety()` on a compatible document.
|
|
155
|
-
*
|
|
156
|
-
* After import, the changefeed fires for all subscribers — the event
|
|
157
|
-
* bridge handles this automatically.
|
|
158
|
-
*
|
|
159
|
-
* ```ts
|
|
160
|
-
* const delta = exportSince(docA, sinceVersion)
|
|
161
|
-
* merge(docB, delta!, "sync")
|
|
162
|
-
* ```
|
|
163
|
-
*
|
|
164
|
-
* @param doc - A document created by `createYjsDoc` or `createYjsDocFromEntirety`.
|
|
165
|
-
* @param payload - The delta or entirety payload to merge.
|
|
166
|
-
* @param origin - Optional provenance tag for the changeset.
|
|
167
|
-
* @throws If `doc` was not created by `createYjsDoc` / `createYjsDocFromEntirety`.
|
|
168
|
-
*/
|
|
169
|
-
declare function merge(doc: object, payload: SubstratePayload, origin?: string): void;
|
|
170
|
-
|
|
171
36
|
/**
|
|
172
37
|
* The Yjs CRDT substrate namespace.
|
|
173
38
|
*
|
|
@@ -175,17 +40,15 @@ declare function merge(doc: object, payload: SubstratePayload, origin?: string):
|
|
|
175
40
|
* - `yjs.bind(schema, "ephemeral")` — ephemeral/presence broadcast
|
|
176
41
|
* - `yjs.replica()` — collaborative replication (default)
|
|
177
42
|
* - `yjs.replica("ephemeral")` — ephemeral replication
|
|
178
|
-
* - `yjs.unwrap(ref)` — access the underlying Y.Doc
|
|
179
43
|
*
|
|
180
44
|
* Strategy is constrained to `CrdtStrategy` (`"collaborative" | "ephemeral"`).
|
|
181
45
|
* Passing `"authoritative"` is a compile error.
|
|
46
|
+
*
|
|
47
|
+
* To access the underlying Y.Doc, use `unwrap(ref)` from `@kyneta/schema`.
|
|
182
48
|
*/
|
|
183
49
|
/** The closed set of capability tags that the Yjs substrate supports. */
|
|
184
50
|
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
|
-
};
|
|
51
|
+
declare const yjs: SubstrateNamespace<CrdtStrategy, YjsCaps, YjsNativeMap>;
|
|
189
52
|
|
|
190
53
|
/**
|
|
191
54
|
* Apply a kyneta Change to the Yjs shared type tree imperatively.
|
|
@@ -260,6 +123,55 @@ declare function ensureContainers(doc: Y.Doc, schema: Schema, conditional?: bool
|
|
|
260
123
|
*/
|
|
261
124
|
declare function yjsReader(doc: Y.Doc, schema: Schema): Reader;
|
|
262
125
|
|
|
126
|
+
/**
|
|
127
|
+
* A Version wrapping a Yjs state vector.
|
|
128
|
+
*
|
|
129
|
+
* State vectors track the complete peer state — which operations from
|
|
130
|
+
* each client have been observed. This is the right abstraction for sync
|
|
131
|
+
* diffing: `exportSince(version)` uses the state vector to compute the
|
|
132
|
+
* minimal update payload via `Y.encodeStateAsUpdate(doc, sv)`.
|
|
133
|
+
*
|
|
134
|
+
* `serialize()` encodes to base64 for text-safe embedding.
|
|
135
|
+
* `compare()` decodes both state vectors and performs standard
|
|
136
|
+
* version-vector partial-order comparison over the client-clock maps.
|
|
137
|
+
*/
|
|
138
|
+
declare class YjsVersion implements Version {
|
|
139
|
+
readonly sv: Uint8Array;
|
|
140
|
+
constructor(sv: Uint8Array);
|
|
141
|
+
/**
|
|
142
|
+
* Serialize the state vector to a base64 string.
|
|
143
|
+
*
|
|
144
|
+
* The encoding is: raw state vector bytes → base64.
|
|
145
|
+
* This is text-safe for embedding in HTML meta tags, URL parameters, etc.
|
|
146
|
+
*/
|
|
147
|
+
serialize(): string;
|
|
148
|
+
/**
|
|
149
|
+
* Compare with another version using version-vector partial order.
|
|
150
|
+
*
|
|
151
|
+
* Delegates to the shared `versionVectorCompare` utility after decoding
|
|
152
|
+
* both state vectors via `Y.decodeStateVector()`.
|
|
153
|
+
*
|
|
154
|
+
* @throws If `other` is not a `YjsVersion`.
|
|
155
|
+
*/
|
|
156
|
+
compare(other: Version): "behind" | "equal" | "ahead" | "concurrent";
|
|
157
|
+
/**
|
|
158
|
+
* Greatest lower bound (lattice meet) of two Yjs versions.
|
|
159
|
+
*
|
|
160
|
+
* Decodes both state vectors, computes the component-wise minimum
|
|
161
|
+
* via the shared `versionVectorMeet` utility, and encodes the result
|
|
162
|
+
* back to a Yjs state vector.
|
|
163
|
+
*
|
|
164
|
+
* @throws If `other` is not a `YjsVersion`.
|
|
165
|
+
*/
|
|
166
|
+
meet(other: Version): YjsVersion;
|
|
167
|
+
/**
|
|
168
|
+
* Parse a serialized YjsVersion string back into a YjsVersion.
|
|
169
|
+
*
|
|
170
|
+
* The inverse of `serialize()`: base64 → `Uint8Array`.
|
|
171
|
+
*/
|
|
172
|
+
static parse(serialized: string): YjsVersion;
|
|
173
|
+
}
|
|
174
|
+
|
|
263
175
|
/**
|
|
264
176
|
* Creates a `Substrate<YjsVersion>` wrapping a user-provided Y.Doc.
|
|
265
177
|
*
|
|
@@ -310,4 +222,4 @@ declare function stepIntoYjs(current: unknown, segment: Segment): unknown;
|
|
|
310
222
|
*/
|
|
311
223
|
declare function resolveYjsType(rootMap: Y.Map<any>, rootSchema: Schema, path: Path): unknown;
|
|
312
224
|
|
|
313
|
-
export { type YjsCaps,
|
|
225
|
+
export { type YjsCaps, type YjsNativeMap, YjsVersion, applyChangeToYjs, createYjsSubstrate, ensureContainers, eventsToOps, resolveYjsType, stepIntoYjs, yjs, yjsReader, yjsReplicaFactory, yjsSubstrateFactory };
|