tinybase 9.4.0-beta.1 → 9.4.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 (239) hide show
  1. package/@types/persisters/index.d.ts +4 -0
  2. package/@types/persisters/persister-durable-object-sql-storage/index.d.ts +9 -2
  3. package/@types/persisters/persister-durable-object-sql-storage/with-schemas/index.d.ts +9 -2
  4. package/@types/persisters/with-schemas/index.d.ts +4 -0
  5. package/@types/queries/index.d.ts +4 -0
  6. package/@types/queries/with-schemas/index.d.ts +4 -0
  7. package/@types/synchronizers/synchronizer-ws-client/index.d.ts +7 -3
  8. package/@types/synchronizers/synchronizer-ws-client/with-schemas/index.d.ts +7 -3
  9. package/@types/synchronizers/synchronizer-ws-server/index.d.ts +9 -3
  10. package/@types/synchronizers/synchronizer-ws-server/with-schemas/index.d.ts +9 -3
  11. package/@types/synchronizers/synchronizer-ws-server-simple/index.d.ts +6 -3
  12. package/@types/synchronizers/synchronizer-ws-server-simple/with-schemas/index.d.ts +6 -3
  13. package/@types/ui-react-dom/index.d.ts +10 -10
  14. package/@types/ui-react-dom/with-schemas/index.d.ts +10 -10
  15. package/@types/ui-react-dom-charts/index.d.ts +7 -7
  16. package/@types/ui-react-dom-charts/with-schemas/index.d.ts +7 -7
  17. package/@types/ui-react-inspector/index.d.ts +1 -1
  18. package/@types/ui-react-inspector/with-schemas/index.d.ts +1 -1
  19. package/@types/ui-solid-dom/index.d.ts +10 -10
  20. package/@types/ui-solid-dom/with-schemas/index.d.ts +10 -10
  21. package/@types/ui-solid-inspector/index.d.ts +1 -1
  22. package/@types/ui-solid-inspector/with-schemas/index.d.ts +1 -1
  23. package/@types/ui-svelte-dom/index.d.ts +10 -10
  24. package/@types/ui-svelte-dom/with-schemas/index.d.ts +10 -10
  25. package/@types/ui-svelte-inspector/index.d.ts +1 -1
  26. package/@types/ui-svelte-inspector/with-schemas/index.d.ts +1 -1
  27. package/index.js +191 -74
  28. package/min/index.js +1 -1
  29. package/min/index.js.gz +0 -0
  30. package/min/omni/index.js +1 -1
  31. package/min/omni/index.js.gz +0 -0
  32. package/min/omni/with-schemas/index.js +1 -1
  33. package/min/omni/with-schemas/index.js.gz +0 -0
  34. package/min/persisters/index.js +1 -1
  35. package/min/persisters/index.js.gz +0 -0
  36. package/min/persisters/persister-automerge/index.js +1 -1
  37. package/min/persisters/persister-automerge/index.js.gz +0 -0
  38. package/min/persisters/persister-automerge/with-schemas/index.js +1 -1
  39. package/min/persisters/persister-automerge/with-schemas/index.js.gz +0 -0
  40. package/min/persisters/persister-browser/index.js +1 -1
  41. package/min/persisters/persister-browser/index.js.gz +0 -0
  42. package/min/persisters/persister-browser/with-schemas/index.js +1 -1
  43. package/min/persisters/persister-browser/with-schemas/index.js.gz +0 -0
  44. package/min/persisters/persister-cr-sqlite-wasm/index.js +1 -1
  45. package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
  46. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
  47. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
  48. package/min/persisters/persister-durable-object-sql-storage/index.js +1 -1
  49. package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
  50. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -1
  51. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
  52. package/min/persisters/persister-durable-object-storage/index.js +1 -1
  53. package/min/persisters/persister-durable-object-storage/index.js.gz +0 -0
  54. package/min/persisters/persister-durable-object-storage/with-schemas/index.js +1 -1
  55. package/min/persisters/persister-durable-object-storage/with-schemas/index.js.gz +0 -0
  56. package/min/persisters/persister-electric-sql/index.js +1 -1
  57. package/min/persisters/persister-electric-sql/index.js.gz +0 -0
  58. package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
  59. package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
  60. package/min/persisters/persister-expo-sqlite/index.js +1 -1
  61. package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
  62. package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
  63. package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
  64. package/min/persisters/persister-file/index.js +1 -1
  65. package/min/persisters/persister-file/index.js.gz +0 -0
  66. package/min/persisters/persister-file/with-schemas/index.js +1 -1
  67. package/min/persisters/persister-file/with-schemas/index.js.gz +0 -0
  68. package/min/persisters/persister-indexed-db/index.js +1 -1
  69. package/min/persisters/persister-indexed-db/index.js.gz +0 -0
  70. package/min/persisters/persister-indexed-db/with-schemas/index.js +1 -1
  71. package/min/persisters/persister-indexed-db/with-schemas/index.js.gz +0 -0
  72. package/min/persisters/persister-libsql/index.js +1 -1
  73. package/min/persisters/persister-libsql/index.js.gz +0 -0
  74. package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
  75. package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
  76. package/min/persisters/persister-partykit-client/index.js +1 -1
  77. package/min/persisters/persister-partykit-client/index.js.gz +0 -0
  78. package/min/persisters/persister-partykit-client/with-schemas/index.js +1 -1
  79. package/min/persisters/persister-partykit-client/with-schemas/index.js.gz +0 -0
  80. package/min/persisters/persister-pglite/index.js +1 -1
  81. package/min/persisters/persister-pglite/index.js.gz +0 -0
  82. package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
  83. package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
  84. package/min/persisters/persister-postgres/index.js +1 -1
  85. package/min/persisters/persister-postgres/index.js.gz +0 -0
  86. package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
  87. package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
  88. package/min/persisters/persister-powersync/index.js +1 -1
  89. package/min/persisters/persister-powersync/index.js.gz +0 -0
  90. package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
  91. package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
  92. package/min/persisters/persister-react-native-mmkv/index.js +1 -1
  93. package/min/persisters/persister-react-native-mmkv/index.js.gz +0 -0
  94. package/min/persisters/persister-react-native-mmkv/with-schemas/index.js +1 -1
  95. package/min/persisters/persister-react-native-mmkv/with-schemas/index.js.gz +0 -0
  96. package/min/persisters/persister-react-native-sqlite/index.js +1 -1
  97. package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
  98. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
  99. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
  100. package/min/persisters/persister-remote/index.js +1 -1
  101. package/min/persisters/persister-remote/index.js.gz +0 -0
  102. package/min/persisters/persister-remote/with-schemas/index.js +1 -1
  103. package/min/persisters/persister-remote/with-schemas/index.js.gz +0 -0
  104. package/min/persisters/persister-sqlite-bun/index.js +1 -1
  105. package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
  106. package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
  107. package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
  108. package/min/persisters/persister-sqlite-wasm/index.js +1 -1
  109. package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
  110. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
  111. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
  112. package/min/persisters/persister-sqlite3/index.js +1 -1
  113. package/min/persisters/persister-sqlite3/index.js.gz +0 -0
  114. package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
  115. package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
  116. package/min/persisters/persister-yjs/index.js +1 -1
  117. package/min/persisters/persister-yjs/index.js.gz +0 -0
  118. package/min/persisters/persister-yjs/with-schemas/index.js +1 -1
  119. package/min/persisters/persister-yjs/with-schemas/index.js.gz +0 -0
  120. package/min/persisters/with-schemas/index.js +1 -1
  121. package/min/persisters/with-schemas/index.js.gz +0 -0
  122. package/min/queries/index.js +1 -1
  123. package/min/queries/index.js.gz +0 -0
  124. package/min/queries/with-schemas/index.js +1 -1
  125. package/min/queries/with-schemas/index.js.gz +0 -0
  126. package/min/synchronizers/index.js +1 -1
  127. package/min/synchronizers/index.js.gz +0 -0
  128. package/min/synchronizers/synchronizer-broadcast-channel/index.js +1 -1
  129. package/min/synchronizers/synchronizer-broadcast-channel/index.js.gz +0 -0
  130. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +1 -1
  131. package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js.gz +0 -0
  132. package/min/synchronizers/synchronizer-local/index.js +1 -1
  133. package/min/synchronizers/synchronizer-local/index.js.gz +0 -0
  134. package/min/synchronizers/synchronizer-local/with-schemas/index.js +1 -1
  135. package/min/synchronizers/synchronizer-local/with-schemas/index.js.gz +0 -0
  136. package/min/synchronizers/synchronizer-ws-client/index.js +1 -1
  137. package/min/synchronizers/synchronizer-ws-client/index.js.gz +0 -0
  138. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js +1 -1
  139. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js.gz +0 -0
  140. package/min/synchronizers/synchronizer-ws-server/index.js +1 -1
  141. package/min/synchronizers/synchronizer-ws-server/index.js.gz +0 -0
  142. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js +1 -1
  143. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js.gz +0 -0
  144. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js +1 -1
  145. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js.gz +0 -0
  146. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +1 -1
  147. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js.gz +0 -0
  148. package/min/synchronizers/synchronizer-ws-server-simple/index.js +1 -1
  149. package/min/synchronizers/synchronizer-ws-server-simple/index.js.gz +0 -0
  150. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +1 -1
  151. package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js.gz +0 -0
  152. package/min/synchronizers/with-schemas/index.js +1 -1
  153. package/min/synchronizers/with-schemas/index.js.gz +0 -0
  154. package/min/ui-react-inspector/index.js +1 -1
  155. package/min/ui-react-inspector/index.js.gz +0 -0
  156. package/min/ui-react-inspector/with-schemas/index.js +1 -1
  157. package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
  158. package/min/ui-solid-inspector/index.js +1 -1
  159. package/min/ui-solid-inspector/index.js.gz +0 -0
  160. package/min/ui-solid-inspector/with-schemas/index.js +1 -1
  161. package/min/ui-solid-inspector/with-schemas/index.js.gz +0 -0
  162. package/min/ui-svelte-inspector/index.js +1 -1
  163. package/min/ui-svelte-inspector/index.js.gz +0 -0
  164. package/min/ui-svelte-inspector/with-schemas/index.js +1 -1
  165. package/min/ui-svelte-inspector/with-schemas/index.js.gz +0 -0
  166. package/min/with-schemas/index.js +1 -1
  167. package/min/with-schemas/index.js.gz +0 -0
  168. package/omni/index.js +606 -179
  169. package/omni/with-schemas/index.js +606 -179
  170. package/package.json +12 -8
  171. package/persisters/index.js +49 -10
  172. package/persisters/persister-automerge/index.js +29 -5
  173. package/persisters/persister-automerge/with-schemas/index.js +29 -5
  174. package/persisters/persister-browser/index.js +29 -5
  175. package/persisters/persister-browser/with-schemas/index.js +29 -5
  176. package/persisters/persister-cr-sqlite-wasm/index.js +29 -5
  177. package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +29 -5
  178. package/persisters/persister-durable-object-sql-storage/index.js +44 -8
  179. package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +44 -8
  180. package/persisters/persister-durable-object-storage/index.js +31 -5
  181. package/persisters/persister-durable-object-storage/with-schemas/index.js +31 -5
  182. package/persisters/persister-electric-sql/index.js +29 -5
  183. package/persisters/persister-electric-sql/with-schemas/index.js +29 -5
  184. package/persisters/persister-expo-sqlite/index.js +29 -5
  185. package/persisters/persister-expo-sqlite/with-schemas/index.js +29 -5
  186. package/persisters/persister-file/index.js +31 -5
  187. package/persisters/persister-file/with-schemas/index.js +31 -5
  188. package/persisters/persister-indexed-db/index.js +29 -5
  189. package/persisters/persister-indexed-db/with-schemas/index.js +29 -5
  190. package/persisters/persister-libsql/index.js +29 -5
  191. package/persisters/persister-libsql/with-schemas/index.js +29 -5
  192. package/persisters/persister-partykit-client/index.js +31 -5
  193. package/persisters/persister-partykit-client/with-schemas/index.js +31 -5
  194. package/persisters/persister-pglite/index.js +49 -10
  195. package/persisters/persister-pglite/with-schemas/index.js +49 -10
  196. package/persisters/persister-postgres/index.js +49 -10
  197. package/persisters/persister-postgres/with-schemas/index.js +49 -10
  198. package/persisters/persister-powersync/index.js +29 -5
  199. package/persisters/persister-powersync/with-schemas/index.js +29 -5
  200. package/persisters/persister-react-native-mmkv/index.js +31 -5
  201. package/persisters/persister-react-native-mmkv/with-schemas/index.js +31 -5
  202. package/persisters/persister-react-native-sqlite/index.js +29 -5
  203. package/persisters/persister-react-native-sqlite/with-schemas/index.js +29 -5
  204. package/persisters/persister-remote/index.js +31 -5
  205. package/persisters/persister-remote/with-schemas/index.js +31 -5
  206. package/persisters/persister-sqlite-bun/index.js +29 -5
  207. package/persisters/persister-sqlite-bun/with-schemas/index.js +29 -5
  208. package/persisters/persister-sqlite-wasm/index.js +29 -5
  209. package/persisters/persister-sqlite-wasm/with-schemas/index.js +29 -5
  210. package/persisters/persister-sqlite3/index.js +29 -5
  211. package/persisters/persister-sqlite3/with-schemas/index.js +29 -5
  212. package/persisters/persister-yjs/index.js +29 -5
  213. package/persisters/persister-yjs/with-schemas/index.js +29 -5
  214. package/persisters/with-schemas/index.js +49 -10
  215. package/queries/index.js +205 -74
  216. package/queries/with-schemas/index.js +205 -74
  217. package/readme.md +14 -14
  218. package/releases.md +84 -67
  219. package/synchronizers/index.js +31 -5
  220. package/synchronizers/synchronizer-broadcast-channel/index.js +39 -13
  221. package/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +39 -13
  222. package/synchronizers/synchronizer-local/index.js +31 -5
  223. package/synchronizers/synchronizer-local/with-schemas/index.js +31 -5
  224. package/synchronizers/synchronizer-ws-client/index.js +220 -61
  225. package/synchronizers/synchronizer-ws-client/with-schemas/index.js +220 -61
  226. package/synchronizers/synchronizer-ws-server/index.js +253 -73
  227. package/synchronizers/synchronizer-ws-server/with-schemas/index.js +253 -73
  228. package/synchronizers/synchronizer-ws-server-durable-object/index.js +88 -40
  229. package/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +88 -40
  230. package/synchronizers/synchronizer-ws-server-simple/index.js +184 -60
  231. package/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +184 -60
  232. package/synchronizers/with-schemas/index.js +31 -5
  233. package/ui-react-inspector/index.js +29 -5
  234. package/ui-react-inspector/with-schemas/index.js +29 -5
  235. package/ui-solid-inspector/index.js +29 -5
  236. package/ui-solid-inspector/with-schemas/index.js +29 -5
  237. package/ui-svelte-inspector/index.js +29 -5
  238. package/ui-svelte-inspector/with-schemas/index.js +29 -5
  239. package/with-schemas/index.js +191 -74
@@ -228,6 +228,8 @@ const tryCatch = async (action, onError) => {
228
228
  onError?.(error);
229
229
  }
230
230
  };
231
+ const tryCatchIgnore = (action, onIgnoredError) =>
232
+ tryCatch(action, (error) => void tryCatch(() => onIgnoredError?.(error)));
231
233
  const tryCatchSync = (action, onError) => {
232
234
  try {
233
235
  return action();
@@ -321,6 +323,8 @@ const FRAGMENT_PAYLOAD = /^[^\n]*\n[-0-9A-Z_a-z]{16}\n\d+\n\d+\n/;
321
323
  const INVALID_CHANNEL_ID_CHARACTERS = /[\n\r?#]/;
322
324
  const MAX_FRAGMENT_BUFFERS = 100;
323
325
  const MAX_FRAGMENT_COUNT = 1e3;
326
+ const MAX_MULTIPLE_CHANNELS = 100;
327
+ const MAX_MULTIPLE_CHANNEL_ID_SIZE = 1024;
324
328
  const MAX_PENDING_REQUESTS = 100;
325
329
  const MAX_WEBSOCKET_BUFFER_SIZE = 16777216;
326
330
  const MAX_WEBSOCKET_QUEUE_SIZE = 1e3;
@@ -458,21 +462,30 @@ const createPayloadDecoder = (
458
462
  receive,
459
463
  fragmentTimeoutSeconds = 1,
460
464
  onInvalid,
465
+ sharedBuffer,
461
466
  ) => {
462
467
  const buffer = mapNew();
468
+ const payloadBuffer = sharedBuffer ?? [0, 0];
463
469
  let bufferedSize = 0;
464
470
  let valid = true;
465
471
  const delPending = (bufferKey, pending) => {
466
472
  stopTimeout(pending[4]);
467
473
  bufferedSize -= pending[5];
474
+ payloadBuffer[0]--;
475
+ payloadBuffer[1] -= pending[5];
468
476
  collDel(buffer, bufferKey);
469
477
  };
478
+ const clear = () => {
479
+ mapForEach(buffer, (_bufferKey, pending) => stopTimeout(pending[4]));
480
+ payloadBuffer[0] -= collSize(buffer);
481
+ payloadBuffer[1] -= bufferedSize;
482
+ collClear(buffer);
483
+ bufferedSize = 0;
484
+ };
470
485
  const invalid = (error = errorNew(ERROR_SYNC_MESSAGE)) => {
471
486
  if (valid) {
472
487
  valid = false;
473
- mapForEach(buffer, (_bufferKey, pending) => stopTimeout(pending[4]));
474
- collClear(buffer);
475
- bufferedSize = 0;
488
+ clear();
476
489
  onInvalid?.(error);
477
490
  }
478
491
  };
@@ -484,7 +497,7 @@ const createPayloadDecoder = (
484
497
  invalid();
485
498
  }
486
499
  };
487
- return (payload) => {
500
+ const decode = (payload) => {
488
501
  if (!valid) {
489
502
  return;
490
503
  }
@@ -517,42 +530,52 @@ const createPayloadDecoder = (
517
530
  const bufferKey = clientId + MESSAGE_SEPARATOR + messageId;
518
531
  let pending = mapGet(buffer, bufferKey);
519
532
  if (!pending) {
520
- if (collSize(buffer) >= MAX_FRAGMENT_BUFFERS) {
533
+ if (payloadBuffer[0] >= MAX_FRAGMENT_BUFFERS) {
521
534
  invalid(errorNew(ERROR_SYNC_OVERFLOW, 'fragments'));
522
535
  return;
523
536
  }
524
- pending = [
525
- [],
526
- [],
527
- total,
528
- total,
529
- startTimeout(() => {
530
- const timedOut = mapGet(buffer, bufferKey);
531
- if (timedOut) {
532
- delPending(bufferKey, timedOut);
533
- }
534
- }, fragmentTimeoutSeconds),
535
- 0,
536
- ];
537
- mapSet(buffer, bufferKey, pending);
538
537
  }
539
- const [fragments, remainders] = pending;
540
- if (total == pending[3] && isUndefined(fragments[index])) {
541
- const fragmentLength = getWebSocketPayloadSize(fragment);
542
- if (bufferedSize + fragmentLength > MAX_WEBSOCKET_BUFFER_SIZE) {
538
+ if (
539
+ (isUndefined(pending) || total == pending[3]) &&
540
+ isUndefined(pending?.[0][index])
541
+ ) {
542
+ const retainedSize =
543
+ getWebSocketPayloadSize(remainder) +
544
+ (pending ? 0 : getWebSocketPayloadSize(bufferKey));
545
+ if (payloadBuffer[1] + retainedSize > MAX_WEBSOCKET_BUFFER_SIZE) {
543
546
  invalid(errorNew(ERROR_SYNC_OVERFLOW, 'fragments'));
544
547
  return;
545
548
  }
549
+ if (!pending) {
550
+ pending = [
551
+ [],
552
+ [],
553
+ total,
554
+ total,
555
+ startTimeout(() => {
556
+ const timedOut = mapGet(buffer, bufferKey);
557
+ if (timedOut) {
558
+ delPending(bufferKey, timedOut);
559
+ }
560
+ }, fragmentTimeoutSeconds),
561
+ 0,
562
+ ];
563
+ mapSet(buffer, bufferKey, pending);
564
+ payloadBuffer[0]++;
565
+ }
566
+ const [fragments, remainders] = pending;
546
567
  fragments[index] = fragment;
547
568
  remainders[index] = remainder;
548
569
  pending[2]--;
549
- pending[5] += fragmentLength;
550
- bufferedSize += fragmentLength;
570
+ pending[5] += retainedSize;
571
+ bufferedSize += retainedSize;
572
+ payloadBuffer[1] += retainedSize;
551
573
  } else {
552
574
  invalid();
553
575
  return;
554
576
  }
555
577
  if (pending[2] == 0) {
578
+ const [fragments, remainders] = pending;
556
579
  delPending(bufferKey, pending);
557
580
  receiveRemainder(clientId, remainders, arrayJoin(fragments));
558
581
  }
@@ -565,6 +588,7 @@ const createPayloadDecoder = (
565
588
  invalid();
566
589
  }
567
590
  };
591
+ return [decode, clear];
568
592
  };
569
593
  const isWebSocketBackpressured = (webSocket, payloadSize) =>
570
594
  (webSocket.bufferedAmount ?? 0) + payloadSize > MAX_WEBSOCKET_BUFFER_SIZE;
@@ -572,12 +596,14 @@ const createPayloadReceiver = (
572
596
  receive,
573
597
  fragmentTimeoutSeconds = 1,
574
598
  onInvalid,
599
+ sharedBuffer,
575
600
  ) =>
576
601
  createPayloadDecoder(
577
602
  (fromClientId, _remainders, requestId, message, body) =>
578
603
  receive(fromClientId, requestId, message, body),
579
604
  fragmentTimeoutSeconds,
580
605
  onInvalid,
606
+ sharedBuffer,
581
607
  );
582
608
  const createRawPayload = (clientId, remainder) =>
583
609
  clientId + MESSAGE_SEPARATOR + remainder;
@@ -610,12 +636,15 @@ const getFragments = (remainder, maxFragmentSize) => {
610
636
  };
611
637
  const createMultiplePayload = (channelId, payload) =>
612
638
  createRawPayload(MULTIPLE_CLIENT_ID, createRawPayload(channelId, payload));
613
- const ifMultiplePayloadValid = (payload, then) => {
639
+ const ifMultiplePayloadValid = (payload, then, onOversized) => {
614
640
  let valid = false;
615
641
  ifPayloadValid(payload, (multipleClientId, remainder) => {
616
642
  if (multipleClientId == MULTIPLE_CLIENT_ID) {
617
643
  ifPayloadValid(remainder, (channelId, channelPayload) => {
618
- if (isMultipleChannelIdValid(channelId)) {
644
+ if (isMultipleChannelIdTooLarge(channelId)) {
645
+ valid = true;
646
+ onOversized?.();
647
+ } else if (isMultipleChannelIdStructureValid(channelId)) {
619
648
  valid = true;
620
649
  then(channelId, channelPayload);
621
650
  }
@@ -629,6 +658,15 @@ const createMultipleControlPayload = (requestId, control, body) =>
629
658
  SERVER_CLIENT_ID,
630
659
  jsonStringWithUndefined([requestId, MULTIPLE_MESSAGE, [control, body]]),
631
660
  );
661
+ const isMultipleChannelIdTooLarge = (channelId) =>
662
+ getWebSocketPayloadSize(channelId) > MAX_MULTIPLE_CHANNEL_ID_SIZE;
663
+ const isMultipleChannelIdStructureValid = (channelId) =>
664
+ !isEmpty(channelId) &&
665
+ !INVALID_CHANNEL_ID_CHARACTERS.test(channelId) &&
666
+ arrayEvery(
667
+ strSplit(channelId, '/'),
668
+ (part) => !isEmpty(part) && part != '.' && part != '..',
669
+ );
632
670
  const ifMultipleControlPayloadValid = (payload, then) => {
633
671
  let valid = false;
634
672
  ifPayloadValid(payload, (serverClientId, remainder) => {
@@ -646,11 +684,13 @@ const ifMultipleControlPayloadValid = (payload, then) => {
646
684
  : control == 1 /* Subscribe */
647
685
  ? isString(requestId) &&
648
686
  isString(body) &&
649
- isMultipleChannelIdValid(body)
687
+ (isMultipleChannelIdTooLarge(body) ||
688
+ isMultipleChannelIdStructureValid(body))
650
689
  : control == 2 /* Unsubscribe */ &&
651
690
  isNull(requestId) &&
652
691
  isString(body) &&
653
- isMultipleChannelIdValid(body))
692
+ (isMultipleChannelIdTooLarge(body) ||
693
+ isMultipleChannelIdStructureValid(body)))
654
694
  ) {
655
695
  valid = true;
656
696
  then(requestId, control, body);
@@ -660,12 +700,8 @@ const ifMultipleControlPayloadValid = (payload, then) => {
660
700
  return valid;
661
701
  };
662
702
  const isMultipleChannelIdValid = (channelId) =>
663
- !isEmpty(channelId) &&
664
- !INVALID_CHANNEL_ID_CHARACTERS.test(channelId) &&
665
- arrayEvery(
666
- strSplit(channelId, '/'),
667
- (part) => !isEmpty(part) && part != '.' && part != '..',
668
- );
703
+ !isMultipleChannelIdTooLarge(channelId) &&
704
+ isMultipleChannelIdStructureValid(channelId);
669
705
  const createPayloads = (toClientId, requestId, message, body, fragmentSize) => {
670
706
  const clientId = toClientId ?? EMPTY_STRING;
671
707
  const remainder = jsonStringWithUndefined([requestId, message, body]);
@@ -1138,11 +1174,35 @@ const createCustomPersister = (
1138
1174
  initialContent,
1139
1175
  startSaveFirst = false,
1140
1176
  ) => {
1141
- const [call1, call2] = startSaveFirst
1142
- ? [startAutoSave, startAutoLoad]
1143
- : [startAutoLoad, startAutoSave];
1144
- await call1(initialContent);
1145
- await call2(initialContent);
1177
+ const [call1, call2, stop1, stop2] = startSaveFirst
1178
+ ? [startAutoSave, startAutoLoad, stopAutoSave, stopAutoLoad]
1179
+ : [startAutoLoad, startAutoSave, stopAutoLoad, stopAutoSave];
1180
+ const start1 = call1(initialContent);
1181
+ const generation1 = startSaveFirst
1182
+ ? autoSaveGeneration
1183
+ : autoLoadGeneration;
1184
+ await start1;
1185
+ const start2 = call2(initialContent);
1186
+ const generation2 = startSaveFirst
1187
+ ? autoLoadGeneration
1188
+ : autoSaveGeneration;
1189
+ try {
1190
+ await start2;
1191
+ } catch (error) {
1192
+ if (
1193
+ generation2 ==
1194
+ (startSaveFirst ? autoLoadGeneration : autoSaveGeneration)
1195
+ ) {
1196
+ await tryCatchIgnore(stop2);
1197
+ }
1198
+ if (
1199
+ generation1 ==
1200
+ (startSaveFirst ? autoSaveGeneration : autoLoadGeneration)
1201
+ ) {
1202
+ await tryCatchIgnore(stop1);
1203
+ }
1204
+ throw error;
1205
+ }
1146
1206
  return persister;
1147
1207
  };
1148
1208
  const stopAutoPersisting = async (stopSaveFirst = false) => {
@@ -1543,6 +1603,7 @@ const createConnection = () => {
1543
1603
  const createMultipleState = (webSocket) => {
1544
1604
  const channels = mapNew();
1545
1605
  const pendingControls = mapNew();
1606
+ const payloadBuffer = [0, 0];
1546
1607
  const removeListeners = [];
1547
1608
  let connection = createConnection();
1548
1609
  let connected = false;
@@ -1554,6 +1615,7 @@ const createMultipleState = (webSocket) => {
1554
1615
  let overflowing = false;
1555
1616
  let queuedCount = 0;
1556
1617
  let queuedSize = 0;
1618
+ let receiving = true;
1557
1619
  const notifyChannelIgnoredError = (channel, error) =>
1558
1620
  tryReturn(() => channel[6 /* OnIgnoredError */]?.(error));
1559
1621
  const notifyIgnoredError = (error) =>
@@ -1565,11 +1627,10 @@ const createMultipleState = (webSocket) => {
1565
1627
  disconnect();
1566
1628
  tryReturn(() => webSocket.close());
1567
1629
  };
1568
- const invalid = createInvalidPayloadHandler(webSocket, notifyIgnoredError);
1569
1630
  const addWebSocketListener = (event, handler) =>
1570
1631
  arrayPush(removeListeners, addEventListener(webSocket, event, handler));
1571
1632
  const rejectPendingControls = (error) =>
1572
- mapForEach(pendingControls, (requestId, [, , reject, timeout]) => {
1633
+ mapForEach(pendingControls, (requestId, [, , , reject, timeout]) => {
1573
1634
  stopTimeout(timeout);
1574
1635
  collDel(pendingControls, requestId);
1575
1636
  reject(error);
@@ -1600,11 +1661,43 @@ const createMultipleState = (webSocket) => {
1600
1661
  channel[7 /* Dirty */] = void 0;
1601
1662
  }
1602
1663
  };
1664
+ const clearReceive = (channel) => channel[0 /* Receive */]?.[1]();
1665
+ const clearReceives = () =>
1666
+ mapForEach(channels, (_channelId, channel) => clearReceive(channel));
1667
+ const handleInvalid = createInvalidPayloadHandler(
1668
+ webSocket,
1669
+ notifyIgnoredError,
1670
+ );
1671
+ const invalid = (error) => {
1672
+ if (receiving) {
1673
+ receiving = false;
1674
+ disconnected = true;
1675
+ connected = false;
1676
+ clearReceives();
1677
+ connection[2](error);
1678
+ rejectPendingControls(error);
1679
+ failChannels(error);
1680
+ mapForEach(channels, (_channelId, channel) => {
1681
+ clearIncoming(channel);
1682
+ clearOutgoing(channel, true);
1683
+ channel[3 /* Subscribed */] = false;
1684
+ channel[4 /* Subscribing */] = void 0;
1685
+ channel[12 /* SubscribeConnection */] = void 0;
1686
+ });
1687
+ if (flushTimeout) {
1688
+ stopTimeout(flushTimeout);
1689
+ flushTimeout = void 0;
1690
+ }
1691
+ handleInvalid(error);
1692
+ }
1693
+ };
1603
1694
  const overflow = (details) => {
1604
1695
  const error = errorNew(ERROR_SYNC_OVERFLOW, details);
1605
1696
  if (!overflowing && !destroyed) {
1606
1697
  overflowing = true;
1698
+ receiving = false;
1607
1699
  connected = false;
1700
+ clearReceives();
1608
1701
  notifyIgnoredError(error);
1609
1702
  rejectPendingControls(error);
1610
1703
  failChannels(error);
@@ -1613,6 +1706,7 @@ const createMultipleState = (webSocket) => {
1613
1706
  clearOutgoing(channel, true);
1614
1707
  channel[3 /* Subscribed */] = false;
1615
1708
  channel[4 /* Subscribing */] = void 0;
1709
+ channel[12 /* SubscribeConnection */] = void 0;
1616
1710
  });
1617
1711
  if (flushTimeout) {
1618
1712
  stopTimeout(flushTimeout);
@@ -1634,7 +1728,7 @@ const createMultipleState = (webSocket) => {
1634
1728
  });
1635
1729
  return timeoutSeconds;
1636
1730
  };
1637
- const sendControl = (control, body, timeoutSeconds) => {
1731
+ const sendControl = (control, body, timeoutSeconds, onSent) => {
1638
1732
  const requestId = getUniqueId();
1639
1733
  return promiseNew((resolve, reject) => {
1640
1734
  if (collSize(pendingControls) >= MAX_PENDING_REQUESTS) {
@@ -1654,9 +1748,16 @@ const createMultipleState = (webSocket) => {
1654
1748
  collDel(pendingControls, requestId);
1655
1749
  reject(errorNew(ERROR_MULTIPLEX_RESPONSE, control));
1656
1750
  }, timeoutSeconds);
1657
- mapSet(pendingControls, requestId, [control, resolve, reject, timeout]);
1751
+ mapSet(pendingControls, requestId, [
1752
+ control,
1753
+ body,
1754
+ resolve,
1755
+ reject,
1756
+ timeout,
1757
+ ]);
1658
1758
  try {
1659
1759
  webSocket.send(payload);
1760
+ onSent?.();
1660
1761
  } catch (error) {
1661
1762
  stopTimeout(timeout);
1662
1763
  collDel(pendingControls, requestId);
@@ -1839,23 +1940,45 @@ const createMultipleState = (webSocket) => {
1839
1940
  const subscribe = async (channelId, channel, timeoutSeconds) => {
1840
1941
  if (!channel[3 /* Subscribed */]) {
1841
1942
  let subscribing = channel[4 /* Subscribing */];
1842
- if (!subscribing) {
1843
- subscribing = (async () => {
1844
- await connection[0];
1943
+ const subscribingConnection = connection;
1944
+ if (subscribing?.[0] !== subscribingConnection) {
1945
+ const subscribingPromise = (async () => {
1946
+ await subscribingConnection[0];
1947
+ if (
1948
+ mapGet(channels, channelId) !== channel ||
1949
+ connection !== subscribingConnection ||
1950
+ !connected
1951
+ ) {
1952
+ return;
1953
+ }
1845
1954
  await sendControl(
1846
1955
  MultipleControl.Subscribe,
1847
1956
  channelId,
1848
1957
  timeoutSeconds,
1958
+ () => {
1959
+ if (
1960
+ mapGet(channels, channelId) === channel &&
1961
+ connection === subscribingConnection &&
1962
+ connected
1963
+ ) {
1964
+ channel[12 /* SubscribeConnection */] = subscribingConnection;
1965
+ }
1966
+ },
1849
1967
  );
1850
- if (mapGet(channels, channelId) === channel && connected) {
1968
+ if (
1969
+ mapGet(channels, channelId) === channel &&
1970
+ connection === subscribingConnection &&
1971
+ connected
1972
+ ) {
1851
1973
  channel[3 /* Subscribed */] = true;
1852
1974
  flushOutgoing(channelId, channel);
1853
1975
  }
1854
1976
  })();
1977
+ subscribing = [subscribingConnection, subscribingPromise];
1855
1978
  channel[4 /* Subscribing */] = subscribing;
1856
1979
  }
1857
1980
  try {
1858
- await subscribing;
1981
+ await subscribing[1];
1859
1982
  } finally {
1860
1983
  if (channel[4 /* Subscribing */] === subscribing) {
1861
1984
  channel[4 /* Subscribing */] = void 0;
@@ -1864,7 +1987,7 @@ const createMultipleState = (webSocket) => {
1864
1987
  }
1865
1988
  };
1866
1989
  const onOpen = () => {
1867
- if (destroyed || opening?.[0] === connection) {
1990
+ if (destroyed || !receiving || connected || opening?.[0] === connection) {
1868
1991
  return;
1869
1992
  }
1870
1993
  disconnected = false;
@@ -1877,7 +2000,7 @@ const createMultipleState = (webSocket) => {
1877
2000
  MULTIPLE_VERSION,
1878
2001
  getConnectionTimeoutSeconds(),
1879
2002
  );
1880
- if (connection === openingConnection) {
2003
+ if (connection === openingConnection && receiving && !destroyed) {
1881
2004
  connected = true;
1882
2005
  openingConnection[1]();
1883
2006
  mapForEach(channels, (channelId, channel) =>
@@ -1925,13 +2048,18 @@ const createMultipleState = (webSocket) => {
1925
2048
  }
1926
2049
  connection = createConnection();
1927
2050
  mapForEach(channels, (_channelId, channel) => {
2051
+ clearReceive(channel);
1928
2052
  channel[3 /* Subscribed */] = false;
1929
2053
  channel[4 /* Subscribing */] = void 0;
2054
+ channel[12 /* SubscribeConnection */] = void 0;
1930
2055
  });
1931
2056
  }
1932
2057
  };
1933
2058
  const onClose = disconnect;
1934
2059
  addWebSocketListener(MESSAGE, ({data}) => {
2060
+ if (!receiving) {
2061
+ return;
2062
+ }
1935
2063
  const payload = data.toString(UTF8);
1936
2064
  if (isWebSocketPayloadTooLarge(getWebSocketPayloadSize(payload))) {
1937
2065
  invalid(errorNew(ERROR_SYNC_OVERFLOW, 'socket'));
@@ -1939,14 +2067,23 @@ const createMultipleState = (webSocket) => {
1939
2067
  }
1940
2068
  const control = ifMultipleControlPayloadValid(
1941
2069
  payload,
1942
- (requestId, control2, _body) => {
2070
+ (requestId, control2, body) => {
2071
+ if (
2072
+ (control2 == MultipleControl.Subscribe ||
2073
+ control2 == MultipleControl.Unsubscribe) &&
2074
+ isString(body) &&
2075
+ !isMultipleChannelIdValid(body)
2076
+ ) {
2077
+ overflow('channels');
2078
+ return;
2079
+ }
1943
2080
  const pendingControl = requestId
1944
2081
  ? mapGet(pendingControls, requestId)
1945
2082
  : void 0;
1946
- if (pendingControl?.[0] == control2) {
1947
- stopTimeout(pendingControl[3]);
2083
+ if (pendingControl?.[0] == control2 && pendingControl[1] === body) {
2084
+ stopTimeout(pendingControl[4]);
1948
2085
  collDel(pendingControls, requestId);
1949
- pendingControl[1]();
2086
+ pendingControl[2]();
1950
2087
  }
1951
2088
  },
1952
2089
  );
@@ -1956,7 +2093,7 @@ const createMultipleState = (webSocket) => {
1956
2093
  const channel2 = mapGet(channels, channelId);
1957
2094
  if (channel2) {
1958
2095
  if (channel2[0 /* Receive */]) {
1959
- channel2[0 /* Receive */](channelPayload);
2096
+ channel2[0 /* Receive */][0](channelPayload);
1960
2097
  } else {
1961
2098
  queueIncoming(channel2, channelPayload);
1962
2099
  }
@@ -1964,6 +2101,7 @@ const createMultipleState = (webSocket) => {
1964
2101
  invalid(errorNew(ERROR_SYNC_MESSAGE));
1965
2102
  }
1966
2103
  },
2104
+ () => overflow('channels'),
1967
2105
  );
1968
2106
  if (!control && !channel) {
1969
2107
  invalid(errorNew(ERROR_SYNC_MESSAGE));
@@ -1976,12 +2114,18 @@ const createMultipleState = (webSocket) => {
1976
2114
  failChannels(errorNew(ERROR_MULTIPLEX_SOCKET));
1977
2115
  });
1978
2116
  const addChannel = async (channelId, timeoutSeconds, onIgnoredError) => {
2117
+ if (!receiving || destroyed) {
2118
+ errorThrow(ERROR_MULTIPLEX_SOCKET);
2119
+ }
1979
2120
  if (!isMultipleChannelIdValid(channelId)) {
1980
2121
  errorThrow(ERROR_MULTIPLEX_CHANNEL, channelId);
1981
2122
  }
1982
2123
  if (collHas(channels, channelId)) {
1983
2124
  errorThrow(ERROR_MULTIPLEX_CHANNEL_DUPLICATE, channelId);
1984
2125
  }
2126
+ if (collSize(channels) >= MAX_MULTIPLE_CHANNELS) {
2127
+ errorThrow(ERROR_SYNC_OVERFLOW, 'channels');
2128
+ }
1985
2129
  const channel = [
1986
2130
  void 0,
1987
2131
  [],
@@ -1995,6 +2139,7 @@ const createMultipleState = (webSocket) => {
1995
2139
  0,
1996
2140
  0,
1997
2141
  void 0,
2142
+ void 0,
1998
2143
  ];
1999
2144
  mapSet(channels, channelId, channel);
2000
2145
  if (webSocket.readyState == webSocket.OPEN) {
@@ -2028,7 +2173,7 @@ const createMultipleState = (webSocket) => {
2028
2173
  queuedCount--;
2029
2174
  queuedSize -= payloadSize;
2030
2175
  incomingIndex++;
2031
- receive(payload);
2176
+ receive[0](payload);
2032
2177
  }
2033
2178
  },
2034
2179
  () => arrayClear(incoming, incomingIndex),
@@ -2036,6 +2181,9 @@ const createMultipleState = (webSocket) => {
2036
2181
  }
2037
2182
  };
2038
2183
  const send = (channelId, payloads, coalesce) => {
2184
+ if (!receiving) {
2185
+ return;
2186
+ }
2039
2187
  const channel = mapGet(channels, channelId);
2040
2188
  if (channel) {
2041
2189
  if (
@@ -2056,6 +2204,9 @@ const createMultipleState = (webSocket) => {
2056
2204
  const destroyIfEmpty = () => {
2057
2205
  if (collIsEmpty(channels) && !destroyed) {
2058
2206
  destroyed = true;
2207
+ const closeWebSocket = receiving && !overflowing;
2208
+ receiving = false;
2209
+ clearReceives();
2059
2210
  const error = errorNew(ERROR_MULTIPLEX_DESTROYED);
2060
2211
  rejectPendingControls(error);
2061
2212
  if (flushTimeout) {
@@ -2067,7 +2218,7 @@ const createMultipleState = (webSocket) => {
2067
2218
  );
2068
2219
  arrayClear(removeListeners);
2069
2220
  multipleStates.delete(webSocket);
2070
- if (!overflowing) {
2221
+ if (closeWebSocket) {
2071
2222
  tryReturn(() => webSocket.close());
2072
2223
  }
2073
2224
  }
@@ -2076,9 +2227,10 @@ const createMultipleState = (webSocket) => {
2076
2227
  const channel = mapGet(channels, channelId);
2077
2228
  if (channel) {
2078
2229
  try {
2230
+ clearReceive(channel);
2079
2231
  clearIncoming(channel);
2080
2232
  clearOutgoing(channel, true);
2081
- if (connected && channel[3 /* Subscribed */]) {
2233
+ if (connected && channel[12 /* SubscribeConnection */] === connection) {
2082
2234
  const payload = createMultipleControlPayload(
2083
2235
  null,
2084
2236
  MultipleControl.Unsubscribe,
@@ -2107,6 +2259,7 @@ const createMultipleState = (webSocket) => {
2107
2259
  delChannel,
2108
2260
  destroyIfEmpty,
2109
2261
  invalid,
2262
+ payloadBuffer,
2110
2263
  registerReceive,
2111
2264
  send,
2112
2265
  };
@@ -2162,7 +2315,12 @@ const createMultipleWsSynchronizer = async (
2162
2315
  (receive, fail) =>
2163
2316
  state.registerReceive(
2164
2317
  channelId,
2165
- createPayloadReceiver(receive, requestTimeoutSeconds, state.invalid),
2318
+ createPayloadReceiver(
2319
+ receive,
2320
+ requestTimeoutSeconds,
2321
+ state.invalid,
2322
+ state.payloadBuffer,
2323
+ ),
2166
2324
  fail,
2167
2325
  ),
2168
2326
  () => state.delChannel(channelId),
@@ -2204,13 +2362,14 @@ const createLegacyWsSynchronizer = async (
2204
2362
  };
2205
2363
  const registerReceive = (receive, fail) => {
2206
2364
  const invalid = createInvalidPayloadHandler(webSocket, notifyIgnoredError);
2207
- const receivePayload = createPayloadReceiver(
2365
+ const [receivePayload, clearReceivePayload] = createPayloadReceiver(
2208
2366
  receive,
2209
2367
  requestTimeoutSeconds,
2210
2368
  invalid,
2211
2369
  );
2212
2370
  let removeListeners = [];
2213
2371
  removeTransportListeners = () => {
2372
+ clearReceivePayload();
2214
2373
  arrayForEach(removeListeners, (removeListener) =>
2215
2374
  tryReturn(removeListener),
2216
2375
  );