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
@@ -113,6 +113,8 @@ const tryCatch = async (action, onError) => {
113
113
  onError?.(error);
114
114
  }
115
115
  };
116
+ const tryCatchIgnore = (action, onIgnoredError) =>
117
+ tryCatch(action, (error) => void tryCatch(() => onIgnoredError?.(error)));
116
118
  const tryCatchSync = (action, onError) => {
117
119
  try {
118
120
  return action();
@@ -434,6 +436,9 @@ const FRAGMENT_PAYLOAD = /^[^\n]*\n[-0-9A-Z_a-z]{16}\n\d+\n\d+\n/;
434
436
  const INVALID_CHANNEL_ID_CHARACTERS = /[\n\r?#]/;
435
437
  const MAX_FRAGMENT_BUFFERS = 100;
436
438
  const MAX_FRAGMENT_COUNT = 1e3;
439
+ const MAX_MULTIPLE_CHANNELS = 100;
440
+ const MAX_MULTIPLE_CHANNEL_RESOURCES = MAX_MULTIPLE_CHANNELS * 2;
441
+ const MAX_MULTIPLE_CHANNEL_ID_SIZE = 1024;
437
442
  const MAX_PENDING_REQUESTS = 100;
438
443
  const MAX_WEBSOCKET_BUFFER_SIZE = 16777216;
439
444
  const MAX_WEBSOCKET_QUEUE_SIZE = 1e3;
@@ -566,21 +571,30 @@ const createPayloadDecoder = (
566
571
  receive,
567
572
  fragmentTimeoutSeconds = 1,
568
573
  onInvalid,
574
+ sharedBuffer,
569
575
  ) => {
570
576
  const buffer = mapNew();
577
+ const payloadBuffer = sharedBuffer ?? [0, 0];
571
578
  let bufferedSize = 0;
572
579
  let valid = true;
573
580
  const delPending = (bufferKey, pending) => {
574
581
  stopTimeout(pending[4]);
575
582
  bufferedSize -= pending[5];
583
+ payloadBuffer[0]--;
584
+ payloadBuffer[1] -= pending[5];
576
585
  collDel(buffer, bufferKey);
577
586
  };
587
+ const clear = () => {
588
+ mapForEach(buffer, (_bufferKey, pending) => stopTimeout(pending[4]));
589
+ payloadBuffer[0] -= collSize(buffer);
590
+ payloadBuffer[1] -= bufferedSize;
591
+ collClear(buffer);
592
+ bufferedSize = 0;
593
+ };
578
594
  const invalid = (error = errorNew(ERROR_SYNC_MESSAGE)) => {
579
595
  if (valid) {
580
596
  valid = false;
581
- mapForEach(buffer, (_bufferKey, pending) => stopTimeout(pending[4]));
582
- collClear(buffer);
583
- bufferedSize = 0;
597
+ clear();
584
598
  onInvalid?.(error);
585
599
  }
586
600
  };
@@ -592,7 +606,7 @@ const createPayloadDecoder = (
592
606
  invalid();
593
607
  }
594
608
  };
595
- return (payload) => {
609
+ const decode = (payload) => {
596
610
  if (!valid) {
597
611
  return;
598
612
  }
@@ -625,42 +639,52 @@ const createPayloadDecoder = (
625
639
  const bufferKey = clientId + MESSAGE_SEPARATOR + messageId;
626
640
  let pending = mapGet(buffer, bufferKey);
627
641
  if (!pending) {
628
- if (collSize(buffer) >= MAX_FRAGMENT_BUFFERS) {
642
+ if (payloadBuffer[0] >= MAX_FRAGMENT_BUFFERS) {
629
643
  invalid(errorNew(ERROR_SYNC_OVERFLOW, 'fragments'));
630
644
  return;
631
645
  }
632
- pending = [
633
- [],
634
- [],
635
- total,
636
- total,
637
- startTimeout(() => {
638
- const timedOut = mapGet(buffer, bufferKey);
639
- if (timedOut) {
640
- delPending(bufferKey, timedOut);
641
- }
642
- }, fragmentTimeoutSeconds),
643
- 0,
644
- ];
645
- mapSet(buffer, bufferKey, pending);
646
646
  }
647
- const [fragments, remainders] = pending;
648
- if (total == pending[3] && isUndefined(fragments[index])) {
649
- const fragmentLength = getWebSocketPayloadSize(fragment);
650
- if (bufferedSize + fragmentLength > MAX_WEBSOCKET_BUFFER_SIZE) {
647
+ if (
648
+ (isUndefined(pending) || total == pending[3]) &&
649
+ isUndefined(pending?.[0][index])
650
+ ) {
651
+ const retainedSize =
652
+ getWebSocketPayloadSize(remainder) +
653
+ (pending ? 0 : getWebSocketPayloadSize(bufferKey));
654
+ if (payloadBuffer[1] + retainedSize > MAX_WEBSOCKET_BUFFER_SIZE) {
651
655
  invalid(errorNew(ERROR_SYNC_OVERFLOW, 'fragments'));
652
656
  return;
653
657
  }
658
+ if (!pending) {
659
+ pending = [
660
+ [],
661
+ [],
662
+ total,
663
+ total,
664
+ startTimeout(() => {
665
+ const timedOut = mapGet(buffer, bufferKey);
666
+ if (timedOut) {
667
+ delPending(bufferKey, timedOut);
668
+ }
669
+ }, fragmentTimeoutSeconds),
670
+ 0,
671
+ ];
672
+ mapSet(buffer, bufferKey, pending);
673
+ payloadBuffer[0]++;
674
+ }
675
+ const [fragments, remainders] = pending;
654
676
  fragments[index] = fragment;
655
677
  remainders[index] = remainder;
656
678
  pending[2]--;
657
- pending[5] += fragmentLength;
658
- bufferedSize += fragmentLength;
679
+ pending[5] += retainedSize;
680
+ bufferedSize += retainedSize;
681
+ payloadBuffer[1] += retainedSize;
659
682
  } else {
660
683
  invalid();
661
684
  return;
662
685
  }
663
686
  if (pending[2] == 0) {
687
+ const [fragments, remainders] = pending;
664
688
  delPending(bufferKey, pending);
665
689
  receiveRemainder(clientId, remainders, arrayJoin(fragments));
666
690
  }
@@ -673,6 +697,7 @@ const createPayloadDecoder = (
673
697
  invalid();
674
698
  }
675
699
  };
700
+ return [decode, clear];
676
701
  };
677
702
  const getPayloadCoalesceKey = (clientId, payload) => {
678
703
  let key;
@@ -689,12 +714,14 @@ const createPayloadReceiver = (
689
714
  receive,
690
715
  fragmentTimeoutSeconds = 1,
691
716
  onInvalid,
717
+ sharedBuffer,
692
718
  ) =>
693
719
  createPayloadDecoder(
694
720
  (fromClientId, _remainders, requestId, message, body) =>
695
721
  receive(fromClientId, requestId, message, body),
696
722
  fragmentTimeoutSeconds,
697
723
  onInvalid,
724
+ sharedBuffer,
698
725
  );
699
726
  const createRawPayload = (clientId, remainder) =>
700
727
  clientId + MESSAGE_SEPARATOR + remainder;
@@ -727,12 +754,15 @@ const getFragments = (remainder, maxFragmentSize) => {
727
754
  };
728
755
  const createMultiplePayload = (channelId, payload) =>
729
756
  createRawPayload(MULTIPLE_CLIENT_ID, createRawPayload(channelId, payload));
730
- const ifMultiplePayloadValid = (payload, then) => {
757
+ const ifMultiplePayloadValid = (payload, then, onOversized) => {
731
758
  let valid = false;
732
759
  ifPayloadValid(payload, (multipleClientId, remainder) => {
733
760
  if (multipleClientId == MULTIPLE_CLIENT_ID) {
734
761
  ifPayloadValid(remainder, (channelId, channelPayload) => {
735
- if (isMultipleChannelIdValid(channelId)) {
762
+ if (isMultipleChannelIdTooLarge(channelId)) {
763
+ valid = true;
764
+ onOversized?.();
765
+ } else if (isMultipleChannelIdStructureValid(channelId)) {
736
766
  valid = true;
737
767
  then(channelId, channelPayload);
738
768
  }
@@ -746,6 +776,15 @@ const createMultipleControlPayload = (requestId, control, body) =>
746
776
  SERVER_CLIENT_ID,
747
777
  jsonStringWithUndefined([requestId, MULTIPLE_MESSAGE, [control, body]]),
748
778
  );
779
+ const isMultipleChannelIdTooLarge = (channelId) =>
780
+ getWebSocketPayloadSize(channelId) > MAX_MULTIPLE_CHANNEL_ID_SIZE;
781
+ const isMultipleChannelIdStructureValid = (channelId) =>
782
+ !isEmpty(channelId) &&
783
+ !INVALID_CHANNEL_ID_CHARACTERS.test(channelId) &&
784
+ arrayEvery(
785
+ strSplit(channelId, '/'),
786
+ (part) => !isEmpty(part) && part != '.' && part != '..',
787
+ );
749
788
  const ifMultipleControlPayloadValid = (payload, then) => {
750
789
  let valid = false;
751
790
  ifPayloadValid(payload, (serverClientId, remainder) => {
@@ -763,11 +802,13 @@ const ifMultipleControlPayloadValid = (payload, then) => {
763
802
  : control == 1 /* Subscribe */
764
803
  ? isString(requestId) &&
765
804
  isString(body) &&
766
- isMultipleChannelIdValid(body)
805
+ (isMultipleChannelIdTooLarge(body) ||
806
+ isMultipleChannelIdStructureValid(body))
767
807
  : control == 2 /* Unsubscribe */ &&
768
808
  isNull(requestId) &&
769
809
  isString(body) &&
770
- isMultipleChannelIdValid(body))
810
+ (isMultipleChannelIdTooLarge(body) ||
811
+ isMultipleChannelIdStructureValid(body)))
771
812
  ) {
772
813
  valid = true;
773
814
  then(requestId, control, body);
@@ -776,13 +817,6 @@ const ifMultipleControlPayloadValid = (payload, then) => {
776
817
  });
777
818
  return valid;
778
819
  };
779
- const isMultipleChannelIdValid = (channelId) =>
780
- !isEmpty(channelId) &&
781
- !INVALID_CHANNEL_ID_CHARACTERS.test(channelId) &&
782
- arrayEvery(
783
- strSplit(channelId, '/'),
784
- (part) => !isEmpty(part) && part != '.' && part != '..',
785
- );
786
820
  const createMultipleServerClient = (
787
821
  basePathId,
788
822
  addChannel,
@@ -795,26 +829,87 @@ const createMultipleServerClient = (
795
829
  ) => {
796
830
  const channels = mapNew();
797
831
  const decoders = mapNew();
832
+ const payloadBuffer = [0, 0];
833
+ const resources = setNew();
834
+ let resourceCount = 0;
798
835
  let negotiated = false;
836
+ let valid = true;
799
837
  const sendControl = (requestId, control, body) =>
800
838
  send(createMultipleControlPayload(requestId, control, body));
801
839
  const delChannelAndDecoder = (channelId) => {
802
840
  collDel(channels, channelId);
841
+ mapGet(decoders, channelId)?.[1]();
803
842
  collDel(decoders, channelId);
804
843
  };
844
+ const clearDecoders = () => {
845
+ mapForEach(decoders, (_channelId, decoder) => decoder[1]());
846
+ collClear(decoders);
847
+ };
848
+ const invalidate = (error) => {
849
+ if (valid) {
850
+ valid = false;
851
+ clearDecoders();
852
+ invalid(error);
853
+ }
854
+ };
855
+ const releaseChannel = (channel) => {
856
+ if (!channel[2 /* Released */]) {
857
+ channel[2 /* Released */] = 1;
858
+ resourceCount--;
859
+ collDel(resources, channel);
860
+ }
861
+ };
862
+ const teardownChannel = (channel) =>
863
+ (channel[1 /* Teardown */] ??= (async () => {
864
+ try {
865
+ const teardown = delChannel(channel[0 /* Channel */]);
866
+ if (isUndefined(teardown)) {
867
+ releaseChannel(channel);
868
+ } else {
869
+ await teardown;
870
+ }
871
+ } finally {
872
+ releaseChannel(channel);
873
+ }
874
+ })());
805
875
  const handleControl = (requestId, control, body) => {
806
876
  if (control == 0 /* Hello */) {
807
877
  negotiated = true;
808
878
  sendControl(requestId, control, body);
809
879
  } else if (negotiated && control == 1 /* Subscribe */) {
810
- if (!collHas(channels, body)) {
880
+ const subscribed = collHas(channels, body);
881
+ if (
882
+ isMultipleChannelIdTooLarge(body) ||
883
+ (!subscribed &&
884
+ (collSize(channels) >= MAX_MULTIPLE_CHANNELS ||
885
+ resourceCount >= MAX_MULTIPLE_CHANNEL_RESOURCES))
886
+ ) {
887
+ invalidate(errorNew(ERROR_SYNC_OVERFLOW, 'channels'));
888
+ return;
889
+ }
890
+ if (!subscribed) {
811
891
  const pathId = basePathId + (basePathId ? '/' : EMPTY_STRING) + body;
812
- const [channel, ready] = addChannel(pathId, body);
892
+ resourceCount++;
893
+ let channelAndReady;
894
+ try {
895
+ channelAndReady = addChannel(pathId, body);
896
+ } catch (error) {
897
+ resourceCount--;
898
+ throw error;
899
+ }
900
+ const [addedChannel, ready] = channelAndReady;
901
+ const channel = [addedChannel];
902
+ setAdd(resources, channel);
813
903
  mapSet(channels, body, channel);
814
904
  sendControl(requestId, control, body);
815
905
  if (ready) {
816
906
  return ready.catch((error) => {
817
- delChannelAndDecoder(body);
907
+ if (mapGet(channels, body) === channel) {
908
+ delChannelAndDecoder(body);
909
+ }
910
+ if (!channel[1 /* Teardown */]) {
911
+ releaseChannel(channel);
912
+ }
818
913
  throw error;
819
914
  });
820
915
  }
@@ -822,16 +917,21 @@ const createMultipleServerClient = (
822
917
  }
823
918
  sendControl(requestId, control, body);
824
919
  } else if (negotiated && control == 2 /* Unsubscribe */) {
920
+ if (isMultipleChannelIdTooLarge(body)) {
921
+ invalidate(errorNew(ERROR_SYNC_OVERFLOW, 'channels'));
922
+ return;
923
+ }
825
924
  const channel = mapGet(channels, body);
826
925
  delChannelAndDecoder(body);
827
- if (!isUndefined(channel)) {
828
- return (async () => delChannel(channel))();
829
- }
926
+ return isUndefined(channel) ? void 0 : teardownChannel(channel);
830
927
  }
831
928
  };
832
929
  const handlePayload = (payload) => {
930
+ if (!valid) {
931
+ return;
932
+ }
833
933
  if (isWebSocketPayloadTooLarge(getWebSocketPayloadSize(payload))) {
834
- invalid(errorNew(ERROR_SYNC_OVERFLOW, 'socket'));
934
+ invalidate(errorNew(ERROR_SYNC_OVERFLOW, 'socket'));
835
935
  return;
836
936
  }
837
937
  const control = ifMultipleControlPayloadValid(
@@ -846,32 +946,54 @@ const createMultipleServerClient = (
846
946
  (channelId, channelPayload) => {
847
947
  const channel2 = negotiated ? mapGet(channels, channelId) : void 0;
848
948
  if (isUndefined(channel2)) {
849
- invalid(errorNew(ERROR_SYNC_MESSAGE));
949
+ invalidate(errorNew(ERROR_SYNC_MESSAGE));
850
950
  } else {
851
951
  mapEnsure(decoders, channelId, () =>
852
952
  createPayloadDecoder(
853
953
  (toClientId, remainders) =>
854
- receive(channel2, toClientId, remainders),
954
+ receive(channel2[0 /* Channel */], toClientId, remainders),
855
955
  fragmentTimeoutSeconds,
856
- invalid,
956
+ invalidate,
957
+ payloadBuffer,
857
958
  ),
858
- )(channelPayload);
959
+ )[0](channelPayload);
859
960
  }
860
961
  },
962
+ () => invalidate(errorNew(ERROR_SYNC_OVERFLOW, 'channels')),
861
963
  );
862
964
  if (!control && !channel) {
863
- invalid(errorNew(ERROR_SYNC_MESSAGE));
965
+ invalidate(errorNew(ERROR_SYNC_MESSAGE));
864
966
  }
865
967
  };
866
- const destroy = () =>
867
- tryFinallyAsync(
868
- () =>
869
- promiseAll(mapMap(channels, async (channel) => delChannel(channel))),
968
+ const destroy = () => {
969
+ valid = false;
970
+ clearDecoders();
971
+ return tryFinallyAsync(
972
+ async () => {
973
+ let errorToThrow;
974
+ let failed = false;
975
+ await promiseAll(
976
+ arrayMap(collValues(resources), async (channel) => {
977
+ try {
978
+ await teardownChannel(channel);
979
+ } catch (error) {
980
+ if (!failed) {
981
+ errorToThrow = error;
982
+ failed = true;
983
+ }
984
+ }
985
+ }),
986
+ );
987
+ if (failed) {
988
+ throw errorToThrow;
989
+ }
990
+ },
870
991
  () => {
871
992
  collClear(channels);
872
- collClear(decoders);
993
+ clearDecoders();
873
994
  },
874
995
  );
996
+ };
875
997
  return [handlePayload, destroy];
876
998
  };
877
999
  const createPayloads = (toClientId, requestId, message, body, fragmentSize) => {
@@ -1233,11 +1355,35 @@ const createCustomPersister = (
1233
1355
  initialContent,
1234
1356
  startSaveFirst = false,
1235
1357
  ) => {
1236
- const [call1, call2] = startSaveFirst
1237
- ? [startAutoSave, startAutoLoad]
1238
- : [startAutoLoad, startAutoSave];
1239
- await call1(initialContent);
1240
- await call2(initialContent);
1358
+ const [call1, call2, stop1, stop2] = startSaveFirst
1359
+ ? [startAutoSave, startAutoLoad, stopAutoSave, stopAutoLoad]
1360
+ : [startAutoLoad, startAutoSave, stopAutoLoad, stopAutoSave];
1361
+ const start1 = call1(initialContent);
1362
+ const generation1 = startSaveFirst
1363
+ ? autoSaveGeneration
1364
+ : autoLoadGeneration;
1365
+ await start1;
1366
+ const start2 = call2(initialContent);
1367
+ const generation2 = startSaveFirst
1368
+ ? autoLoadGeneration
1369
+ : autoSaveGeneration;
1370
+ try {
1371
+ await start2;
1372
+ } catch (error) {
1373
+ if (
1374
+ generation2 ==
1375
+ (startSaveFirst ? autoLoadGeneration : autoSaveGeneration)
1376
+ ) {
1377
+ await tryCatchIgnore(stop2);
1378
+ }
1379
+ if (
1380
+ generation1 ==
1381
+ (startSaveFirst ? autoSaveGeneration : autoLoadGeneration)
1382
+ ) {
1383
+ await tryCatchIgnore(stop1);
1384
+ }
1385
+ throw error;
1386
+ }
1241
1387
  return persister;
1242
1388
  };
1243
1389
  const stopAutoPersisting = async (stopSaveFirst = false) => {
@@ -1630,6 +1776,7 @@ const createWsServer = (
1630
1776
  const clientIdListeners = mapNew();
1631
1777
  const paths = mapNew();
1632
1778
  const webSockets = setNew();
1779
+ const buffersByWebSocket = mapNew();
1633
1780
  const removeServerListeners = [];
1634
1781
  const removeListenersByWebSocket = mapNew();
1635
1782
  let destroying;
@@ -1679,7 +1826,7 @@ const createWsServer = (
1679
1826
  serverClient[3 /* Send */] = createPayloadReceiver(
1680
1827
  receive,
1681
1828
  requestTimeoutSeconds,
1682
- );
1829
+ )[0];
1683
1830
  },
1684
1831
  noop,
1685
1832
  requestTimeoutSeconds,
@@ -1743,18 +1890,37 @@ const createWsServer = (
1743
1890
  client.send(outgoingPayload);
1744
1891
  }
1745
1892
  };
1893
+ const updateWebSocketBuffer = (webSocket, count, size2) => {
1894
+ const webSocketBuffer = mapEnsure(buffersByWebSocket, webSocket, () => [
1895
+ 0, 0,
1896
+ ]);
1897
+ webSocketBuffer[0] += count;
1898
+ webSocketBuffer[1] += size2;
1899
+ if (!webSocketBuffer[0]) {
1900
+ mapSet(buffersByWebSocket, webSocket);
1901
+ }
1902
+ };
1903
+ const delBufferedFromWebSocket = ([, payload, , webSocket]) =>
1904
+ updateWebSocketBuffer(webSocket, -1, -getWebSocketPayloadSize(payload));
1746
1905
  const clearServerBuffer = (serverClient) => {
1747
1906
  if (serverClient[7 /* BufferTimeout */]) {
1748
1907
  stopTimeout(serverClient[7 /* BufferTimeout */]);
1749
1908
  serverClient[7 /* BufferTimeout */] = void 0;
1750
1909
  }
1910
+ arrayForEach(serverClient[4 /* Buffer */], delBufferedFromWebSocket);
1751
1911
  serverClient[4 /* Buffer */] = [];
1752
1912
  serverClient[6 /* BufferSize */] = 0;
1753
1913
  };
1754
1914
  const delBufferedClient = (serverClient, clientId) => {
1755
1915
  serverClient[4 /* Buffer */] = arrayFilter(
1756
1916
  serverClient[4 /* Buffer */],
1757
- ([bufferedClientId]) => bufferedClientId != clientId,
1917
+ (buffered) => {
1918
+ if (buffered[0] == clientId) {
1919
+ delBufferedFromWebSocket(buffered);
1920
+ return false;
1921
+ }
1922
+ return true;
1923
+ },
1758
1924
  );
1759
1925
  serverClient[6 /* BufferSize */] = arrayReduce(
1760
1926
  serverClient[4 /* Buffer */],
@@ -1768,10 +1934,8 @@ const createWsServer = (
1768
1934
  const expireServerBuffer = (path) => {
1769
1935
  const serverClient = path[1 /* ServerClient */];
1770
1936
  const clients = setNew();
1771
- arrayForEach(serverClient[4 /* Buffer */], ([clientId]) =>
1772
- ifNotUndefined(mapGet(path[0 /* Clients */], clientId), ([client]) =>
1773
- setAdd(clients, client),
1774
- ),
1937
+ arrayForEach(serverClient[4 /* Buffer */], ([, , , client]) =>
1938
+ setAdd(clients, client),
1775
1939
  );
1776
1940
  clearServerBuffer(serverClient);
1777
1941
  arrayForEach(collValues(clients), (client) =>
@@ -1781,6 +1945,10 @@ const createWsServer = (
1781
1945
  const bufferMessage = (path, clientId, payload) => {
1782
1946
  const serverClient = path[1 /* ServerClient */];
1783
1947
  const buffer = serverClient[4 /* Buffer */];
1948
+ const webSocket = mapGet(path[0 /* Clients */], clientId)?.[0];
1949
+ if (isUndefined(webSocket)) {
1950
+ return;
1951
+ }
1784
1952
  const coalesceKey = getPayloadCoalesceKey(clientId, payload);
1785
1953
  const coalesceIndex = coalesceKey
1786
1954
  ? arrayFindIndex(
@@ -1788,14 +1956,22 @@ const createWsServer = (
1788
1956
  ([, , bufferedKey]) => bufferedKey == coalesceKey,
1789
1957
  )
1790
1958
  : -1;
1791
- const replacedSize =
1792
- coalesceIndex > -1
1793
- ? getWebSocketPayloadSize(buffer[coalesceIndex][1])
1794
- : 0;
1959
+ const replaced = coalesceIndex > -1 ? buffer[coalesceIndex] : void 0;
1960
+ const replacedSize = isUndefined(replaced)
1961
+ ? 0
1962
+ : getWebSocketPayloadSize(replaced[1]);
1795
1963
  const payloadSize = getWebSocketPayloadSize(payload);
1964
+ const webSocketBuffer = mapGet(buffersByWebSocket, webSocket);
1965
+ const sameWebSocket = replaced?.[3] === webSocket;
1796
1966
  if (
1797
1967
  (coalesceIndex < 0 && size(buffer) >= MAX_WEBSOCKET_QUEUE_SIZE) ||
1798
1968
  serverClient[6 /* BufferSize */] + payloadSize - replacedSize >
1969
+ MAX_WEBSOCKET_BUFFER_SIZE ||
1970
+ (!sameWebSocket &&
1971
+ (webSocketBuffer?.[0] ?? 0) >= MAX_WEBSOCKET_QUEUE_SIZE) ||
1972
+ (webSocketBuffer?.[1] ?? 0) +
1973
+ payloadSize -
1974
+ (sameWebSocket ? replacedSize : 0) >
1799
1975
  MAX_WEBSOCKET_BUFFER_SIZE
1800
1976
  ) {
1801
1977
  delBufferedClient(serverClient, clientId);
@@ -1804,12 +1980,14 @@ const createWsServer = (
1804
1980
  );
1805
1981
  return;
1806
1982
  }
1807
- const buffered = [clientId, payload, coalesceKey];
1983
+ const buffered = [clientId, payload, coalesceKey, webSocket];
1808
1984
  if (coalesceIndex > -1) {
1985
+ delBufferedFromWebSocket(buffer[coalesceIndex]);
1809
1986
  buffer[coalesceIndex] = buffered;
1810
1987
  } else {
1811
1988
  arrayPush(buffer, buffered);
1812
1989
  }
1990
+ updateWebSocketBuffer(webSocket, 1, payloadSize);
1813
1991
  serverClient[6 /* BufferSize */] += payloadSize - replacedSize;
1814
1992
  serverClient[7 /* BufferTimeout */] ??= startTimeout(
1815
1993
  () => expireServerBuffer(path),
@@ -1968,7 +2146,7 @@ const createWsServer = (
1968
2146
  };
1969
2147
  const addLegacyClient = async (client, clientId, pathId) => {
1970
2148
  const [path, ready] = addClientToPath(pathId, clientId, [client]);
1971
- const decode = createPayloadDecoder(
2149
+ const [decode, clearDecoder] = createPayloadDecoder(
1972
2150
  (toClientId, remainders) =>
1973
2151
  handleDecodedMessage(path, clientId, toClientId, remainders),
1974
2152
  requestTimeoutSeconds,
@@ -1977,9 +2155,10 @@ const createWsServer = (
1977
2155
  addWebSocketListener(client, MESSAGE, (data) =>
1978
2156
  decode(data.toString(UTF8)),
1979
2157
  );
1980
- addWebSocketListener(client, CLOSE, () =>
1981
- delClientFromPath(pathId, path, clientId).catch(handleError),
1982
- );
2158
+ addWebSocketListener(client, CLOSE, () => {
2159
+ clearDecoder();
2160
+ delClientFromPath(pathId, path, clientId).catch(handleError);
2161
+ });
1983
2162
  await ready;
1984
2163
  };
1985
2164
  const addMultipleClient = (client, clientId, basePathId) => {
@@ -2124,6 +2303,7 @@ const createWsServer = (
2124
2303
  arrayForEach(removers, (remove) => tryReturn(remove)),
2125
2304
  );
2126
2305
  collClear(removeListenersByWebSocket);
2306
+ collClear(buffersByWebSocket);
2127
2307
  collClear(paths);
2128
2308
  collClear(webSockets);
2129
2309
  const failure = serverFailure[0]