cojson 0.15.8 → 0.15.9

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 (218) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +10 -0
  3. package/dist/IncomingMessagesQueue.d.ts +27 -0
  4. package/dist/IncomingMessagesQueue.d.ts.map +1 -0
  5. package/dist/IncomingMessagesQueue.js +114 -0
  6. package/dist/IncomingMessagesQueue.js.map +1 -0
  7. package/dist/PeerState.d.ts +2 -10
  8. package/dist/PeerState.d.ts.map +1 -1
  9. package/dist/PeerState.js +9 -90
  10. package/dist/PeerState.js.map +1 -1
  11. package/dist/PriorityBasedMessageQueue.d.ts +2 -1
  12. package/dist/PriorityBasedMessageQueue.d.ts.map +1 -1
  13. package/dist/PriorityBasedMessageQueue.js +9 -6
  14. package/dist/PriorityBasedMessageQueue.js.map +1 -1
  15. package/dist/SyncStateManager.d.ts +1 -0
  16. package/dist/SyncStateManager.d.ts.map +1 -1
  17. package/dist/SyncStateManager.js +1 -1
  18. package/dist/SyncStateManager.js.map +1 -1
  19. package/dist/coValue.d.ts +1 -1
  20. package/dist/coValueCore/coValueCore.d.ts +9 -17
  21. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  22. package/dist/coValueCore/coValueCore.js +75 -50
  23. package/dist/coValueCore/coValueCore.js.map +1 -1
  24. package/dist/coValueCore/verifiedState.d.ts +10 -3
  25. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  26. package/dist/coValueCore/verifiedState.js +73 -14
  27. package/dist/coValueCore/verifiedState.js.map +1 -1
  28. package/dist/coValues/coMap.d.ts +3 -3
  29. package/dist/coValues/coStream.d.ts +2 -2
  30. package/dist/coValues/group.d.ts +1 -1
  31. package/dist/coValues/group.d.ts.map +1 -1
  32. package/dist/coValues/group.js +2 -4
  33. package/dist/coValues/group.js.map +1 -1
  34. package/dist/config.d.ts +19 -0
  35. package/dist/config.d.ts.map +1 -0
  36. package/dist/config.js +23 -0
  37. package/dist/config.js.map +1 -0
  38. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  39. package/dist/crypto/WasmCrypto.js +2 -1
  40. package/dist/crypto/WasmCrypto.js.map +1 -1
  41. package/dist/exports.d.ts +18 -7
  42. package/dist/exports.d.ts.map +1 -1
  43. package/dist/exports.js +11 -8
  44. package/dist/exports.js.map +1 -1
  45. package/dist/localNode.d.ts +8 -2
  46. package/dist/localNode.d.ts.map +1 -1
  47. package/dist/localNode.js +19 -12
  48. package/dist/localNode.js.map +1 -1
  49. package/dist/storage/StoreQueue.d.ts +15 -0
  50. package/dist/storage/StoreQueue.d.ts.map +1 -0
  51. package/dist/storage/StoreQueue.js +35 -0
  52. package/dist/storage/StoreQueue.js.map +1 -0
  53. package/dist/storage/index.d.ts +6 -0
  54. package/dist/storage/index.d.ts.map +1 -0
  55. package/dist/storage/index.js +6 -0
  56. package/dist/storage/index.js.map +1 -0
  57. package/dist/storage/knownState.d.ts +18 -0
  58. package/dist/storage/knownState.d.ts.map +1 -0
  59. package/dist/storage/knownState.js +63 -0
  60. package/dist/storage/knownState.js.map +1 -0
  61. package/dist/storage/sqlite/client.d.ts +37 -0
  62. package/dist/storage/sqlite/client.d.ts.map +1 -0
  63. package/dist/storage/sqlite/client.js +89 -0
  64. package/dist/storage/sqlite/client.js.map +1 -0
  65. package/dist/storage/sqlite/index.d.ts +5 -0
  66. package/dist/storage/sqlite/index.d.ts.map +1 -0
  67. package/dist/storage/sqlite/index.js +13 -0
  68. package/dist/storage/sqlite/index.js.map +1 -0
  69. package/dist/storage/sqlite/sqliteMigrations.d.ts +3 -0
  70. package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -0
  71. package/dist/storage/sqlite/sqliteMigrations.js +44 -0
  72. package/dist/storage/sqlite/sqliteMigrations.js.map +1 -0
  73. package/dist/storage/sqlite/types.d.ts +8 -0
  74. package/dist/storage/sqlite/types.d.ts.map +1 -0
  75. package/dist/storage/sqlite/types.js +2 -0
  76. package/dist/storage/sqlite/types.js.map +1 -0
  77. package/dist/storage/sqliteAsync/client.d.ts +37 -0
  78. package/dist/storage/sqliteAsync/client.d.ts.map +1 -0
  79. package/dist/storage/sqliteAsync/client.js +88 -0
  80. package/dist/storage/sqliteAsync/client.js.map +1 -0
  81. package/dist/storage/sqliteAsync/index.d.ts +6 -0
  82. package/dist/storage/sqliteAsync/index.d.ts.map +1 -0
  83. package/dist/storage/sqliteAsync/index.js +15 -0
  84. package/dist/storage/sqliteAsync/index.js.map +1 -0
  85. package/dist/storage/sqliteAsync/types.d.ts +9 -0
  86. package/dist/storage/sqliteAsync/types.d.ts.map +1 -0
  87. package/dist/storage/sqliteAsync/types.js +2 -0
  88. package/dist/storage/sqliteAsync/types.js.map +1 -0
  89. package/dist/storage/storageAsync.d.ts +22 -0
  90. package/dist/storage/storageAsync.d.ts.map +1 -0
  91. package/dist/storage/storageAsync.js +214 -0
  92. package/dist/storage/storageAsync.js.map +1 -0
  93. package/dist/storage/storageSync.d.ts +21 -0
  94. package/dist/storage/storageSync.d.ts.map +1 -0
  95. package/dist/storage/storageSync.js +206 -0
  96. package/dist/storage/storageSync.js.map +1 -0
  97. package/dist/storage/syncUtils.d.ts +13 -0
  98. package/dist/storage/syncUtils.d.ts.map +1 -0
  99. package/dist/storage/syncUtils.js +25 -0
  100. package/dist/storage/syncUtils.js.map +1 -0
  101. package/dist/storage/types.d.ts +82 -0
  102. package/dist/storage/types.d.ts.map +1 -0
  103. package/dist/storage/types.js +2 -0
  104. package/dist/storage/types.js.map +1 -0
  105. package/dist/streamUtils.d.ts +13 -9
  106. package/dist/streamUtils.d.ts.map +1 -1
  107. package/dist/streamUtils.js +46 -13
  108. package/dist/streamUtils.js.map +1 -1
  109. package/dist/sync.d.ts +22 -14
  110. package/dist/sync.d.ts.map +1 -1
  111. package/dist/sync.js +143 -125
  112. package/dist/sync.js.map +1 -1
  113. package/dist/tests/IncomingMessagesQueue.test.d.ts +2 -0
  114. package/dist/tests/IncomingMessagesQueue.test.d.ts.map +1 -0
  115. package/dist/tests/IncomingMessagesQueue.test.js +437 -0
  116. package/dist/tests/IncomingMessagesQueue.test.js.map +1 -0
  117. package/dist/tests/PeerState.test.js +6 -94
  118. package/dist/tests/PeerState.test.js.map +1 -1
  119. package/dist/tests/PriorityBasedMessageQueue.test.js +14 -14
  120. package/dist/tests/PriorityBasedMessageQueue.test.js.map +1 -1
  121. package/dist/tests/StoreQueue.test.d.ts +2 -0
  122. package/dist/tests/StoreQueue.test.d.ts.map +1 -0
  123. package/dist/tests/StoreQueue.test.js +208 -0
  124. package/dist/tests/StoreQueue.test.js.map +1 -0
  125. package/dist/tests/SyncStateManager.test.js +3 -1
  126. package/dist/tests/SyncStateManager.test.js.map +1 -1
  127. package/dist/tests/account.test.js +9 -9
  128. package/dist/tests/account.test.js.map +1 -1
  129. package/dist/tests/coStream.test.js +1 -1
  130. package/dist/tests/coStream.test.js.map +1 -1
  131. package/dist/tests/coValueCore.test.js +208 -1
  132. package/dist/tests/coValueCore.test.js.map +1 -1
  133. package/dist/tests/coValueCoreLoadingState.test.js +2 -2
  134. package/dist/tests/coValueCoreLoadingState.test.js.map +1 -1
  135. package/dist/tests/group.addMember.test.js.map +1 -1
  136. package/dist/tests/group.removeMember.test.js +1 -1
  137. package/dist/tests/group.removeMember.test.js.map +1 -1
  138. package/dist/tests/messagesTestUtils.js +1 -1
  139. package/dist/tests/messagesTestUtils.js.map +1 -1
  140. package/dist/tests/sync.auth.test.js +23 -15
  141. package/dist/tests/sync.auth.test.js.map +1 -1
  142. package/dist/tests/sync.invite.test.js +10 -16
  143. package/dist/tests/sync.invite.test.js.map +1 -1
  144. package/dist/tests/sync.load.test.js +52 -50
  145. package/dist/tests/sync.load.test.js.map +1 -1
  146. package/dist/tests/sync.mesh.test.js +173 -56
  147. package/dist/tests/sync.mesh.test.js.map +1 -1
  148. package/dist/tests/sync.peerReconciliation.test.js +42 -32
  149. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  150. package/dist/tests/sync.storage.test.js +162 -62
  151. package/dist/tests/sync.storage.test.js.map +1 -1
  152. package/dist/tests/sync.storageAsync.test.d.ts +2 -0
  153. package/dist/tests/sync.storageAsync.test.d.ts.map +1 -0
  154. package/dist/tests/sync.storageAsync.test.js +361 -0
  155. package/dist/tests/sync.storageAsync.test.js.map +1 -0
  156. package/dist/tests/sync.test.js +16 -21
  157. package/dist/tests/sync.test.js.map +1 -1
  158. package/dist/tests/sync.upload.test.js +28 -25
  159. package/dist/tests/sync.upload.test.js.map +1 -1
  160. package/dist/tests/testStorage.d.ts +12 -0
  161. package/dist/tests/testStorage.d.ts.map +1 -0
  162. package/dist/tests/testStorage.js +151 -0
  163. package/dist/tests/testStorage.js.map +1 -0
  164. package/dist/tests/testUtils.d.ts +20 -15
  165. package/dist/tests/testUtils.d.ts.map +1 -1
  166. package/dist/tests/testUtils.js +79 -45
  167. package/dist/tests/testUtils.js.map +1 -1
  168. package/package.json +2 -2
  169. package/src/IncomingMessagesQueue.ts +142 -0
  170. package/src/PeerState.ts +11 -110
  171. package/src/PriorityBasedMessageQueue.ts +13 -5
  172. package/src/SyncStateManager.ts +1 -1
  173. package/src/coValueCore/coValueCore.ts +100 -66
  174. package/src/coValueCore/verifiedState.ts +91 -21
  175. package/src/coValues/group.ts +2 -4
  176. package/src/config.ts +26 -0
  177. package/src/crypto/WasmCrypto.ts +3 -1
  178. package/src/exports.ts +20 -27
  179. package/src/localNode.ts +27 -12
  180. package/src/storage/StoreQueue.ts +56 -0
  181. package/src/storage/index.ts +5 -0
  182. package/src/storage/knownState.ts +88 -0
  183. package/src/storage/sqlite/client.ts +180 -0
  184. package/src/storage/sqlite/index.ts +19 -0
  185. package/src/storage/sqlite/sqliteMigrations.ts +44 -0
  186. package/src/storage/sqlite/types.ts +7 -0
  187. package/src/storage/sqliteAsync/client.ts +179 -0
  188. package/src/storage/sqliteAsync/index.ts +25 -0
  189. package/src/storage/sqliteAsync/types.ts +8 -0
  190. package/src/storage/storageAsync.ts +367 -0
  191. package/src/storage/storageSync.ts +343 -0
  192. package/src/storage/syncUtils.ts +50 -0
  193. package/src/storage/types.ts +162 -0
  194. package/src/streamUtils.ts +61 -19
  195. package/src/sync.ts +191 -160
  196. package/src/tests/IncomingMessagesQueue.test.ts +626 -0
  197. package/src/tests/PeerState.test.ts +6 -118
  198. package/src/tests/PriorityBasedMessageQueue.test.ts +18 -14
  199. package/src/tests/StoreQueue.test.ts +283 -0
  200. package/src/tests/SyncStateManager.test.ts +4 -1
  201. package/src/tests/account.test.ts +11 -12
  202. package/src/tests/coStream.test.ts +1 -3
  203. package/src/tests/coValueCore.test.ts +270 -1
  204. package/src/tests/coValueCoreLoadingState.test.ts +2 -2
  205. package/src/tests/group.addMember.test.ts +1 -0
  206. package/src/tests/group.removeMember.test.ts +2 -8
  207. package/src/tests/messagesTestUtils.ts +2 -2
  208. package/src/tests/sync.auth.test.ts +24 -14
  209. package/src/tests/sync.invite.test.ts +11 -17
  210. package/src/tests/sync.load.test.ts +53 -49
  211. package/src/tests/sync.mesh.test.ts +198 -56
  212. package/src/tests/sync.peerReconciliation.test.ts +44 -34
  213. package/src/tests/sync.storage.test.ts +231 -64
  214. package/src/tests/sync.storageAsync.test.ts +486 -0
  215. package/src/tests/sync.test.ts +17 -23
  216. package/src/tests/sync.upload.test.ts +29 -24
  217. package/src/tests/testStorage.ts +216 -0
  218. package/src/tests/testUtils.ts +89 -54
@@ -0,0 +1,343 @@
1
+ import { UpDownCounter, metrics } from "@opentelemetry/api";
2
+ import {
3
+ CoValueCore,
4
+ MAX_RECOMMENDED_TX_SIZE,
5
+ RawCoID,
6
+ type SessionID,
7
+ type StorageAPI,
8
+ } from "../exports.js";
9
+ import { getPriorityFromHeader } from "../priority.js";
10
+ import {
11
+ CoValueKnownState,
12
+ NewContentMessage,
13
+ emptyKnownState,
14
+ } from "../sync.js";
15
+ import { StorageKnownState } from "./knownState.js";
16
+ import { collectNewTxs, getDependedOnCoValues } from "./syncUtils.js";
17
+ import type {
18
+ DBClientInterfaceSync,
19
+ SignatureAfterRow,
20
+ StoredCoValueRow,
21
+ StoredSessionRow,
22
+ } from "./types.js";
23
+
24
+ export class StorageApiSync implements StorageAPI {
25
+ private streamingCounter: UpDownCounter;
26
+
27
+ private readonly dbClient: DBClientInterfaceSync;
28
+ private loadedCoValues = new Set<RawCoID>();
29
+
30
+ constructor(dbClient: DBClientInterfaceSync) {
31
+ this.dbClient = dbClient;
32
+ this.streamingCounter = metrics
33
+ .getMeter("cojson")
34
+ .createUpDownCounter(`jazz.storage.streaming`, {
35
+ description: "Number of streaming coValues",
36
+ unit: "1",
37
+ });
38
+ this.streamingCounter.add(0);
39
+ }
40
+
41
+ knwonStates = new StorageKnownState();
42
+
43
+ getKnownState(id: string): CoValueKnownState {
44
+ return this.knwonStates.getKnownState(id);
45
+ }
46
+
47
+ async load(
48
+ id: string,
49
+ callback: (data: NewContentMessage) => void,
50
+ done: (found: boolean) => void,
51
+ ) {
52
+ await this.loadCoValue(id, callback, done);
53
+ }
54
+
55
+ async loadCoValue(
56
+ id: string,
57
+ callback: (data: NewContentMessage) => void,
58
+ done?: (found: boolean) => void,
59
+ ) {
60
+ const coValueRow = this.dbClient.getCoValue(id);
61
+
62
+ if (!coValueRow) {
63
+ done?.(false);
64
+ return;
65
+ }
66
+
67
+ const allCoValueSessions = this.dbClient.getCoValueSessions(
68
+ coValueRow.rowID,
69
+ );
70
+
71
+ const signaturesBySession = new Map<
72
+ SessionID,
73
+ Pick<SignatureAfterRow, "idx" | "signature">[]
74
+ >();
75
+
76
+ let contentStreaming = false;
77
+ for (const sessionRow of allCoValueSessions) {
78
+ const signatures = this.dbClient.getSignatures(sessionRow.rowID, 0);
79
+
80
+ if (signatures.length > 0) {
81
+ contentStreaming = true;
82
+ signaturesBySession.set(sessionRow.sessionID, signatures);
83
+ }
84
+ }
85
+
86
+ const knownState = this.knwonStates.getKnownState(coValueRow.id);
87
+
88
+ for (const sessionRow of allCoValueSessions) {
89
+ knownState.sessions[sessionRow.sessionID] = sessionRow.lastIdx;
90
+ }
91
+
92
+ this.loadedCoValues.add(coValueRow.id);
93
+
94
+ let contentMessage = {
95
+ action: "content",
96
+ id: coValueRow.id,
97
+ header: coValueRow.header,
98
+ new: {},
99
+ priority: getPriorityFromHeader(coValueRow.header),
100
+ } as NewContentMessage;
101
+
102
+ if (contentStreaming) {
103
+ this.streamingCounter.add(1);
104
+ contentMessage.expectContentUntil = knownState["sessions"];
105
+ }
106
+
107
+ for (const sessionRow of allCoValueSessions) {
108
+ const signatures = signaturesBySession.get(sessionRow.sessionID) || [];
109
+
110
+ let idx = 0;
111
+
112
+ signatures.push({
113
+ idx: sessionRow.lastIdx,
114
+ signature: sessionRow.lastSignature,
115
+ });
116
+
117
+ for (const signature of signatures) {
118
+ const newTxsInSession = this.dbClient.getNewTransactionInSession(
119
+ sessionRow.rowID,
120
+ idx,
121
+ signature.idx,
122
+ );
123
+
124
+ collectNewTxs({
125
+ newTxsInSession,
126
+ contentMessage,
127
+ sessionRow,
128
+ firstNewTxIdx: idx,
129
+ signature: signature.signature,
130
+ });
131
+
132
+ idx = signature.idx + 1;
133
+
134
+ if (signatures.length > 1) {
135
+ this.pushContentWithDependencies(
136
+ coValueRow,
137
+ contentMessage,
138
+ callback,
139
+ );
140
+ contentMessage = {
141
+ action: "content",
142
+ id: coValueRow.id,
143
+ header: coValueRow.header,
144
+ new: {},
145
+ priority: getPriorityFromHeader(coValueRow.header),
146
+ } satisfies NewContentMessage;
147
+
148
+ // Introduce a delay to not block the main thread
149
+ // for the entire content processing
150
+ await new Promise((resolve) => setTimeout(resolve));
151
+ }
152
+ }
153
+ }
154
+
155
+ const hasNewContent = Object.keys(contentMessage.new).length > 0;
156
+
157
+ // If there is no new content but steaming is not active, it's the case for a coValue with the header but no transactions
158
+ // For streaming the push has already been done in the loop above
159
+ if (hasNewContent || !contentStreaming) {
160
+ this.pushContentWithDependencies(coValueRow, contentMessage, callback);
161
+ }
162
+
163
+ if (contentStreaming) {
164
+ this.streamingCounter.add(-1);
165
+ }
166
+
167
+ this.knwonStates.handleUpdate(coValueRow.id, knownState);
168
+ done?.(true);
169
+ }
170
+
171
+ async pushContentWithDependencies(
172
+ coValueRow: StoredCoValueRow,
173
+ contentMessage: NewContentMessage,
174
+ pushCallback: (data: NewContentMessage) => void,
175
+ ) {
176
+ const dependedOnCoValuesList = getDependedOnCoValues(
177
+ coValueRow.header,
178
+ contentMessage,
179
+ );
180
+
181
+ for (const dependedOnCoValue of dependedOnCoValuesList) {
182
+ if (this.loadedCoValues.has(dependedOnCoValue)) {
183
+ continue;
184
+ }
185
+
186
+ this.loadCoValue(dependedOnCoValue, pushCallback);
187
+ }
188
+
189
+ pushCallback(contentMessage);
190
+ }
191
+
192
+ store(
193
+ msgs: NewContentMessage[],
194
+ correctionCallback: (data: CoValueKnownState) => void,
195
+ ) {
196
+ for (const msg of msgs) {
197
+ const success = this.storeSingle(msg, correctionCallback);
198
+
199
+ if (!success) {
200
+ return false;
201
+ }
202
+ }
203
+ }
204
+
205
+ private storeSingle(
206
+ msg: NewContentMessage,
207
+ correctionCallback: (data: CoValueKnownState) => void,
208
+ ): boolean {
209
+ const id = msg.id;
210
+ const coValueRow = this.dbClient.getCoValue(id);
211
+
212
+ // We have no info about coValue header
213
+ const invalidAssumptionOnHeaderPresence = !msg.header && !coValueRow;
214
+
215
+ if (invalidAssumptionOnHeaderPresence) {
216
+ const knownState = emptyKnownState(id as RawCoID);
217
+ correctionCallback(knownState);
218
+
219
+ this.knwonStates.setKnownState(id, knownState);
220
+
221
+ return false;
222
+ }
223
+
224
+ const storedCoValueRowID: number = coValueRow
225
+ ? coValueRow.rowID
226
+ : this.dbClient.addCoValue(msg);
227
+
228
+ const knownState = this.knwonStates.getKnownState(id);
229
+ knownState.header = true;
230
+
231
+ let invalidAssumptions = false;
232
+
233
+ for (const sessionID of Object.keys(msg.new) as SessionID[]) {
234
+ this.dbClient.transaction(() => {
235
+ const sessionRow = this.dbClient.getSingleCoValueSession(
236
+ storedCoValueRowID,
237
+ sessionID,
238
+ );
239
+
240
+ if (sessionRow) {
241
+ knownState.sessions[sessionRow.sessionID] = sessionRow.lastIdx;
242
+ }
243
+
244
+ if ((sessionRow?.lastIdx || 0) < (msg.new[sessionID]?.after || 0)) {
245
+ invalidAssumptions = true;
246
+ } else {
247
+ const newLastIdx = this.putNewTxs(
248
+ msg,
249
+ sessionID,
250
+ sessionRow,
251
+ storedCoValueRowID,
252
+ );
253
+ knownState.sessions[sessionID] = newLastIdx;
254
+ }
255
+ });
256
+ }
257
+
258
+ this.knwonStates.handleUpdate(id, knownState);
259
+
260
+ if (invalidAssumptions) {
261
+ correctionCallback(knownState);
262
+ return false;
263
+ }
264
+
265
+ return true;
266
+ }
267
+
268
+ private putNewTxs(
269
+ msg: NewContentMessage,
270
+ sessionID: SessionID,
271
+ sessionRow: StoredSessionRow | undefined,
272
+ storedCoValueRowID: number,
273
+ ) {
274
+ const newTransactions = msg.new[sessionID]?.newTransactions || [];
275
+
276
+ const actuallyNewOffset =
277
+ (sessionRow?.lastIdx || 0) - (msg.new[sessionID]?.after || 0);
278
+
279
+ const actuallyNewTransactions = newTransactions.slice(actuallyNewOffset);
280
+
281
+ if (actuallyNewTransactions.length === 0) {
282
+ return sessionRow?.lastIdx || 0;
283
+ }
284
+
285
+ let newBytesSinceLastSignature =
286
+ (sessionRow?.bytesSinceLastSignature || 0) +
287
+ actuallyNewTransactions.reduce(
288
+ (sum, tx) =>
289
+ sum +
290
+ (tx.privacy === "private"
291
+ ? tx.encryptedChanges.length
292
+ : tx.changes.length),
293
+ 0,
294
+ );
295
+
296
+ const newLastIdx =
297
+ (sessionRow?.lastIdx || 0) + actuallyNewTransactions.length;
298
+
299
+ let shouldWriteSignature = false;
300
+
301
+ if (newBytesSinceLastSignature > MAX_RECOMMENDED_TX_SIZE) {
302
+ shouldWriteSignature = true;
303
+ newBytesSinceLastSignature = 0;
304
+ }
305
+
306
+ const nextIdx = sessionRow?.lastIdx || 0;
307
+
308
+ if (!msg.new[sessionID]) throw new Error("Session ID not found");
309
+
310
+ const sessionUpdate = {
311
+ coValue: storedCoValueRowID,
312
+ sessionID,
313
+ lastIdx: newLastIdx,
314
+ lastSignature: msg.new[sessionID].lastSignature,
315
+ bytesSinceLastSignature: newBytesSinceLastSignature,
316
+ };
317
+
318
+ const sessionRowID: number = this.dbClient.addSessionUpdate({
319
+ sessionUpdate,
320
+ sessionRow,
321
+ });
322
+
323
+ if (shouldWriteSignature) {
324
+ this.dbClient.addSignatureAfter({
325
+ sessionRowID,
326
+ idx: newLastIdx - 1,
327
+ signature: msg.new[sessionID].lastSignature,
328
+ });
329
+ }
330
+
331
+ actuallyNewTransactions.map((newTransaction, i) =>
332
+ this.dbClient.addTransaction(sessionRowID, nextIdx + i, newTransaction),
333
+ );
334
+
335
+ return newLastIdx;
336
+ }
337
+
338
+ waitForSync(id: string, coValue: CoValueCore) {
339
+ return this.knwonStates.waitForSync(id, coValue);
340
+ }
341
+
342
+ close() {}
343
+ }
@@ -0,0 +1,50 @@
1
+ import { getDependedOnCoValuesFromRawData } from "../coValueCore/utils.js";
2
+ import type { CoValueHeader } from "../coValueCore/verifiedState.js";
3
+ import type { Signature } from "../crypto/crypto.js";
4
+ import type { SessionID } from "../exports.js";
5
+ import type { NewContentMessage } from "../sync.js";
6
+ import type { StoredSessionRow, TransactionRow } from "./types.js";
7
+
8
+ export function collectNewTxs({
9
+ newTxsInSession,
10
+ contentMessage,
11
+ sessionRow,
12
+ firstNewTxIdx,
13
+ signature,
14
+ }: {
15
+ newTxsInSession: TransactionRow[];
16
+ contentMessage: NewContentMessage;
17
+ sessionRow: StoredSessionRow;
18
+ signature: Signature;
19
+ firstNewTxIdx: number;
20
+ }) {
21
+ let sessionEntry = contentMessage.new[sessionRow.sessionID];
22
+
23
+ if (!sessionEntry) {
24
+ sessionEntry = {
25
+ after: firstNewTxIdx,
26
+ lastSignature: signature,
27
+ newTransactions: [],
28
+ };
29
+ contentMessage.new[sessionRow.sessionID] = sessionEntry;
30
+ } else {
31
+ sessionEntry.lastSignature = signature;
32
+ }
33
+
34
+ for (const tx of newTxsInSession) {
35
+ sessionEntry.newTransactions.push(tx.tx);
36
+ }
37
+ }
38
+
39
+ export function getDependedOnCoValues(
40
+ header: CoValueHeader,
41
+ contentMessage: NewContentMessage,
42
+ ) {
43
+ const id = contentMessage.id;
44
+ const sessionIDs = Object.keys(contentMessage.new) as SessionID[];
45
+ const transactions = Object.values(contentMessage.new).map(
46
+ (entry) => entry.newTransactions,
47
+ );
48
+
49
+ return getDependedOnCoValuesFromRawData(id, header, sessionIDs, transactions);
50
+ }
@@ -0,0 +1,162 @@
1
+ import type {
2
+ CoValueHeader,
3
+ Transaction,
4
+ } from "../coValueCore/verifiedState.js";
5
+ import { Signature } from "../crypto/crypto.js";
6
+ import type { CoValueCore, RawCoID, SessionID } from "../exports.js";
7
+ import { CoValueKnownState, NewContentMessage } from "../sync.js";
8
+
9
+ /**
10
+ * The StorageAPI is the interface that the StorageSync and StorageAsync classes implement.
11
+ *
12
+ * It uses callbacks instead of promises to have no overhead when using the StorageSync and less overhead when using the StorageAsync.
13
+ */
14
+ export interface StorageAPI {
15
+ load(
16
+ id: string,
17
+ // This callback is fired when data is found, might be called multiple times if the content requires streaming (e.g when loading files)
18
+ callback: (data: NewContentMessage) => void,
19
+ done?: (found: boolean) => void,
20
+ ): void;
21
+ store(
22
+ data: NewContentMessage[] | undefined,
23
+ handleCorrection: (correction: CoValueKnownState) => void,
24
+ ): void;
25
+
26
+ getKnownState(id: string): CoValueKnownState;
27
+
28
+ waitForSync(id: string, coValue: CoValueCore): Promise<void>;
29
+
30
+ close(): void;
31
+ }
32
+
33
+ export type CoValueRow = {
34
+ id: RawCoID;
35
+ header: CoValueHeader;
36
+ };
37
+
38
+ export type StoredCoValueRow = CoValueRow & { rowID: number };
39
+
40
+ export type SessionRow = {
41
+ coValue: number;
42
+ sessionID: SessionID;
43
+ lastIdx: number;
44
+ lastSignature: Signature;
45
+ bytesSinceLastSignature?: number;
46
+ };
47
+
48
+ export type StoredSessionRow = SessionRow & { rowID: number };
49
+
50
+ export type TransactionRow = {
51
+ ses: number;
52
+ idx: number;
53
+ tx: Transaction;
54
+ };
55
+
56
+ export type SignatureAfterRow = {
57
+ ses: number;
58
+ idx: number;
59
+ signature: Signature;
60
+ };
61
+
62
+ export interface DBClientInterfaceAsync {
63
+ getCoValue(
64
+ coValueId: string,
65
+ ): Promise<StoredCoValueRow | undefined> | undefined;
66
+
67
+ getCoValueSessions(coValueRowId: number): Promise<StoredSessionRow[]>;
68
+
69
+ getSingleCoValueSession(
70
+ coValueRowId: number,
71
+ sessionID: SessionID,
72
+ ): Promise<StoredSessionRow | undefined>;
73
+
74
+ getNewTransactionInSession(
75
+ sessionRowId: number,
76
+ fromIdx: number,
77
+ toIdx: number,
78
+ ): Promise<TransactionRow[]>;
79
+
80
+ getSignatures(
81
+ sessionRowId: number,
82
+ firstNewTxIdx: number,
83
+ ): Promise<SignatureAfterRow[]>;
84
+
85
+ addCoValue(msg: NewContentMessage): Promise<number>;
86
+
87
+ addSessionUpdate({
88
+ sessionUpdate,
89
+ sessionRow,
90
+ }: {
91
+ sessionUpdate: SessionRow;
92
+ sessionRow?: StoredSessionRow;
93
+ }): Promise<number>;
94
+
95
+ addTransaction(
96
+ sessionRowID: number,
97
+ idx: number,
98
+ newTransaction: Transaction,
99
+ ): Promise<number> | undefined | unknown;
100
+
101
+ addSignatureAfter({
102
+ sessionRowID,
103
+ idx,
104
+ signature,
105
+ }: {
106
+ sessionRowID: number;
107
+ idx: number;
108
+ signature: Signature;
109
+ }): Promise<unknown>;
110
+
111
+ transaction(callback: () => unknown): Promise<unknown>;
112
+ }
113
+
114
+ export interface DBClientInterfaceSync {
115
+ getCoValue(coValueId: string): StoredCoValueRow | undefined;
116
+
117
+ getCoValueSessions(coValueRowId: number): StoredSessionRow[];
118
+
119
+ getSingleCoValueSession(
120
+ coValueRowId: number,
121
+ sessionID: SessionID,
122
+ ): StoredSessionRow | undefined;
123
+
124
+ getNewTransactionInSession(
125
+ sessionRowId: number,
126
+ fromIdx: number,
127
+ toIdx: number,
128
+ ): TransactionRow[];
129
+
130
+ getSignatures(
131
+ sessionRowId: number,
132
+ firstNewTxIdx: number,
133
+ ): Pick<SignatureAfterRow, "idx" | "signature">[];
134
+
135
+ addCoValue(msg: NewContentMessage): number;
136
+
137
+ addSessionUpdate({
138
+ sessionUpdate,
139
+ sessionRow,
140
+ }: {
141
+ sessionUpdate: SessionRow;
142
+ sessionRow?: StoredSessionRow;
143
+ }): number;
144
+
145
+ addTransaction(
146
+ sessionRowID: number,
147
+ idx: number,
148
+ newTransaction: Transaction,
149
+ ): number | undefined | unknown;
150
+
151
+ addSignatureAfter({
152
+ sessionRowID,
153
+ idx,
154
+ signature,
155
+ }: {
156
+ sessionRowID: number;
157
+ idx: number;
158
+ signature: Signature;
159
+ }): number | undefined | unknown;
160
+
161
+ transaction(callback: () => unknown): unknown;
162
+ }
@@ -1,6 +1,11 @@
1
- import { Channel } from "queueueue";
2
- import { Peer, PeerID, SyncMessage } from "./sync.js";
3
- export { Channel } from "queueueue";
1
+ import {
2
+ DisconnectedError,
3
+ IncomingPeerChannel,
4
+ OutgoingPeerChannel,
5
+ Peer,
6
+ PeerID,
7
+ SyncMessage,
8
+ } from "./sync.js";
4
9
 
5
10
  export function connectedPeers(
6
11
  peer1id: PeerID,
@@ -8,40 +13,77 @@ export function connectedPeers(
8
13
  {
9
14
  peer1role = "client",
10
15
  peer2role = "client",
11
- crashOnClose = false,
12
16
  }: {
13
17
  peer1role?: Peer["role"];
14
18
  peer2role?: Peer["role"];
15
- crashOnClose?: boolean;
16
19
  } = {},
17
20
  ): [Peer, Peer] {
18
- const [from1to2Rx, from1to2Tx] = newQueuePair();
19
- const [from2to1Rx, from2to1Tx] = newQueuePair();
21
+ const from1to2 = new ConnectedPeerChannel();
22
+ const from2to1 = new ConnectedPeerChannel();
20
23
 
21
24
  const peer2AsPeer: Peer = {
22
25
  id: peer2id,
23
- incoming: from2to1Rx,
24
- outgoing: from1to2Tx,
26
+ incoming: from2to1,
27
+ outgoing: from1to2,
25
28
  role: peer2role,
26
- crashOnClose: crashOnClose,
27
29
  };
28
30
 
29
31
  const peer1AsPeer: Peer = {
30
32
  id: peer1id,
31
- incoming: from1to2Rx,
32
- outgoing: from2to1Tx,
33
+ incoming: from1to2,
34
+ outgoing: from2to1,
33
35
  role: peer1role,
34
- crashOnClose: crashOnClose,
35
36
  };
36
37
 
37
38
  return [peer1AsPeer, peer2AsPeer];
38
39
  }
39
40
 
40
- export function newQueuePair(): [
41
- AsyncIterable<SyncMessage>,
42
- Channel<SyncMessage>,
43
- ] {
44
- const channel = new Channel<SyncMessage>();
41
+ export function newQueuePair(): [ConnectedPeerChannel, ConnectedPeerChannel] {
42
+ const channel = new ConnectedPeerChannel();
45
43
 
46
- return [channel.wrap(), channel];
44
+ return [channel, channel];
45
+ }
46
+
47
+ export class ConnectedPeerChannel
48
+ implements IncomingPeerChannel, OutgoingPeerChannel
49
+ {
50
+ buffer: (SyncMessage | DisconnectedError)[] = [];
51
+
52
+ push(msg: SyncMessage | DisconnectedError) {
53
+ if (!this.listeners.size) {
54
+ this.buffer.push(msg);
55
+ return;
56
+ }
57
+
58
+ for (const listener of this.listeners) {
59
+ listener(msg);
60
+ }
61
+ }
62
+
63
+ close() {
64
+ this.closed = true;
65
+ for (const listener of this.closeListeners) {
66
+ listener();
67
+ }
68
+ this.closeListeners.clear();
69
+ this.listeners.clear();
70
+ }
71
+
72
+ listeners = new Set<(msg: SyncMessage | DisconnectedError) => void>();
73
+ onMessage(callback: (msg: SyncMessage | DisconnectedError) => void) {
74
+ if (this.buffer.length) {
75
+ for (const msg of this.buffer) {
76
+ callback(msg);
77
+ }
78
+ this.buffer = [];
79
+ }
80
+
81
+ this.listeners.add(callback);
82
+ }
83
+
84
+ closed = false;
85
+ closeListeners = new Set<() => void>();
86
+ onClose(callback: () => void) {
87
+ this.closeListeners.add(callback);
88
+ }
47
89
  }