@roc-db/test-utils 0.2.0-pre.101 → 0.2.0-pre.103
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.js +504 -117
- package/dist/types/entities/BlockParagraph.d.ts +1 -0
- package/dist/types/entities/index.d.ts +1 -0
- package/dist/types/operations/deleteUnmentionedBlockParagraph.d.ts +5 -0
- package/dist/types/operations/duplicateBlockParagraph.d.ts +4 -0
- package/dist/types/operations/updateBlockParagraphMentions.d.ts +9 -0
- package/dist/types/schemas/BlockParagraphSchema.d.ts +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21,7 +21,8 @@ import { entitySchemaGenerator as entitySchemaGenerator2 } from "roc-db";
|
|
|
21
21
|
import { z as z2 } from "zod";
|
|
22
22
|
var BlockParagraphSchema = entitySchemaGenerator2("BlockParagraph", {
|
|
23
23
|
data: z2.object({
|
|
24
|
-
content: z2.string().optional()
|
|
24
|
+
content: z2.string().optional(),
|
|
25
|
+
mentions: z2.array(z2.string()).optional()
|
|
25
26
|
}),
|
|
26
27
|
parents: z2.object({
|
|
27
28
|
parent: PostRefSchema
|
|
@@ -168,7 +169,8 @@ var BlockRefSchema = z13.union([
|
|
|
168
169
|
// src/entities/BlockParagraph.ts
|
|
169
170
|
var BlockParagraph = new Entity4("BlockParagraph", {
|
|
170
171
|
data: z14.object({
|
|
171
|
-
content: z14.string().optional()
|
|
172
|
+
content: z14.string().optional(),
|
|
173
|
+
mentions: z14.array(z14.string()).optional()
|
|
172
174
|
}),
|
|
173
175
|
parents: z14.object({
|
|
174
176
|
parent: z14.union([PostEntity.refSchema, BlockRefSchema])
|
|
@@ -235,6 +237,9 @@ var entities = [
|
|
|
235
237
|
PostVersion,
|
|
236
238
|
UserEntity
|
|
237
239
|
];
|
|
240
|
+
// src/operations/index.ts
|
|
241
|
+
import { pageEntities, pageMutations, redo, undo } from "roc-db";
|
|
242
|
+
|
|
238
243
|
// src/operations/applyDraft.ts
|
|
239
244
|
import { Query, QueryChain, writeOperation } from "roc-db";
|
|
240
245
|
var applyDraft = writeOperation("applyDraft", DraftRefSchema, (txn) => {
|
|
@@ -437,29 +442,62 @@ var deleteDraft = writeOperation13("deleteDraft", DraftRefSchema, (txn) => {
|
|
|
437
442
|
return QueryChain6(Query13(() => txn.deleteEntity(ref, true)), Query13(() => txn.deleteChangeSet(ref)));
|
|
438
443
|
});
|
|
439
444
|
|
|
440
|
-
// src/operations/
|
|
441
|
-
import { Query as Query14, writeOperation as writeOperation14 } from "roc-db";
|
|
445
|
+
// src/operations/deleteUnmentionedBlockParagraph.ts
|
|
446
|
+
import { BadRequestError, Query as Query14, QueryChain as QueryChain7, writeOperation as writeOperation14 } from "roc-db";
|
|
442
447
|
import { z as z30 } from "zod";
|
|
443
|
-
var
|
|
448
|
+
var deleteUnmentionedBlockParagraph = writeOperation14("deleteUnmentionedBlockParagraph", z30.object({
|
|
449
|
+
ref: BlockParagraphRefSchema,
|
|
450
|
+
mentionedByRef: BlockParagraphRefSchema
|
|
451
|
+
}).strict(), (txn) => {
|
|
452
|
+
const { ref, mentionedByRef } = txn.payload;
|
|
453
|
+
return QueryChain7(Query14(() => txn.readEntity(mentionedByRef)), Query14((mentionedBy) => {
|
|
454
|
+
if (mentionedBy.data.mentions?.includes(ref)) {
|
|
455
|
+
throw new BadRequestError("Delete not allowed. Paragraph is mentioned");
|
|
456
|
+
}
|
|
457
|
+
return txn.readEntity(ref);
|
|
458
|
+
}), Query14((block) => txn.readEntity(block.parents.parent)), Query14((parent) => txn.patchEntity(parent.ref, {
|
|
459
|
+
children: {
|
|
460
|
+
blocks: parent.children.blocks.filter((blockRef) => blockRef !== ref)
|
|
461
|
+
}
|
|
462
|
+
})), Query14(() => txn.deleteEntity(ref)));
|
|
463
|
+
}, { changeSetOnly: true });
|
|
464
|
+
|
|
465
|
+
// src/operations/duplicateBlockParagraph.ts
|
|
466
|
+
import { Query as Query15, QueryChain as QueryChain8, writeOperation as writeOperation15 } from "roc-db";
|
|
467
|
+
import { z as z31 } from "zod";
|
|
468
|
+
var duplicateBlockParagraph = writeOperation15("duplicateBlockParagraph", z31.object({ ref: BlockParagraphRefSchema }).strict(), (txn) => {
|
|
469
|
+
const copyRef = txn.createRef("BlockParagraph");
|
|
470
|
+
return QueryChain8(Query15(() => txn.readEntity(txn.payload.ref)), Query15((source) => txn.readEntity(source.parents.parent)), Query15((parent) => txn.patchEntity(parent.ref, {
|
|
471
|
+
children: { blocks: [...parent.children.blocks, copyRef] }
|
|
472
|
+
})), Query15((_parent, _readParent, source) => txn.createEntity(copyRef, {
|
|
473
|
+
parents: source.parents,
|
|
474
|
+
data: source.data
|
|
475
|
+
})));
|
|
476
|
+
}, { changeSetOnly: true, outputSchema: BlockParagraphSchema });
|
|
477
|
+
|
|
478
|
+
// src/operations/deleteOrgSettings.ts
|
|
479
|
+
import { Query as Query16, writeOperation as writeOperation16 } from "roc-db";
|
|
480
|
+
import { z as z32 } from "zod";
|
|
481
|
+
var deleteOrgSettings = writeOperation16("deleteOrgSettings", z32.object({}).optional(), (txn) => Query16(() => txn.deleteEntity("OrgSettings")));
|
|
444
482
|
|
|
445
483
|
// src/operations/deletePost.ts
|
|
446
|
-
import { Query as
|
|
447
|
-
var deletePost =
|
|
484
|
+
import { Query as Query17, writeOperation as writeOperation17 } from "roc-db";
|
|
485
|
+
var deletePost = writeOperation17("deletePost", PostRefSchema, (txn) => {
|
|
448
486
|
const ref = txn.payload;
|
|
449
|
-
return
|
|
487
|
+
return Query17(() => txn.deleteEntity(ref, true));
|
|
450
488
|
});
|
|
451
489
|
|
|
452
490
|
// src/operations/duplicateDraft.ts
|
|
453
491
|
import {
|
|
454
|
-
Query as
|
|
455
|
-
QueryChain as
|
|
456
|
-
writeOperation as
|
|
492
|
+
Query as Query18,
|
|
493
|
+
QueryChain as QueryChain9,
|
|
494
|
+
writeOperation as writeOperation18
|
|
457
495
|
} from "roc-db";
|
|
458
|
-
import { z as
|
|
459
|
-
var duplicateDraft =
|
|
496
|
+
import { z as z33 } from "zod";
|
|
497
|
+
var duplicateDraft = writeOperation18("duplicateDraft", z33.object({ sourceRef: DraftRefSchema, postRef: PostRefSchema }).strict(), (txn) => {
|
|
460
498
|
const newDraftRef = txn.createRef("Draft");
|
|
461
499
|
const { sourceRef, postRef } = txn.payload;
|
|
462
|
-
return
|
|
500
|
+
return QueryChain9(Query18(() => txn.createEntity(newDraftRef, { parents: { post: postRef } })), Query18(() => txn.duplicateChangeSetMutations(sourceRef, newDraftRef)), Query18((dup) => {
|
|
463
501
|
const result = dup;
|
|
464
502
|
return {
|
|
465
503
|
draftRef: newDraftRef,
|
|
@@ -470,28 +508,28 @@ var duplicateDraft = writeOperation16("duplicateDraft", z31.object({ sourceRef:
|
|
|
470
508
|
});
|
|
471
509
|
|
|
472
510
|
// src/operations/moveBlocks.ts
|
|
473
|
-
import { Query as
|
|
474
|
-
import { z as
|
|
475
|
-
var PayloadSchema3 =
|
|
476
|
-
refs:
|
|
477
|
-
targetPosition:
|
|
511
|
+
import { Query as Query19, QueryChain as QueryChain10, writeOperation as writeOperation19 } from "roc-db";
|
|
512
|
+
import { z as z34 } from "zod";
|
|
513
|
+
var PayloadSchema3 = z34.object({
|
|
514
|
+
refs: z34.array(z34.union([BlockParagraphRefSchema, BlockRowRefSchema])),
|
|
515
|
+
targetPosition: z34.number().min(0)
|
|
478
516
|
}).strict();
|
|
479
|
-
var moveBlocks =
|
|
517
|
+
var moveBlocks = writeOperation19("moveBlocks", PayloadSchema3, (txn) => {
|
|
480
518
|
const { refs } = txn.payload;
|
|
481
|
-
return
|
|
519
|
+
return QueryChain10(Query19(() => txn.batchReadEntities(refs)), Query19((items) => {
|
|
482
520
|
return "todo";
|
|
483
521
|
}));
|
|
484
522
|
});
|
|
485
523
|
|
|
486
524
|
// src/operations/pageBlocks.ts
|
|
487
|
-
import { Query as
|
|
488
|
-
import { z as
|
|
489
|
-
var pageBlocks = readOperation("pageBlocks",
|
|
490
|
-
size:
|
|
491
|
-
parentRef:
|
|
525
|
+
import { Query as Query20, readOperation } from "roc-db";
|
|
526
|
+
import { z as z35 } from "zod";
|
|
527
|
+
var pageBlocks = readOperation("pageBlocks", z35.object({
|
|
528
|
+
size: z35.number().default(100),
|
|
529
|
+
parentRef: z35.string().optional()
|
|
492
530
|
}), (txn) => {
|
|
493
531
|
const { size, parentRef } = txn.payload;
|
|
494
|
-
return
|
|
532
|
+
return Query20(() => txn.pageEntities({
|
|
495
533
|
entities: ["BlockImage", "BlockParagraph"],
|
|
496
534
|
size,
|
|
497
535
|
childrenOf: parentRef ? [parentRef] : []
|
|
@@ -499,50 +537,50 @@ var pageBlocks = readOperation("pageBlocks", z33.object({
|
|
|
499
537
|
});
|
|
500
538
|
|
|
501
539
|
// src/operations/pageEmptyEntitiesArray.ts
|
|
502
|
-
import { Query as
|
|
503
|
-
import { z as
|
|
504
|
-
var pageEmptyEntitiesArray = readOperation2("pageEmptyEntitiesArray",
|
|
540
|
+
import { Query as Query21, readOperation as readOperation2 } from "roc-db";
|
|
541
|
+
import { z as z36 } from "zod";
|
|
542
|
+
var pageEmptyEntitiesArray = readOperation2("pageEmptyEntitiesArray", z36.object({ size: z36.number().default(100) }), (txn) => {
|
|
505
543
|
const { size } = txn.payload;
|
|
506
|
-
return
|
|
544
|
+
return Query21(() => txn.pageEntities({ entities: [], size }));
|
|
507
545
|
});
|
|
508
546
|
|
|
509
547
|
// src/operations/pagePosts.ts
|
|
510
|
-
import { Query as
|
|
511
|
-
import { z as
|
|
512
|
-
var pagePosts = readOperation3("pagePosts",
|
|
548
|
+
import { Query as Query22, readOperation as readOperation3 } from "roc-db";
|
|
549
|
+
import { z as z37 } from "zod";
|
|
550
|
+
var pagePosts = readOperation3("pagePosts", z37.object({ size: z37.number().default(100) }).default({}), (txn) => {
|
|
513
551
|
const { size } = txn.payload;
|
|
514
|
-
return
|
|
552
|
+
return Query22(() => txn.pageEntities({ entities: ["Post"], size }));
|
|
515
553
|
});
|
|
516
554
|
|
|
517
555
|
// src/operations/pageSplat.ts
|
|
518
|
-
import { Query as
|
|
519
|
-
import { z as
|
|
520
|
-
var pageSplat = readOperation4("pageSplat",
|
|
556
|
+
import { Query as Query23, readOperation as readOperation4 } from "roc-db";
|
|
557
|
+
import { z as z38 } from "zod";
|
|
558
|
+
var pageSplat = readOperation4("pageSplat", z38.object({ size: z38.number().default(100) }), (txn) => {
|
|
521
559
|
const { size } = txn.payload;
|
|
522
|
-
return
|
|
560
|
+
return Query23(() => txn.pageEntities({ entities: "*", size }));
|
|
523
561
|
});
|
|
524
562
|
|
|
525
563
|
// src/operations/pagePostsByTag.ts
|
|
526
|
-
import { Query as
|
|
527
|
-
import { z as
|
|
528
|
-
var pagePostsByTag = readOperation5("pagePostsByTag",
|
|
564
|
+
import { Query as Query24, readOperation as readOperation5 } from "roc-db";
|
|
565
|
+
import { z as z39 } from "zod";
|
|
566
|
+
var pagePostsByTag = readOperation5("pagePostsByTag", z39.string(), (txn) => {
|
|
529
567
|
const tag = txn.payload;
|
|
530
|
-
return
|
|
568
|
+
return Query24(() => txn.pageEntitiesByIndex("Post", "tags", tag));
|
|
531
569
|
});
|
|
532
570
|
|
|
533
571
|
// src/operations/readEntity.ts
|
|
534
|
-
import { NotFoundError, Query as
|
|
535
|
-
import { z as
|
|
536
|
-
var readEntity = readOperation6("readEntity",
|
|
572
|
+
import { NotFoundError, Query as Query25, QueryChain as QueryChain11, readOperation as readOperation6 } from "roc-db";
|
|
573
|
+
import { z as z40 } from "zod";
|
|
574
|
+
var readEntity = readOperation6("readEntity", z40.string(), (txn) => {
|
|
537
575
|
const ref = txn.payload;
|
|
538
|
-
return
|
|
576
|
+
return QueryChain11(Query25(() => txn.readEntity(ref)), Query25((entity) => {
|
|
539
577
|
if (!entity) {
|
|
540
578
|
throw new NotFoundError(ref);
|
|
541
579
|
}
|
|
542
580
|
return entity;
|
|
543
581
|
}));
|
|
544
582
|
}, {
|
|
545
|
-
outputSchema:
|
|
583
|
+
outputSchema: z40.union([
|
|
546
584
|
PostSchema,
|
|
547
585
|
BlockImageSchema,
|
|
548
586
|
BlockParagraphSchema
|
|
@@ -550,11 +588,11 @@ var readEntity = readOperation6("readEntity", z38.string(), (txn) => {
|
|
|
550
588
|
});
|
|
551
589
|
|
|
552
590
|
// src/operations/readMutation.ts
|
|
553
|
-
import { NotFoundError as NotFoundError2, Query as
|
|
554
|
-
import { z as
|
|
555
|
-
var readMutation = readOperation7("readMutation",
|
|
591
|
+
import { NotFoundError as NotFoundError2, Query as Query26, QueryChain as QueryChain12, readOperation as readOperation7 } from "roc-db";
|
|
592
|
+
import { z as z41 } from "zod";
|
|
593
|
+
var readMutation = readOperation7("readMutation", z41.string(), (txn) => {
|
|
556
594
|
const ref = txn.payload;
|
|
557
|
-
return
|
|
595
|
+
return QueryChain12(Query26(() => txn.readMutation(ref)), Query26((entity) => {
|
|
558
596
|
if (!entity) {
|
|
559
597
|
throw new NotFoundError2(ref);
|
|
560
598
|
}
|
|
@@ -563,57 +601,57 @@ var readMutation = readOperation7("readMutation", z39.string(), (txn) => {
|
|
|
563
601
|
});
|
|
564
602
|
|
|
565
603
|
// src/operations/readOrgSettings.ts
|
|
566
|
-
import { Query as
|
|
567
|
-
import { z as
|
|
568
|
-
var readOrgSettings = readOperation8("readOrgSettings",
|
|
604
|
+
import { Query as Query27, readOperation as readOperation8 } from "roc-db";
|
|
605
|
+
import { z as z42 } from "zod";
|
|
606
|
+
var readOrgSettings = readOperation8("readOrgSettings", z42.object({}).optional(), (txn) => Query27(() => txn.readEntity("OrgSettings", true)));
|
|
569
607
|
|
|
570
608
|
// src/operations/readPost.ts
|
|
571
|
-
import { Query as
|
|
609
|
+
import { Query as Query28, readOperation as readOperation9 } from "roc-db";
|
|
572
610
|
var readPost = readOperation9("readPost", PostRefSchema, (txn) => {
|
|
573
611
|
const ref = txn.payload;
|
|
574
|
-
return
|
|
612
|
+
return Query28(() => txn.readEntity(ref, true));
|
|
575
613
|
}, { outputSchema: PostSchema });
|
|
576
614
|
|
|
577
615
|
// src/operations/readPostBySlug.ts
|
|
578
|
-
import { Query as
|
|
579
|
-
import { z as
|
|
580
|
-
var readPostBySlug = readOperation10("readPostBySlug",
|
|
616
|
+
import { Query as Query29, readOperation as readOperation10 } from "roc-db";
|
|
617
|
+
import { z as z43 } from "zod";
|
|
618
|
+
var readPostBySlug = readOperation10("readPostBySlug", z43.string(), (txn) => {
|
|
581
619
|
const slug = txn.payload;
|
|
582
|
-
return
|
|
620
|
+
return Query29(() => txn.readEntityByUniqueField("Post", "slug", slug));
|
|
583
621
|
}, { outputSchema: PostSchema });
|
|
584
622
|
|
|
585
623
|
// src/operations/testTransactionalEdits.ts
|
|
586
624
|
import { expect } from "bun:test";
|
|
587
|
-
import { Query as
|
|
588
|
-
import { z as
|
|
625
|
+
import { Query as Query30, QueryChain as QueryChain13, writeOperation as writeOperation20 } from "roc-db";
|
|
626
|
+
import { z as z44 } from "zod";
|
|
589
627
|
var TestCreatePatchDeleteInSameTransaction = (txn, title) => {
|
|
590
628
|
const postRef = txn.createRef("Post");
|
|
591
|
-
return
|
|
629
|
+
return QueryChain13(Query30(() => txn.createEntity(postRef, {
|
|
592
630
|
data: { title, tags: [] },
|
|
593
631
|
children: { blocks: [] }
|
|
594
|
-
})),
|
|
632
|
+
})), Query30(() => txn.patchEntity(postRef, { data: { foo: "bar" } })), Query30((post) => {
|
|
595
633
|
expect(post.data.title).toBe(title);
|
|
596
634
|
expect(post.data.foo).toBe("bar");
|
|
597
|
-
}),
|
|
635
|
+
}), Query30(() => txn.patchEntity(postRef, { data: { foo: null, bar: "foo" } })), Query30((post) => {
|
|
598
636
|
expect(post.data.title).toBe(title);
|
|
599
637
|
expect(post.data.foo).toBeUndefined();
|
|
600
638
|
expect(post.data.bar).toBe("foo");
|
|
601
639
|
expect(txn.log).toHaveLength(1);
|
|
602
640
|
expect(txn.log.get(postRef)?.[0]).toBe("create");
|
|
603
|
-
}),
|
|
641
|
+
}), Query30((post) => txn.deleteEntity(postRef)), Query30((res) => {
|
|
604
642
|
expect(res).toBe(1);
|
|
605
643
|
expect(txn.log).toHaveLength(0);
|
|
606
644
|
}));
|
|
607
645
|
};
|
|
608
646
|
var TestUpdateAndDeleteInSameTransaction = (txn, ref) => {
|
|
609
|
-
return
|
|
647
|
+
return QueryChain13(Query30(() => txn.patchEntity(ref, { data: { foo: "bar" } })), Query30((post) => {
|
|
610
648
|
expect(post.data.foo).toBe("bar");
|
|
611
|
-
}),
|
|
649
|
+
}), Query30(() => txn.patchEntity(ref, { data: { foo: null, bar: "foo" } })), Query30((post) => {
|
|
612
650
|
expect(post.data.foo).toBeUndefined();
|
|
613
651
|
expect(post.data.bar).toBe("foo");
|
|
614
652
|
expect(txn.log).toHaveLength(1);
|
|
615
653
|
expect(txn.log.get(ref)?.[0]).toBe("update");
|
|
616
|
-
}),
|
|
654
|
+
}), Query30((post) => txn.deleteEntity(ref)), Query30((res) => {
|
|
617
655
|
expect(res).toBe(1);
|
|
618
656
|
expect(txn.log).toHaveLength(1);
|
|
619
657
|
expect(txn.log.get(ref)?.[0]).toBe("delete");
|
|
@@ -621,14 +659,14 @@ var TestUpdateAndDeleteInSameTransaction = (txn, ref) => {
|
|
|
621
659
|
};
|
|
622
660
|
var TestCreateOnExistingRef = (txn) => {
|
|
623
661
|
const ref = txn.createRef("Post");
|
|
624
|
-
return
|
|
662
|
+
return QueryChain13(Query30(() => txn.createEntity(ref, { data: { foo: "bar" } })), Query30(() => txn.createEntity(ref, { data: { foo: "bar" } })));
|
|
625
663
|
};
|
|
626
|
-
var PayloadSchema4 =
|
|
627
|
-
name:
|
|
664
|
+
var PayloadSchema4 = z44.object({
|
|
665
|
+
name: z44.string().optional(),
|
|
628
666
|
post1ref: PostRefSchema.optional(),
|
|
629
667
|
post2ref: PostRefSchema.optional()
|
|
630
668
|
}).strict();
|
|
631
|
-
var testTransactionalEdits =
|
|
669
|
+
var testTransactionalEdits = writeOperation20("testTransactionalEdits", PayloadSchema4, (txn) => {
|
|
632
670
|
const { name, post1ref, post2ref } = txn.payload;
|
|
633
671
|
if (name)
|
|
634
672
|
return TestCreatePatchDeleteInSameTransaction(txn, name);
|
|
@@ -639,38 +677,56 @@ var testTransactionalEdits = writeOperation18("testTransactionalEdits", PayloadS
|
|
|
639
677
|
}, {});
|
|
640
678
|
|
|
641
679
|
// src/operations/updateBlockParagraph.ts
|
|
642
|
-
import { Query as
|
|
643
|
-
import { z as
|
|
644
|
-
var updateBlockParagraph =
|
|
680
|
+
import { Query as Query31, writeOperation as writeOperation21 } from "roc-db";
|
|
681
|
+
import { z as z45 } from "zod";
|
|
682
|
+
var updateBlockParagraph = writeOperation21("updateBlockParagraph", z45.object({
|
|
645
683
|
ref: BlockParagraphRefSchema,
|
|
646
|
-
content:
|
|
684
|
+
content: z45.string()
|
|
647
685
|
}), (txn) => {
|
|
648
686
|
const { ref, content } = txn.payload;
|
|
649
|
-
return
|
|
687
|
+
return Query31(() => txn.patchEntity(ref, {
|
|
650
688
|
data: { content }
|
|
651
689
|
}));
|
|
652
690
|
}, { outputSchema: BlockParagraphSchema });
|
|
653
691
|
|
|
692
|
+
// src/operations/updateBlockParagraphMentions.ts
|
|
693
|
+
import {
|
|
694
|
+
Query as Query32,
|
|
695
|
+
QueryChain as QueryChain14,
|
|
696
|
+
writeOperation as writeOperation22
|
|
697
|
+
} from "roc-db";
|
|
698
|
+
import { z as z46 } from "zod";
|
|
699
|
+
var PayloadSchema5 = z46.object({
|
|
700
|
+
ref: BlockParagraphRefSchema,
|
|
701
|
+
mentions: z46.array(BlockParagraphRefSchema)
|
|
702
|
+
}).strict();
|
|
703
|
+
var setMentions = (txn) => {
|
|
704
|
+
const { ref, mentions } = txn.payload;
|
|
705
|
+
return QueryChain14(...mentions.map((mention) => Query32(() => txn.readEntity(mention))), Query32(() => txn.patchEntity(ref, { data: { mentions } })));
|
|
706
|
+
};
|
|
707
|
+
var updateBlockParagraphMentions = writeOperation22("updateBlockParagraphMentions", PayloadSchema5, setMentions, { debounce: 10, changeSetOnly: true, outputSchema: BlockParagraphSchema });
|
|
708
|
+
var setBlockParagraphMentions = writeOperation22("setBlockParagraphMentions", PayloadSchema5, setMentions, { changeSetOnly: true, outputSchema: BlockParagraphSchema });
|
|
709
|
+
|
|
654
710
|
// src/operations/updateOrgSettingsName.ts
|
|
655
|
-
import { Query as
|
|
656
|
-
import { z as
|
|
657
|
-
var updateOrgSettingsName =
|
|
711
|
+
import { Query as Query33, writeOperation as writeOperation23 } from "roc-db";
|
|
712
|
+
import { z as z47 } from "zod";
|
|
713
|
+
var updateOrgSettingsName = writeOperation23("updateOrgSettingsName", z47.object({ name: z47.string() }), (txn) => {
|
|
658
714
|
const { name } = txn.payload;
|
|
659
|
-
return
|
|
715
|
+
return Query33(() => txn.patchEntity("OrgSettings", { data: { name } }));
|
|
660
716
|
}, { debounce: 10 });
|
|
661
717
|
|
|
662
718
|
// src/operations/updatePost.ts
|
|
663
|
-
import { Query as
|
|
664
|
-
import { z as
|
|
665
|
-
var
|
|
719
|
+
import { Query as Query34, writeOperation as writeOperation24 } from "roc-db";
|
|
720
|
+
import { z as z48 } from "zod";
|
|
721
|
+
var PayloadSchema6 = z48.object({
|
|
666
722
|
ref: PostRefSchema,
|
|
667
|
-
title:
|
|
668
|
-
tags:
|
|
723
|
+
title: z48.string().optional(),
|
|
724
|
+
tags: z48.array(z48.string()).optional().default([]),
|
|
669
725
|
authorRef: UserRefSchema.optional()
|
|
670
726
|
});
|
|
671
|
-
var updatePost =
|
|
727
|
+
var updatePost = writeOperation24("updatePost", PayloadSchema6, (txn) => {
|
|
672
728
|
const { ref, authorRef, ...data } = txn.payload;
|
|
673
|
-
return
|
|
729
|
+
return Query34(() => txn.updateEntity(ref, {
|
|
674
730
|
data,
|
|
675
731
|
parents: { author: authorRef },
|
|
676
732
|
children: { blocks: [] }
|
|
@@ -678,43 +734,43 @@ var updatePost = writeOperation21("updatePost", PayloadSchema5, (txn) => {
|
|
|
678
734
|
}, { outputSchema: PostSchema });
|
|
679
735
|
|
|
680
736
|
// src/operations/updatePostDescription.ts
|
|
681
|
-
import { Query as
|
|
682
|
-
import { z as
|
|
683
|
-
var updatePostDescription =
|
|
737
|
+
import { Query as Query35, writeOperation as writeOperation25 } from "roc-db";
|
|
738
|
+
import { z as z49 } from "zod";
|
|
739
|
+
var updatePostDescription = writeOperation25("updatePostDescription", z49.object({ ref: PostRefSchema, description: z49.string() }), (txn) => {
|
|
684
740
|
const { ref, description } = txn.payload;
|
|
685
|
-
return
|
|
741
|
+
return Query35(() => txn.patchEntity(ref, { data: { description } }));
|
|
686
742
|
}, { debounce: 10, changeSetOnly: true, outputSchema: PostSchema });
|
|
687
743
|
|
|
688
744
|
// src/operations/updatePostSlug.ts
|
|
689
|
-
import { Query as
|
|
690
|
-
import { z as
|
|
691
|
-
var updatePostSlug =
|
|
745
|
+
import { Query as Query36, writeOperation as writeOperation26 } from "roc-db";
|
|
746
|
+
import { z as z50 } from "zod";
|
|
747
|
+
var updatePostSlug = writeOperation26("updatePostSlug", z50.object({ ref: PostRefSchema, slug: z50.string() }), (txn) => {
|
|
692
748
|
const { ref, slug } = txn.payload;
|
|
693
|
-
return
|
|
749
|
+
return Query36(() => txn.patchEntity(ref, { data: { slug } }));
|
|
694
750
|
}, { debounce: 10, outputSchema: PostSchema });
|
|
695
751
|
|
|
696
752
|
// src/operations/updatePostTags.ts
|
|
697
|
-
import { Query as
|
|
698
|
-
import { z as
|
|
699
|
-
var updatePostTags =
|
|
753
|
+
import { Query as Query37, writeOperation as writeOperation27 } from "roc-db";
|
|
754
|
+
import { z as z51 } from "zod";
|
|
755
|
+
var updatePostTags = writeOperation27("updatePostTags", z51.object({ ref: PostRefSchema, tags: z51.array(z51.string()) }), (txn) => {
|
|
700
756
|
const { ref, tags } = txn.payload;
|
|
701
|
-
return
|
|
757
|
+
return Query37(() => txn.patchEntity(ref, { data: { tags } }));
|
|
702
758
|
}, { debounce: 10, outputSchema: PostSchema });
|
|
703
759
|
|
|
704
760
|
// src/operations/updatePostTitle.ts
|
|
705
|
-
import { Query as
|
|
706
|
-
import { z as
|
|
707
|
-
var updatePostTitle =
|
|
761
|
+
import { Query as Query38, writeOperation as writeOperation28 } from "roc-db";
|
|
762
|
+
import { z as z52 } from "zod";
|
|
763
|
+
var updatePostTitle = writeOperation28("updatePostTitle", z52.object({ ref: PostRefSchema, title: z52.string() }), (txn) => {
|
|
708
764
|
const { ref, title } = txn.payload;
|
|
709
|
-
return
|
|
765
|
+
return Query38(() => txn.patchEntity(ref, { data: { title } }));
|
|
710
766
|
}, { debounce: 10, outputSchema: PostSchema });
|
|
711
767
|
|
|
712
768
|
// src/operations/crudBySlug.ts
|
|
713
|
-
import { Query as
|
|
714
|
-
import { z as
|
|
715
|
-
var crudBySlug =
|
|
769
|
+
import { Query as Query39, QueryChain as QueryChain15, writeOperation as writeOperation29 } from "roc-db";
|
|
770
|
+
import { z as z53 } from "zod";
|
|
771
|
+
var crudBySlug = writeOperation29("crudBySlug", z53.object({ slug: z53.string(), newSlug: z53.string() }), (txn) => {
|
|
716
772
|
const { slug, newSlug } = txn.payload;
|
|
717
|
-
return
|
|
773
|
+
return QueryChain15(Query39(() => txn.readEntityByUniqueField("Post", "slug", slug)), Query39((post) => txn.patchEntity(post.ref, { data: { slug: newSlug } })), Query39(() => txn.readEntityByUniqueField("Post", "slug", newSlug)), Query39((post) => txn.deleteEntity(post.ref)), Query39(() => {
|
|
718
774
|
return [
|
|
719
775
|
txn.readEntityByUniqueField("Post", "slug", slug, false),
|
|
720
776
|
txn.readEntityByUniqueField("Post", "slug", newSlug, false)
|
|
@@ -724,6 +780,10 @@ var crudBySlug = writeOperation26("crudBySlug", z50.object({ slug: z50.string(),
|
|
|
724
780
|
|
|
725
781
|
// src/operations/index.ts
|
|
726
782
|
var operations = [
|
|
783
|
+
pageMutations,
|
|
784
|
+
pageEntities,
|
|
785
|
+
undo,
|
|
786
|
+
redo,
|
|
727
787
|
applyDraft,
|
|
728
788
|
createBlockImage,
|
|
729
789
|
createBlockTitle,
|
|
@@ -738,6 +798,8 @@ var operations = [
|
|
|
738
798
|
crudBySlug,
|
|
739
799
|
deleteBlocks,
|
|
740
800
|
deleteDraft,
|
|
801
|
+
deleteUnmentionedBlockParagraph,
|
|
802
|
+
duplicateBlockParagraph,
|
|
741
803
|
deleteOrgSettings,
|
|
742
804
|
deletePost,
|
|
743
805
|
duplicateDraft,
|
|
@@ -754,6 +816,8 @@ var operations = [
|
|
|
754
816
|
pageSplat,
|
|
755
817
|
testTransactionalEdits,
|
|
756
818
|
updateBlockParagraph,
|
|
819
|
+
updateBlockParagraphMentions,
|
|
820
|
+
setBlockParagraphMentions,
|
|
757
821
|
updateOrgSettingsName,
|
|
758
822
|
updatePost,
|
|
759
823
|
updatePostDescription,
|
|
@@ -764,13 +828,13 @@ var operations = [
|
|
|
764
828
|
// src/testAdapterImplementation.ts
|
|
765
829
|
import { beforeAll, describe, expect as expect2, test } from "bun:test";
|
|
766
830
|
import {
|
|
767
|
-
BadRequestError,
|
|
831
|
+
BadRequestError as BadRequestError2,
|
|
768
832
|
ConflictError,
|
|
769
833
|
Entity as Entity9,
|
|
770
834
|
NotFoundError as NotFoundError3,
|
|
771
835
|
Snowflake
|
|
772
836
|
} from "roc-db";
|
|
773
|
-
import { z as
|
|
837
|
+
import { z as z54 } from "zod";
|
|
774
838
|
import { faker } from "@faker-js/faker";
|
|
775
839
|
var snowflake = new Snowflake(10, 10);
|
|
776
840
|
var prepareChangeSetTest = async (adapter, adapter2) => {
|
|
@@ -961,7 +1025,7 @@ var testAdapterImplementation = async (adapterConstructor, generateArgs) => {
|
|
|
961
1025
|
expect2(() => adapter1.updatePostDescription({
|
|
962
1026
|
ref: postRef,
|
|
963
1027
|
description: "Description 1"
|
|
964
|
-
})).toThrow(
|
|
1028
|
+
})).toThrow(BadRequestError2);
|
|
965
1029
|
});
|
|
966
1030
|
test("delete (deleteEntity function)", async () => {
|
|
967
1031
|
const currentPost = await adapter1.readPost(postRef);
|
|
@@ -1018,7 +1082,7 @@ var testAdapterImplementation = async (adapterConstructor, generateArgs) => {
|
|
|
1018
1082
|
expect2(mutation2.log[0][1]).toBe("delete");
|
|
1019
1083
|
expect2(() => adapter1.testTransactionalEdits({
|
|
1020
1084
|
post2ref: post2.ref
|
|
1021
|
-
})).toThrow(
|
|
1085
|
+
})).toThrow(BadRequestError2);
|
|
1022
1086
|
});
|
|
1023
1087
|
});
|
|
1024
1088
|
describe("OrgSettings singleton", () => {
|
|
@@ -1094,12 +1158,12 @@ var testAdapterImplementation = async (adapterConstructor, generateArgs) => {
|
|
|
1094
1158
|
test("constructor rejects singleton + uniqueDataKeys", () => {
|
|
1095
1159
|
expect2(() => new Entity9("Bad", {
|
|
1096
1160
|
singleton: true,
|
|
1097
|
-
data:
|
|
1161
|
+
data: z54.object({ name: z54.string() }),
|
|
1098
1162
|
uniqueDataKeys: ["name"]
|
|
1099
1163
|
})).toThrow(/singleton/);
|
|
1100
1164
|
});
|
|
1101
1165
|
test("createRef throws on unregistered entity", async () => {
|
|
1102
|
-
expect2(() => adapter.createUnknownRef()).toThrow(
|
|
1166
|
+
expect2(() => adapter.createUnknownRef()).toThrow(BadRequestError2);
|
|
1103
1167
|
});
|
|
1104
1168
|
});
|
|
1105
1169
|
describe("paging", () => {
|
|
@@ -1232,7 +1296,7 @@ var testAdapterImplementation = async (adapterConstructor, generateArgs) => {
|
|
|
1232
1296
|
});
|
|
1233
1297
|
test("invalid changeSetRef throws error", async () => {
|
|
1234
1298
|
const changeSetAdapter2 = adapter1.changeSet("Draft/123");
|
|
1235
|
-
expect2(() => changeSetAdapter2.createPost({ title: "Title 1" })).toThrow(
|
|
1299
|
+
expect2(() => changeSetAdapter2.createPost({ title: "Title 1" })).toThrow(BadRequestError2);
|
|
1236
1300
|
});
|
|
1237
1301
|
test("duplicateDraft (txn.duplicateChangeSetMutations) produces an independent copy with remapped refs", async () => {
|
|
1238
1302
|
const [post2] = await adapter1.createPost({
|
|
@@ -1424,6 +1488,329 @@ var testAdapterImplementation = async (adapterConstructor, generateArgs) => {
|
|
|
1424
1488
|
});
|
|
1425
1489
|
});
|
|
1426
1490
|
});
|
|
1491
|
+
describe("debounce ordering", () => {
|
|
1492
|
+
const tick = () => Bun.sleep(2);
|
|
1493
|
+
const createSyncQueue = (server) => {
|
|
1494
|
+
const queue = [];
|
|
1495
|
+
return {
|
|
1496
|
+
add: ([result, mutation]) => {
|
|
1497
|
+
const index = queue.findIndex((m) => m.ref === mutation.ref);
|
|
1498
|
+
if (index === -1)
|
|
1499
|
+
queue.push(mutation);
|
|
1500
|
+
else
|
|
1501
|
+
queue[index] = mutation;
|
|
1502
|
+
return result;
|
|
1503
|
+
},
|
|
1504
|
+
flush: async () => {
|
|
1505
|
+
while (queue.length) {
|
|
1506
|
+
await tick();
|
|
1507
|
+
await server.persistOptimisticMutations([queue[0]]);
|
|
1508
|
+
queue.shift();
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
};
|
|
1513
|
+
const replay = async (source) => {
|
|
1514
|
+
const mutations = await source.pageMutations({ size: 1000 });
|
|
1515
|
+
const replayed = await createAdapter({ optimistic: true });
|
|
1516
|
+
await replayed.loadMutations(mutations);
|
|
1517
|
+
return replayed;
|
|
1518
|
+
};
|
|
1519
|
+
const setupClient = async () => {
|
|
1520
|
+
const server = await createAdapter();
|
|
1521
|
+
const client = await createAdapter({ optimistic: true });
|
|
1522
|
+
const queue = createSyncQueue(server);
|
|
1523
|
+
const post = queue.add(await client.createPost({ title: "Post", tags: [] }));
|
|
1524
|
+
const draft = queue.add(await client.createDraft({ postRef: post.ref }));
|
|
1525
|
+
const changeSet = client.changeSet(draft.ref);
|
|
1526
|
+
const createParagraph = async () => queue.add(await changeSet.createBlockParagraph({
|
|
1527
|
+
parentRef: post.ref
|
|
1528
|
+
})).block;
|
|
1529
|
+
const holder = await createParagraph();
|
|
1530
|
+
await queue.flush();
|
|
1531
|
+
return {
|
|
1532
|
+
server,
|
|
1533
|
+
queue,
|
|
1534
|
+
post,
|
|
1535
|
+
draft,
|
|
1536
|
+
changeSet,
|
|
1537
|
+
createParagraph,
|
|
1538
|
+
holder
|
|
1539
|
+
};
|
|
1540
|
+
};
|
|
1541
|
+
test("forward: a version that reads an entity created after the previous version syncs", async () => {
|
|
1542
|
+
const { server, queue, draft, changeSet, createParagraph, holder } = await setupClient();
|
|
1543
|
+
queue.add(await changeSet.updateBlockParagraphMentions({
|
|
1544
|
+
ref: holder.ref,
|
|
1545
|
+
mentions: []
|
|
1546
|
+
}));
|
|
1547
|
+
const mentioned = await createParagraph();
|
|
1548
|
+
queue.add(await changeSet.updateBlockParagraphMentions({
|
|
1549
|
+
ref: holder.ref,
|
|
1550
|
+
mentions: [mentioned.ref]
|
|
1551
|
+
}));
|
|
1552
|
+
await queue.flush();
|
|
1553
|
+
const replayed = (await replay(server)).changeSet(draft.ref);
|
|
1554
|
+
const replayedHolder = await replayed.readEntity(holder.ref);
|
|
1555
|
+
expect2(replayedHolder.data.mentions).toEqual([mentioned.ref]);
|
|
1556
|
+
});
|
|
1557
|
+
test("reverse: a version that frees an entity before it is deleted replays", async () => {
|
|
1558
|
+
const {
|
|
1559
|
+
server,
|
|
1560
|
+
queue,
|
|
1561
|
+
post,
|
|
1562
|
+
draft,
|
|
1563
|
+
changeSet,
|
|
1564
|
+
createParagraph,
|
|
1565
|
+
holder
|
|
1566
|
+
} = await setupClient();
|
|
1567
|
+
const first = await createParagraph();
|
|
1568
|
+
const second = await createParagraph();
|
|
1569
|
+
const third = await createParagraph();
|
|
1570
|
+
queue.add(await changeSet.setBlockParagraphMentions({
|
|
1571
|
+
ref: holder.ref,
|
|
1572
|
+
mentions: [first.ref]
|
|
1573
|
+
}));
|
|
1574
|
+
await queue.flush();
|
|
1575
|
+
queue.add(await changeSet.updateBlockParagraphMentions({
|
|
1576
|
+
ref: holder.ref,
|
|
1577
|
+
mentions: [second.ref]
|
|
1578
|
+
}));
|
|
1579
|
+
await queue.flush();
|
|
1580
|
+
queue.add(await changeSet.deleteUnmentionedBlockParagraph({
|
|
1581
|
+
ref: first.ref,
|
|
1582
|
+
mentionedByRef: holder.ref
|
|
1583
|
+
}));
|
|
1584
|
+
await queue.flush();
|
|
1585
|
+
queue.add(await changeSet.updateBlockParagraphMentions({
|
|
1586
|
+
ref: holder.ref,
|
|
1587
|
+
mentions: [third.ref]
|
|
1588
|
+
}));
|
|
1589
|
+
await queue.flush();
|
|
1590
|
+
const replayed = (await replay(server)).changeSet(draft.ref);
|
|
1591
|
+
const replayedHolder = await replayed.readEntity(holder.ref);
|
|
1592
|
+
expect2(replayedHolder.data.mentions).toEqual([third.ref]);
|
|
1593
|
+
const replayedPost = await replayed.readEntity(post.ref);
|
|
1594
|
+
expect2(replayedPost.children.blocks).not.toContain(first.ref);
|
|
1595
|
+
});
|
|
1596
|
+
test("copy: an entity copied between two versions keeps the version it copied", async () => {
|
|
1597
|
+
const { server, queue, draft, changeSet, createParagraph, holder } = await setupClient();
|
|
1598
|
+
const first = await createParagraph();
|
|
1599
|
+
const second = await createParagraph();
|
|
1600
|
+
queue.add(await changeSet.updateBlockParagraphMentions({
|
|
1601
|
+
ref: holder.ref,
|
|
1602
|
+
mentions: [first.ref]
|
|
1603
|
+
}));
|
|
1604
|
+
await queue.flush();
|
|
1605
|
+
const copy = queue.add(await changeSet.duplicateBlockParagraph({ ref: holder.ref }));
|
|
1606
|
+
await queue.flush();
|
|
1607
|
+
queue.add(await changeSet.updateBlockParagraphMentions({
|
|
1608
|
+
ref: holder.ref,
|
|
1609
|
+
mentions: [second.ref]
|
|
1610
|
+
}));
|
|
1611
|
+
await queue.flush();
|
|
1612
|
+
expect2(copy.data.mentions).toEqual([first.ref]);
|
|
1613
|
+
const replayed = (await replay(server)).changeSet(draft.ref);
|
|
1614
|
+
const replayedCopy = await replayed.readEntity(copy.ref);
|
|
1615
|
+
expect2(replayedCopy.data.mentions).toEqual([first.ref]);
|
|
1616
|
+
const replayedHolder = await replayed.readEntity(holder.ref);
|
|
1617
|
+
expect2(replayedHolder.data.mentions).toEqual([second.ref]);
|
|
1618
|
+
});
|
|
1619
|
+
test("server-side: non-optimistic versions around another write replay", async () => {
|
|
1620
|
+
const server = await createAdapter();
|
|
1621
|
+
const [post] = await server.createPost({ title: "Post", tags: [] });
|
|
1622
|
+
const [draft] = await server.createDraft({ postRef: post.ref });
|
|
1623
|
+
const changeSet = server.changeSet(draft.ref);
|
|
1624
|
+
const [{ block: holder }] = await changeSet.createBlockParagraph({
|
|
1625
|
+
parentRef: post.ref
|
|
1626
|
+
});
|
|
1627
|
+
await changeSet.updateBlockParagraphMentions({
|
|
1628
|
+
ref: holder.ref,
|
|
1629
|
+
mentions: []
|
|
1630
|
+
});
|
|
1631
|
+
await tick();
|
|
1632
|
+
const [{ block: mentioned }] = await changeSet.createBlockParagraph({ parentRef: post.ref });
|
|
1633
|
+
await tick();
|
|
1634
|
+
await changeSet.updateBlockParagraphMentions({
|
|
1635
|
+
ref: holder.ref,
|
|
1636
|
+
mentions: [mentioned.ref]
|
|
1637
|
+
});
|
|
1638
|
+
await changeSet.createBlockParagraph({ parentRef: post.ref });
|
|
1639
|
+
const replayed = (await replay(server)).changeSet(draft.ref);
|
|
1640
|
+
const replayedHolder = await replayed.readEntity(holder.ref);
|
|
1641
|
+
expect2(replayedHolder.data.mentions).toEqual([mentioned.ref]);
|
|
1642
|
+
});
|
|
1643
|
+
test("old client: merged versions whose row is no longer last persist once and replay", async () => {
|
|
1644
|
+
const {
|
|
1645
|
+
server,
|
|
1646
|
+
queue,
|
|
1647
|
+
post,
|
|
1648
|
+
draft,
|
|
1649
|
+
changeSet,
|
|
1650
|
+
createParagraph,
|
|
1651
|
+
holder
|
|
1652
|
+
} = await setupClient();
|
|
1653
|
+
const first = await createParagraph();
|
|
1654
|
+
const second = await createParagraph();
|
|
1655
|
+
const third = await createParagraph();
|
|
1656
|
+
queue.add(await changeSet.setBlockParagraphMentions({
|
|
1657
|
+
ref: holder.ref,
|
|
1658
|
+
mentions: [first.ref]
|
|
1659
|
+
}));
|
|
1660
|
+
const [, versionMutation] = await changeSet.updateBlockParagraphMentions({
|
|
1661
|
+
ref: holder.ref,
|
|
1662
|
+
mentions: [second.ref]
|
|
1663
|
+
});
|
|
1664
|
+
queue.add([undefined, versionMutation]);
|
|
1665
|
+
queue.add(await changeSet.deleteUnmentionedBlockParagraph({
|
|
1666
|
+
ref: first.ref,
|
|
1667
|
+
mentionedByRef: holder.ref
|
|
1668
|
+
}));
|
|
1669
|
+
await queue.flush();
|
|
1670
|
+
const mergedPayload = { ref: holder.ref, mentions: [third.ref] };
|
|
1671
|
+
await tick();
|
|
1672
|
+
const resentTimestamp = new Date().toISOString();
|
|
1673
|
+
await server.persistOptimisticMutations([
|
|
1674
|
+
{
|
|
1675
|
+
...versionMutation,
|
|
1676
|
+
payload: mergedPayload,
|
|
1677
|
+
debounceCount: versionMutation.debounceCount + 1,
|
|
1678
|
+
timestamp: resentTimestamp
|
|
1679
|
+
}
|
|
1680
|
+
]);
|
|
1681
|
+
await expect2((async () => server.persistOptimisticMutations([
|
|
1682
|
+
{
|
|
1683
|
+
...versionMutation,
|
|
1684
|
+
payload: mergedPayload,
|
|
1685
|
+
debounceCount: versionMutation.debounceCount + 1,
|
|
1686
|
+
timestamp: resentTimestamp
|
|
1687
|
+
}
|
|
1688
|
+
]))()).rejects.toThrow("Mutation already synced");
|
|
1689
|
+
await tick();
|
|
1690
|
+
const finalPayload = { ref: holder.ref, mentions: [] };
|
|
1691
|
+
await server.persistOptimisticMutations([
|
|
1692
|
+
{
|
|
1693
|
+
...versionMutation,
|
|
1694
|
+
payload: finalPayload,
|
|
1695
|
+
debounceCount: versionMutation.debounceCount + 2,
|
|
1696
|
+
timestamp: new Date().toISOString()
|
|
1697
|
+
}
|
|
1698
|
+
]);
|
|
1699
|
+
const stored = await server.pageMutations({ size: 1000 });
|
|
1700
|
+
const versions = stored.filter((m) => m.operation.name === "updateBlockParagraphMentions");
|
|
1701
|
+
expect2(versions).toHaveLength(2);
|
|
1702
|
+
expect2(versions.map((m) => m.payload)).toContainEqual({
|
|
1703
|
+
ref: holder.ref,
|
|
1704
|
+
mentions: [second.ref]
|
|
1705
|
+
});
|
|
1706
|
+
expect2(versions.map((m) => m.payload)).toContainEqual(finalPayload);
|
|
1707
|
+
const replayed = (await replay(server)).changeSet(draft.ref);
|
|
1708
|
+
const replayedHolder = await replayed.readEntity(holder.ref);
|
|
1709
|
+
expect2(replayedHolder.data.mentions).toEqual([]);
|
|
1710
|
+
const replayedPost = await replayed.readEntity(post.ref);
|
|
1711
|
+
expect2(replayedPost.children.blocks).not.toContain(first.ref);
|
|
1712
|
+
});
|
|
1713
|
+
test("old client: a merged version and a write depending on it in one batch replay in order", async () => {
|
|
1714
|
+
const { server, queue, draft, changeSet, createParagraph, holder } = await setupClient();
|
|
1715
|
+
const first = await createParagraph();
|
|
1716
|
+
const second = await createParagraph();
|
|
1717
|
+
const [, versionMutation] = await changeSet.updateBlockParagraphMentions({
|
|
1718
|
+
ref: holder.ref,
|
|
1719
|
+
mentions: [first.ref]
|
|
1720
|
+
});
|
|
1721
|
+
queue.add([undefined, versionMutation]);
|
|
1722
|
+
await queue.flush();
|
|
1723
|
+
await createParagraph();
|
|
1724
|
+
await queue.flush();
|
|
1725
|
+
await tick();
|
|
1726
|
+
const [, version] = await changeSet.updateBlockParagraphMentions({
|
|
1727
|
+
ref: holder.ref,
|
|
1728
|
+
mentions: [second.ref]
|
|
1729
|
+
});
|
|
1730
|
+
const mergedVersion = {
|
|
1731
|
+
...versionMutation,
|
|
1732
|
+
payload: version.payload,
|
|
1733
|
+
debounceCount: versionMutation.debounceCount + 1,
|
|
1734
|
+
timestamp: version.timestamp
|
|
1735
|
+
};
|
|
1736
|
+
await tick();
|
|
1737
|
+
const [, deleteMutation] = await changeSet.deleteUnmentionedBlockParagraph({
|
|
1738
|
+
ref: first.ref,
|
|
1739
|
+
mentionedByRef: holder.ref
|
|
1740
|
+
});
|
|
1741
|
+
await tick();
|
|
1742
|
+
await server.persistOptimisticMutations([
|
|
1743
|
+
mergedVersion,
|
|
1744
|
+
deleteMutation
|
|
1745
|
+
]);
|
|
1746
|
+
const replayed = (await replay(server)).changeSet(draft.ref);
|
|
1747
|
+
const replayedHolder = await replayed.readEntity(holder.ref);
|
|
1748
|
+
expect2(replayedHolder.data.mentions).toEqual([second.ref]);
|
|
1749
|
+
});
|
|
1750
|
+
test("consecutive versions coalesce into the row after an intervening write", async () => {
|
|
1751
|
+
const { changeSet, createParagraph, holder } = await setupClient();
|
|
1752
|
+
const [, version1] = await changeSet.updateBlockParagraphMentions({
|
|
1753
|
+
ref: holder.ref,
|
|
1754
|
+
mentions: []
|
|
1755
|
+
});
|
|
1756
|
+
await tick();
|
|
1757
|
+
const mentioned = await createParagraph();
|
|
1758
|
+
await tick();
|
|
1759
|
+
const [, version2] = await changeSet.updateBlockParagraphMentions({
|
|
1760
|
+
ref: holder.ref,
|
|
1761
|
+
mentions: [mentioned.ref]
|
|
1762
|
+
});
|
|
1763
|
+
const [, version3] = await changeSet.updateBlockParagraphMentions({
|
|
1764
|
+
ref: holder.ref,
|
|
1765
|
+
mentions: []
|
|
1766
|
+
});
|
|
1767
|
+
expect2(version2.ref).not.toBe(version1.ref);
|
|
1768
|
+
expect2(version3.ref).toBe(version2.ref);
|
|
1769
|
+
expect2(version3.debounceCount).toBe(version2.debounceCount + 1);
|
|
1770
|
+
});
|
|
1771
|
+
test("another identity's write in the changeSet, or the identity's own write elsewhere, ends coalescing", async () => {
|
|
1772
|
+
const engineArgs = await generateArgs();
|
|
1773
|
+
const adapterA = await createAdapter({
|
|
1774
|
+
session: { identityRef: "User/A" },
|
|
1775
|
+
engineArgs
|
|
1776
|
+
});
|
|
1777
|
+
const adapterB = await createAdapter({
|
|
1778
|
+
session: { identityRef: "User/B" },
|
|
1779
|
+
engineArgs
|
|
1780
|
+
});
|
|
1781
|
+
const [post] = await adapterA.createPost({
|
|
1782
|
+
title: "Post",
|
|
1783
|
+
tags: []
|
|
1784
|
+
});
|
|
1785
|
+
const [draft] = await adapterA.createDraft({ postRef: post.ref });
|
|
1786
|
+
const changeSetA = adapterA.changeSet(draft.ref);
|
|
1787
|
+
const changeSetB = adapterB.changeSet(draft.ref);
|
|
1788
|
+
const [{ block: holder }] = await changeSetA.createBlockParagraph({
|
|
1789
|
+
parentRef: post.ref
|
|
1790
|
+
});
|
|
1791
|
+
const update = async () => {
|
|
1792
|
+
await tick();
|
|
1793
|
+
const [, mutation] = await changeSetA.updateBlockParagraphMentions({
|
|
1794
|
+
ref: holder.ref,
|
|
1795
|
+
mentions: []
|
|
1796
|
+
});
|
|
1797
|
+
return mutation;
|
|
1798
|
+
};
|
|
1799
|
+
const version1 = await update();
|
|
1800
|
+
await tick();
|
|
1801
|
+
await changeSetB.createBlockParagraph({ parentRef: post.ref });
|
|
1802
|
+
const version2 = await update();
|
|
1803
|
+
await tick();
|
|
1804
|
+
await adapterA.createPost({ title: "Elsewhere", tags: [] });
|
|
1805
|
+
const version3 = await update();
|
|
1806
|
+
await tick();
|
|
1807
|
+
await adapterB.createPost({ title: "Not A", tags: [] });
|
|
1808
|
+
const version4 = await update();
|
|
1809
|
+
expect2(version2.ref).not.toBe(version1.ref);
|
|
1810
|
+
expect2(version3.ref).not.toBe(version2.ref);
|
|
1811
|
+
expect2(version4.ref).toBe(version3.ref);
|
|
1812
|
+
});
|
|
1813
|
+
});
|
|
1427
1814
|
};
|
|
1428
1815
|
// src/inMemoryAdapter.ts
|
|
1429
1816
|
import { createInMemoryAdapter } from "@roc-db/in-memory";
|
|
@@ -2,6 +2,7 @@ import { Entity } from "roc-db";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
export declare const BlockParagraph: Entity<"BlockParagraph", z.ZodObject<{
|
|
4
4
|
content: z.ZodOptional<z.ZodString>;
|
|
5
|
+
mentions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
5
6
|
}, z.core.$strip>, z.ZodObject<{}, z.core.$strip>, z.ZodObject<{
|
|
6
7
|
parent: z.ZodUnion<readonly [z.ZodType<`Post/${number}`, `Post/${number}`, z.core.$ZodTypeInternals<`Post/${number}`, `Post/${number}`>>, z.ZodUnion<readonly [z.ZodTemplateLiteral<`BlockParagraph/${number}`>, z.ZodTemplateLiteral<`BlockRow/${number}`>]>]>;
|
|
7
8
|
}, z.core.$strip>, z.ZodObject<{}, z.core.$strip>, false>;
|
|
@@ -18,6 +18,7 @@ export declare const entities: (import("roc-db").Entity<"Draft", import("zod").Z
|
|
|
18
18
|
author: import("zod").ZodOptional<import("zod").ZodTemplateLiteral<`User/${number}`>>;
|
|
19
19
|
}, import("zod/v4/core").$strip>, import("zod").ZodObject<{}, import("zod/v4/core").$strip>, false> | import("roc-db").Entity<"BlockParagraph", import("zod").ZodObject<{
|
|
20
20
|
content: import("zod").ZodOptional<import("zod").ZodString>;
|
|
21
|
+
mentions: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
21
22
|
}, import("zod/v4/core").$strip>, import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
|
|
22
23
|
parent: import("zod").ZodUnion<readonly [import("zod").ZodType<`Post/${number}`, `Post/${number}`, import("zod/v4/core").$ZodTypeInternals<`Post/${number}`, `Post/${number}`>>, import("zod").ZodUnion<readonly [import("zod").ZodTemplateLiteral<`BlockParagraph/${number}`>, import("zod").ZodTemplateLiteral<`BlockRow/${number}`>]>]>;
|
|
23
24
|
}, import("zod/v4/core").$strip>, import("zod").ZodObject<{}, import("zod/v4/core").$strip>, false> | import("roc-db").Entity<"BlockRow", import("zod").ZodObject<{}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const deleteUnmentionedBlockParagraph: import("roc-db").WriteOperation<"deleteUnmentionedBlockParagraph", z.ZodObject<{
|
|
3
|
+
ref: z.ZodTemplateLiteral<`BlockParagraph/${number}`>;
|
|
4
|
+
mentionedByRef: z.ZodTemplateLiteral<`BlockParagraph/${number}`>;
|
|
5
|
+
}, z.core.$strict>>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const updateBlockParagraphMentions: import("roc-db").WriteOperation<"updateBlockParagraphMentions", z.ZodObject<{
|
|
3
|
+
ref: z.ZodTemplateLiteral<`BlockParagraph/${number}`>;
|
|
4
|
+
mentions: z.ZodArray<z.ZodTemplateLiteral<`BlockParagraph/${number}`>>;
|
|
5
|
+
}, z.core.$strict>>;
|
|
6
|
+
export declare const setBlockParagraphMentions: import("roc-db").WriteOperation<"setBlockParagraphMentions", z.ZodObject<{
|
|
7
|
+
ref: z.ZodTemplateLiteral<`BlockParagraph/${number}`>;
|
|
8
|
+
mentions: z.ZodArray<z.ZodTemplateLiteral<`BlockParagraph/${number}`>>;
|
|
9
|
+
}, z.core.$strict>>;
|
|
@@ -12,6 +12,7 @@ export declare const BlockParagraphSchema: z.ZodObject<{
|
|
|
12
12
|
}, z.core.$strict>>;
|
|
13
13
|
data: z.ZodNonOptional<z.ZodObject<{
|
|
14
14
|
content: z.ZodOptional<z.ZodString>;
|
|
15
|
+
mentions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
15
16
|
}, z.core.$strip>>;
|
|
16
17
|
children: z.ZodNonOptional<z.ZodObject<{}, z.core.$strip>>;
|
|
17
18
|
parents: z.ZodNonOptional<z.ZodObject<{
|