@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.
@@ -1,24 +1,23 @@
1
- import { describe, expect, it, vi } from "vitest"
1
+ import { change, Schema, subscribe } from "@kyneta/schema"
2
+ import { describe, expect, it } from "vitest"
2
3
  import * as Y from "yjs"
3
- import { Schema, change, subscribe } from "@kyneta/schema"
4
- import { createYjsDoc, createYjsDocFromSnapshot } from "../create.js"
5
- import { version, exportSnapshot, exportSince, importDelta } from "../sync.js"
6
- import { YjsVersion } from "../version.js"
4
+ import { yjs } from "../bind-yjs.js"
5
+ import { createYjsDoc, createYjsDocFromEntirety } from "../create.js"
7
6
  import { ensureContainers } from "../populate.js"
8
- import { yjsSubstrateFactory } from "../substrate.js"
9
- import { yjs } from "../yjs-escape.js"
7
+ import { exportEntirety, exportSince, merge, version } from "../sync.js"
8
+ import { YjsVersion } from "../version.js"
10
9
 
11
10
  // ===========================================================================
12
11
  // Schemas used across tests
13
12
  // ===========================================================================
14
13
 
15
- const SimpleSchema = Schema.doc({
16
- title: Schema.annotated("text"),
14
+ const SimpleSchema = Schema.struct({
15
+ title: Schema.text(),
17
16
  count: Schema.number(),
18
17
  items: Schema.list(Schema.string()),
19
18
  })
20
19
 
21
- const StructListSchema = Schema.doc({
20
+ const StructListSchema = Schema.struct({
22
21
  tasks: Schema.list(
23
22
  Schema.struct({
24
23
  name: Schema.string(),
@@ -27,8 +26,8 @@ const StructListSchema = Schema.doc({
27
26
  ),
28
27
  })
29
28
 
30
- const NestedSchema = Schema.doc({
31
- title: Schema.annotated("text"),
29
+ const NestedSchema = Schema.struct({
30
+ title: Schema.text(),
32
31
  meta: Schema.struct({
33
32
  author: Schema.string(),
34
33
  tags: Schema.list(Schema.string()),
@@ -179,12 +178,12 @@ describe("createYjsDoc", () => {
179
178
  expect(doc.count()).toBe(77)
180
179
  })
181
180
 
182
- it("yjs() escape hatch returns the same Y.Doc", () => {
181
+ it("yjs.unwrap() escape hatch returns the same Y.Doc", () => {
183
182
  const yjsDoc = new Y.Doc()
184
183
  ensureContainers(yjsDoc, SimpleSchema)
185
184
 
186
185
  const doc = createYjsDoc(SimpleSchema, yjsDoc)
187
- const escaped = yjs(doc)
186
+ const escaped = yjs.unwrap(doc)
188
187
 
189
188
  expect(escaped).toBe(yjsDoc)
190
189
  })
@@ -192,10 +191,10 @@ describe("createYjsDoc", () => {
192
191
  })
193
192
 
194
193
  // ===========================================================================
195
- // createYjsDocFromSnapshot
194
+ // createYjsDocFromEntirety
196
195
  // ===========================================================================
197
196
 
198
- describe("createYjsDocFromSnapshot", () => {
197
+ describe("createYjsDocFromEntirety", () => {
199
198
  it("reconstructs state from a snapshot", () => {
200
199
  const doc1 = createYjsDoc(SimpleSchema)
201
200
  change(doc1, (d: any) => {
@@ -205,8 +204,8 @@ describe("createYjsDocFromSnapshot", () => {
205
204
  change(doc1, (d: any) => d.items.push("a"))
206
205
  change(doc1, (d: any) => d.items.push("b"))
207
206
 
208
- const payload = exportSnapshot(doc1)
209
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, payload)
207
+ const payload = exportEntirety(doc1)
208
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, payload)
210
209
 
211
210
  expect(doc2.title()).toBe("Snapshot")
212
211
  expect(doc2.count()).toBe(42)
@@ -225,8 +224,8 @@ describe("createYjsDocFromSnapshot", () => {
225
224
  d.items.push("x")
226
225
  })
227
226
 
228
- const payload = exportSnapshot(doc1)
229
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, payload)
227
+ const payload = exportEntirety(doc1)
228
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, payload)
230
229
 
231
230
  expect(doc2.title()).toBe("Start End")
232
231
  expect(doc2.count()).toBe(99)
@@ -243,8 +242,8 @@ describe("createYjsDocFromSnapshot", () => {
243
242
  change(doc1, (d: any) => d.meta.tags.push("v1"))
244
243
  change(doc1, (d: any) => d.meta.tags.push("v2"))
245
244
 
246
- const payload = exportSnapshot(doc1)
247
- const doc2 = createYjsDocFromSnapshot(NestedSchema, payload)
245
+ const payload = exportEntirety(doc1)
246
+ const doc2 = createYjsDocFromEntirety(NestedSchema, payload)
248
247
 
249
248
  expect(doc2.title()).toBe("Nested")
250
249
  expect(doc2.meta.author()).toBe("Alice")
@@ -258,8 +257,8 @@ describe("createYjsDocFromSnapshot", () => {
258
257
  change(doc1, (d: any) => d.tasks.push({ name: "Task A", done: false }))
259
258
  change(doc1, (d: any) => d.tasks.push({ name: "Task B", done: true }))
260
259
 
261
- const payload = exportSnapshot(doc1)
262
- const doc2 = createYjsDocFromSnapshot(StructListSchema, payload)
260
+ const payload = exportEntirety(doc1)
261
+ const doc2 = createYjsDocFromEntirety(StructListSchema, payload)
263
262
 
264
263
  expect(doc2.tasks.length).toBe(2)
265
264
  expect((doc2.tasks.at(0) as any).name()).toBe("Task A")
@@ -271,8 +270,8 @@ describe("createYjsDocFromSnapshot", () => {
271
270
  change(doc1, (d: any) => {
272
271
  d.title.insert(0, "Original")
273
272
  })
274
- const payload = exportSnapshot(doc1)
275
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, payload)
273
+ const payload = exportEntirety(doc1)
274
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, payload)
276
275
 
277
276
  change(doc2, (d: any) => {
278
277
  d.title.insert(8, " Copy")
@@ -288,8 +287,8 @@ describe("createYjsDocFromSnapshot", () => {
288
287
  change(doc1, (d: any) => {
289
288
  d.title.insert(0, "Original")
290
289
  })
291
- const payload = exportSnapshot(doc1)
292
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, payload)
290
+ const payload = exportEntirety(doc1)
291
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, payload)
293
292
 
294
293
  const received: any[] = []
295
294
  subscribe(doc2, (changeset: any) => {
@@ -330,7 +329,9 @@ describe("sync primitives", () => {
330
329
 
331
330
  it("serialize/parse round-trips", () => {
332
331
  const doc = createYjsDoc(SimpleSchema)
333
- change(doc, (d: any) => { d.title.insert(0, "Test") })
332
+ change(doc, (d: any) => {
333
+ d.title.insert(0, "Test")
334
+ })
334
335
  const v = version(doc)
335
336
  const serialized = v.serialize()
336
337
  const parsed = YjsVersion.parse(serialized)
@@ -338,22 +339,26 @@ describe("sync primitives", () => {
338
339
  })
339
340
  })
340
341
 
341
- describe("exportSnapshot", () => {
342
+ describe("exportEntirety", () => {
342
343
  it("returns a binary payload", () => {
343
344
  const doc = createYjsDoc(SimpleSchema)
344
- change(doc, (d: any) => { d.title.insert(0, "Snap") })
345
- const payload = exportSnapshot(doc)
345
+ change(doc, (d: any) => {
346
+ d.title.insert(0, "Snap")
347
+ })
348
+ const payload = exportEntirety(doc)
346
349
  expect(payload.encoding).toBe("binary")
347
350
  expect(payload.data).toBeInstanceOf(Uint8Array)
348
351
  expect((payload.data as Uint8Array).byteLength).toBeGreaterThan(0)
349
352
  })
350
353
  })
351
354
 
352
- describe("exportSince + importDelta", () => {
355
+ describe("exportSince + merge", () => {
353
356
  it("syncs incremental changes between two docs", () => {
354
357
  const doc1 = createYjsDoc(SimpleSchema)
355
- change(doc1, (d: any) => { d.title.insert(0, "Start") })
356
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, exportSnapshot(doc1))
358
+ change(doc1, (d: any) => {
359
+ d.title.insert(0, "Start")
360
+ })
361
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, exportEntirety(doc1))
357
362
 
358
363
  const v2Before = version(doc2)
359
364
 
@@ -367,9 +372,9 @@ describe("sync primitives", () => {
367
372
  // Export delta and apply to doc2
368
373
  const delta = exportSince(doc1, v2Before)
369
374
  expect(delta).not.toBeNull()
370
- expect(delta!.encoding).toBe("binary")
375
+ expect(delta?.encoding).toBe("binary")
371
376
 
372
- importDelta(doc2, delta!)
377
+ merge(doc2, delta!)
373
378
 
374
379
  expect(doc2.title()).toBe("Start Edited")
375
380
  expect(doc2.count()).toBe(42)
@@ -378,37 +383,39 @@ describe("sync primitives", () => {
378
383
 
379
384
  it("syncs multiple incremental deltas", () => {
380
385
  const doc1 = createYjsDoc(SimpleSchema)
381
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, exportSnapshot(doc1))
386
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, exportEntirety(doc1))
382
387
 
383
388
  // First round
384
389
  let vBefore = version(doc2)
385
390
  change(doc1, (d: any) => {
386
391
  d.title.insert(0, "A")
387
392
  })
388
- importDelta(doc2, exportSince(doc1, vBefore)!)
393
+ merge(doc2, exportSince(doc1, vBefore)!)
389
394
 
390
395
  // Second round
391
396
  vBefore = version(doc2)
392
397
  change(doc1, (d: any) => {
393
398
  d.title.insert(1, "B")
394
399
  })
395
- importDelta(doc2, exportSince(doc1, vBefore)!)
400
+ merge(doc2, exportSince(doc1, vBefore)!)
396
401
 
397
402
  // Third round
398
403
  vBefore = version(doc2)
399
404
  change(doc1, (d: any) => {
400
405
  d.count.set(3)
401
406
  })
402
- importDelta(doc2, exportSince(doc1, vBefore)!)
407
+ merge(doc2, exportSince(doc1, vBefore)!)
403
408
 
404
409
  expect(doc2.title()).toBe("AB")
405
410
  expect(doc2.count()).toBe(3)
406
411
  })
407
412
 
408
- it("changefeed fires on importDelta", () => {
413
+ it("changefeed fires on merge", () => {
409
414
  const doc1 = createYjsDoc(SimpleSchema)
410
- change(doc1, (d: any) => { d.title.insert(0, "Source") })
411
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, exportSnapshot(doc1))
415
+ change(doc1, (d: any) => {
416
+ d.title.insert(0, "Source")
417
+ })
418
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, exportEntirety(doc1))
412
419
 
413
420
  const v2Before = version(doc2)
414
421
 
@@ -423,15 +430,15 @@ describe("sync primitives", () => {
423
430
  received.push(changeset)
424
431
  })
425
432
 
426
- importDelta(doc2, delta!)
433
+ merge(doc2, delta!)
427
434
 
428
435
  expect(received.length).toBeGreaterThanOrEqual(1)
429
436
  expect(doc2.count()).toBe(77)
430
437
  })
431
438
 
432
- it("importDelta passes origin to changefeed", () => {
439
+ it("merge passes origin to changefeed", () => {
433
440
  const doc1 = createYjsDoc(SimpleSchema)
434
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, exportSnapshot(doc1))
441
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, exportEntirety(doc1))
435
442
 
436
443
  const v2Before = version(doc2)
437
444
  change(doc1, (d: any) => {
@@ -443,7 +450,7 @@ describe("sync primitives", () => {
443
450
  receivedOrigins.push(changeset.origin)
444
451
  })
445
452
 
446
- importDelta(doc2, exportSince(doc1, v2Before)!, "my-sync-origin")
453
+ merge(doc2, exportSince(doc1, v2Before)!, "my-sync-origin")
447
454
 
448
455
  expect(receivedOrigins).toContain("my-sync-origin")
449
456
  })
@@ -452,19 +459,21 @@ describe("sync primitives", () => {
452
459
  describe("versions equal after sync", () => {
453
460
  it("versions equal after full snapshot sync", () => {
454
461
  const doc1 = createYjsDoc(SimpleSchema)
455
- change(doc1, (d: any) => { d.title.insert(0, "Same") })
462
+ change(doc1, (d: any) => {
463
+ d.title.insert(0, "Same")
464
+ })
456
465
  change(doc1, (d: any) => {
457
466
  d.count.set(42)
458
467
  })
459
468
 
460
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, exportSnapshot(doc1))
469
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, exportEntirety(doc1))
461
470
 
462
471
  expect(version(doc1).compare(version(doc2))).toBe("equal")
463
472
  })
464
473
 
465
474
  it("versions equal after bidirectional delta sync", () => {
466
475
  const doc1 = createYjsDoc(SimpleSchema)
467
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, exportSnapshot(doc1))
476
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, exportEntirety(doc1))
468
477
 
469
478
  const v1Before = version(doc1)
470
479
  const v2Before = version(doc2)
@@ -480,8 +489,8 @@ describe("sync primitives", () => {
480
489
  // Bidirectional sync
481
490
  const d1to2 = exportSince(doc1, v2Before)
482
491
  const d2to1 = exportSince(doc2, v1Before)
483
- importDelta(doc2, d1to2!)
484
- importDelta(doc1, d2to1!)
492
+ merge(doc2, d1to2!)
493
+ merge(doc1, d2to1!)
485
494
 
486
495
  expect(version(doc1).compare(version(doc2))).toBe("equal")
487
496
  })
@@ -496,9 +505,9 @@ describe("full workflow", () => {
496
505
  it("create → mutate → sync → observe", () => {
497
506
  // 1. Create two docs
498
507
  const doc1 = createYjsDoc(StructListSchema)
499
- const doc2 = createYjsDocFromSnapshot(
508
+ const doc2 = createYjsDocFromEntirety(
500
509
  StructListSchema,
501
- exportSnapshot(doc1),
510
+ exportEntirety(doc1),
502
511
  )
503
512
 
504
513
  // 2. Set up observer on doc2
@@ -520,7 +529,7 @@ describe("full workflow", () => {
520
529
 
521
530
  // 4. Sync doc1 → doc2
522
531
  const delta = exportSince(doc1, vBefore)
523
- importDelta(doc2, delta!)
532
+ merge(doc2, delta!)
524
533
 
525
534
  // 5. Verify state converged
526
535
  expect(doc2.tasks.length).toBe(2)
@@ -541,7 +550,7 @@ describe("full workflow", () => {
541
550
  })
542
551
 
543
552
  const delta2 = exportSince(doc2, v1Before)
544
- importDelta(doc1, delta2!)
553
+ merge(doc1, delta2!)
545
554
 
546
555
  expect(doc1.tasks.length).toBe(3)
547
556
  expect((doc1.tasks.at(2) as any).name()).toBe("Read book")
@@ -561,10 +570,10 @@ describe("full workflow", () => {
561
570
  })
562
571
 
563
572
  // 2. Snapshot
564
- const snapshot = exportSnapshot(doc1)
573
+ const snapshot = exportEntirety(doc1)
565
574
 
566
575
  // 3. Reconstruct
567
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, snapshot)
576
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, snapshot)
568
577
  expect(doc2.title()).toBe("Start Middle")
569
578
  expect(doc2.count()).toBe(10)
570
579
  expect(doc2.items()).toEqual(["first"])
@@ -590,7 +599,7 @@ describe("full workflow", () => {
590
599
  it("concurrent edits converge correctly", () => {
591
600
  // 1. Create two peers from the same initial state
592
601
  const doc1 = createYjsDoc(SimpleSchema)
593
- const doc2 = createYjsDocFromSnapshot(SimpleSchema, exportSnapshot(doc1))
602
+ const doc2 = createYjsDocFromEntirety(SimpleSchema, exportEntirety(doc1))
594
603
 
595
604
  const v1Before = version(doc1)
596
605
  const v2Before = version(doc2)
@@ -611,8 +620,8 @@ describe("full workflow", () => {
611
620
  // 4. Bidirectional sync
612
621
  const d1to2 = exportSince(doc1, v2Before)
613
622
  const d2to1 = exportSince(doc2, v1Before)
614
- importDelta(doc2, d1to2!)
615
- importDelta(doc1, d2to1!)
623
+ merge(doc2, d1to2!)
624
+ merge(doc1, d2to1!)
616
625
 
617
626
  // 5. Versions converge
618
627
  expect(version(doc1).compare(version(doc2))).toBe("equal")
@@ -1,8 +1,8 @@
1
+ import { KIND, RawPath, Schema } from "@kyneta/schema"
1
2
  import { describe, expect, it } from "vitest"
2
3
  import * as Y from "yjs"
3
- import { RawPath, Schema } from "@kyneta/schema"
4
- import { yjsStoreReader } from "../store-reader.js"
5
4
  import { ensureContainers } from "../populate.js"
5
+ import { yjsReader } from "../reader.js"
6
6
 
7
7
  // ===========================================================================
8
8
  // Helpers
@@ -16,7 +16,7 @@ import { ensureContainers } from "../populate.js"
16
16
  * values. We populate values via raw Yjs API within a single transact.
17
17
  */
18
18
  function setup(
19
- schema: ReturnType<typeof Schema.doc>,
19
+ schema: any,
20
20
  seed?: Record<string, unknown>,
21
21
  ) {
22
22
  const doc = new Y.Doc()
@@ -27,7 +27,7 @@ function setup(
27
27
  populateSeed(rootMap, schema, seed)
28
28
  })
29
29
  }
30
- const reader = yjsStoreReader(doc, schema)
30
+ const reader = yjsReader(doc, schema)
31
31
  return { doc, reader }
32
32
  }
33
33
 
@@ -42,11 +42,11 @@ function setup(
42
42
  */
43
43
  function populateSeed(
44
44
  ymap: Y.Map<unknown>,
45
- schema: ReturnType<typeof Schema.doc>,
45
+ schema: any,
46
46
  seed: Record<string, unknown>,
47
47
  ) {
48
- const rootProduct = unwrapToProduct(schema)
49
- if (!rootProduct) return
48
+ if (schema[KIND] !== "product") return
49
+ const rootProduct = schema
50
50
 
51
51
  for (const [key, value] of Object.entries(seed)) {
52
52
  if (value === undefined) continue
@@ -63,20 +63,15 @@ function populateField(
63
63
  fieldSchema: any,
64
64
  value: unknown,
65
65
  ) {
66
- const tag = fieldSchema._kind === "annotated" ? fieldSchema.tag : undefined
67
-
68
- if (tag === "text") {
69
- // Text field the Y.Text was already created by ensureContainers
70
- const text = ymap.get(key) as Y.Text
71
- if (text && typeof value === "string" && value.length > 0) {
72
- text.insert(0, value)
66
+ switch (fieldSchema[KIND]) {
67
+ case "text": {
68
+ // Text field the Y.Text was already created by ensureContainers
69
+ const text = ymap.get(key) as Y.Text
70
+ if (text && typeof value === "string" && value.length > 0) {
71
+ text.insert(0, value)
72
+ }
73
+ return
73
74
  }
74
- return
75
- }
76
-
77
- const structural = unwrapAnnotations(fieldSchema)
78
-
79
- switch (structural._kind) {
80
75
  case "product": {
81
76
  // Struct — recurse into the existing Y.Map
82
77
  const childMap = ymap.get(key) as Y.Map<unknown>
@@ -84,9 +79,9 @@ function populateField(
84
79
  for (const [childKey, childValue] of Object.entries(
85
80
  value as Record<string, unknown>,
86
81
  )) {
87
- const childFieldSchema = (
88
- structural.fields as Record<string, any>
89
- )[childKey]
82
+ const childFieldSchema = (fieldSchema.fields as Record<string, any>)[
83
+ childKey
84
+ ]
90
85
  if (!childFieldSchema) continue
91
86
  populateField(childMap, childKey, childFieldSchema, childValue)
92
87
  }
@@ -99,14 +94,11 @@ function populateField(
99
94
  const arr = ymap.get(key) as Y.Array<unknown>
100
95
  if (arr && Array.isArray(value)) {
101
96
  for (const item of value) {
102
- const itemSchema = structural.item
103
- if (
104
- itemSchema &&
105
- unwrapAnnotations(itemSchema)._kind === "product"
106
- ) {
97
+ const itemSchema = fieldSchema.item
98
+ if (itemSchema && itemSchema[KIND] === "product") {
107
99
  // Struct items: create a Y.Map for each
108
100
  const itemMap = buildStructMap(
109
- unwrapAnnotations(itemSchema),
101
+ itemSchema,
110
102
  item as Record<string, unknown>,
111
103
  )
112
104
  arr.push([itemMap])
@@ -153,23 +145,19 @@ function buildStructMap(
153
145
  const value = seed[key]
154
146
  if (value === undefined) continue
155
147
 
156
- const tag =
157
- fieldSchema._kind === "annotated" ? fieldSchema.tag : undefined
158
- if (tag === "text") {
159
- const text = new Y.Text()
160
- if (typeof value === "string" && value.length > 0) {
161
- text.insert(0, value)
148
+ switch (fieldSchema[KIND]) {
149
+ case "text": {
150
+ const text = new Y.Text()
151
+ if (typeof value === "string" && value.length > 0) {
152
+ text.insert(0, value)
153
+ }
154
+ map.set(key, text)
155
+ break
162
156
  }
163
- map.set(key, text)
164
- continue
165
- }
166
-
167
- const structural = unwrapAnnotations(fieldSchema)
168
- switch (structural._kind) {
169
157
  case "product": {
170
158
  map.set(
171
159
  key,
172
- buildStructMap(structural, value as Record<string, unknown>),
160
+ buildStructMap(fieldSchema, value as Record<string, unknown>),
173
161
  )
174
162
  break
175
163
  }
@@ -177,14 +165,14 @@ function buildStructMap(
177
165
  const arr = new Y.Array()
178
166
  if (Array.isArray(value)) {
179
167
  for (const item of value) {
180
- const itemSchema = structural.element ?? structural.schema
168
+ const itemSchema = fieldSchema.item
181
169
  if (
182
170
  itemSchema &&
183
- unwrapAnnotations(itemSchema)._kind === "product"
171
+ itemSchema[KIND] === "product"
184
172
  ) {
185
173
  arr.push([
186
174
  buildStructMap(
187
- unwrapAnnotations(itemSchema),
175
+ itemSchema,
188
176
  item as Record<string, unknown>,
189
177
  ),
190
178
  ])
@@ -216,22 +204,7 @@ function buildStructMap(
216
204
  return map
217
205
  }
218
206
 
219
- function unwrapToProduct(schema: any): any {
220
- let s = schema
221
- while (s._kind === "annotated" && s.schema !== undefined) {
222
- s = s.schema
223
- }
224
- if (s._kind === "product") return s
225
- return null
226
- }
227
207
 
228
- function unwrapAnnotations(schema: any): any {
229
- let s = schema
230
- while (s._kind === "annotated" && s.schema !== undefined) {
231
- s = s.schema
232
- }
233
- return s
234
- }
235
208
 
236
209
  /** Build a RawPath from variadic key/index segments. */
237
210
  function p(...segs: (string | number)[]): RawPath {
@@ -246,18 +219,18 @@ function p(...segs: (string | number)[]): RawPath {
246
219
  // Schemas used across tests
247
220
  // ===========================================================================
248
221
 
249
- const TextSchema = Schema.doc({
250
- title: Schema.annotated("text"),
251
- subtitle: Schema.annotated("text"),
222
+ const TextSchema = Schema.struct({
223
+ title: Schema.text(),
224
+ subtitle: Schema.text(),
252
225
  })
253
226
 
254
- const ScalarSchema = Schema.doc({
227
+ const ScalarSchema = Schema.struct({
255
228
  name: Schema.string(),
256
229
  count: Schema.number(),
257
230
  active: Schema.boolean(),
258
231
  })
259
232
 
260
- const NestedStructSchema = Schema.doc({
233
+ const NestedStructSchema = Schema.struct({
261
234
  profile: Schema.struct({
262
235
  first: Schema.string(),
263
236
  last: Schema.string(),
@@ -268,7 +241,7 @@ const NestedStructSchema = Schema.doc({
268
241
  }),
269
242
  })
270
243
 
271
- const ListSchema = Schema.doc({
244
+ const ListSchema = Schema.struct({
272
245
  items: Schema.list(Schema.string()),
273
246
  structs: Schema.list(
274
247
  Schema.struct({
@@ -278,12 +251,12 @@ const ListSchema = Schema.doc({
278
251
  ),
279
252
  })
280
253
 
281
- const MapSchema = Schema.doc({
254
+ const MapSchema = Schema.struct({
282
255
  labels: Schema.record(Schema.string()),
283
256
  })
284
257
 
285
- const MixedSchema = Schema.doc({
286
- title: Schema.annotated("text"),
258
+ const MixedSchema = Schema.struct({
259
+ title: Schema.text(),
287
260
  count: Schema.number(),
288
261
  items: Schema.list(
289
262
  Schema.struct({
@@ -302,7 +275,7 @@ const MixedSchema = Schema.doc({
302
275
  // Tests
303
276
  // ===========================================================================
304
277
 
305
- describe("YjsStoreReader", () => {
278
+ describe("YjsReader", () => {
306
279
  // -------------------------------------------------------------------------
307
280
  // read
308
281
  // -------------------------------------------------------------------------
@@ -347,12 +320,8 @@ describe("YjsStoreReader", () => {
347
320
  })
348
321
  expect(reader.read(p("profile", "first"))).toBe("Jane")
349
322
  expect(reader.read(p("profile", "last"))).toBe("Doe")
350
- expect(
351
- reader.read(p("profile", "address", "city")),
352
- ).toBe("Portland")
353
- expect(
354
- reader.read(p("profile", "address", "zip")),
355
- ).toBe("97201")
323
+ expect(reader.read(p("profile", "address", "city"))).toBe("Portland")
324
+ expect(reader.read(p("profile", "address", "zip"))).toBe("97201")
356
325
  })
357
326
 
358
327
  it("reads nested struct as plain object", () => {
@@ -395,14 +364,9 @@ describe("YjsStoreReader", () => {
395
364
  { name: "Task 2", done: true },
396
365
  ],
397
366
  })
398
- expect(reader.read(p("structs", 0, "name"))).toBe(
399
- "Task 1",
400
- )
367
+ expect(reader.read(p("structs", 0, "name"))).toBe("Task 1")
401
368
  expect(reader.read(p("structs", 1, "done"))).toBe(true)
402
- const item = reader.read(p("structs", 0)) as Record<
403
- string,
404
- unknown
405
- >
369
+ const item = reader.read(p("structs", 0)) as Record<string, unknown>
406
370
  expect(item.name).toBe("Task 1")
407
371
  expect(item.done).toBe(false)
408
372
  })
@@ -468,7 +432,11 @@ describe("YjsStoreReader", () => {
468
432
  })
469
433
 
470
434
  it("returns 0 for non-list paths", () => {
471
- const { reader } = setup(ScalarSchema, { name: "test", count: 0, active: true })
435
+ const { reader } = setup(ScalarSchema, {
436
+ name: "test",
437
+ count: 0,
438
+ active: true,
439
+ })
472
440
  expect(reader.arrayLength(p("name"))).toBe(0)
473
441
  })
474
442
  })
@@ -516,7 +484,11 @@ describe("YjsStoreReader", () => {
516
484
  })
517
485
 
518
486
  it("returns empty array for non-map paths", () => {
519
- const { reader } = setup(ScalarSchema, { name: "test", count: 0, active: true })
487
+ const { reader } = setup(ScalarSchema, {
488
+ name: "test",
489
+ count: 0,
490
+ active: true,
491
+ })
520
492
  expect(reader.keys(p("name"))).toEqual([])
521
493
  })
522
494
  })
@@ -564,7 +536,11 @@ describe("YjsStoreReader", () => {
564
536
  })
565
537
 
566
538
  it("returns false for non-map paths", () => {
567
- const { reader } = setup(ScalarSchema, { name: "test", count: 0, active: true })
539
+ const { reader } = setup(ScalarSchema, {
540
+ name: "test",
541
+ count: 0,
542
+ active: true,
543
+ })
568
544
  expect(reader.hasKey(p("name"), "anything")).toBe(false)
569
545
  })
570
546
  })
@@ -655,9 +631,7 @@ describe("YjsStoreReader", () => {
655
631
  const profile = rootMap.get("profile") as Y.Map<unknown>
656
632
  const address = profile.get("address") as Y.Map<string>
657
633
  address.set("city", "Seattle")
658
- expect(
659
- reader.read(p("profile", "address", "city")),
660
- ).toBe("Seattle")
634
+ expect(reader.read(p("profile", "address", "city"))).toBe("Seattle")
661
635
  })
662
636
 
663
637
  it("list delete + insert mutations are immediately visible", () => {
@@ -719,4 +693,4 @@ describe("YjsStoreReader", () => {
719
693
  expect(reader.keys(p("labels"))).toEqual(["priority"])
720
694
  })
721
695
  })
722
- })
696
+ })