tinybase 8.6.0-beta.0 → 9.0.0-beta.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 (151) hide show
  1. package/@types/_internal/queries/with-schemas/index.d.ts +1 -2
  2. package/@types/_internal/ui/with-schemas/index.d.ts +8 -16
  3. package/@types/persisters/index.d.ts +32 -12
  4. package/@types/persisters/persister-durable-object-sql-storage/index.d.ts +9 -8
  5. package/@types/persisters/persister-durable-object-sql-storage/with-schemas/index.d.ts +10 -10
  6. package/@types/persisters/with-schemas/index.d.ts +56 -20
  7. package/@types/queries/index.d.ts +1 -7
  8. package/@types/queries/with-schemas/index.d.ts +5 -15
  9. package/@types/store/index.d.ts +1 -2
  10. package/@types/store/with-schemas/index.d.ts +19 -29
  11. package/@types/synchronizers/synchronizer-ws-client/index.d.ts +9 -3
  12. package/@types/synchronizers/synchronizer-ws-client/with-schemas/index.d.ts +10 -3
  13. package/@types/synchronizers/synchronizer-ws-server/index.d.ts +15 -2
  14. package/@types/synchronizers/synchronizer-ws-server/with-schemas/index.d.ts +17 -2
  15. package/@types/synchronizers/synchronizer-ws-server-durable-object/index.d.ts +51 -0
  16. package/@types/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.d.ts +51 -0
  17. package/@types/ui-react/with-schemas/index.d.ts +3 -6
  18. package/@types/ui-react-dom/with-schemas/index.d.ts +6 -12
  19. package/@types/ui-react-dom-charts/with-schemas/index.d.ts +3 -4
  20. package/@types/ui-solid/with-schemas/index.d.ts +4 -8
  21. package/@types/ui-solid-dom/with-schemas/index.d.ts +6 -12
  22. package/@types/ui-svelte-dom/with-schemas/index.d.ts +6 -12
  23. package/index.js +21 -19
  24. package/indexes/index.js +6 -8
  25. package/indexes/with-schemas/index.js +6 -8
  26. package/metrics/index.js +6 -8
  27. package/metrics/with-schemas/index.js +6 -8
  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-cr-sqlite-wasm/index.js +1 -1
  37. package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
  38. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
  39. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
  40. package/min/persisters/persister-durable-object-sql-storage/index.js +1 -1
  41. package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
  42. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -1
  43. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
  44. package/min/persisters/persister-electric-sql/index.js +1 -1
  45. package/min/persisters/persister-electric-sql/index.js.gz +0 -0
  46. package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
  47. package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
  48. package/min/persisters/persister-expo-sqlite/index.js +1 -1
  49. package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
  50. package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
  51. package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
  52. package/min/persisters/persister-libsql/index.js +1 -1
  53. package/min/persisters/persister-libsql/index.js.gz +0 -0
  54. package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
  55. package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
  56. package/min/persisters/persister-pglite/index.js +1 -1
  57. package/min/persisters/persister-pglite/index.js.gz +0 -0
  58. package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
  59. package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
  60. package/min/persisters/persister-postgres/index.js +1 -1
  61. package/min/persisters/persister-postgres/index.js.gz +0 -0
  62. package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
  63. package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
  64. package/min/persisters/persister-powersync/index.js +1 -1
  65. package/min/persisters/persister-powersync/index.js.gz +0 -0
  66. package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
  67. package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
  68. package/min/persisters/persister-react-native-sqlite/index.js +1 -1
  69. package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
  70. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
  71. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
  72. package/min/persisters/persister-sqlite-bun/index.js +1 -1
  73. package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
  74. package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
  75. package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
  76. package/min/persisters/persister-sqlite-wasm/index.js +1 -1
  77. package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
  78. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
  79. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
  80. package/min/persisters/persister-sqlite3/index.js +1 -1
  81. package/min/persisters/persister-sqlite3/index.js.gz +0 -0
  82. package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
  83. package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
  84. package/min/persisters/with-schemas/index.js +1 -1
  85. package/min/persisters/with-schemas/index.js.gz +0 -0
  86. package/min/queries/index.js +1 -1
  87. package/min/queries/index.js.gz +0 -0
  88. package/min/queries/with-schemas/index.js +1 -1
  89. package/min/queries/with-schemas/index.js.gz +0 -0
  90. package/min/synchronizers/synchronizer-ws-client/index.js +1 -1
  91. package/min/synchronizers/synchronizer-ws-client/index.js.gz +0 -0
  92. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js +1 -1
  93. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js.gz +0 -0
  94. package/min/synchronizers/synchronizer-ws-server/index.js +1 -1
  95. package/min/synchronizers/synchronizer-ws-server/index.js.gz +0 -0
  96. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js +1 -1
  97. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js.gz +0 -0
  98. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js +1 -1
  99. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js.gz +0 -0
  100. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +1 -1
  101. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js.gz +0 -0
  102. package/min/with-schemas/index.js +1 -1
  103. package/min/with-schemas/index.js.gz +0 -0
  104. package/omni/index.js +311 -141
  105. package/omni/with-schemas/index.js +311 -141
  106. package/package.json +7 -7
  107. package/persisters/index.js +129 -37
  108. package/persisters/persister-cr-sqlite-wasm/index.js +129 -37
  109. package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +129 -37
  110. package/persisters/persister-durable-object-sql-storage/index.js +271 -110
  111. package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +271 -110
  112. package/persisters/persister-electric-sql/index.js +129 -37
  113. package/persisters/persister-electric-sql/with-schemas/index.js +129 -37
  114. package/persisters/persister-expo-sqlite/index.js +129 -37
  115. package/persisters/persister-expo-sqlite/with-schemas/index.js +129 -37
  116. package/persisters/persister-libsql/index.js +129 -37
  117. package/persisters/persister-libsql/with-schemas/index.js +129 -37
  118. package/persisters/persister-pglite/index.js +130 -37
  119. package/persisters/persister-pglite/with-schemas/index.js +130 -37
  120. package/persisters/persister-postgres/index.js +130 -37
  121. package/persisters/persister-postgres/with-schemas/index.js +130 -37
  122. package/persisters/persister-powersync/index.js +182 -117
  123. package/persisters/persister-powersync/with-schemas/index.js +182 -117
  124. package/persisters/persister-react-native-sqlite/index.js +129 -37
  125. package/persisters/persister-react-native-sqlite/with-schemas/index.js +129 -37
  126. package/persisters/persister-sqlite-bun/index.js +129 -37
  127. package/persisters/persister-sqlite-bun/with-schemas/index.js +129 -37
  128. package/persisters/persister-sqlite-wasm/index.js +129 -37
  129. package/persisters/persister-sqlite-wasm/with-schemas/index.js +129 -37
  130. package/persisters/persister-sqlite3/index.js +129 -37
  131. package/persisters/persister-sqlite3/with-schemas/index.js +129 -37
  132. package/persisters/with-schemas/index.js +129 -37
  133. package/queries/index.js +21 -19
  134. package/queries/with-schemas/index.js +21 -19
  135. package/readme.md +14 -14
  136. package/relationships/index.js +6 -8
  137. package/relationships/with-schemas/index.js +6 -8
  138. package/releases.md +57 -57
  139. package/synchronizers/synchronizer-ws-client/index.js +116 -42
  140. package/synchronizers/synchronizer-ws-client/with-schemas/index.js +116 -42
  141. package/synchronizers/synchronizer-ws-server/index.js +109 -28
  142. package/synchronizers/synchronizer-ws-server/with-schemas/index.js +109 -28
  143. package/synchronizers/synchronizer-ws-server-durable-object/index.js +122 -38
  144. package/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +122 -38
  145. package/ui-svelte/index.js +1 -1
  146. package/ui-svelte/with-schemas/index.js +1 -1
  147. package/ui-svelte-dom/index.js +2 -2
  148. package/ui-svelte-dom/with-schemas/index.js +2 -2
  149. package/ui-svelte-inspector/index.js +2 -2
  150. package/ui-svelte-inspector/with-schemas/index.js +2 -2
  151. package/with-schemas/index.js +21 -19
@@ -19,6 +19,7 @@ const GLOBAL = globalThis;
19
19
  const THOUSAND = 1e3;
20
20
  const startTimeout = (callback, sec = 0) =>
21
21
  setTimeout(callback, sec * THOUSAND);
22
+ const stopTimeout = clearTimeout;
22
23
  const mathFloor = math.floor;
23
24
  const mathRandom = math.random;
24
25
  const isNullish = (thing) => thing == null;
@@ -46,6 +47,7 @@ const tryCatch = async (action, then1, then2) => {
46
47
  };
47
48
 
48
49
  const arrayForEach = (array, cb) => array.forEach(cb);
50
+ const arrayJoin = (array, sep = EMPTY_STRING) => array.join(sep);
49
51
  const arrayMap = (array, cb) => array.map(cb);
50
52
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
51
53
  const arrayClear = (array, to) => array.splice(0, to);
@@ -230,6 +232,22 @@ const getListenerFunctions = (getThing) => {
230
232
  return [addListener, callListeners, delListener, callListener];
231
233
  };
232
234
 
235
+ const MASK6 = 63;
236
+ const ENCODE = /* @__PURE__ */ strSplit(
237
+ '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz',
238
+ );
239
+ const encode = (num) => ENCODE[num & MASK6];
240
+ const getRandomValues = GLOBAL.crypto
241
+ ? (array) => GLOBAL.crypto.getRandomValues(array)
242
+ : /* istanbul ignore next */
243
+ (array) => arrayMap(array, () => mathFloor(mathRandom() * 256));
244
+ const getUniqueId = (length = 16) =>
245
+ arrayReduce(
246
+ getRandomValues(new Uint8Array(length)),
247
+ (uniqueId, number) => uniqueId + encode(number),
248
+ EMPTY_STRING,
249
+ );
250
+
233
251
  const jsonString = JSON.stringify;
234
252
  const jsonParse = JSON.parse;
235
253
  const jsonStringWithUndefined = (obj) =>
@@ -247,36 +265,85 @@ const replaceUndefinedString = (obj) =>
247
265
  : obj;
248
266
 
249
267
  const MESSAGE_SEPARATOR = '\n';
268
+ const FRAGMENT = /^(.+)\n(\d+)\n(\d+)\n([\s\S]*)$/;
250
269
  const ifPayloadValid = (payload, then) => {
251
270
  const splitAt = payload.indexOf(MESSAGE_SEPARATOR);
252
271
  if (splitAt !== -1) {
253
272
  then(slice(payload, 0, splitAt), slice(payload, splitAt + 1));
254
273
  }
255
274
  };
256
- const receivePayload = (payload, receive) =>
257
- ifPayloadValid(payload, (fromClientId, remainder) =>
258
- receive(fromClientId, ...jsonParseWithUndefined(remainder)),
259
- );
260
- const createPayload = (toClientId, ...args) =>
261
- createRawPayload(toClientId ?? EMPTY_STRING, jsonStringWithUndefined(args));
275
+ const receivePayloadRemainder = (fromClientId, remainder, receive) =>
276
+ receive(fromClientId, ...jsonParseWithUndefined(remainder));
277
+ const createPayloadReceiver = (receive, fragmentTimeoutSeconds = 1) => {
278
+ const buffer = mapNew();
279
+ const setPendingTimeout = (bufferKey) =>
280
+ startTimeout(() => collDel(buffer, bufferKey), fragmentTimeoutSeconds);
281
+ const delPending = (bufferKey, pending) => {
282
+ stopTimeout(pending[3]);
283
+ collDel(buffer, bufferKey);
284
+ };
285
+ return (payload) =>
286
+ ifPayloadValid(payload, (fromClientId, remainder) => {
287
+ const [, messageId, indexStr, totalStr, fragment] =
288
+ strMatch(remainder, FRAGMENT) ?? [];
289
+ if (messageId) {
290
+ const index = parseInt(indexStr);
291
+ const total = parseInt(totalStr);
292
+ if (total > 0 && index >= 0 && index < total) {
293
+ const bufferKey = fromClientId + MESSAGE_SEPARATOR + messageId;
294
+ const pending = mapEnsure(buffer, bufferKey, () => [
295
+ [],
296
+ total,
297
+ total,
298
+ setPendingTimeout(bufferKey),
299
+ ]);
300
+ const [fragments] = pending;
301
+ if (total == pending[2] && isUndefined(fragments[index])) {
302
+ stopTimeout(pending[3]);
303
+ pending[3] = setPendingTimeout(bufferKey);
304
+ fragments[index] = fragment;
305
+ pending[1]--;
306
+ }
307
+ if (pending[1] == 0) {
308
+ delPending(bufferKey, pending);
309
+ receivePayloadRemainder(
310
+ fromClientId,
311
+ arrayJoin(fragments),
312
+ receive,
313
+ );
314
+ }
315
+ }
316
+ } else {
317
+ receivePayloadRemainder(fromClientId, remainder, receive);
318
+ }
319
+ });
320
+ };
262
321
  const createRawPayload = (clientId, remainder) =>
263
322
  clientId + MESSAGE_SEPARATOR + remainder;
264
-
265
- const MASK6 = 63;
266
- const ENCODE = /* @__PURE__ */ strSplit(
267
- '-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz',
268
- );
269
- const encode = (num) => ENCODE[num & MASK6];
270
- const getRandomValues = GLOBAL.crypto
271
- ? (array) => GLOBAL.crypto.getRandomValues(array)
272
- : /* istanbul ignore next */
273
- (array) => arrayMap(array, () => mathFloor(mathRandom() * 256));
274
- const getUniqueId = (length = 16) =>
275
- arrayReduce(
276
- getRandomValues(new Uint8Array(length)),
277
- (uniqueId, number) => uniqueId + encode(number),
278
- EMPTY_STRING,
323
+ const createPayloads = (toClientId, requestId, message, body, fragmentSize) => {
324
+ const clientId = toClientId ?? EMPTY_STRING;
325
+ const remainder = jsonStringWithUndefined([requestId, message, body]);
326
+ const maxFragmentSize = mathFloor(fragmentSize ?? 0);
327
+ if (
328
+ isUndefined(fragmentSize) ||
329
+ maxFragmentSize < 1 ||
330
+ size(remainder) <= maxFragmentSize
331
+ ) {
332
+ return [createRawPayload(clientId, remainder)];
333
+ }
334
+ const fragments = strMatch(
335
+ remainder,
336
+ new RegExp(`.{1,${maxFragmentSize}}`, 'g'),
279
337
  );
338
+ const total = size(fragments ?? []);
339
+ const messageId = getUniqueId();
340
+ return arrayMap(fragments ?? [], (fragment, index) =>
341
+ createRawPayload(
342
+ clientId,
343
+ arrayJoin([messageId, index, total, fragment], MESSAGE_SEPARATOR),
344
+ ),
345
+ );
346
+ };
280
347
 
281
348
  const stampNew = (value, hlc) => (hlc ? [value, hlc] : [value]);
282
349
  const getLatestHlc = (hlc1, hlc2) =>
@@ -753,6 +820,8 @@ const createWsServer = (
753
820
  webSocketServer,
754
821
  createPersisterForPath,
755
822
  onIgnoredError,
823
+ requestTimeoutSeconds = 1,
824
+ fragmentSize,
756
825
  ) => {
757
826
  const pathIdListeners = mapNew();
758
827
  const clientIdListeners = mapNew();
@@ -777,12 +846,24 @@ const createWsServer = (
777
846
  serverClient[2 /* Synchronizer */] = createCustomSynchronizer(
778
847
  serverClient[1 /* Persister */].getStore(),
779
848
  (toClientId, requestId, message, body) =>
780
- messageHandler(createPayload(toClientId, requestId, message, body)),
781
- (receive) =>
782
- (serverClient[3 /* Send */] = (payload) =>
783
- receivePayload(payload, receive)),
849
+ arrayForEach(
850
+ createPayloads(
851
+ toClientId,
852
+ requestId,
853
+ message,
854
+ body,
855
+ fragmentSize,
856
+ ),
857
+ messageHandler,
858
+ ),
859
+ (receive) => {
860
+ serverClient[3 /* Send */] = createPayloadReceiver(
861
+ receive,
862
+ requestTimeoutSeconds,
863
+ );
864
+ },
784
865
  noop,
785
- 1,
866
+ requestTimeoutSeconds,
786
867
  void 0,
787
868
  void 0,
788
869
  onIgnoredError,
@@ -794,14 +875,14 @@ const createWsServer = (
794
875
  },
795
876
  );
796
877
  const startServerClient = async (serverClient) => {
797
- serverClient[0 /* State */] = 2 /* Starting */;
878
+ serverClient[0 /* State */] = 2; /* Starting */
798
879
  await serverClient[1 /* Persister */].schedule(
799
880
  serverClient[1 /* Persister */].startAutoLoad,
800
881
  serverClient[1 /* Persister */].startAutoSave,
801
882
  serverClient[2 /* Synchronizer */].startSync,
802
883
  );
803
884
  serverClient[5 /* Then */](serverClient[1 /* Persister */].getStore());
804
- serverClient[0 /* State */] = 0 /* Ready */;
885
+ serverClient[0 /* State */] = 0; /* Ready */
805
886
  };
806
887
  const stopServerClient = async (serverClient) => {
807
888
  await serverClient[1 /* Persister */]?.destroy();
@@ -18,6 +18,7 @@ const GLOBAL = globalThis;
18
18
  const THOUSAND = 1e3;
19
19
  const startTimeout = (callback, sec = 0) =>
20
20
  setTimeout(callback, sec * THOUSAND);
21
+ const stopTimeout = clearTimeout;
21
22
  const mathFloor = math.floor;
22
23
  const mathRandom = math.random;
23
24
  const isNullish = (thing) => thing == null;
@@ -44,6 +45,7 @@ const tryCatch = async (action, then1, then2) => {
44
45
  };
45
46
 
46
47
  const arrayForEach = (array, cb) => array.forEach(cb);
48
+ const arrayJoin = (array, sep = EMPTY_STRING) => array.join(sep);
47
49
  const arrayMap = (array, cb) => array.map(cb);
48
50
  const arrayIsEmpty = (array) => size(array) == 0;
49
51
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
@@ -85,38 +87,6 @@ const objEnsure = (obj, id, getDefaultValue) => {
85
87
  return obj[id];
86
88
  };
87
89
 
88
- const jsonString = JSON.stringify;
89
- const jsonParse = JSON.parse;
90
- const jsonStringWithUndefined = (obj) =>
91
- jsonString(obj, (_key, value) => (isUndefined(value) ? UNDEFINED : value));
92
- const jsonParseWithUndefined = (str) =>
93
- // JSON.parse reviver removes properties with undefined values
94
- replaceUndefinedString(jsonParse(str));
95
- const replaceUndefinedString = (obj) =>
96
- obj === UNDEFINED
97
- ? void 0
98
- : isArray(obj)
99
- ? arrayMap(obj, replaceUndefinedString)
100
- : isObject(obj)
101
- ? objMap(obj, replaceUndefinedString)
102
- : obj;
103
-
104
- const MESSAGE_SEPARATOR = '\n';
105
- const ifPayloadValid = (payload, then) => {
106
- const splitAt = payload.indexOf(MESSAGE_SEPARATOR);
107
- if (splitAt !== -1) {
108
- then(slice(payload, 0, splitAt), slice(payload, splitAt + 1));
109
- }
110
- };
111
- const receivePayload = (payload, receive) =>
112
- ifPayloadValid(payload, (fromClientId, remainder) =>
113
- receive(fromClientId, ...jsonParseWithUndefined(remainder)),
114
- );
115
- const createPayload = (toClientId, ...args) =>
116
- createRawPayload(toClientId ?? EMPTY_STRING, jsonStringWithUndefined(args));
117
- const createRawPayload = (clientId, remainder) =>
118
- clientId + MESSAGE_SEPARATOR + remainder;
119
-
120
90
  const collSize = (coll) => coll?.size ?? 0;
121
91
  const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
122
92
  const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
@@ -174,6 +144,105 @@ const getUniqueId = (length = 16) =>
174
144
  EMPTY_STRING,
175
145
  );
176
146
 
147
+ const jsonString = JSON.stringify;
148
+ const jsonParse = JSON.parse;
149
+ const jsonStringWithUndefined = (obj) =>
150
+ jsonString(obj, (_key, value) => (isUndefined(value) ? UNDEFINED : value));
151
+ const jsonParseWithUndefined = (str) =>
152
+ // JSON.parse reviver removes properties with undefined values
153
+ replaceUndefinedString(jsonParse(str));
154
+ const replaceUndefinedString = (obj) =>
155
+ obj === UNDEFINED
156
+ ? void 0
157
+ : isArray(obj)
158
+ ? arrayMap(obj, replaceUndefinedString)
159
+ : isObject(obj)
160
+ ? objMap(obj, replaceUndefinedString)
161
+ : obj;
162
+
163
+ const MESSAGE_SEPARATOR = '\n';
164
+ const FRAGMENT = /^(.+)\n(\d+)\n(\d+)\n([\s\S]*)$/;
165
+ const ifPayloadValid = (payload, then) => {
166
+ const splitAt = payload.indexOf(MESSAGE_SEPARATOR);
167
+ if (splitAt !== -1) {
168
+ then(slice(payload, 0, splitAt), slice(payload, splitAt + 1));
169
+ }
170
+ };
171
+ const receivePayloadRemainder = (fromClientId, remainder, receive) =>
172
+ receive(fromClientId, ...jsonParseWithUndefined(remainder));
173
+ const createPayloadReceiver = (receive, fragmentTimeoutSeconds = 1) => {
174
+ const buffer = mapNew();
175
+ const setPendingTimeout = (bufferKey) =>
176
+ startTimeout(() => collDel(buffer, bufferKey), fragmentTimeoutSeconds);
177
+ const delPending = (bufferKey, pending) => {
178
+ stopTimeout(pending[3]);
179
+ collDel(buffer, bufferKey);
180
+ };
181
+ return (payload) =>
182
+ ifPayloadValid(payload, (fromClientId, remainder) => {
183
+ const [, messageId, indexStr, totalStr, fragment] =
184
+ strMatch(remainder, FRAGMENT) ?? [];
185
+ if (messageId) {
186
+ const index = parseInt(indexStr);
187
+ const total = parseInt(totalStr);
188
+ if (total > 0 && index >= 0 && index < total) {
189
+ const bufferKey = fromClientId + MESSAGE_SEPARATOR + messageId;
190
+ const pending = mapEnsure(buffer, bufferKey, () => [
191
+ [],
192
+ total,
193
+ total,
194
+ setPendingTimeout(bufferKey),
195
+ ]);
196
+ const [fragments] = pending;
197
+ if (total == pending[2] && isUndefined(fragments[index])) {
198
+ stopTimeout(pending[3]);
199
+ pending[3] = setPendingTimeout(bufferKey);
200
+ fragments[index] = fragment;
201
+ pending[1]--;
202
+ }
203
+ if (pending[1] == 0) {
204
+ delPending(bufferKey, pending);
205
+ receivePayloadRemainder(
206
+ fromClientId,
207
+ arrayJoin(fragments),
208
+ receive,
209
+ );
210
+ }
211
+ }
212
+ } else {
213
+ receivePayloadRemainder(fromClientId, remainder, receive);
214
+ }
215
+ });
216
+ };
217
+ const createPayload = (toClientId, ...args) =>
218
+ createRawPayload(toClientId, jsonStringWithUndefined(args));
219
+ const createRawPayload = (clientId, remainder) =>
220
+ clientId + MESSAGE_SEPARATOR + remainder;
221
+ const createPayloads = (toClientId, requestId, message, body, fragmentSize) => {
222
+ const clientId = toClientId ?? EMPTY_STRING;
223
+ const remainder = jsonStringWithUndefined([requestId, message, body]);
224
+ const maxFragmentSize = mathFloor(fragmentSize ?? 0);
225
+ if (
226
+ isUndefined(fragmentSize) ||
227
+ maxFragmentSize < 1 ||
228
+ size(remainder) <= maxFragmentSize
229
+ ) {
230
+ return [createRawPayload(clientId, remainder)];
231
+ }
232
+ const fragments = strMatch(
233
+ remainder,
234
+ new RegExp(`.{1,${maxFragmentSize}}`, 'g'),
235
+ );
236
+ const total = size(fragments ?? []);
237
+ const messageId = getUniqueId();
238
+ return arrayMap(fragments ?? [], (fragment, index) =>
239
+ createRawPayload(
240
+ clientId,
241
+ arrayJoin([messageId, index, total, fragment], MESSAGE_SEPARATOR),
242
+ ),
243
+ );
244
+ };
245
+
177
246
  const stampNew = (value, hlc) => (hlc ? [value, hlc] : [value]);
178
247
  const getLatestHlc = (hlc1, hlc2) =>
179
248
  /* istanbul ignore next */
@@ -759,18 +828,27 @@ class WsServerDurableObject extends DurableObject {
759
828
  await ifNotUndefined(
760
829
  await this.createPersister(),
761
830
  async (persister) => {
831
+ const requestTimeoutSeconds = this.getRequestTimeoutSeconds();
762
832
  const synchronizer = createCustomSynchronizer(
763
833
  persister.getStore(),
764
834
  (toClientId, requestId, message, body) =>
765
- this.#handleMessage(
766
- SERVER_CLIENT_ID,
767
- createPayload(toClientId, requestId, message, body),
835
+ arrayForEach(
836
+ createPayloads(
837
+ toClientId,
838
+ requestId,
839
+ message,
840
+ body,
841
+ this.getFragmentSize(),
842
+ ),
843
+ (payload) => this.#handleMessage(SERVER_CLIENT_ID, payload),
768
844
  ),
769
845
  (receive) =>
770
- (this.#serverClientSend = (payload) =>
771
- receivePayload(payload, receive)),
846
+ (this.#serverClientSend = createPayloadReceiver(
847
+ receive,
848
+ requestTimeoutSeconds,
849
+ )),
772
850
  noop,
773
- 1,
851
+ requestTimeoutSeconds,
774
852
  );
775
853
  await persister.load();
776
854
  await persister.startAutoSave();
@@ -845,6 +923,12 @@ class WsServerDurableObject extends DurableObject {
845
923
  (client) => this.ctx.getTags(client)[0],
846
924
  );
847
925
  }
926
+ getFragmentSize() {
927
+ return void 0;
928
+ }
929
+ getRequestTimeoutSeconds() {
930
+ return 1;
931
+ }
848
932
  onPathId(_pathId, _addedOrRemoved) {}
849
933
  onClientId(_pathId, _clientId, _addedOrRemoved) {}
850
934
  onMessage(_fromClientId, _toClientId, _remainder) {}
@@ -18,6 +18,7 @@ const GLOBAL = globalThis;
18
18
  const THOUSAND = 1e3;
19
19
  const startTimeout = (callback, sec = 0) =>
20
20
  setTimeout(callback, sec * THOUSAND);
21
+ const stopTimeout = clearTimeout;
21
22
  const mathFloor = math.floor;
22
23
  const mathRandom = math.random;
23
24
  const isNullish = (thing) => thing == null;
@@ -44,6 +45,7 @@ const tryCatch = async (action, then1, then2) => {
44
45
  };
45
46
 
46
47
  const arrayForEach = (array, cb) => array.forEach(cb);
48
+ const arrayJoin = (array, sep = EMPTY_STRING) => array.join(sep);
47
49
  const arrayMap = (array, cb) => array.map(cb);
48
50
  const arrayIsEmpty = (array) => size(array) == 0;
49
51
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
@@ -85,38 +87,6 @@ const objEnsure = (obj, id, getDefaultValue) => {
85
87
  return obj[id];
86
88
  };
87
89
 
88
- const jsonString = JSON.stringify;
89
- const jsonParse = JSON.parse;
90
- const jsonStringWithUndefined = (obj) =>
91
- jsonString(obj, (_key, value) => (isUndefined(value) ? UNDEFINED : value));
92
- const jsonParseWithUndefined = (str) =>
93
- // JSON.parse reviver removes properties with undefined values
94
- replaceUndefinedString(jsonParse(str));
95
- const replaceUndefinedString = (obj) =>
96
- obj === UNDEFINED
97
- ? void 0
98
- : isArray(obj)
99
- ? arrayMap(obj, replaceUndefinedString)
100
- : isObject(obj)
101
- ? objMap(obj, replaceUndefinedString)
102
- : obj;
103
-
104
- const MESSAGE_SEPARATOR = '\n';
105
- const ifPayloadValid = (payload, then) => {
106
- const splitAt = payload.indexOf(MESSAGE_SEPARATOR);
107
- if (splitAt !== -1) {
108
- then(slice(payload, 0, splitAt), slice(payload, splitAt + 1));
109
- }
110
- };
111
- const receivePayload = (payload, receive) =>
112
- ifPayloadValid(payload, (fromClientId, remainder) =>
113
- receive(fromClientId, ...jsonParseWithUndefined(remainder)),
114
- );
115
- const createPayload = (toClientId, ...args) =>
116
- createRawPayload(toClientId ?? EMPTY_STRING, jsonStringWithUndefined(args));
117
- const createRawPayload = (clientId, remainder) =>
118
- clientId + MESSAGE_SEPARATOR + remainder;
119
-
120
90
  const collSize = (coll) => coll?.size ?? 0;
121
91
  const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
122
92
  const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
@@ -174,6 +144,105 @@ const getUniqueId = (length = 16) =>
174
144
  EMPTY_STRING,
175
145
  );
176
146
 
147
+ const jsonString = JSON.stringify;
148
+ const jsonParse = JSON.parse;
149
+ const jsonStringWithUndefined = (obj) =>
150
+ jsonString(obj, (_key, value) => (isUndefined(value) ? UNDEFINED : value));
151
+ const jsonParseWithUndefined = (str) =>
152
+ // JSON.parse reviver removes properties with undefined values
153
+ replaceUndefinedString(jsonParse(str));
154
+ const replaceUndefinedString = (obj) =>
155
+ obj === UNDEFINED
156
+ ? void 0
157
+ : isArray(obj)
158
+ ? arrayMap(obj, replaceUndefinedString)
159
+ : isObject(obj)
160
+ ? objMap(obj, replaceUndefinedString)
161
+ : obj;
162
+
163
+ const MESSAGE_SEPARATOR = '\n';
164
+ const FRAGMENT = /^(.+)\n(\d+)\n(\d+)\n([\s\S]*)$/;
165
+ const ifPayloadValid = (payload, then) => {
166
+ const splitAt = payload.indexOf(MESSAGE_SEPARATOR);
167
+ if (splitAt !== -1) {
168
+ then(slice(payload, 0, splitAt), slice(payload, splitAt + 1));
169
+ }
170
+ };
171
+ const receivePayloadRemainder = (fromClientId, remainder, receive) =>
172
+ receive(fromClientId, ...jsonParseWithUndefined(remainder));
173
+ const createPayloadReceiver = (receive, fragmentTimeoutSeconds = 1) => {
174
+ const buffer = mapNew();
175
+ const setPendingTimeout = (bufferKey) =>
176
+ startTimeout(() => collDel(buffer, bufferKey), fragmentTimeoutSeconds);
177
+ const delPending = (bufferKey, pending) => {
178
+ stopTimeout(pending[3]);
179
+ collDel(buffer, bufferKey);
180
+ };
181
+ return (payload) =>
182
+ ifPayloadValid(payload, (fromClientId, remainder) => {
183
+ const [, messageId, indexStr, totalStr, fragment] =
184
+ strMatch(remainder, FRAGMENT) ?? [];
185
+ if (messageId) {
186
+ const index = parseInt(indexStr);
187
+ const total = parseInt(totalStr);
188
+ if (total > 0 && index >= 0 && index < total) {
189
+ const bufferKey = fromClientId + MESSAGE_SEPARATOR + messageId;
190
+ const pending = mapEnsure(buffer, bufferKey, () => [
191
+ [],
192
+ total,
193
+ total,
194
+ setPendingTimeout(bufferKey),
195
+ ]);
196
+ const [fragments] = pending;
197
+ if (total == pending[2] && isUndefined(fragments[index])) {
198
+ stopTimeout(pending[3]);
199
+ pending[3] = setPendingTimeout(bufferKey);
200
+ fragments[index] = fragment;
201
+ pending[1]--;
202
+ }
203
+ if (pending[1] == 0) {
204
+ delPending(bufferKey, pending);
205
+ receivePayloadRemainder(
206
+ fromClientId,
207
+ arrayJoin(fragments),
208
+ receive,
209
+ );
210
+ }
211
+ }
212
+ } else {
213
+ receivePayloadRemainder(fromClientId, remainder, receive);
214
+ }
215
+ });
216
+ };
217
+ const createPayload = (toClientId, ...args) =>
218
+ createRawPayload(toClientId, jsonStringWithUndefined(args));
219
+ const createRawPayload = (clientId, remainder) =>
220
+ clientId + MESSAGE_SEPARATOR + remainder;
221
+ const createPayloads = (toClientId, requestId, message, body, fragmentSize) => {
222
+ const clientId = toClientId ?? EMPTY_STRING;
223
+ const remainder = jsonStringWithUndefined([requestId, message, body]);
224
+ const maxFragmentSize = mathFloor(fragmentSize ?? 0);
225
+ if (
226
+ isUndefined(fragmentSize) ||
227
+ maxFragmentSize < 1 ||
228
+ size(remainder) <= maxFragmentSize
229
+ ) {
230
+ return [createRawPayload(clientId, remainder)];
231
+ }
232
+ const fragments = strMatch(
233
+ remainder,
234
+ new RegExp(`.{1,${maxFragmentSize}}`, 'g'),
235
+ );
236
+ const total = size(fragments ?? []);
237
+ const messageId = getUniqueId();
238
+ return arrayMap(fragments ?? [], (fragment, index) =>
239
+ createRawPayload(
240
+ clientId,
241
+ arrayJoin([messageId, index, total, fragment], MESSAGE_SEPARATOR),
242
+ ),
243
+ );
244
+ };
245
+
177
246
  const stampNew = (value, hlc) => (hlc ? [value, hlc] : [value]);
178
247
  const getLatestHlc = (hlc1, hlc2) =>
179
248
  /* istanbul ignore next */
@@ -759,18 +828,27 @@ class WsServerDurableObject extends DurableObject {
759
828
  await ifNotUndefined(
760
829
  await this.createPersister(),
761
830
  async (persister) => {
831
+ const requestTimeoutSeconds = this.getRequestTimeoutSeconds();
762
832
  const synchronizer = createCustomSynchronizer(
763
833
  persister.getStore(),
764
834
  (toClientId, requestId, message, body) =>
765
- this.#handleMessage(
766
- SERVER_CLIENT_ID,
767
- createPayload(toClientId, requestId, message, body),
835
+ arrayForEach(
836
+ createPayloads(
837
+ toClientId,
838
+ requestId,
839
+ message,
840
+ body,
841
+ this.getFragmentSize(),
842
+ ),
843
+ (payload) => this.#handleMessage(SERVER_CLIENT_ID, payload),
768
844
  ),
769
845
  (receive) =>
770
- (this.#serverClientSend = (payload) =>
771
- receivePayload(payload, receive)),
846
+ (this.#serverClientSend = createPayloadReceiver(
847
+ receive,
848
+ requestTimeoutSeconds,
849
+ )),
772
850
  noop,
773
- 1,
851
+ requestTimeoutSeconds,
774
852
  );
775
853
  await persister.load();
776
854
  await persister.startAutoSave();
@@ -845,6 +923,12 @@ class WsServerDurableObject extends DurableObject {
845
923
  (client) => this.ctx.getTags(client)[0],
846
924
  );
847
925
  }
926
+ getFragmentSize() {
927
+ return void 0;
928
+ }
929
+ getRequestTimeoutSeconds() {
930
+ return 1;
931
+ }
848
932
  onPathId(_pathId, _addedOrRemoved) {}
849
933
  onClientId(_pathId, _clientId, _addedOrRemoved) {}
850
934
  onMessage(_fromClientId, _toClientId, _remainder) {}
@@ -60,7 +60,7 @@ const objGet = (obj, id) => ifNotUndefined(obj, (obj2) => obj2[id]);
60
60
 
61
61
  const TINYBASE_CONTEXT_KEY = TINYBASE + '_uisc';
62
62
 
63
- /* functions.svelte.ts generated by Svelte v5.56.3 */
63
+ /* functions.svelte.ts generated by Svelte v5.56.4 */
64
64
 
65
65
  class ReactiveHandle {
66
66
  #get;
@@ -60,7 +60,7 @@ const objGet = (obj, id) => ifNotUndefined(obj, (obj2) => obj2[id]);
60
60
 
61
61
  const TINYBASE_CONTEXT_KEY = TINYBASE + '_uisc';
62
62
 
63
- /* functions.svelte.ts generated by Svelte v5.56.3 */
63
+ /* functions.svelte.ts generated by Svelte v5.56.4 */
64
64
 
65
65
  class ReactiveHandle {
66
66
  #get;
@@ -91,7 +91,7 @@ const objMap = (obj, cb) =>
91
91
 
92
92
  const TINYBASE_CONTEXT_KEY = TINYBASE + '_uisc';
93
93
 
94
- /* functions.svelte.ts generated by Svelte v5.56.3 */
94
+ /* functions.svelte.ts generated by Svelte v5.56.4 */
95
95
 
96
96
  class ReactiveHandle {
97
97
  #get;
@@ -1693,7 +1693,7 @@ function SortedTablePaginator($$anchor, $$props) {
1693
1693
 
1694
1694
  $.delegate(['click']);
1695
1695
 
1696
- /* sortingAndPagination.svelte.ts generated by Svelte v5.56.3 */
1696
+ /* sortingAndPagination.svelte.ts generated by Svelte v5.56.4 */
1697
1697
 
1698
1698
  const createSortingAndPagination = (
1699
1699
  getCellId,
@@ -91,7 +91,7 @@ const objMap = (obj, cb) =>
91
91
 
92
92
  const TINYBASE_CONTEXT_KEY = TINYBASE + '_uisc';
93
93
 
94
- /* functions.svelte.ts generated by Svelte v5.56.3 */
94
+ /* functions.svelte.ts generated by Svelte v5.56.4 */
95
95
 
96
96
  class ReactiveHandle {
97
97
  #get;
@@ -1693,7 +1693,7 @@ function SortedTablePaginator($$anchor, $$props) {
1693
1693
 
1694
1694
  $.delegate(['click']);
1695
1695
 
1696
- /* sortingAndPagination.svelte.ts generated by Svelte v5.56.3 */
1696
+ /* sortingAndPagination.svelte.ts generated by Svelte v5.56.4 */
1697
1697
 
1698
1698
  const createSortingAndPagination = (
1699
1699
  getCellId,
@@ -2879,7 +2879,7 @@ const createStore = () => {
2879
2879
 
2880
2880
  const TINYBASE_CONTEXT_KEY = TINYBASE + '_uisc';
2881
2881
 
2882
- /* functions.svelte.ts generated by Svelte v5.56.3 */
2882
+ /* functions.svelte.ts generated by Svelte v5.56.4 */
2883
2883
 
2884
2884
  class ReactiveHandle {
2885
2885
  #get;
@@ -4724,7 +4724,7 @@ function SortedTablePaginator($$anchor, $$props) {
4724
4724
 
4725
4725
  $.delegate(['click']);
4726
4726
 
4727
- /* sortingAndPagination.svelte.ts generated by Svelte v5.56.3 */
4727
+ /* sortingAndPagination.svelte.ts generated by Svelte v5.56.4 */
4728
4728
 
4729
4729
  const createSortingAndPagination = (
4730
4730
  getCellId,