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
@@ -62,6 +62,7 @@ const arrayPush = (array, ...values) => array.push(...values);
62
62
  const collSize = (coll) => coll?.size ?? 0;
63
63
  const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
64
64
  const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
65
+ const collValues = (coll) => [...(coll?.values() ?? [])];
65
66
  const collClear = (coll) => coll.clear();
66
67
  const collForEach = (coll, cb) => coll?.forEach(cb);
67
68
  const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
@@ -153,11 +154,6 @@ const mapKeys = (map2) => [...(map2?.keys() ?? [])];
153
154
  const mapGet = (map2, key) => map2?.get(key);
154
155
  const mapForEach = (map2, cb) =>
155
156
  collForEach(map2, (value, key) => cb(key, value));
156
- const mapMap = (coll, cb) => {
157
- const mapped = [];
158
- mapForEach(coll, (key, value) => arrayPush(mapped, cb(value, key)));
159
- return mapped;
160
- };
161
157
  const mapSet = (map2, key, value) =>
162
158
  isUndefined(value) ? (collDel(map2, key), map2) : map2?.set(key, value);
163
159
  const mapEnsure = (map2, key, getDefaultValue, hadExistingValue) => {
@@ -231,12 +227,23 @@ const isHlc = (hlc, maxLogicalTime) =>
231
227
  hlc == EMPTY_STRING ||
232
228
  (isString(hlc) && test(HLC, hlc) && decodeHlcTime(hlc) <= maxLogicalTime);
233
229
 
230
+ const setNew = (entryOrEntries) =>
231
+ new Set(
232
+ isArray(entryOrEntries) || isUndefined(entryOrEntries)
233
+ ? entryOrEntries
234
+ : [entryOrEntries],
235
+ );
236
+ const setAdd = (set, value) => set?.add(value);
237
+
234
238
  const MESSAGE_SEPARATOR = '\n';
235
239
  const FRAGMENT = /^([-0-9A-Z_a-z]{16})\n(\d+)\n(\d+)\n([\s\S]*)$/;
236
240
  const FRAGMENT_PAYLOAD = /^[^\n]*\n[-0-9A-Z_a-z]{16}\n\d+\n\d+\n/;
237
241
  const INVALID_CHANNEL_ID_CHARACTERS = /[\n\r?#]/;
238
242
  const MAX_FRAGMENT_BUFFERS = 100;
239
243
  const MAX_FRAGMENT_COUNT = 1e3;
244
+ const MAX_MULTIPLE_CHANNELS = 100;
245
+ const MAX_MULTIPLE_CHANNEL_RESOURCES = MAX_MULTIPLE_CHANNELS * 2;
246
+ const MAX_MULTIPLE_CHANNEL_ID_SIZE = 1024;
240
247
  const MAX_WEBSOCKET_BUFFER_SIZE = 16777216;
241
248
  const getWebSocketPayloadSize = (value) => {
242
249
  let byteSize = 0;
@@ -367,21 +374,30 @@ const createPayloadDecoder = (
367
374
  receive,
368
375
  fragmentTimeoutSeconds = 1,
369
376
  onInvalid,
377
+ sharedBuffer,
370
378
  ) => {
371
379
  const buffer = mapNew();
380
+ const payloadBuffer = sharedBuffer ?? [0, 0];
372
381
  let bufferedSize = 0;
373
382
  let valid = true;
374
383
  const delPending = (bufferKey, pending) => {
375
384
  stopTimeout(pending[4]);
376
385
  bufferedSize -= pending[5];
386
+ payloadBuffer[0]--;
387
+ payloadBuffer[1] -= pending[5];
377
388
  collDel(buffer, bufferKey);
378
389
  };
390
+ const clear = () => {
391
+ mapForEach(buffer, (_bufferKey, pending) => stopTimeout(pending[4]));
392
+ payloadBuffer[0] -= collSize(buffer);
393
+ payloadBuffer[1] -= bufferedSize;
394
+ collClear(buffer);
395
+ bufferedSize = 0;
396
+ };
379
397
  const invalid = (error = errorNew(ERROR_SYNC_MESSAGE)) => {
380
398
  if (valid) {
381
399
  valid = false;
382
- mapForEach(buffer, (_bufferKey, pending) => stopTimeout(pending[4]));
383
- collClear(buffer);
384
- bufferedSize = 0;
400
+ clear();
385
401
  onInvalid?.(error);
386
402
  }
387
403
  };
@@ -393,7 +409,7 @@ const createPayloadDecoder = (
393
409
  invalid();
394
410
  }
395
411
  };
396
- return (payload) => {
412
+ const decode = (payload) => {
397
413
  if (!valid) {
398
414
  return;
399
415
  }
@@ -426,42 +442,52 @@ const createPayloadDecoder = (
426
442
  const bufferKey = clientId + MESSAGE_SEPARATOR + messageId;
427
443
  let pending = mapGet(buffer, bufferKey);
428
444
  if (!pending) {
429
- if (collSize(buffer) >= MAX_FRAGMENT_BUFFERS) {
445
+ if (payloadBuffer[0] >= MAX_FRAGMENT_BUFFERS) {
430
446
  invalid(errorNew(ERROR_SYNC_OVERFLOW, 'fragments'));
431
447
  return;
432
448
  }
433
- pending = [
434
- [],
435
- [],
436
- total,
437
- total,
438
- startTimeout(() => {
439
- const timedOut = mapGet(buffer, bufferKey);
440
- if (timedOut) {
441
- delPending(bufferKey, timedOut);
442
- }
443
- }, fragmentTimeoutSeconds),
444
- 0,
445
- ];
446
- mapSet(buffer, bufferKey, pending);
447
449
  }
448
- const [fragments, remainders] = pending;
449
- if (total == pending[3] && isUndefined(fragments[index])) {
450
- const fragmentLength = getWebSocketPayloadSize(fragment);
451
- if (bufferedSize + fragmentLength > MAX_WEBSOCKET_BUFFER_SIZE) {
450
+ if (
451
+ (isUndefined(pending) || total == pending[3]) &&
452
+ isUndefined(pending?.[0][index])
453
+ ) {
454
+ const retainedSize =
455
+ getWebSocketPayloadSize(remainder) +
456
+ (pending ? 0 : getWebSocketPayloadSize(bufferKey));
457
+ if (payloadBuffer[1] + retainedSize > MAX_WEBSOCKET_BUFFER_SIZE) {
452
458
  invalid(errorNew(ERROR_SYNC_OVERFLOW, 'fragments'));
453
459
  return;
454
460
  }
461
+ if (!pending) {
462
+ pending = [
463
+ [],
464
+ [],
465
+ total,
466
+ total,
467
+ startTimeout(() => {
468
+ const timedOut = mapGet(buffer, bufferKey);
469
+ if (timedOut) {
470
+ delPending(bufferKey, timedOut);
471
+ }
472
+ }, fragmentTimeoutSeconds),
473
+ 0,
474
+ ];
475
+ mapSet(buffer, bufferKey, pending);
476
+ payloadBuffer[0]++;
477
+ }
478
+ const [fragments, remainders] = pending;
455
479
  fragments[index] = fragment;
456
480
  remainders[index] = remainder;
457
481
  pending[2]--;
458
- pending[5] += fragmentLength;
459
- bufferedSize += fragmentLength;
482
+ pending[5] += retainedSize;
483
+ bufferedSize += retainedSize;
484
+ payloadBuffer[1] += retainedSize;
460
485
  } else {
461
486
  invalid();
462
487
  return;
463
488
  }
464
489
  if (pending[2] == 0) {
490
+ const [fragments, remainders] = pending;
465
491
  delPending(bufferKey, pending);
466
492
  receiveRemainder(clientId, remainders, arrayJoin(fragments));
467
493
  }
@@ -474,6 +500,7 @@ const createPayloadDecoder = (
474
500
  invalid();
475
501
  }
476
502
  };
503
+ return [decode, clear];
477
504
  };
478
505
  const isWebSocketBackpressured = (webSocket, payloadSize) =>
479
506
  (webSocket.bufferedAmount ?? 0) + payloadSize > MAX_WEBSOCKET_BUFFER_SIZE;
@@ -481,12 +508,15 @@ const createRawPayload = (clientId, remainder) =>
481
508
  clientId + MESSAGE_SEPARATOR + remainder;
482
509
  const createMultiplePayload = (channelId, payload) =>
483
510
  createRawPayload(MULTIPLE_CLIENT_ID, createRawPayload(channelId, payload));
484
- const ifMultiplePayloadValid = (payload, then) => {
511
+ const ifMultiplePayloadValid = (payload, then, onOversized) => {
485
512
  let valid = false;
486
513
  ifPayloadValid(payload, (multipleClientId, remainder) => {
487
514
  if (multipleClientId == MULTIPLE_CLIENT_ID) {
488
515
  ifPayloadValid(remainder, (channelId, channelPayload) => {
489
- if (isMultipleChannelIdValid(channelId)) {
516
+ if (isMultipleChannelIdTooLarge(channelId)) {
517
+ valid = true;
518
+ onOversized?.();
519
+ } else if (isMultipleChannelIdStructureValid(channelId)) {
490
520
  valid = true;
491
521
  then(channelId, channelPayload);
492
522
  }
@@ -500,6 +530,15 @@ const createMultipleControlPayload = (requestId, control, body) =>
500
530
  SERVER_CLIENT_ID,
501
531
  jsonStringWithUndefined([requestId, MULTIPLE_MESSAGE, [control, body]]),
502
532
  );
533
+ const isMultipleChannelIdTooLarge = (channelId) =>
534
+ getWebSocketPayloadSize(channelId) > MAX_MULTIPLE_CHANNEL_ID_SIZE;
535
+ const isMultipleChannelIdStructureValid = (channelId) =>
536
+ !isEmpty(channelId) &&
537
+ !INVALID_CHANNEL_ID_CHARACTERS.test(channelId) &&
538
+ arrayEvery(
539
+ strSplit(channelId, '/'),
540
+ (part) => !isEmpty(part) && part != '.' && part != '..',
541
+ );
503
542
  const ifMultipleControlPayloadValid = (payload, then) => {
504
543
  let valid = false;
505
544
  ifPayloadValid(payload, (serverClientId, remainder) => {
@@ -517,11 +556,13 @@ const ifMultipleControlPayloadValid = (payload, then) => {
517
556
  : control == 1 /* Subscribe */
518
557
  ? isString(requestId) &&
519
558
  isString(body) &&
520
- isMultipleChannelIdValid(body)
559
+ (isMultipleChannelIdTooLarge(body) ||
560
+ isMultipleChannelIdStructureValid(body))
521
561
  : control == 2 /* Unsubscribe */ &&
522
562
  isNull(requestId) &&
523
563
  isString(body) &&
524
- isMultipleChannelIdValid(body))
564
+ (isMultipleChannelIdTooLarge(body) ||
565
+ isMultipleChannelIdStructureValid(body)))
525
566
  ) {
526
567
  valid = true;
527
568
  then(requestId, control, body);
@@ -530,13 +571,6 @@ const ifMultipleControlPayloadValid = (payload, then) => {
530
571
  });
531
572
  return valid;
532
573
  };
533
- const isMultipleChannelIdValid = (channelId) =>
534
- !isEmpty(channelId) &&
535
- !INVALID_CHANNEL_ID_CHARACTERS.test(channelId) &&
536
- arrayEvery(
537
- strSplit(channelId, '/'),
538
- (part) => !isEmpty(part) && part != '.' && part != '..',
539
- );
540
574
  const createMultipleServerClient = (
541
575
  basePathId,
542
576
  addChannel,
@@ -549,26 +583,87 @@ const createMultipleServerClient = (
549
583
  ) => {
550
584
  const channels = mapNew();
551
585
  const decoders = mapNew();
586
+ const payloadBuffer = [0, 0];
587
+ const resources = setNew();
588
+ let resourceCount = 0;
552
589
  let negotiated = false;
590
+ let valid = true;
553
591
  const sendControl = (requestId, control, body) =>
554
592
  send(createMultipleControlPayload(requestId, control, body));
555
593
  const delChannelAndDecoder = (channelId) => {
556
594
  collDel(channels, channelId);
595
+ mapGet(decoders, channelId)?.[1]();
557
596
  collDel(decoders, channelId);
558
597
  };
598
+ const clearDecoders = () => {
599
+ mapForEach(decoders, (_channelId, decoder) => decoder[1]());
600
+ collClear(decoders);
601
+ };
602
+ const invalidate = (error) => {
603
+ if (valid) {
604
+ valid = false;
605
+ clearDecoders();
606
+ invalid(error);
607
+ }
608
+ };
609
+ const releaseChannel = (channel) => {
610
+ if (!channel[2 /* Released */]) {
611
+ channel[2 /* Released */] = 1;
612
+ resourceCount--;
613
+ collDel(resources, channel);
614
+ }
615
+ };
616
+ const teardownChannel = (channel) =>
617
+ (channel[1 /* Teardown */] ??= (async () => {
618
+ try {
619
+ const teardown = delChannel(channel[0 /* Channel */]);
620
+ if (isUndefined(teardown)) {
621
+ releaseChannel(channel);
622
+ } else {
623
+ await teardown;
624
+ }
625
+ } finally {
626
+ releaseChannel(channel);
627
+ }
628
+ })());
559
629
  const handleControl = (requestId, control, body) => {
560
630
  if (control == 0 /* Hello */) {
561
631
  negotiated = true;
562
632
  sendControl(requestId, control, body);
563
633
  } else if (negotiated && control == 1 /* Subscribe */) {
564
- if (!collHas(channels, body)) {
634
+ const subscribed = collHas(channels, body);
635
+ if (
636
+ isMultipleChannelIdTooLarge(body) ||
637
+ (!subscribed &&
638
+ (collSize(channels) >= MAX_MULTIPLE_CHANNELS ||
639
+ resourceCount >= MAX_MULTIPLE_CHANNEL_RESOURCES))
640
+ ) {
641
+ invalidate(errorNew(ERROR_SYNC_OVERFLOW, 'channels'));
642
+ return;
643
+ }
644
+ if (!subscribed) {
565
645
  const pathId = basePathId + (basePathId ? '/' : EMPTY_STRING) + body;
566
- const [channel, ready] = addChannel(pathId, body);
646
+ resourceCount++;
647
+ let channelAndReady;
648
+ try {
649
+ channelAndReady = addChannel(pathId, body);
650
+ } catch (error) {
651
+ resourceCount--;
652
+ throw error;
653
+ }
654
+ const [addedChannel, ready] = channelAndReady;
655
+ const channel = [addedChannel];
656
+ setAdd(resources, channel);
567
657
  mapSet(channels, body, channel);
568
658
  sendControl(requestId, control, body);
569
659
  if (ready) {
570
660
  return ready.catch((error) => {
571
- delChannelAndDecoder(body);
661
+ if (mapGet(channels, body) === channel) {
662
+ delChannelAndDecoder(body);
663
+ }
664
+ if (!channel[1 /* Teardown */]) {
665
+ releaseChannel(channel);
666
+ }
572
667
  throw error;
573
668
  });
574
669
  }
@@ -576,16 +671,21 @@ const createMultipleServerClient = (
576
671
  }
577
672
  sendControl(requestId, control, body);
578
673
  } else if (negotiated && control == 2 /* Unsubscribe */) {
674
+ if (isMultipleChannelIdTooLarge(body)) {
675
+ invalidate(errorNew(ERROR_SYNC_OVERFLOW, 'channels'));
676
+ return;
677
+ }
579
678
  const channel = mapGet(channels, body);
580
679
  delChannelAndDecoder(body);
581
- if (!isUndefined(channel)) {
582
- return (async () => delChannel(channel))();
583
- }
680
+ return isUndefined(channel) ? void 0 : teardownChannel(channel);
584
681
  }
585
682
  };
586
683
  const handlePayload = (payload) => {
684
+ if (!valid) {
685
+ return;
686
+ }
587
687
  if (isWebSocketPayloadTooLarge(getWebSocketPayloadSize(payload))) {
588
- invalid(errorNew(ERROR_SYNC_OVERFLOW, 'socket'));
688
+ invalidate(errorNew(ERROR_SYNC_OVERFLOW, 'socket'));
589
689
  return;
590
690
  }
591
691
  const control = ifMultipleControlPayloadValid(
@@ -600,32 +700,54 @@ const createMultipleServerClient = (
600
700
  (channelId, channelPayload) => {
601
701
  const channel2 = negotiated ? mapGet(channels, channelId) : void 0;
602
702
  if (isUndefined(channel2)) {
603
- invalid(errorNew(ERROR_SYNC_MESSAGE));
703
+ invalidate(errorNew(ERROR_SYNC_MESSAGE));
604
704
  } else {
605
705
  mapEnsure(decoders, channelId, () =>
606
706
  createPayloadDecoder(
607
707
  (toClientId, remainders) =>
608
- receive(channel2, toClientId, remainders),
708
+ receive(channel2[0 /* Channel */], toClientId, remainders),
609
709
  fragmentTimeoutSeconds,
610
- invalid,
710
+ invalidate,
711
+ payloadBuffer,
611
712
  ),
612
- )(channelPayload);
713
+ )[0](channelPayload);
613
714
  }
614
715
  },
716
+ () => invalidate(errorNew(ERROR_SYNC_OVERFLOW, 'channels')),
615
717
  );
616
718
  if (!control && !channel) {
617
- invalid(errorNew(ERROR_SYNC_MESSAGE));
719
+ invalidate(errorNew(ERROR_SYNC_MESSAGE));
618
720
  }
619
721
  };
620
- const destroy = () =>
621
- tryFinallyAsync(
622
- () =>
623
- promiseAll(mapMap(channels, async (channel) => delChannel(channel))),
722
+ const destroy = () => {
723
+ valid = false;
724
+ clearDecoders();
725
+ return tryFinallyAsync(
726
+ async () => {
727
+ let errorToThrow;
728
+ let failed = false;
729
+ await promiseAll(
730
+ arrayMap(collValues(resources), async (channel) => {
731
+ try {
732
+ await teardownChannel(channel);
733
+ } catch (error) {
734
+ if (!failed) {
735
+ errorToThrow = error;
736
+ failed = true;
737
+ }
738
+ }
739
+ }),
740
+ );
741
+ if (failed) {
742
+ throw errorToThrow;
743
+ }
744
+ },
624
745
  () => {
625
746
  collClear(channels);
626
- collClear(decoders);
747
+ clearDecoders();
627
748
  },
628
749
  );
750
+ };
629
751
  return [handlePayload, destroy];
630
752
  };
631
753
 
@@ -714,7 +836,7 @@ const createWsServerSimple = (webSocketServer) => {
714
836
  };
715
837
  const addLegacyClient = (client, clientId, pathId) => {
716
838
  addClientToPath(pathId, clientId, [client]);
717
- const decode = createPayloadDecoder(
839
+ const [decode, clearDecoder] = createPayloadDecoder(
718
840
  (toClientId, remainders) =>
719
841
  handleDecodedMessage(pathId, clientId, toClientId, remainders),
720
842
  1,
@@ -723,8 +845,10 @@ const createWsServerSimple = (webSocketServer) => {
723
845
  addWebSocketListener(client, MESSAGE, (data) =>
724
846
  decode(data.toString(UTF8)),
725
847
  );
726
- mapGet(webSocketStates, client)[1] = () =>
848
+ mapGet(webSocketStates, client)[1] = () => {
849
+ clearDecoder();
727
850
  delClientFromPath(pathId, clientId);
851
+ };
728
852
  };
729
853
  const addMultipleClient = (client, clientId, basePathId) => {
730
854
  const invalid = createInvalidPayloadHandler(client);