@evolu/common 7.4.0 → 8.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (251) hide show
  1. package/README.md +4 -61
  2. package/dist/src/Array.d.ts +343 -102
  3. package/dist/src/Array.d.ts.map +1 -1
  4. package/dist/src/Array.js +181 -85
  5. package/dist/src/Assert.d.ts +57 -11
  6. package/dist/src/Assert.d.ts.map +1 -1
  7. package/dist/src/Assert.js +47 -11
  8. package/dist/src/BigInt.d.ts +6 -1
  9. package/dist/src/BigInt.d.ts.map +1 -1
  10. package/dist/src/BigInt.js +5 -0
  11. package/dist/src/Brand.d.ts +40 -12
  12. package/dist/src/Brand.d.ts.map +1 -1
  13. package/dist/src/Brand.js +5 -0
  14. package/dist/src/Buffer.d.ts +6 -1
  15. package/dist/src/Buffer.d.ts.map +1 -1
  16. package/dist/src/Buffer.js +7 -2
  17. package/dist/src/Cache.d.ts +10 -2
  18. package/dist/src/Cache.d.ts.map +1 -1
  19. package/dist/src/Cache.js +6 -0
  20. package/dist/src/Callbacks.d.ts +10 -7
  21. package/dist/src/Callbacks.d.ts.map +1 -1
  22. package/dist/src/Callbacks.js +23 -13
  23. package/dist/src/Console.d.ts +361 -69
  24. package/dist/src/Console.d.ts.map +1 -1
  25. package/dist/src/Console.js +217 -117
  26. package/dist/src/Crypto.d.ts +70 -21
  27. package/dist/src/Crypto.d.ts.map +1 -1
  28. package/dist/src/Crypto.js +72 -27
  29. package/dist/src/Eq.d.ts +22 -2
  30. package/dist/src/Eq.d.ts.map +1 -1
  31. package/dist/src/Eq.js +21 -1
  32. package/dist/src/Error.d.ts +23 -12
  33. package/dist/src/Error.d.ts.map +1 -1
  34. package/dist/src/Error.js +27 -11
  35. package/dist/src/Function.d.ts +128 -56
  36. package/dist/src/Function.d.ts.map +1 -1
  37. package/dist/src/Function.js +118 -14
  38. package/dist/src/Identicon.d.ts +5 -0
  39. package/dist/src/Identicon.d.ts.map +1 -1
  40. package/dist/src/Identicon.js +6 -1
  41. package/dist/src/Lookup.d.ts +160 -0
  42. package/dist/src/Lookup.d.ts.map +1 -0
  43. package/dist/src/Lookup.js +192 -0
  44. package/dist/src/Microtask.d.ts +21 -0
  45. package/dist/src/Microtask.d.ts.map +1 -0
  46. package/dist/src/Microtask.js +37 -0
  47. package/dist/src/Number.d.ts +18 -4
  48. package/dist/src/Number.d.ts.map +1 -1
  49. package/dist/src/Number.js +23 -1
  50. package/dist/src/Object.d.ts +110 -5
  51. package/dist/src/Object.d.ts.map +1 -1
  52. package/dist/src/Object.js +108 -5
  53. package/dist/src/Option.d.ts +58 -0
  54. package/dist/src/Option.d.ts.map +1 -0
  55. package/dist/src/Option.js +43 -0
  56. package/dist/src/Order.d.ts +5 -0
  57. package/dist/src/Order.d.ts.map +1 -1
  58. package/dist/src/Order.js +8 -3
  59. package/dist/src/Platform.d.ts +36 -2
  60. package/dist/src/Platform.d.ts.map +1 -1
  61. package/dist/src/Platform.js +10 -6
  62. package/dist/src/Polyfills.d.ts +27 -0
  63. package/dist/src/Polyfills.d.ts.map +1 -0
  64. package/dist/src/Polyfills.js +299 -0
  65. package/dist/src/Random.d.ts +18 -7
  66. package/dist/src/Random.d.ts.map +1 -1
  67. package/dist/src/Random.js +9 -7
  68. package/dist/src/Redacted.d.ts +7 -4
  69. package/dist/src/Redacted.d.ts.map +1 -1
  70. package/dist/src/Redacted.js +5 -0
  71. package/dist/src/Ref.d.ts +31 -16
  72. package/dist/src/Ref.d.ts.map +1 -1
  73. package/dist/src/Ref.js +35 -7
  74. package/dist/src/RefCount.d.ts +62 -0
  75. package/dist/src/RefCount.d.ts.map +1 -0
  76. package/dist/src/RefCount.js +83 -0
  77. package/dist/src/Relation.d.ts +84 -29
  78. package/dist/src/Relation.d.ts.map +1 -1
  79. package/dist/src/Relation.js +83 -105
  80. package/dist/src/Resource.d.ts +263 -0
  81. package/dist/src/Resource.d.ts.map +1 -0
  82. package/dist/src/Resource.js +389 -0
  83. package/dist/src/Result.d.ts +390 -374
  84. package/dist/src/Result.d.ts.map +1 -1
  85. package/dist/src/Result.js +142 -70
  86. package/dist/src/Schedule.d.ts +953 -0
  87. package/dist/src/Schedule.d.ts.map +1 -0
  88. package/dist/src/Schedule.js +1199 -0
  89. package/dist/src/Set.d.ts +181 -0
  90. package/dist/src/Set.d.ts.map +1 -0
  91. package/dist/src/Set.js +137 -0
  92. package/dist/src/Skiplist.d.ts +6 -1
  93. package/dist/src/Skiplist.d.ts.map +1 -1
  94. package/dist/src/Skiplist.js +5 -0
  95. package/dist/src/Sqlite.d.ts +160 -60
  96. package/dist/src/Sqlite.d.ts.map +1 -1
  97. package/dist/src/Sqlite.js +290 -190
  98. package/dist/src/Store.d.ts +36 -28
  99. package/dist/src/Store.d.ts.map +1 -1
  100. package/dist/src/Store.js +55 -15
  101. package/dist/src/String.d.ts +5 -0
  102. package/dist/src/String.d.ts.map +1 -1
  103. package/dist/src/String.js +5 -0
  104. package/dist/src/Task.d.ts +2263 -423
  105. package/dist/src/Task.d.ts.map +1 -1
  106. package/dist/src/Task.js +1488 -348
  107. package/dist/src/Test.d.ts +122 -0
  108. package/dist/src/Test.d.ts.map +1 -0
  109. package/dist/src/Test.js +66 -0
  110. package/dist/src/Time.d.ts +201 -48
  111. package/dist/src/Time.d.ts.map +1 -1
  112. package/dist/src/Time.js +190 -86
  113. package/dist/src/Tracer.d.ts +48 -0
  114. package/dist/src/Tracer.d.ts.map +1 -0
  115. package/dist/src/Tracer.js +6 -0
  116. package/dist/src/Type.d.ts +582 -209
  117. package/dist/src/Type.d.ts.map +1 -1
  118. package/dist/src/Type.js +362 -240
  119. package/dist/src/Types.d.ts +160 -6
  120. package/dist/src/Types.d.ts.map +1 -1
  121. package/dist/src/Types.js +22 -2
  122. package/dist/src/WebSocket.d.ts +113 -58
  123. package/dist/src/WebSocket.d.ts.map +1 -1
  124. package/dist/src/WebSocket.js +210 -118
  125. package/dist/src/Worker.d.ts +245 -60
  126. package/dist/src/Worker.d.ts.map +1 -1
  127. package/dist/src/Worker.js +220 -64
  128. package/dist/src/index.d.ts +25 -3
  129. package/dist/src/index.d.ts.map +1 -1
  130. package/dist/src/index.js +19 -3
  131. package/dist/src/local-first/Db.d.ts +24 -213
  132. package/dist/src/local-first/Db.d.ts.map +1 -1
  133. package/dist/src/local-first/Db.js +622 -293
  134. package/dist/src/local-first/Error.d.ts +12 -0
  135. package/dist/src/local-first/Error.d.ts.map +1 -0
  136. package/dist/src/local-first/Error.js +6 -0
  137. package/dist/src/local-first/Evolu.d.ts +330 -285
  138. package/dist/src/local-first/Evolu.d.ts.map +1 -1
  139. package/dist/src/local-first/Evolu.js +472 -402
  140. package/dist/src/local-first/LocalAuth.d.ts +10 -10
  141. package/dist/src/local-first/LocalAuth.d.ts.map +1 -1
  142. package/dist/src/local-first/LocalAuth.js +6 -1
  143. package/dist/src/local-first/Owner.d.ts +70 -27
  144. package/dist/src/local-first/Owner.d.ts.map +1 -1
  145. package/dist/src/local-first/Owner.js +27 -14
  146. package/dist/src/local-first/Protocol.d.ts +58 -50
  147. package/dist/src/local-first/Protocol.d.ts.map +1 -1
  148. package/dist/src/local-first/Protocol.js +253 -155
  149. package/dist/src/local-first/Query.d.ts +133 -43
  150. package/dist/src/local-first/Query.d.ts.map +1 -1
  151. package/dist/src/local-first/Query.js +139 -90
  152. package/dist/src/local-first/Relay.d.ts +18 -38
  153. package/dist/src/local-first/Relay.d.ts.map +1 -1
  154. package/dist/src/local-first/Relay.js +59 -171
  155. package/dist/src/local-first/Schema.d.ts +139 -144
  156. package/dist/src/local-first/Schema.d.ts.map +1 -1
  157. package/dist/src/local-first/Schema.js +78 -163
  158. package/dist/src/local-first/Shared.d.ts +125 -0
  159. package/dist/src/local-first/Shared.d.ts.map +1 -0
  160. package/dist/src/local-first/Shared.js +569 -0
  161. package/dist/src/local-first/Storage.d.ts +50 -53
  162. package/dist/src/local-first/Storage.d.ts.map +1 -1
  163. package/dist/src/local-first/Storage.js +121 -173
  164. package/dist/src/local-first/Timestamp.d.ts +28 -35
  165. package/dist/src/local-first/Timestamp.d.ts.map +1 -1
  166. package/dist/src/local-first/Timestamp.js +24 -25
  167. package/dist/src/local-first/index.d.ts +2 -15
  168. package/dist/src/local-first/index.d.ts.map +1 -1
  169. package/dist/src/local-first/index.js +2 -15
  170. package/package.json +36 -26
  171. package/src/Array.ts +467 -106
  172. package/src/Assert.ts +87 -12
  173. package/src/BigInt.ts +7 -1
  174. package/src/Brand.ts +41 -12
  175. package/src/Buffer.ts +10 -4
  176. package/src/Cache.ts +11 -2
  177. package/src/Callbacks.ts +33 -23
  178. package/src/Console.ts +567 -178
  179. package/src/Crypto.ts +111 -62
  180. package/src/Eq.ts +30 -3
  181. package/src/Error.ts +50 -34
  182. package/src/Function.ts +140 -68
  183. package/src/Identicon.ts +7 -1
  184. package/src/Lookup.ts +415 -0
  185. package/src/Microtask.ts +58 -0
  186. package/src/Number.ts +40 -7
  187. package/src/Object.ts +155 -16
  188. package/src/Option.ts +74 -0
  189. package/src/Order.ts +11 -3
  190. package/src/Platform.ts +43 -7
  191. package/src/Polyfills.ts +465 -0
  192. package/src/Random.ts +25 -11
  193. package/src/Redacted.ts +8 -4
  194. package/src/Ref.ts +72 -21
  195. package/src/RefCount.ts +170 -0
  196. package/src/Relation.ts +188 -136
  197. package/src/Resource.ts +864 -0
  198. package/src/Result.ts +548 -381
  199. package/src/Schedule.ts +1471 -0
  200. package/src/Set.ts +247 -0
  201. package/src/Skiplist.ts +7 -1
  202. package/src/Sqlite.ts +409 -304
  203. package/src/Store.ts +94 -48
  204. package/src/String.ts +6 -0
  205. package/src/Task.ts +3685 -740
  206. package/src/Test.ts +162 -0
  207. package/src/Time.ts +348 -145
  208. package/src/Tracer.ts +54 -0
  209. package/src/Type.ts +1064 -630
  210. package/src/Types.ts +202 -7
  211. package/src/WebSocket.ts +311 -178
  212. package/src/Worker.ts +527 -147
  213. package/src/index.ts +74 -3
  214. package/src/local-first/Db.ts +871 -597
  215. package/src/local-first/Error.ts +17 -0
  216. package/src/local-first/Evolu.ts +884 -823
  217. package/src/local-first/LocalAuth.ts +10 -10
  218. package/src/local-first/Owner.ts +94 -42
  219. package/src/local-first/Protocol.ts +440 -303
  220. package/src/local-first/Query.ts +243 -194
  221. package/src/local-first/Relay.ts +91 -252
  222. package/src/local-first/Schema.ts +284 -394
  223. package/src/local-first/Shared.ts +932 -0
  224. package/src/local-first/Storage.ts +209 -282
  225. package/src/local-first/Timestamp.ts +38 -45
  226. package/src/local-first/index.ts +2 -16
  227. package/LICENSE +0 -21
  228. package/dist/src/Instances.d.ts +0 -34
  229. package/dist/src/Instances.d.ts.map +0 -1
  230. package/dist/src/Instances.js +0 -44
  231. package/dist/src/Resources.d.ts +0 -118
  232. package/dist/src/Resources.d.ts.map +0 -1
  233. package/dist/src/Resources.js +0 -197
  234. package/dist/src/local-first/Platform.d.ts +0 -25
  235. package/dist/src/local-first/Platform.d.ts.map +0 -1
  236. package/dist/src/local-first/Platform.js +0 -1
  237. package/dist/src/local-first/Public.d.ts +0 -22
  238. package/dist/src/local-first/Public.d.ts.map +0 -1
  239. package/dist/src/local-first/Public.js +0 -15
  240. package/dist/src/local-first/PublicKysely.d.ts +0 -148
  241. package/dist/src/local-first/PublicKysely.d.ts.map +0 -1
  242. package/dist/src/local-first/PublicKysely.js +0 -184
  243. package/dist/src/local-first/Sync.d.ts +0 -112
  244. package/dist/src/local-first/Sync.d.ts.map +0 -1
  245. package/dist/src/local-first/Sync.js +0 -529
  246. package/src/Instances.ts +0 -90
  247. package/src/Resources.ts +0 -367
  248. package/src/local-first/Platform.ts +0 -27
  249. package/src/local-first/Public.ts +0 -42
  250. package/src/local-first/PublicKysely.ts +0 -239
  251. package/src/local-first/Sync.ts +0 -960
@@ -20,7 +20,7 @@
20
20
  * transfer, ownership, real-time broadcasting, request-response semantics, and
21
21
  * error handling.
22
22
  *
23
- * ### Message structure
23
+ * ## Message structure
24
24
  *
25
25
  * | Field | Notes |
26
26
  * | :----------------------------- | :------------------------ |
@@ -43,7 +43,7 @@
43
43
  * | - {@link NonNegativeInt} | Number of ranges. |
44
44
  * | - {@link Range} | |
45
45
  *
46
- * ### WriteKey validation
46
+ * ## WriteKey validation
47
47
  *
48
48
  * The initiator sends a hasWriteKey flag and optionally a WriteKey. The
49
49
  * WriteKey is required when sending messages as a secure token proving the
@@ -52,7 +52,7 @@
52
52
  * validates the WriteKey immediately after parsing the initiator header, before
53
53
  * processing any messages or ranges.
54
54
  *
55
- * ### Synchronization
55
+ * ## Synchronization
56
56
  *
57
57
  * - **Messages**: Sends {@link EncryptedCrdtMessage}s in either direction.
58
58
  * - **Ranges**: Determines messages to sync. Usage varies by transport—e.g., sent
@@ -79,7 +79,7 @@
79
79
  * initiator. In relay-to-relay or P2P sync, both sides may require the
80
80
  * {@link OwnerWriteKey} depending on who is the initiator.
81
81
  *
82
- * ### Protocol errors
82
+ * ## Protocol errors
83
83
  *
84
84
  * The protocol uses error codes in the header to signal issues:
85
85
  *
@@ -94,7 +94,7 @@
94
94
  * All protocol errors except `ProtocolInvalidDataError` include the `OwnerId`
95
95
  * to allow clients to associate errors with the correct owner.
96
96
  *
97
- * ### Message size limit
97
+ * ## Message size limit
98
98
  *
99
99
  * The protocol enforces a strict maximum size for all messages, defined by
100
100
  * {@link ProtocolMessageMaxSize}. This ensures every {@link ProtocolMessage} is
@@ -107,7 +107,7 @@
107
107
  * message limit to ensure efficient sync with
108
108
  * {@link defaultProtocolMessageRangesMaxSize}.
109
109
  *
110
- * ### Why Binary?
110
+ * ## Why Binary?
111
111
  *
112
112
  * The protocol avoids JSON because:
113
113
  *
@@ -127,7 +127,7 @@
127
127
  * To avoid reinventing serialization where it’s unnecessary—like for JSON and
128
128
  * certain numbers—the Evolu Protocol relies on MessagePack.
129
129
  *
130
- * ### Versioning
130
+ * ## Versioning
131
131
  *
132
132
  * Evolu Protocol uses explicit versioning to ensure compatibility between
133
133
  * clients and relays (or peers). Each protocol message begins with a version
@@ -148,7 +148,7 @@
148
148
  * Version negotiation is per-owner, allowing Evolu Protocol to evolve safely
149
149
  * over time and provide clear feedback about version mismatches.
150
150
  *
151
- * ### Credible exit
151
+ * ## Credible exit
152
152
  *
153
153
  * The protocol specification is intentionally non-configurable to ensure
154
154
  * universal compatibility. This design allows applications (users) to switch
@@ -176,18 +176,20 @@
176
176
  * indicators) to be forwarded by relays without storage overhead.
177
177
  */
178
178
  import { Packr } from "msgpackr";
179
- import { isNonEmptyReadonlyArray } from "../Array.js";
179
+ import { isNonEmptyArray } from "../Array.js";
180
180
  import { assert } from "../Assert.js";
181
181
  import { bytesToHex, bytesToUtf8, createBuffer, hexToBytes, utf8ToBytes, } from "../Buffer.js";
182
- import { createPadmePadding, } from "../Crypto.js";
182
+ import { createPadmePadding, decryptWithXChaCha20Poly1305, EncryptionKey, encryptWithXChaCha20Poly1305, Entropy24, XChaCha20Poly1305Ciphertext, xChaCha20Poly1305NonceLength, } from "../Crypto.js";
183
183
  import { eqArrayNumber } from "../Eq.js";
184
184
  import { computeBalancedBuckets } from "../Number.js";
185
185
  import { createRecord, objectToEntries } from "../Object.js";
186
186
  import { err, ok } from "../Result.js";
187
- import { Base64Url, base64UrlToUint8Array, between, DateIso, Id, idBytesToId, idBytesTypeValueLength, idToIdBytes, Int, Json, jsonToJsonValue, NonNegativeInt, Number, PositiveInt, uint8ArrayToBase64Url, } from "../Type.js";
188
- import { ownerIdToOwnerIdBytes, ownerWriteKeyLength, } from "./Owner.js";
187
+ import { SqliteValue } from "../Sqlite.js";
188
+ import { Millis } from "../Time.js";
189
+ import { Base64Url, base64UrlToUint8Array, between, DateIso, Id, IdBytes, idBytesToId, idBytesTypeValueLength, idToIdBytes, Int, Json, jsonToJsonValue, NonNegativeInt, Number, onePositiveInt, PositiveInt, uint8ArrayToBase64Url, zeroNonNegativeInt, } from "../Type.js";
190
+ import { OwnerId, OwnerIdBytes, ownerIdToOwnerIdBytes, OwnerWriteKey, ownerWriteKeyLength, } from "./Owner.js";
189
191
  import { DbChange, fingerprintSize, InfiniteUpperBound, RangeType, } from "./Storage.js";
190
- import { Counter, eqTimestamp, Millis, timestampBytesLength, timestampBytesToTimestamp, timestampToTimestampBytes, } from "./Timestamp.js";
192
+ import { Counter, eqTimestamp, NodeId, Timestamp, TimestampBytes, timestampBytesLength, timestampBytesToTimestamp, timestampToTimestampBytes, } from "./Timestamp.js";
191
193
  /**
192
194
  * Evolu uses MessagePack for numbers and JSONs.
193
195
  *
@@ -213,7 +215,7 @@ const maxProtocolMessageMaxSize = 100_000_000;
213
215
  * Increasing this value on the client side would break compatibility with
214
216
  * relays that enforce smaller limits.
215
217
  */
216
- export const ProtocolMessageMaxSize = between(minProtocolMessageMaxSize, maxProtocolMessageMaxSize)(Int);
218
+ export const ProtocolMessageMaxSize = /*#__PURE__*/ between(minProtocolMessageMaxSize, maxProtocolMessageMaxSize)(Int);
217
219
  /**
218
220
  * Default {@link ProtocolMessageMaxSize} (1MB).
219
221
  *
@@ -232,7 +234,7 @@ export const defaultProtocolMessageMaxSize = minProtocolMessageMaxSize;
232
234
  * {@link defaultProtocolMessageMaxSize}, maintaining compatibility between all
233
235
  * clients and relays.
234
236
  */
235
- export const ProtocolMessageRangesMaxSize = between(3_000, 100_000)(Int);
237
+ export const ProtocolMessageRangesMaxSize = /*#__PURE__*/ between(3_000, 100_000)(Int);
236
238
  /**
237
239
  * Default {@link ProtocolMessageRangesMaxSize} (30KB).
238
240
  *
@@ -241,7 +243,7 @@ export const ProtocolMessageRangesMaxSize = between(3_000, 100_000)(Int);
241
243
  */
242
244
  export const defaultProtocolMessageRangesMaxSize = 30_000;
243
245
  /** Evolu Protocol version. */
244
- export const protocolVersion = NonNegativeInt.orThrow(1);
246
+ export const protocolVersion = onePositiveInt;
245
247
  export const MessageType = {
246
248
  /** Request message from initiator (client) to non-initiator (relay). */
247
249
  Request: 0,
@@ -250,6 +252,24 @@ export const MessageType = {
250
252
  /** Broadcast message from non-initiator (relay) to subscribed clients. */
251
253
  Broadcast: 2,
252
254
  };
255
+ /**
256
+ * Parses the protocol header needed for routing.
257
+ *
258
+ * Every protocol message begins with `protocolVersion`, `ownerId`, and
259
+ * `messageType`.
260
+ */
261
+ export const parseProtocolHeader = (inputMessage) => {
262
+ try {
263
+ return ok(parseProtocolHeaderFromBuffer(createBuffer(inputMessage)));
264
+ }
265
+ catch (error) {
266
+ return err({
267
+ type: "ProtocolInvalidDataError",
268
+ data: inputMessage,
269
+ error,
270
+ });
271
+ }
272
+ };
253
273
  export const SubscriptionFlags = {
254
274
  /** No subscription changes for this owner. */
255
275
  None: 0,
@@ -328,10 +348,7 @@ export const createProtocolMessageForSync = (deps) => (ownerId, subscriptionFlag
328
348
  });
329
349
  const ownerIdBytes = ownerIdToOwnerIdBytes(ownerId);
330
350
  const size = deps.storage.getSize(ownerIdBytes);
331
- // Errors are handled by the storage.
332
- if (size == null)
333
- return null;
334
- splitRange(deps)(ownerIdBytes, NonNegativeInt.orThrow(0), size, InfiniteUpperBound, buffer);
351
+ splitRange(deps)(ownerIdBytes, zeroNonNegativeInt, size, InfiniteUpperBound, buffer);
335
352
  return buffer.unwrap();
336
353
  };
337
354
  export const createProtocolMessageForUnsubscribe = (ownerId) => createProtocolMessageBuffer(ownerId, {
@@ -413,9 +430,7 @@ export const createProtocolMessageBuffer = (ownerId, options) => {
413
430
  buffers.messages.dbChanges.extend(message.change);
414
431
  assert(isWithinSizeLimits(), "the message is too big");
415
432
  },
416
- canSplitRange: () => {
417
- return getRangesSize() + safeMargins.splitRange <= rangesMaxSize;
418
- },
433
+ canSplitRange: () => getRangesSize() + safeMargins.splitRange <= rangesMaxSize,
419
434
  canAddTimestampsRangeAndMessage: (timestamps, message) => {
420
435
  const rangesNewSize = getRangesSize() + timestamps.getLength() + safeMargins.timestampsRange;
421
436
  return (rangesNewSize <= rangesMaxSize &&
@@ -472,7 +487,7 @@ export const createProtocolMessageBuffer = (ownerId, options) => {
472
487
  };
473
488
  };
474
489
  export const createTimestampsBuffer = () => {
475
- let count = NonNegativeInt.orThrow(0);
490
+ let count = zeroNonNegativeInt;
476
491
  const countBuffer = createBuffer();
477
492
  const syncCount = () => {
478
493
  countBuffer.reset();
@@ -517,9 +532,9 @@ export const createTimestampsBuffer = () => {
517
532
  };
518
533
  const createRunLengthEncoder = (encodeValue) => {
519
534
  const buffer = createBuffer();
520
- let previousLength = NonNegativeInt.orThrow(0);
535
+ let previousLength = zeroNonNegativeInt;
521
536
  let previousValue = null;
522
- let runLength = NonNegativeInt.orThrow(0);
537
+ let runLength = zeroNonNegativeInt;
523
538
  return {
524
539
  add: (value) => {
525
540
  if (value === previousValue) {
@@ -538,7 +553,8 @@ const createRunLengthEncoder = (encodeValue) => {
538
553
  unwrap: () => buffer.unwrap(),
539
554
  };
540
555
  };
541
- export const applyProtocolMessageAsClient = (deps) => async (inputMessage, options = {}) => {
556
+ export const applyProtocolMessageAsClient = (inputMessage, options = {}) => async (run) => {
557
+ const { storage } = run.deps;
542
558
  try {
543
559
  const input = createBuffer(inputMessage);
544
560
  const [requestedVersion, ownerId] = decodeVersionAndOwner(input);
@@ -585,39 +601,45 @@ export const applyProtocolMessageAsClient = (deps) => async (inputMessage, optio
585
601
  }
586
602
  const messages = decodeMessages(input);
587
603
  const ownerIdBytes = ownerIdToOwnerIdBytes(ownerId);
588
- if (isNonEmptyReadonlyArray(messages)) {
589
- const result = await deps.storage.writeMessages(ownerIdBytes, messages);
590
- // Errors are handled by the Storage. Here we just stop syncing.
591
- if (!result.ok)
592
- return ok({ type: "no-response" });
604
+ if (isNonEmptyArray(messages)) {
605
+ try {
606
+ const result = await run(storage.writeMessages(ownerIdBytes, messages));
607
+ // Quota errors are handled by Storage; protocol just stops syncing.
608
+ if (!result.ok)
609
+ return ok({ type: "NoResponse" });
610
+ }
611
+ catch (error) {
612
+ run.deps.console.error(error);
613
+ return ok({ type: "NoResponse" });
614
+ }
593
615
  }
594
616
  // Now: No writeKey, no sync.
595
617
  // TODO: Allow to sync SharedReadonlyOwner
596
618
  // Without local changes, writeKey will not be required.
597
619
  // With local changes, writeKey will be required and if not provided,
598
620
  // the sync will stop.
599
- const writeKey = options.getWriteKey?.(ownerId);
621
+ const writeKey = options.writeKey;
600
622
  if (writeKey == null) {
601
- return ok({ type: "no-response" });
623
+ return ok({ type: "NoResponse" });
602
624
  }
603
625
  if (messageType === MessageType.Broadcast) {
604
- return ok({ type: "broadcast" });
626
+ return ok({ type: "Broadcast" });
605
627
  }
606
628
  const ranges = decodeRanges(input);
607
- if (!isNonEmptyReadonlyArray(ranges)) {
608
- return ok({ type: "no-response" });
629
+ if (!isNonEmptyArray(ranges)) {
630
+ return ok({ type: "NoResponse" });
609
631
  }
610
632
  const output = createProtocolMessageBuffer(ownerId, {
611
633
  messageType: MessageType.Request,
612
634
  writeKey,
613
635
  rangesMaxSize: options.rangesMaxSize,
614
636
  });
615
- const result = sync(deps)(ranges, output, ownerIdBytes);
637
+ const result = sync(run.deps)(ranges, output, ownerIdBytes);
616
638
  // Client sync error (handled via Storage) or no changes.
617
639
  if (!result.ok || !result.value) {
618
- return ok({ type: "no-response" });
640
+ return ok({ type: "NoResponse" });
619
641
  }
620
- return ok({ type: "response", message: output.unwrap() });
642
+ return ok({ type: "Response", message: output.unwrap() });
621
643
  }
622
644
  catch (error) {
623
645
  return err({
@@ -627,9 +649,10 @@ export const applyProtocolMessageAsClient = (deps) => async (inputMessage, optio
627
649
  });
628
650
  }
629
651
  };
630
- export const applyProtocolMessageAsRelay = (deps) => async (inputMessage, options = {},
652
+ export const applyProtocolMessageAsRelay = (inputMessage, options = {},
631
653
  /** For tests only. */
632
- version = protocolVersion) => {
654
+ version = protocolVersion) => async (run) => {
655
+ const { storage } = run.deps;
633
656
  try {
634
657
  const input = createBuffer(inputMessage);
635
658
  const [requestedVersion, ownerId] = decodeVersionAndOwner(input);
@@ -640,7 +663,7 @@ version = protocolVersion) => {
640
663
  encodeNonNegativeInt(output, version);
641
664
  output.extend(ownerIdBytes);
642
665
  return ok({
643
- type: "response",
666
+ type: "Response",
644
667
  message: output.unwrap(),
645
668
  });
646
669
  }
@@ -663,10 +686,10 @@ version = protocolVersion) => {
663
686
  break;
664
687
  }
665
688
  if (writeKey) {
666
- const isValid = deps.storage.validateWriteKey(ownerIdBytes, writeKey);
689
+ const isValid = storage.validateWriteKey(ownerIdBytes, writeKey);
667
690
  if (!isValid) {
668
691
  return ok({
669
- type: "response",
692
+ type: "Response",
670
693
  message: createProtocolMessageBuffer(ownerId, {
671
694
  messageType: MessageType.Response,
672
695
  errorCode: ProtocolErrorCode.WriteKeyError,
@@ -675,26 +698,33 @@ version = protocolVersion) => {
675
698
  }
676
699
  }
677
700
  const messages = decodeMessages(input);
678
- if (isNonEmptyReadonlyArray(messages)) {
701
+ if (isNonEmptyArray(messages)) {
679
702
  if (!writeKey) {
680
703
  return ok({
681
- type: "response",
704
+ type: "Response",
682
705
  message: createProtocolMessageBuffer(ownerId, {
683
706
  messageType: MessageType.Response,
684
707
  errorCode: ProtocolErrorCode.WriteKeyError,
685
708
  }).unwrap(),
686
709
  });
687
710
  }
688
- const result = await deps.storage.writeMessages(ownerIdBytes, messages);
689
- if (!result.ok) {
690
- const errorCode = result.error.type === "StorageWriteError"
691
- ? ProtocolErrorCode.WriteError
692
- : ProtocolErrorCode.QuotaError;
711
+ try {
712
+ const result = await run(storage.writeMessages(ownerIdBytes, messages));
713
+ if (!result.ok) {
714
+ const message = createProtocolMessageBuffer(ownerId, {
715
+ messageType: MessageType.Response,
716
+ errorCode: ProtocolErrorCode.QuotaError,
717
+ }).unwrap();
718
+ return ok({ type: "Response", message });
719
+ }
720
+ }
721
+ catch (error) {
722
+ run.deps.console.error(error);
693
723
  const message = createProtocolMessageBuffer(ownerId, {
694
724
  messageType: MessageType.Response,
695
- errorCode,
725
+ errorCode: ProtocolErrorCode.WriteError,
696
726
  }).unwrap();
697
- return ok({ type: "response", message });
727
+ return ok({ type: "Response", message });
698
728
  }
699
729
  /**
700
730
  * Broadcast messages to all subscribed owners for real-time
@@ -735,10 +765,10 @@ version = protocolVersion) => {
735
765
  });
736
766
  // Non-initiators always respond to provide sync completion feedback,
737
767
  // even when there's nothing to sync.
738
- if (!isNonEmptyReadonlyArray(ranges)) {
739
- return ok({ type: "response", message: output.unwrap() });
768
+ if (!isNonEmptyArray(ranges)) {
769
+ return ok({ type: "Response", message: output.unwrap() });
740
770
  }
741
- const result = sync(deps)(ranges, output, ownerIdBytes);
771
+ const result = sync(run.deps)(ranges, output, ownerIdBytes);
742
772
  const message = result.ok
743
773
  ? output.unwrap()
744
774
  : createProtocolMessageBuffer(ownerId, {
@@ -746,7 +776,7 @@ version = protocolVersion) => {
746
776
  errorCode: result.error,
747
777
  }).unwrap();
748
778
  // Non-initiators always respond to provide sync completion feedback,
749
- return ok({ type: "response", message });
779
+ return ok({ type: "Response", message });
750
780
  }
751
781
  catch (error) {
752
782
  return err({
@@ -765,6 +795,33 @@ const decodeVersionAndOwner = (input) => {
765
795
  const ownerId = decodeId(input);
766
796
  return [version, ownerId];
767
797
  };
798
+ const parseProtocolHeaderFromBuffer = (input) => {
799
+ const [version, ownerId] = decodeVersionAndOwner(input);
800
+ if (version !== protocolVersion) {
801
+ throw new ProtocolDecodeError(`Unsupported protocol version: ${version}`);
802
+ }
803
+ const messageTypeValue = input.shift();
804
+ let messageType;
805
+ switch (messageTypeValue) {
806
+ case MessageType.Request:
807
+ messageType = MessageType.Request;
808
+ break;
809
+ case MessageType.Response:
810
+ messageType = MessageType.Response;
811
+ break;
812
+ case MessageType.Broadcast:
813
+ messageType = MessageType.Broadcast;
814
+ break;
815
+ default:
816
+ throw new ProtocolDecodeError("Invalid MessageType");
817
+ }
818
+ return {
819
+ type: "ProtocolHeader",
820
+ version: 1,
821
+ ownerId,
822
+ messageType,
823
+ };
824
+ };
768
825
  /**
769
826
  * Error thrown for internal protocol validation failures, such as invalid data
770
827
  * or type errors.
@@ -778,21 +835,27 @@ class ProtocolDecodeError extends Error {
778
835
  }
779
836
  const decodeMessages = (buffer) => {
780
837
  const timestamps = decodeTimestamps(buffer);
781
- const messages = [];
782
- for (const timestamp of timestamps) {
838
+ const messages = new Array(timestamps.length);
839
+ for (let i = 0; i < timestamps.length; i++) {
840
+ const timestamp = timestamps[i];
783
841
  const changeLength = decodeLength(buffer);
784
842
  const change = buffer.shiftN(changeLength);
785
- messages.push({ timestamp, change });
843
+ messages[i] = { timestamp, change };
786
844
  }
787
845
  return messages;
788
846
  };
789
847
  const sync = (deps) => (ranges, output, ownerIdBytes) => {
790
848
  const outputInitialSize = output.getSize();
791
- const storageSize = deps.storage.getSize(ownerIdBytes);
792
- if (storageSize == null)
849
+ let storageSize;
850
+ try {
851
+ storageSize = deps.storage.getSize(ownerIdBytes);
852
+ }
853
+ catch (error) {
854
+ deps.console.error(error);
793
855
  return err(ProtocolErrorCode.SyncError);
856
+ }
794
857
  let prevUpperBound = null;
795
- let prevIndex = NonNegativeInt.orThrow(0);
858
+ let prevIndex = zeroNonNegativeInt;
796
859
  let skip = false;
797
860
  let nonSkipRangeAdded = false;
798
861
  const skipRange = (range) => {
@@ -822,9 +885,14 @@ const sync = (deps) => (ranges, output, ownerIdBytes) => {
822
885
  };
823
886
  // When we don't have a space...
824
887
  const addFingerprintForRemainingRange = (begin) => {
825
- const fingerprint = deps.storage.fingerprint(ownerIdBytes, begin, storageSize);
826
- if (!fingerprint)
888
+ let fingerprint;
889
+ try {
890
+ fingerprint = deps.storage.fingerprint(ownerIdBytes, begin, storageSize);
891
+ }
892
+ catch (error) {
893
+ deps.console.error(error);
827
894
  return false;
895
+ }
828
896
  // There is always a space for a ramaining range.
829
897
  output.addRange({
830
898
  type: RangeType.Fingerprint,
@@ -836,18 +904,28 @@ const sync = (deps) => (ranges, output, ownerIdBytes) => {
836
904
  for (const range of ranges) {
837
905
  const currentUpperBound = range.upperBound;
838
906
  const lower = prevIndex;
839
- let upper = deps.storage.findLowerBound(ownerIdBytes, prevIndex, storageSize, currentUpperBound);
840
- if (upper == null)
907
+ let upper;
908
+ try {
909
+ upper = deps.storage.findLowerBound(ownerIdBytes, prevIndex, storageSize, currentUpperBound);
910
+ }
911
+ catch (error) {
912
+ deps.console.error(error);
841
913
  return err(ProtocolErrorCode.SyncError);
914
+ }
842
915
  switch (range.type) {
843
916
  case RangeType.Skip: {
844
917
  skipRange(range);
845
918
  break;
846
919
  }
847
920
  case RangeType.Fingerprint: {
848
- const ourFingerprint = deps.storage.fingerprint(ownerIdBytes, lower, upper);
849
- if (ourFingerprint == null)
921
+ let ourFingerprint;
922
+ try {
923
+ ourFingerprint = deps.storage.fingerprint(ownerIdBytes, lower, upper);
924
+ }
925
+ catch (error) {
926
+ deps.console.error(error);
850
927
  return err(ProtocolErrorCode.SyncError);
928
+ }
851
929
  if (eqArrayNumber(range.fingerprint, ourFingerprint)) {
852
930
  skipRange(range);
853
931
  }
@@ -868,38 +946,46 @@ const sync = (deps) => (ranges, output, ownerIdBytes) => {
868
946
  let endBound = currentUpperBound;
869
947
  const timestampsWeNeed = new Map(range.timestamps.map((t) => [t.join(), true]));
870
948
  const ourTimestamps = createTimestampsBuffer();
871
- let cantReadDbChange = false;
872
949
  let exceeded = false;
873
- deps.storage.iterate(ownerIdBytes, lower, upper, (timestamp, index) => {
874
- const timestampString = timestamp.join();
875
- const timestampBinary = timestampBytesToTimestamp(timestamp);
876
- let message = null;
877
- if (timestampsWeNeed.has(timestampString)) {
878
- timestampsWeNeed.delete(timestampString);
879
- }
880
- else {
881
- const dbChange = deps.storage.readDbChange(ownerIdBytes, timestamp);
882
- if (dbChange == null) {
883
- cantReadDbChange = true;
950
+ let iterateFailed = false;
951
+ try {
952
+ deps.storage.iterate(ownerIdBytes, lower, upper, (timestamp, index) => {
953
+ const timestampString = timestamp.join();
954
+ const timestampBinary = timestampBytesToTimestamp(timestamp);
955
+ let message = null;
956
+ if (timestampsWeNeed.has(timestampString)) {
957
+ timestampsWeNeed.delete(timestampString);
958
+ }
959
+ else {
960
+ try {
961
+ message = {
962
+ timestamp: timestampBinary,
963
+ change: deps.storage.readDbChange(ownerIdBytes, timestamp),
964
+ };
965
+ }
966
+ catch (error) {
967
+ deps.console.error(error);
968
+ iterateFailed = true;
969
+ return false;
970
+ }
971
+ }
972
+ if (!output.canAddTimestampsRangeAndMessage(ourTimestamps, message)) {
973
+ exceeded = true;
974
+ endBound = timestamp;
975
+ upper = index;
884
976
  return false;
885
977
  }
886
- message = {
887
- timestamp: timestampBinary,
888
- change: dbChange,
889
- };
890
- }
891
- if (!output.canAddTimestampsRangeAndMessage(ourTimestamps, message)) {
892
- exceeded = true;
893
- endBound = timestamp;
894
- upper = index;
895
- return false;
896
- }
897
- ourTimestamps.add(timestampBinary);
898
- if (message)
899
- output.addMessage(message);
900
- return true;
901
- });
902
- if (cantReadDbChange) {
978
+ ourTimestamps.add(timestampBinary);
979
+ if (message)
980
+ output.addMessage(message);
981
+ return true;
982
+ });
983
+ }
984
+ catch (error) {
985
+ deps.console.error(error);
986
+ return err(ProtocolErrorCode.SyncError);
987
+ }
988
+ if (iterateFailed) {
903
989
  return err(ProtocolErrorCode.SyncError);
904
990
  }
905
991
  const addRange = () => {
@@ -943,7 +1029,7 @@ const splitRange = (deps) => (ownerId, lower, upper, upperBound, buffer) => {
943
1029
  upperBound,
944
1030
  timestamps: createTimestampsBuffer(),
945
1031
  };
946
- deps.storage.iterate(ownerId, NonNegativeInt.orThrow(0), itemCount, (timestamp) => {
1032
+ deps.storage.iterate(ownerId, zeroNonNegativeInt, itemCount, (timestamp) => {
947
1033
  range.timestamps.add(timestampBytesToTimestamp(timestamp));
948
1034
  return true;
949
1035
  });
@@ -957,10 +1043,14 @@ const splitRange = (deps) => (ownerId, lower, upper, upperBound, buffer) => {
957
1043
  lower,
958
1044
  ...buckets.value.map((b) => NonNegativeInt.orThrow(b + lower)),
959
1045
  ];
960
- const fingerprintRanges = deps.storage.fingerprintRanges(ownerId, fingerprintRangesBuckets, upperBound);
961
- // Errors are handled by the storage.
962
- if (fingerprintRanges == null)
1046
+ let fingerprintRanges;
1047
+ try {
1048
+ fingerprintRanges = deps.storage.fingerprintRanges(ownerId, fingerprintRangesBuckets, upperBound);
1049
+ }
1050
+ catch (error) {
1051
+ deps.console.error(error);
963
1052
  return;
1053
+ }
964
1054
  const rangesToUse = lower > 0 ? fingerprintRanges.slice(1) : fingerprintRanges;
965
1055
  for (const range of rangesToUse) {
966
1056
  buffer.addRange(range);
@@ -974,20 +1064,20 @@ const decodeRanges = (buffer) => {
974
1064
  return [];
975
1065
  const timestampsCount = NonNegativeInt.orThrow(rangesCount - 1);
976
1066
  const timestamps = decodeTimestamps(buffer, timestampsCount);
977
- const rangeTypes = [];
1067
+ const rangeTypes = new Array(rangesCount);
978
1068
  for (let i = 0; i < rangesCount; i++) {
979
1069
  const rangeType = decodeNonNegativeInt(buffer);
980
1070
  switch (rangeType) {
981
1071
  case RangeType.Fingerprint:
982
1072
  case RangeType.Skip:
983
1073
  case RangeType.Timestamps:
984
- rangeTypes.push(rangeType);
1074
+ rangeTypes[i] = rangeType;
985
1075
  break;
986
1076
  default:
987
1077
  throw new ProtocolDecodeError(`Invalid RangeType: ${rangeType}`);
988
1078
  }
989
1079
  }
990
- const ranges = [];
1080
+ const ranges = new Array(rangesCount);
991
1081
  for (let i = 0; i < rangesCount; i++) {
992
1082
  const upperBound = i < timestampsCount
993
1083
  ? timestampToTimestampBytes(timestamps[i])
@@ -995,75 +1085,81 @@ const decodeRanges = (buffer) => {
995
1085
  const rangeType = rangeTypes[i];
996
1086
  switch (rangeType) {
997
1087
  case RangeType.Skip:
998
- ranges.push({ type: RangeType.Skip, upperBound });
1088
+ ranges[i] = { type: RangeType.Skip, upperBound };
999
1089
  break;
1000
1090
  case RangeType.Fingerprint: {
1001
1091
  const fingerprint = buffer.shiftN(fingerprintSize);
1002
- ranges.push({
1092
+ ranges[i] = {
1003
1093
  type: RangeType.Fingerprint,
1004
1094
  upperBound,
1005
1095
  fingerprint,
1006
- });
1096
+ };
1007
1097
  break;
1008
1098
  }
1009
1099
  case RangeType.Timestamps: {
1010
1100
  const timestamps = decodeTimestamps(buffer).map(timestampToTimestampBytes);
1011
- ranges.push({
1101
+ ranges[i] = {
1012
1102
  type: RangeType.Timestamps,
1013
1103
  upperBound,
1014
1104
  timestamps,
1015
- });
1105
+ };
1016
1106
  break;
1017
1107
  }
1018
1108
  }
1019
1109
  }
1020
1110
  return ranges;
1021
1111
  };
1022
- /** Decodes an array of sorted timestamps with delta-encoded millis. */
1023
1112
  const decodeTimestamps = (buffer, length) => {
1024
1113
  length ??= decodeNonNegativeInt(buffer);
1025
1114
  let previousMillis = 0;
1026
- const millises = [];
1115
+ const millises = new Array(length);
1027
1116
  for (let i = 0; i < length; i++) {
1028
1117
  const deltaMillis = decodeNonNegativeInt(buffer);
1029
1118
  const millis = Millis.from(previousMillis + deltaMillis);
1030
1119
  if (!millis.ok)
1031
1120
  throw new ProtocolDecodeError(millis.error.type);
1032
- millises.push(millis.value);
1121
+ millises[i] = millis.value;
1033
1122
  previousMillis = millis.value;
1034
1123
  }
1035
- const counters = [];
1036
- let counterIndex = 0;
1037
- while (counterIndex < length) {
1124
+ const counters = decodeRle(buffer, length, () => {
1038
1125
  const counter = Counter.from(decodeNonNegativeInt(buffer));
1039
1126
  if (!counter.ok)
1040
1127
  throw new ProtocolDecodeError(counter.error.type);
1041
- const runLength = decodeNonNegativeInt(buffer);
1042
- for (let i = 0; i < runLength; i++) {
1043
- counters.push(counter.value);
1044
- counterIndex++;
1045
- }
1046
- }
1047
- const nodeIds = [];
1048
- let nodeIdIndex = 0;
1049
- while (nodeIdIndex < length) {
1050
- const nodeId = decodeNodeId(buffer);
1051
- const runLength = decodeNonNegativeInt(buffer);
1052
- for (let i = 0; i < runLength; i++) {
1053
- nodeIds.push(nodeId);
1054
- nodeIdIndex++;
1055
- }
1056
- }
1057
- const timestamps = [];
1128
+ return counter.value;
1129
+ });
1130
+ const nodeIds = decodeRle(buffer, length, () => decodeNodeId(buffer));
1131
+ const timestamps = new Array(length);
1058
1132
  for (let i = 0; i < length; i++) {
1059
- timestamps.push({
1133
+ timestamps[i] = {
1060
1134
  millis: millises[i],
1061
1135
  counter: counters[i],
1062
1136
  nodeId: nodeIds[i],
1063
- });
1137
+ };
1064
1138
  }
1065
1139
  return timestamps;
1066
1140
  };
1141
+ export const decodeRle = (buffer, length, decodeValue) => {
1142
+ const values = new Array(length);
1143
+ let index = 0;
1144
+ while (index < length) {
1145
+ const value = decodeValue();
1146
+ const runLength = decodeNonNegativeInt(buffer);
1147
+ // Prevent infinite loop on malformed input.
1148
+ if (runLength === 0) {
1149
+ throw new ProtocolDecodeError("Invalid RLE encoding: runLength must be positive");
1150
+ }
1151
+ const remaining = length - index;
1152
+ // Prevent CPU/memory amplification via oversized runLength.
1153
+ if (runLength > remaining) {
1154
+ throw new ProtocolDecodeError(`Invalid RLE encoding: runLength ${runLength} exceeds remaining ${remaining}`);
1155
+ }
1156
+ for (let i = 0; i < runLength; i++) {
1157
+ values[index] = value;
1158
+ index++;
1159
+ }
1160
+ }
1161
+ return values;
1162
+ };
1067
1163
  const decodeId = (buffer) => {
1068
1164
  const bytes = buffer.shiftN(idBytesTypeValueLength);
1069
1165
  return idBytesToId(bytes);
@@ -1124,9 +1220,10 @@ export const encodeFlags = (buffer, flags) => {
1124
1220
  */
1125
1221
  export const decodeFlags = (buffer, count) => {
1126
1222
  const byte = buffer.shift();
1127
- const flags = [];
1128
- for (let i = 0; i < count && i < 8; i++) {
1129
- flags.push((byte & (1 << i)) !== 0);
1223
+ const length = globalThis.Math.min(count, 8);
1224
+ const flags = new Array(length);
1225
+ for (let i = 0; i < length; i++) {
1226
+ flags[i] = (byte & (1 << i)) !== 0;
1130
1227
  }
1131
1228
  return flags;
1132
1229
  };
@@ -1146,6 +1243,7 @@ export const encodeAndEncryptDbChange = (deps) => (message, key) => {
1146
1243
  buffer.extend(timestampToTimestampBytes(message.timestamp));
1147
1244
  encodeFlags(buffer, [
1148
1245
  message.change.isInsert,
1246
+ // Encode nullable boolean as two flags: presence + value.
1149
1247
  message.change.isDelete != null,
1150
1248
  message.change.isDelete ?? false,
1151
1249
  ]);
@@ -1159,7 +1257,7 @@ export const encodeAndEncryptDbChange = (deps) => (message, key) => {
1159
1257
  }
1160
1258
  // Add PADMÉ padding (ignored during decoding)
1161
1259
  buffer.extend(createPadmePadding(buffer.getLength()));
1162
- const { nonce, ciphertext } = deps.symmetricCrypto.encrypt(buffer.unwrap(), key);
1260
+ const [ciphertext, nonce] = encryptWithXChaCha20Poly1305(deps)(buffer.unwrap(), key);
1163
1261
  buffer.reset();
1164
1262
  buffer.extend(nonce);
1165
1263
  encodeLength(buffer, ciphertext);
@@ -1171,19 +1269,19 @@ export const encodeAndEncryptDbChange = (deps) => (message, key) => {
1171
1269
  * owner's encryption key. Verifies that the embedded timestamp matches the
1172
1270
  * expected timestamp to ensure message integrity.
1173
1271
  */
1174
- export const decryptAndDecodeDbChange = (deps) => (message, key) => {
1272
+ export const decryptAndDecodeDbChange = (message, key) => {
1175
1273
  try {
1176
1274
  const buffer = createBuffer(message.change);
1177
- const nonce = buffer.shiftN(deps.symmetricCrypto.nonceLength);
1275
+ const nonce = buffer.shiftN(xChaCha20Poly1305NonceLength);
1178
1276
  const ciphertext = buffer.shiftN(decodeLength(buffer));
1179
- const plaintextBytes = deps.symmetricCrypto.decrypt(ciphertext, key, nonce);
1277
+ const plaintextBytes = decryptWithXChaCha20Poly1305(XChaCha20Poly1305Ciphertext.orThrow(ciphertext), Entropy24.orThrow(nonce), key);
1180
1278
  if (!plaintextBytes.ok)
1181
1279
  return plaintextBytes;
1182
1280
  buffer.reset();
1183
1281
  buffer.extend(plaintextBytes.value);
1184
1282
  // Decode version (for future compatibility, not need yet)
1185
1283
  decodeNonNegativeInt(buffer);
1186
- const timestamp = timestampBytesToTimestamp(buffer.shiftN(timestampBytesLength));
1284
+ const timestamp = timestampBytesToTimestamp(TimestampBytes.orThrow(buffer.shiftN(timestampBytesLength)));
1187
1285
  if (!eqTimestamp(timestamp, message.timestamp)) {
1188
1286
  return err({
1189
1287
  type: "ProtocolTimestampMismatchError",
@@ -1289,23 +1387,23 @@ const isSmallInt = (value) => value >= 0 && value < 20;
1289
1387
  export const ProtocolValueType = {
1290
1388
  // 0-19 small ints
1291
1389
  // SQLite types
1292
- String: NonNegativeInt.orThrow(20),
1293
- Number: NonNegativeInt.orThrow(21),
1294
- Null: NonNegativeInt.orThrow(22),
1295
- Bytes: NonNegativeInt.orThrow(23),
1390
+ String: /*#__PURE__*/ NonNegativeInt.orThrow(20),
1391
+ Number: /*#__PURE__*/ NonNegativeInt.orThrow(21),
1392
+ Null: /*#__PURE__*/ NonNegativeInt.orThrow(22),
1393
+ Bytes: /*#__PURE__*/ NonNegativeInt.orThrow(23),
1296
1394
  // We can add more types for other DBs or anything else later.
1297
1395
  // Optimized types
1298
- NonNegativeInt: NonNegativeInt.orThrow(30),
1396
+ NonNegativeInt: /*#__PURE__*/ NonNegativeInt.orThrow(30),
1299
1397
  // String optimizations
1300
- EmptyString: NonNegativeInt.orThrow(31), // 1 byte vs 2 bytes (50% reduction)
1301
- Base64Url: NonNegativeInt.orThrow(32),
1302
- Id: NonNegativeInt.orThrow(33),
1303
- Json: NonNegativeInt.orThrow(34),
1398
+ EmptyString: /*#__PURE__*/ NonNegativeInt.orThrow(31), // 1 byte vs 2 bytes (50% reduction)
1399
+ Base64Url: /*#__PURE__*/ NonNegativeInt.orThrow(32),
1400
+ Id: /*#__PURE__*/ NonNegativeInt.orThrow(33),
1401
+ Json: /*#__PURE__*/ NonNegativeInt.orThrow(34),
1304
1402
  // new Date().toISOString() - 24 bytes
1305
1403
  // encoded with fixed length - 8 bytes
1306
1404
  // encode as NonNegativeInt - 6 bytes (additional 25% reduction)
1307
- DateIsoWithNonNegativeTime: NonNegativeInt.orThrow(35),
1308
- DateIsoWithNegativeTime: NonNegativeInt.orThrow(36), // 9 bytes
1405
+ DateIsoWithNonNegativeTime: /*#__PURE__*/ NonNegativeInt.orThrow(35),
1406
+ DateIsoWithNegativeTime: /*#__PURE__*/ NonNegativeInt.orThrow(36), // 9 bytes
1309
1407
  // TODO: Operations (from 40)
1310
1408
  // Increment, Decrement, Patch, whatever.
1311
1409
  };