@vuer-ai/vuer-rtc-server 0.1.1

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.
Files changed (114) hide show
  1. package/.env +1 -0
  2. package/PHASE1_SUMMARY.md +94 -0
  3. package/README.md +423 -0
  4. package/dist/broker/InMemoryBroker.d.ts +24 -0
  5. package/dist/broker/InMemoryBroker.d.ts.map +1 -0
  6. package/dist/broker/InMemoryBroker.js +65 -0
  7. package/dist/broker/InMemoryBroker.js.map +1 -0
  8. package/dist/broker/index.d.ts +3 -0
  9. package/dist/broker/index.d.ts.map +1 -0
  10. package/dist/broker/index.js +2 -0
  11. package/dist/broker/index.js.map +1 -0
  12. package/dist/broker/types.d.ts +47 -0
  13. package/dist/broker/types.d.ts.map +1 -0
  14. package/dist/broker/types.js +9 -0
  15. package/dist/broker/types.js.map +1 -0
  16. package/dist/index.d.ts +13 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +18 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/journal/JournalRepository.d.ts +39 -0
  21. package/dist/journal/JournalRepository.d.ts.map +1 -0
  22. package/dist/journal/JournalRepository.js +102 -0
  23. package/dist/journal/JournalRepository.js.map +1 -0
  24. package/dist/journal/JournalService.d.ts +69 -0
  25. package/dist/journal/JournalService.d.ts.map +1 -0
  26. package/dist/journal/JournalService.js +224 -0
  27. package/dist/journal/JournalService.js.map +1 -0
  28. package/dist/journal/index.d.ts +6 -0
  29. package/dist/journal/index.d.ts.map +1 -0
  30. package/dist/journal/index.js +6 -0
  31. package/dist/journal/index.js.map +1 -0
  32. package/dist/persistence/DocumentRepository.d.ts +22 -0
  33. package/dist/persistence/DocumentRepository.d.ts.map +1 -0
  34. package/dist/persistence/DocumentRepository.js +66 -0
  35. package/dist/persistence/DocumentRepository.js.map +1 -0
  36. package/dist/persistence/PrismaClient.d.ts +8 -0
  37. package/dist/persistence/PrismaClient.d.ts.map +1 -0
  38. package/dist/persistence/PrismaClient.js +21 -0
  39. package/dist/persistence/PrismaClient.js.map +1 -0
  40. package/dist/persistence/SessionRepository.d.ts +22 -0
  41. package/dist/persistence/SessionRepository.d.ts.map +1 -0
  42. package/dist/persistence/SessionRepository.js +103 -0
  43. package/dist/persistence/SessionRepository.js.map +1 -0
  44. package/dist/persistence/index.d.ts +7 -0
  45. package/dist/persistence/index.d.ts.map +1 -0
  46. package/dist/persistence/index.js +7 -0
  47. package/dist/persistence/index.js.map +1 -0
  48. package/dist/serve.d.ts +18 -0
  49. package/dist/serve.d.ts.map +1 -0
  50. package/dist/serve.js +211 -0
  51. package/dist/serve.js.map +1 -0
  52. package/dist/transport/RTCServer.d.ts +92 -0
  53. package/dist/transport/RTCServer.d.ts.map +1 -0
  54. package/dist/transport/RTCServer.js +273 -0
  55. package/dist/transport/RTCServer.js.map +1 -0
  56. package/dist/transport/index.d.ts +2 -0
  57. package/dist/transport/index.d.ts.map +1 -0
  58. package/dist/transport/index.js +2 -0
  59. package/dist/transport/index.js.map +1 -0
  60. package/dist/version.d.ts +2 -0
  61. package/dist/version.d.ts.map +1 -0
  62. package/dist/version.js +2 -0
  63. package/dist/version.js.map +1 -0
  64. package/jest.config.js +36 -0
  65. package/package.json +56 -0
  66. package/prisma/schema.prisma +121 -0
  67. package/src/broker/InMemoryBroker.ts +81 -0
  68. package/src/broker/index.ts +2 -0
  69. package/src/broker/types.ts +60 -0
  70. package/src/index.ts +23 -0
  71. package/src/journal/JournalRepository.ts +119 -0
  72. package/src/journal/JournalService.ts +291 -0
  73. package/src/journal/index.ts +10 -0
  74. package/src/persistence/DocumentRepository.ts +76 -0
  75. package/src/persistence/PrismaClient.ts +24 -0
  76. package/src/persistence/SessionRepository.ts +114 -0
  77. package/src/persistence/index.ts +7 -0
  78. package/src/serve.ts +240 -0
  79. package/src/transport/RTCServer.ts +327 -0
  80. package/src/transport/index.ts +1 -0
  81. package/src/version.ts +1 -0
  82. package/tests/README.md +112 -0
  83. package/tests/demo.ts +555 -0
  84. package/tests/e2e/convergence.test.ts +221 -0
  85. package/tests/e2e/helpers/assertions.ts +158 -0
  86. package/tests/e2e/helpers/createTestServer.ts +220 -0
  87. package/tests/e2e/latency.test.ts +512 -0
  88. package/tests/e2e/packet-loss.test.ts +229 -0
  89. package/tests/e2e/relay.test.ts +255 -0
  90. package/tests/e2e/sync-perf.test.ts +365 -0
  91. package/tests/e2e/sync-reconciliation.test.ts +237 -0
  92. package/tests/e2e/text-sync.test.ts +199 -0
  93. package/tests/e2e/tombstone-convergence.test.ts +356 -0
  94. package/tests/fixtures/array-ops.jsonl +6 -0
  95. package/tests/fixtures/boolean-ops.jsonl +6 -0
  96. package/tests/fixtures/color-ops.jsonl +4 -0
  97. package/tests/fixtures/edit-buffer.jsonl +3 -0
  98. package/tests/fixtures/messages.jsonl +4 -0
  99. package/tests/fixtures/node-ops.jsonl +6 -0
  100. package/tests/fixtures/number-ops.jsonl +7 -0
  101. package/tests/fixtures/object-ops.jsonl +4 -0
  102. package/tests/fixtures/operations.jsonl +7 -0
  103. package/tests/fixtures/string-ops.jsonl +4 -0
  104. package/tests/fixtures/undo-redo.jsonl +3 -0
  105. package/tests/fixtures/vector-ops.jsonl +9 -0
  106. package/tests/integration/repositories.test.ts +320 -0
  107. package/tests/journal/journal-service.test.ts +185 -0
  108. package/tests/test-data/datatypes.ts +677 -0
  109. package/tests/test-data/operations-example.ts +306 -0
  110. package/tests/test-data/scene-example.ts +247 -0
  111. package/tests/unit/operations.test.ts +310 -0
  112. package/tests/unit/vectorClock.test.ts +281 -0
  113. package/tsconfig.json +19 -0
  114. package/tsconfig.test.json +8 -0
@@ -0,0 +1,9 @@
1
+ /**
2
+ * RoomBroker — Room-scoped pub/sub, sequencing, and membership.
3
+ *
4
+ * The sync layer is data-type agnostic. It operates on CRDTMessage
5
+ * envelopes without inspecting ops[]. Swap InMemoryBroker for
6
+ * RedisBroker when scaling to multiple server instances.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/broker/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Vuer RTC Server - Main exports
3
+ *
4
+ * Server-specific: RoomBroker, WebSocket transport, MongoDB persistence
5
+ * Core CRDT logic is in @vuer-ai/vuer-rtc
6
+ */
7
+ export { VERSION } from './version.js';
8
+ export * from '@vuer-ai/vuer-rtc';
9
+ export * from './broker/index.js';
10
+ export * from './transport/index.js';
11
+ export * from './journal/index.js';
12
+ export * from './persistence/index.js';
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,mBAAmB,CAAC;AAGlC,cAAc,sBAAsB,CAAC;AAGrC,cAAc,oBAAoB,CAAC;AAGnC,cAAc,wBAAwB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Vuer RTC Server - Main exports
3
+ *
4
+ * Server-specific: RoomBroker, WebSocket transport, MongoDB persistence
5
+ * Core CRDT logic is in @vuer-ai/vuer-rtc
6
+ */
7
+ export { VERSION } from './version.js';
8
+ // Re-export core CRDT functionality from @vuer-ai/vuer-rtc
9
+ export * from '@vuer-ai/vuer-rtc';
10
+ // Broker (room-based pub/sub)
11
+ export * from './broker/index.js';
12
+ // Transport (WebSocket layer)
13
+ export * from './transport/index.js';
14
+ // Journal (server-side state management)
15
+ export * from './journal/index.js';
16
+ // Persistence (MongoDB via Prisma)
17
+ export * from './persistence/index.js';
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,2DAA2D;AAC3D,cAAc,mBAAmB,CAAC;AAElC,8BAA8B;AAC9B,cAAc,mBAAmB,CAAC;AAElC,8BAA8B;AAC9B,cAAc,sBAAsB,CAAC;AAErC,yCAAyC;AACzC,cAAc,oBAAoB,CAAC;AAEnC,mCAAmC;AACnC,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Journal Repository - CRUD operations for journal entries
3
+ *
4
+ * Stores CRDTMessages in MongoDB with support for:
5
+ * - Append journal entries
6
+ * - Query entries since a given lamportTime
7
+ * - Compact (remove old entries after snapshot)
8
+ */
9
+ import type { PrismaClient, JournalBatch } from '@prisma/client';
10
+ import type { CRDTMessage } from '@vuer-ai/vuer-rtc';
11
+ export declare class JournalRepository {
12
+ private prisma;
13
+ constructor(prisma: PrismaClient);
14
+ /**
15
+ * Append a message to the journal
16
+ */
17
+ append(documentId: string, msg: CRDTMessage): Promise<JournalBatch>;
18
+ /**
19
+ * Get all journal entries for a document since a given lamportTime
20
+ */
21
+ getSince(documentId: string, sinceLamportTime: number): Promise<CRDTMessage[]>;
22
+ /**
23
+ * Get full journal for a document
24
+ */
25
+ getAll(documentId: string): Promise<JournalBatch[]>;
26
+ /**
27
+ * Delete journal entries before a given timestamp (for compaction)
28
+ */
29
+ deleteBefore(documentId: string, beforeTimestamp: Date): Promise<number>;
30
+ /**
31
+ * Delete all journal entries for a document (for room-reset).
32
+ */
33
+ deleteAll(documentId: string): Promise<number>;
34
+ /**
35
+ * Check if a message already exists (for deduplication)
36
+ */
37
+ exists(documentId: string, msgId: string): Promise<boolean>;
38
+ }
39
+ //# sourceMappingURL=JournalRepository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JournalRepository.d.ts","sourceRoot":"","sources":["../../src/journal/JournalRepository.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAKrD,qBAAa,iBAAiB;IAChB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAExC;;OAEG;IACG,MAAM,CACV,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,YAAY,CAAC;IAaxB;;OAEG;IACG,QAAQ,CACZ,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,WAAW,EAAE,CAAC;IAuBzB;;OAEG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAOzD;;OAEG;IACG,YAAY,CAChB,UAAU,EAAE,MAAM,EAClB,eAAe,EAAE,IAAI,GACpB,OAAO,CAAC,MAAM,CAAC;IAYlB;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOpD;;OAEG;IACG,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAWlE"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Journal Repository - CRUD operations for journal entries
3
+ *
4
+ * Stores CRDTMessages in MongoDB with support for:
5
+ * - Append journal entries
6
+ * - Query entries since a given lamportTime
7
+ * - Compact (remove old entries after snapshot)
8
+ */
9
+ // Note: JournalEntry is also defined in @vuer-ai/vuer-rtc client module
10
+ // This is the server-side version with slightly different fields
11
+ export class JournalRepository {
12
+ prisma;
13
+ constructor(prisma) {
14
+ this.prisma = prisma;
15
+ }
16
+ /**
17
+ * Append a message to the journal
18
+ */
19
+ async append(documentId, msg) {
20
+ return this.prisma.journalBatch.create({
21
+ data: {
22
+ documentId,
23
+ sessionId: msg.sessionId,
24
+ batchId: msg.id,
25
+ operations: msg.ops,
26
+ startTime: new Date(msg.timestamp * 1000),
27
+ endTime: new Date(msg.timestamp * 1000),
28
+ },
29
+ });
30
+ }
31
+ /**
32
+ * Get all journal entries for a document since a given lamportTime
33
+ */
34
+ async getSince(documentId, sinceLamportTime) {
35
+ const batches = await this.prisma.journalBatch.findMany({
36
+ where: {
37
+ documentId,
38
+ },
39
+ orderBy: {
40
+ persistedAt: 'asc',
41
+ },
42
+ });
43
+ // Convert JournalBatch back to CRDTMessage
44
+ // Note: We need to store full CRDTMessage, not just operations
45
+ // For now, reconstruct what we can
46
+ return batches.map((batch) => ({
47
+ id: batch.batchId,
48
+ sessionId: batch.sessionId,
49
+ clock: {}, // TODO: Store in JournalBatch
50
+ lamportTime: 0, // TODO: Store in JournalBatch
51
+ timestamp: batch.startTime.getTime() / 1000,
52
+ ops: batch.operations,
53
+ }));
54
+ }
55
+ /**
56
+ * Get full journal for a document
57
+ */
58
+ async getAll(documentId) {
59
+ return this.prisma.journalBatch.findMany({
60
+ where: { documentId },
61
+ orderBy: { persistedAt: 'asc' },
62
+ });
63
+ }
64
+ /**
65
+ * Delete journal entries before a given timestamp (for compaction)
66
+ */
67
+ async deleteBefore(documentId, beforeTimestamp) {
68
+ const result = await this.prisma.journalBatch.deleteMany({
69
+ where: {
70
+ documentId,
71
+ persistedAt: {
72
+ lt: beforeTimestamp,
73
+ },
74
+ },
75
+ });
76
+ return result.count;
77
+ }
78
+ /**
79
+ * Delete all journal entries for a document (for room-reset).
80
+ */
81
+ async deleteAll(documentId) {
82
+ const result = await this.prisma.journalBatch.deleteMany({
83
+ where: { documentId },
84
+ });
85
+ return result.count;
86
+ }
87
+ /**
88
+ * Check if a message already exists (for deduplication)
89
+ */
90
+ async exists(documentId, msgId) {
91
+ const batch = await this.prisma.journalBatch.findUnique({
92
+ where: {
93
+ documentId_batchId: {
94
+ documentId,
95
+ batchId: msgId,
96
+ },
97
+ },
98
+ });
99
+ return batch !== null;
100
+ }
101
+ }
102
+ //# sourceMappingURL=JournalRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JournalRepository.js","sourceRoot":"","sources":["../../src/journal/JournalRepository.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,wEAAwE;AACxE,iEAAiE;AAEjE,MAAM,OAAO,iBAAiB;IACR;IAApB,YAAoB,MAAoB;QAApB,WAAM,GAAN,MAAM,CAAc;IAAG,CAAC;IAE5C;;OAEG;IACH,KAAK,CAAC,MAAM,CACV,UAAkB,EAClB,GAAgB;QAEhB,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;YACrC,IAAI,EAAE;gBACJ,UAAU;gBACV,SAAS,EAAE,GAAG,CAAC,SAAS;gBACxB,OAAO,EAAE,GAAG,CAAC,EAAE;gBACf,UAAU,EAAE,GAAG,CAAC,GAAY;gBAC5B,SAAS,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;gBACzC,OAAO,EAAE,IAAI,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;aACxC;SACF,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CACZ,UAAkB,EAClB,gBAAwB;QAExB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC;YACtD,KAAK,EAAE;gBACL,UAAU;aACX;YACD,OAAO,EAAE;gBACP,WAAW,EAAE,KAAK;aACnB;SACF,CAAC,CAAC;QAEH,2CAA2C;QAC3C,+DAA+D;QAC/D,mCAAmC;QACnC,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC7B,EAAE,EAAE,KAAK,CAAC,OAAO;YACjB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,KAAK,EAAE,EAAE,EAAE,8BAA8B;YACzC,WAAW,EAAE,CAAC,EAAE,8BAA8B;YAC9C,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,IAAI;YAC3C,GAAG,EAAE,KAAK,CAAC,UAAmB;SAC/B,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,UAAkB;QAC7B,OAAO,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC;YACvC,KAAK,EAAE,EAAE,UAAU,EAAE;YACrB,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE;SAChC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,UAAkB,EAClB,eAAqB;QAErB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE;gBACL,UAAU;gBACV,WAAW,EAAE;oBACX,EAAE,EAAE,eAAe;iBACpB;aACF;SACF,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS,CAAC,UAAkB;QAChC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC;YACvD,KAAK,EAAE,EAAE,UAAU,EAAE;SACtB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,MAAM,CAAC,UAAkB,EAAE,KAAa;QAC5C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC;YACtD,KAAK,EAAE;gBACL,kBAAkB,EAAE;oBAClB,UAAU;oBACV,OAAO,EAAE,KAAK;iBACf;aACF;SACF,CAAC,CAAC;QACH,OAAO,KAAK,KAAK,IAAI,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Journal Service - Business logic for journal operations
3
+ *
4
+ * Handles:
5
+ * - Receiving and validating CRDTMessages
6
+ * - Storing messages in journal
7
+ * - Processing meta.undo/meta.redo operations
8
+ * - Computing current graph state
9
+ * - Providing snapshot + journal for new clients
10
+ */
11
+ import type { PrismaClient } from '@prisma/client';
12
+ import { type CRDTMessage, type SceneGraph, type Snapshot } from '@vuer-ai/vuer-rtc';
13
+ export interface JournalEntry {
14
+ msg: CRDTMessage;
15
+ deletedAt?: number;
16
+ }
17
+ export interface DocumentState {
18
+ snapshot: Snapshot;
19
+ journal: JournalEntry[];
20
+ }
21
+ export declare class JournalService {
22
+ private journalRepo;
23
+ private documentRepo;
24
+ private validator;
25
+ private documentStates;
26
+ constructor(prisma: PrismaClient);
27
+ /**
28
+ * Load document state from database
29
+ */
30
+ loadDocument(documentId: string): Promise<DocumentState | null>;
31
+ /**
32
+ * Process incoming message from client
33
+ *
34
+ * @returns Acknowledgement with processed message, or error
35
+ */
36
+ processMessage(documentId: string, msg: CRDTMessage): Promise<{
37
+ success: boolean;
38
+ error?: string;
39
+ }>;
40
+ /**
41
+ * Get current graph state by replaying journal
42
+ */
43
+ computeGraph(state: DocumentState): SceneGraph;
44
+ /**
45
+ * Get state for new client (snapshot + journal)
46
+ */
47
+ getStateForClient(documentId: string): Promise<{
48
+ snapshot: Snapshot;
49
+ journal: CRDTMessage[];
50
+ } | null>;
51
+ /**
52
+ * Compact journal (create new snapshot from acknowledged entries)
53
+ */
54
+ compact(documentId: string): Promise<void>;
55
+ /**
56
+ * Create a new document
57
+ */
58
+ createDocument(name: string, ownerId: string): Promise<string>;
59
+ /**
60
+ * Unload document from memory
61
+ */
62
+ unloadDocument(documentId: string): void;
63
+ /**
64
+ * Clear all journal data for a document, resetting it to empty state.
65
+ * The document record is kept (unlike delete). Used for room-reset.
66
+ */
67
+ clearDocument(documentId: string): Promise<void>;
68
+ }
69
+ //# sourceMappingURL=JournalService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JournalService.d.ts","sourceRoot":"","sources":["../../src/journal/JournalService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAY,MAAM,gBAAgB,CAAC;AAC7D,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,UAAU,EACf,KAAK,QAAQ,EAId,MAAM,mBAAmB,CAAC;AAK3B,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,WAAW,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,SAAS,CAAqB;IAGtC,OAAO,CAAC,cAAc,CAAyC;gBAEnD,MAAM,EAAE,YAAY;IAMhC;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAyCrE;;;;OAIG;IACG,cAAc,CAClB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,WAAW,GACf,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IA6ChD;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU;IAkB9C;;OAEG;IACG,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;QACnD,QAAQ,EAAE,QAAQ,CAAC;QACnB,OAAO,EAAE,WAAW,EAAE,CAAC;KACxB,GAAG,IAAI,CAAC;IAUT;;OAEG;IACG,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkDhD;;OAEG;IACG,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,CAAC;IAiBlB;;OAEG;IACH,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAIxC;;;OAGG;IACG,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAqBvD"}
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Journal Service - Business logic for journal operations
3
+ *
4
+ * Handles:
5
+ * - Receiving and validating CRDTMessages
6
+ * - Storing messages in journal
7
+ * - Processing meta.undo/meta.redo operations
8
+ * - Computing current graph state
9
+ * - Providing snapshot + journal for new clients
10
+ */
11
+ import { applyMessage, createEmptyGraph, OperationValidator, } from '@vuer-ai/vuer-rtc';
12
+ import { JournalRepository } from './JournalRepository.js';
13
+ import { DocumentRepository } from '../persistence/DocumentRepository.js';
14
+ export class JournalService {
15
+ journalRepo;
16
+ documentRepo;
17
+ validator;
18
+ // In-memory state per document (for fast access)
19
+ documentStates = new Map();
20
+ constructor(prisma) {
21
+ this.journalRepo = new JournalRepository(prisma);
22
+ this.documentRepo = new DocumentRepository(prisma);
23
+ this.validator = new OperationValidator();
24
+ }
25
+ /**
26
+ * Load document state from database
27
+ */
28
+ async loadDocument(documentId) {
29
+ // Check in-memory cache first
30
+ if (this.documentStates.has(documentId)) {
31
+ return this.documentStates.get(documentId);
32
+ }
33
+ // Load from database
34
+ const doc = await this.documentRepo.findById(documentId);
35
+ if (!doc)
36
+ return null;
37
+ const batches = await this.journalRepo.getAll(documentId);
38
+ // Reconstruct journal entries
39
+ const journal = batches.map((batch) => ({
40
+ msg: {
41
+ id: batch.batchId,
42
+ sessionId: batch.sessionId,
43
+ clock: {}, // TODO: Store in schema
44
+ lamportTime: 0, // TODO: Store in schema
45
+ timestamp: batch.startTime.getTime(),
46
+ ops: batch.operations,
47
+ },
48
+ deletedAt: undefined, // TODO: Store in schema
49
+ }));
50
+ // Parse snapshot from currentState
51
+ const snapshotGraph = doc.currentState?.graph || createEmptyGraph();
52
+ const state = {
53
+ snapshot: {
54
+ graph: snapshotGraph,
55
+ vectorClock: doc.currentState?.vectorClock || {},
56
+ lamportTime: doc.currentState?.lamportTime || 0,
57
+ journalIndex: doc.currentState?.journalIndex || 0,
58
+ },
59
+ journal,
60
+ };
61
+ this.documentStates.set(documentId, state);
62
+ return state;
63
+ }
64
+ /**
65
+ * Process incoming message from client
66
+ *
67
+ * @returns Acknowledgement with processed message, or error
68
+ */
69
+ async processMessage(documentId, msg) {
70
+ // Validate message
71
+ const validation = this.validator.validateMessage(msg);
72
+ if (!validation.valid) {
73
+ return { success: false, error: validation.errors?.join(', ') };
74
+ }
75
+ // Load state
76
+ const state = await this.loadDocument(documentId);
77
+ if (!state) {
78
+ return { success: false, error: 'Document not found' };
79
+ }
80
+ // Check for duplicate
81
+ const exists = await this.journalRepo.exists(documentId, msg.id);
82
+ if (exists) {
83
+ return { success: true }; // Already processed, idempotent success
84
+ }
85
+ // Process meta operations (undo/redo)
86
+ for (const op of msg.ops) {
87
+ if (op.otype === 'meta.undo') {
88
+ const targetId = op.targetMsgId;
89
+ const target = state.journal.find((e) => e.msg.id === targetId);
90
+ if (target) {
91
+ target.deletedAt = msg.timestamp;
92
+ }
93
+ }
94
+ else if (op.otype === 'meta.redo') {
95
+ const targetId = op.targetMsgId;
96
+ const target = state.journal.find((e) => e.msg.id === targetId);
97
+ if (target) {
98
+ delete target.deletedAt;
99
+ }
100
+ }
101
+ }
102
+ // Add to journal
103
+ state.journal.push({ msg });
104
+ // Persist to database
105
+ await this.journalRepo.append(documentId, msg);
106
+ return { success: true };
107
+ }
108
+ /**
109
+ * Get current graph state by replaying journal
110
+ */
111
+ computeGraph(state) {
112
+ let graph = state.snapshot.graph;
113
+ for (const entry of state.journal) {
114
+ if (entry.deletedAt)
115
+ continue;
116
+ // Filter out meta ops for graph computation
117
+ const realOps = entry.msg.ops.filter((op) => !op.otype.startsWith('meta.'));
118
+ if (realOps.length > 0) {
119
+ graph = applyMessage(graph, { ...entry.msg, ops: realOps });
120
+ }
121
+ }
122
+ return graph;
123
+ }
124
+ /**
125
+ * Get state for new client (snapshot + journal)
126
+ */
127
+ async getStateForClient(documentId) {
128
+ const state = await this.loadDocument(documentId);
129
+ if (!state)
130
+ return null;
131
+ return {
132
+ snapshot: state.snapshot,
133
+ journal: state.journal.map((e) => e.msg),
134
+ };
135
+ }
136
+ /**
137
+ * Compact journal (create new snapshot from acknowledged entries)
138
+ */
139
+ async compact(documentId) {
140
+ const state = await this.loadDocument(documentId);
141
+ if (!state)
142
+ return;
143
+ // Compute new snapshot graph
144
+ const newGraph = this.computeGraph(state);
145
+ // Get max lamport time
146
+ let maxLamport = state.snapshot.journalIndex;
147
+ for (const entry of state.journal) {
148
+ maxLamport = Math.max(maxLamport, entry.msg.lamportTime);
149
+ }
150
+ // Merge vector clocks
151
+ let mergedClock = { ...state.snapshot.vectorClock };
152
+ for (const entry of state.journal) {
153
+ for (const [sessionId, time] of Object.entries(entry.msg.clock)) {
154
+ mergedClock[sessionId] = Math.max(mergedClock[sessionId] || 0, time);
155
+ }
156
+ }
157
+ // Update snapshot
158
+ state.snapshot = {
159
+ graph: newGraph,
160
+ vectorClock: mergedClock,
161
+ lamportTime: maxLamport,
162
+ journalIndex: maxLamport,
163
+ };
164
+ // Clear journal
165
+ const oldJournal = state.journal;
166
+ state.journal = [];
167
+ // Persist snapshot
168
+ await this.documentRepo.update(documentId, {
169
+ currentState: state.snapshot,
170
+ });
171
+ // Delete old journal entries
172
+ if (oldJournal.length > 0) {
173
+ const lastTimestamp = new Date(oldJournal[oldJournal.length - 1].msg.timestamp);
174
+ await this.journalRepo.deleteBefore(documentId, lastTimestamp);
175
+ }
176
+ }
177
+ /**
178
+ * Create a new document
179
+ */
180
+ async createDocument(name, ownerId) {
181
+ const doc = await this.documentRepo.create({ name, ownerId });
182
+ // Initialize in-memory state
183
+ this.documentStates.set(doc.id, {
184
+ snapshot: {
185
+ graph: createEmptyGraph(),
186
+ vectorClock: {},
187
+ lamportTime: 0,
188
+ journalIndex: 0,
189
+ },
190
+ journal: [],
191
+ });
192
+ return doc.id;
193
+ }
194
+ /**
195
+ * Unload document from memory
196
+ */
197
+ unloadDocument(documentId) {
198
+ this.documentStates.delete(documentId);
199
+ }
200
+ /**
201
+ * Clear all journal data for a document, resetting it to empty state.
202
+ * The document record is kept (unlike delete). Used for room-reset.
203
+ */
204
+ async clearDocument(documentId) {
205
+ // Reset in-memory state to empty
206
+ this.documentStates.set(documentId, {
207
+ snapshot: {
208
+ graph: createEmptyGraph(),
209
+ vectorClock: {},
210
+ lamportTime: 0,
211
+ journalIndex: 0,
212
+ },
213
+ journal: [],
214
+ });
215
+ // Delete all journal batches from DB
216
+ await this.journalRepo.deleteAll(documentId);
217
+ // Reset the document's currentState in DB
218
+ await this.documentRepo.update(documentId, {
219
+ currentState: {},
220
+ version: 0,
221
+ });
222
+ }
223
+ }
224
+ //# sourceMappingURL=JournalService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JournalService.js","sourceRoot":"","sources":["../../src/journal/JournalService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Journal module exports
3
+ */
4
+ export { JournalRepository } from './JournalRepository.js';
5
+ export { JournalService, type JournalEntry as ServerJournalEntry, type DocumentState, } from './JournalService.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/journal/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EACL,cAAc,EACd,KAAK,YAAY,IAAI,kBAAkB,EACvC,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Journal module exports
3
+ */
4
+ export { JournalRepository } from './JournalRepository.js';
5
+ export { JournalService, } from './JournalService.js';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/journal/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EACL,cAAc,GAGf,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Document Repository - CRUD operations for documents
3
+ */
4
+ import type { PrismaClient, Document } from '@prisma/client';
5
+ export declare class DocumentRepository {
6
+ private prisma;
7
+ constructor(prisma: PrismaClient);
8
+ create(data: {
9
+ name: string;
10
+ ownerId: string;
11
+ }): Promise<Document>;
12
+ findById(id: string): Promise<Document | null>;
13
+ findByOwner(ownerId: string): Promise<Document[]>;
14
+ update(id: string, data: Partial<{
15
+ name: string;
16
+ currentState: any;
17
+ version: number;
18
+ }>): Promise<Document>;
19
+ delete(id: string): Promise<void>;
20
+ incrementVersion(id: string): Promise<Document>;
21
+ }
22
+ //# sourceMappingURL=DocumentRepository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentRepository.d.ts","sourceRoot":"","sources":["../../src/persistence/DocumentRepository.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE7D,qBAAa,kBAAkB;IACjB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,YAAY;IAElC,MAAM,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;IAWlE,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAW9C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAOjD,MAAM,CACV,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,OAAO,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,GAAG,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,GACD,OAAO,CAAC,QAAQ,CAAC;IAOd,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAajC,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC;CAUtD"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Document Repository - CRUD operations for documents
3
+ */
4
+ export class DocumentRepository {
5
+ prisma;
6
+ constructor(prisma) {
7
+ this.prisma = prisma;
8
+ }
9
+ async create(data) {
10
+ return this.prisma.document.create({
11
+ data: {
12
+ name: data.name,
13
+ ownerId: data.ownerId,
14
+ currentState: {}, // Empty initial state
15
+ version: 0,
16
+ },
17
+ });
18
+ }
19
+ async findById(id) {
20
+ try {
21
+ return await this.prisma.document.findUnique({
22
+ where: { id },
23
+ });
24
+ }
25
+ catch (error) {
26
+ // Invalid ObjectId format
27
+ return null;
28
+ }
29
+ }
30
+ async findByOwner(ownerId) {
31
+ return this.prisma.document.findMany({
32
+ where: { ownerId },
33
+ orderBy: { updatedAt: 'desc' },
34
+ });
35
+ }
36
+ async update(id, data) {
37
+ return this.prisma.document.update({
38
+ where: { id },
39
+ data,
40
+ });
41
+ }
42
+ async delete(id) {
43
+ try {
44
+ await this.prisma.document.delete({
45
+ where: { id },
46
+ });
47
+ }
48
+ catch (error) {
49
+ // Ignore "record not found" errors (P2025)
50
+ if (error?.code !== 'P2025') {
51
+ throw error;
52
+ }
53
+ }
54
+ }
55
+ async incrementVersion(id) {
56
+ return this.prisma.document.update({
57
+ where: { id },
58
+ data: {
59
+ version: {
60
+ increment: 1,
61
+ },
62
+ },
63
+ });
64
+ }
65
+ }
66
+ //# sourceMappingURL=DocumentRepository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentRepository.js","sourceRoot":"","sources":["../../src/persistence/DocumentRepository.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,OAAO,kBAAkB;IACT;IAApB,YAAoB,MAAoB;QAApB,WAAM,GAAN,MAAM,CAAc;IAAG,CAAC;IAE5C,KAAK,CAAC,MAAM,CAAC,IAAuC;QAClD,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACjC,IAAI,EAAE;gBACJ,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,YAAY,EAAE,EAAE,EAAE,sBAAsB;gBACxC,OAAO,EAAE,CAAC;aACX;SACF,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,EAAU;QACvB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC;gBAC3C,KAAK,EAAE,EAAE,EAAE,EAAE;aACd,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,0BAA0B;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAe;QAC/B,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACnC,KAAK,EAAE,EAAE,OAAO,EAAE;YAClB,OAAO,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;SAC/B,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CACV,EAAU,EACV,IAIE;QAEF,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACjC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAChC,KAAK,EAAE,EAAE,EAAE,EAAE;aACd,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,2CAA2C;YAC3C,IAAI,KAAK,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC5B,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,EAAU;QAC/B,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACjC,KAAK,EAAE,EAAE,EAAE,EAAE;YACb,IAAI,EAAE;gBACJ,OAAO,EAAE;oBACP,SAAS,EAAE,CAAC;iBACb;aACF;SACF,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Prisma Client singleton
3
+ * Ensures only one Prisma Client instance is created
4
+ */
5
+ import { PrismaClient } from '@prisma/client';
6
+ export declare function createPrismaClient(): PrismaClient;
7
+ export declare function disconnectPrisma(): Promise<void>;
8
+ //# sourceMappingURL=PrismaClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrismaClient.d.ts","sourceRoot":"","sources":["../../src/persistence/PrismaClient.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAI9C,wBAAgB,kBAAkB,IAAI,YAAY,CAOjD;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKtD"}