tinybase 8.5.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 (161) 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/index.d.ts +10 -10
  19. package/@types/ui-react-dom/with-schemas/index.d.ts +16 -22
  20. package/@types/ui-react-dom-charts/index.d.ts +7 -7
  21. package/@types/ui-react-dom-charts/with-schemas/index.d.ts +10 -11
  22. package/@types/ui-react-inspector/index.d.ts +1 -1
  23. package/@types/ui-react-inspector/with-schemas/index.d.ts +1 -1
  24. package/@types/ui-solid/with-schemas/index.d.ts +4 -8
  25. package/@types/ui-solid-dom/index.d.ts +10 -10
  26. package/@types/ui-solid-dom/with-schemas/index.d.ts +16 -22
  27. package/@types/ui-solid-inspector/index.d.ts +1 -1
  28. package/@types/ui-solid-inspector/with-schemas/index.d.ts +1 -1
  29. package/@types/ui-svelte-dom/index.d.ts +10 -10
  30. package/@types/ui-svelte-dom/with-schemas/index.d.ts +16 -22
  31. package/@types/ui-svelte-inspector/index.d.ts +1 -1
  32. package/@types/ui-svelte-inspector/with-schemas/index.d.ts +1 -1
  33. package/index.js +21 -19
  34. package/indexes/index.js +6 -8
  35. package/indexes/with-schemas/index.js +6 -8
  36. package/metrics/index.js +6 -8
  37. package/metrics/with-schemas/index.js +6 -8
  38. package/min/index.js +1 -1
  39. package/min/index.js.gz +0 -0
  40. package/min/omni/index.js +1 -1
  41. package/min/omni/index.js.gz +0 -0
  42. package/min/omni/with-schemas/index.js +1 -1
  43. package/min/omni/with-schemas/index.js.gz +0 -0
  44. package/min/persisters/index.js +1 -1
  45. package/min/persisters/index.js.gz +0 -0
  46. package/min/persisters/persister-cr-sqlite-wasm/index.js +1 -1
  47. package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
  48. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
  49. package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
  50. package/min/persisters/persister-durable-object-sql-storage/index.js +1 -1
  51. package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
  52. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -1
  53. package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
  54. package/min/persisters/persister-electric-sql/index.js +1 -1
  55. package/min/persisters/persister-electric-sql/index.js.gz +0 -0
  56. package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
  57. package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
  58. package/min/persisters/persister-expo-sqlite/index.js +1 -1
  59. package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
  60. package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
  61. package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
  62. package/min/persisters/persister-libsql/index.js +1 -1
  63. package/min/persisters/persister-libsql/index.js.gz +0 -0
  64. package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
  65. package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
  66. package/min/persisters/persister-pglite/index.js +1 -1
  67. package/min/persisters/persister-pglite/index.js.gz +0 -0
  68. package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
  69. package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
  70. package/min/persisters/persister-postgres/index.js +1 -1
  71. package/min/persisters/persister-postgres/index.js.gz +0 -0
  72. package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
  73. package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
  74. package/min/persisters/persister-powersync/index.js +1 -1
  75. package/min/persisters/persister-powersync/index.js.gz +0 -0
  76. package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
  77. package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
  78. package/min/persisters/persister-react-native-sqlite/index.js +1 -1
  79. package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
  80. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
  81. package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
  82. package/min/persisters/persister-sqlite-bun/index.js +1 -1
  83. package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
  84. package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
  85. package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
  86. package/min/persisters/persister-sqlite-wasm/index.js +1 -1
  87. package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
  88. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
  89. package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
  90. package/min/persisters/persister-sqlite3/index.js +1 -1
  91. package/min/persisters/persister-sqlite3/index.js.gz +0 -0
  92. package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
  93. package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
  94. package/min/persisters/with-schemas/index.js +1 -1
  95. package/min/persisters/with-schemas/index.js.gz +0 -0
  96. package/min/queries/index.js +1 -1
  97. package/min/queries/index.js.gz +0 -0
  98. package/min/queries/with-schemas/index.js +1 -1
  99. package/min/queries/with-schemas/index.js.gz +0 -0
  100. package/min/synchronizers/synchronizer-ws-client/index.js +1 -1
  101. package/min/synchronizers/synchronizer-ws-client/index.js.gz +0 -0
  102. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js +1 -1
  103. package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js.gz +0 -0
  104. package/min/synchronizers/synchronizer-ws-server/index.js +1 -1
  105. package/min/synchronizers/synchronizer-ws-server/index.js.gz +0 -0
  106. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js +1 -1
  107. package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js.gz +0 -0
  108. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js +1 -1
  109. package/min/synchronizers/synchronizer-ws-server-durable-object/index.js.gz +0 -0
  110. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +1 -1
  111. package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js.gz +0 -0
  112. package/min/with-schemas/index.js +1 -1
  113. package/min/with-schemas/index.js.gz +0 -0
  114. package/omni/index.js +311 -141
  115. package/omni/with-schemas/index.js +311 -141
  116. package/package.json +7 -7
  117. package/persisters/index.js +129 -37
  118. package/persisters/persister-cr-sqlite-wasm/index.js +129 -37
  119. package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +129 -37
  120. package/persisters/persister-durable-object-sql-storage/index.js +271 -110
  121. package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +271 -110
  122. package/persisters/persister-electric-sql/index.js +129 -37
  123. package/persisters/persister-electric-sql/with-schemas/index.js +129 -37
  124. package/persisters/persister-expo-sqlite/index.js +129 -37
  125. package/persisters/persister-expo-sqlite/with-schemas/index.js +129 -37
  126. package/persisters/persister-libsql/index.js +129 -37
  127. package/persisters/persister-libsql/with-schemas/index.js +129 -37
  128. package/persisters/persister-pglite/index.js +130 -37
  129. package/persisters/persister-pglite/with-schemas/index.js +130 -37
  130. package/persisters/persister-postgres/index.js +130 -37
  131. package/persisters/persister-postgres/with-schemas/index.js +130 -37
  132. package/persisters/persister-powersync/index.js +182 -117
  133. package/persisters/persister-powersync/with-schemas/index.js +182 -117
  134. package/persisters/persister-react-native-sqlite/index.js +129 -37
  135. package/persisters/persister-react-native-sqlite/with-schemas/index.js +129 -37
  136. package/persisters/persister-sqlite-bun/index.js +129 -37
  137. package/persisters/persister-sqlite-bun/with-schemas/index.js +129 -37
  138. package/persisters/persister-sqlite-wasm/index.js +129 -37
  139. package/persisters/persister-sqlite-wasm/with-schemas/index.js +129 -37
  140. package/persisters/persister-sqlite3/index.js +129 -37
  141. package/persisters/persister-sqlite3/with-schemas/index.js +129 -37
  142. package/persisters/with-schemas/index.js +129 -37
  143. package/queries/index.js +21 -19
  144. package/queries/with-schemas/index.js +21 -19
  145. package/readme.md +14 -14
  146. package/relationships/index.js +6 -8
  147. package/relationships/with-schemas/index.js +6 -8
  148. package/releases.md +57 -57
  149. package/synchronizers/synchronizer-ws-client/index.js +116 -42
  150. package/synchronizers/synchronizer-ws-client/with-schemas/index.js +116 -42
  151. package/synchronizers/synchronizer-ws-server/index.js +109 -28
  152. package/synchronizers/synchronizer-ws-server/with-schemas/index.js +109 -28
  153. package/synchronizers/synchronizer-ws-server-durable-object/index.js +122 -38
  154. package/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +122 -38
  155. package/ui-svelte/index.js +1 -1
  156. package/ui-svelte/with-schemas/index.js +1 -1
  157. package/ui-svelte-dom/index.js +2 -2
  158. package/ui-svelte-dom/with-schemas/index.js +2 -2
  159. package/ui-svelte-inspector/index.js +2 -2
  160. package/ui-svelte-inspector/with-schemas/index.js +2 -2
  161. package/with-schemas/index.js +21 -19
@@ -5,6 +5,7 @@ const OPEN = 'open';
5
5
  const MESSAGE = 'message';
6
6
  const ERROR = 'error';
7
7
  const UNDEFINED = '\uFFFC';
8
+ const strMatch = (str, regex) => str?.match(regex);
8
9
  const strSplit = (str, separator = EMPTY_STRING, limit) =>
9
10
  str.split(separator, limit);
10
11
 
@@ -19,6 +20,7 @@ const GLOBAL = globalThis;
19
20
  const THOUSAND = 1e3;
20
21
  const startTimeout = (callback, sec = 0) =>
21
22
  setTimeout(callback, sec * THOUSAND);
23
+ const stopTimeout = clearTimeout;
22
24
  const mathFloor = math.floor;
23
25
  const mathRandom = math.random;
24
26
  const isNullish = (thing) => thing == null;
@@ -44,12 +46,19 @@ const tryCatch = async (action, then1, then2) => {
44
46
  };
45
47
 
46
48
  const arrayForEach = (array, cb) => array.forEach(cb);
49
+ const arrayJoin = (array, sep = EMPTY_STRING) => array.join(sep);
47
50
  const arrayMap = (array, cb) => array.map(cb);
48
51
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
49
52
  const arrayClear = (array, to) => array.splice(0, to);
50
53
  const arrayPush = (array, ...values) => array.push(...values);
51
54
  const arrayShift = (array) => array.shift();
52
55
 
56
+ const collSize = (coll) => coll?.size ?? 0;
57
+ const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
58
+ const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
59
+ const collForEach = (coll, cb) => coll?.forEach(cb);
60
+ const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
61
+
53
62
  const object = Object;
54
63
  const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
55
64
  const objEntries = object.entries;
@@ -83,44 +92,6 @@ const objEnsure = (obj, id, getDefaultValue) => {
83
92
  return obj[id];
84
93
  };
85
94
 
86
- const jsonString = JSON.stringify;
87
- const jsonParse = JSON.parse;
88
- const jsonStringWithUndefined = (obj) =>
89
- jsonString(obj, (_key, value) => (isUndefined(value) ? UNDEFINED : value));
90
- const jsonParseWithUndefined = (str) =>
91
- // JSON.parse reviver removes properties with undefined values
92
- replaceUndefinedString(jsonParse(str));
93
- const replaceUndefinedString = (obj) =>
94
- obj === UNDEFINED
95
- ? void 0
96
- : isArray(obj)
97
- ? arrayMap(obj, replaceUndefinedString)
98
- : isObject(obj)
99
- ? objMap(obj, replaceUndefinedString)
100
- : obj;
101
-
102
- const MESSAGE_SEPARATOR = '\n';
103
- const ifPayloadValid = (payload, then) => {
104
- const splitAt = payload.indexOf(MESSAGE_SEPARATOR);
105
- if (splitAt !== -1) {
106
- then(slice(payload, 0, splitAt), slice(payload, splitAt + 1));
107
- }
108
- };
109
- const receivePayload = (payload, receive) =>
110
- ifPayloadValid(payload, (fromClientId, remainder) =>
111
- receive(fromClientId, ...jsonParseWithUndefined(remainder)),
112
- );
113
- const createPayload = (toClientId, ...args) =>
114
- createRawPayload(toClientId ?? EMPTY_STRING, jsonStringWithUndefined(args));
115
- const createRawPayload = (clientId, remainder) =>
116
- clientId + MESSAGE_SEPARATOR + remainder;
117
-
118
- const collSize = (coll) => coll?.size ?? 0;
119
- const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
120
- const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
121
- const collForEach = (coll, cb) => coll?.forEach(cb);
122
- const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
123
-
124
95
  const map = Map;
125
96
  const mapNew = (entries) => new map(entries);
126
97
  const mapGet = (map2, key) => map2?.get(key);
@@ -172,6 +143,103 @@ const getUniqueId = (length = 16) =>
172
143
  EMPTY_STRING,
173
144
  );
174
145
 
146
+ const jsonString = JSON.stringify;
147
+ const jsonParse = JSON.parse;
148
+ const jsonStringWithUndefined = (obj) =>
149
+ jsonString(obj, (_key, value) => (isUndefined(value) ? UNDEFINED : value));
150
+ const jsonParseWithUndefined = (str) =>
151
+ // JSON.parse reviver removes properties with undefined values
152
+ replaceUndefinedString(jsonParse(str));
153
+ const replaceUndefinedString = (obj) =>
154
+ obj === UNDEFINED
155
+ ? void 0
156
+ : isArray(obj)
157
+ ? arrayMap(obj, replaceUndefinedString)
158
+ : isObject(obj)
159
+ ? objMap(obj, replaceUndefinedString)
160
+ : obj;
161
+
162
+ const MESSAGE_SEPARATOR = '\n';
163
+ const FRAGMENT = /^(.+)\n(\d+)\n(\d+)\n([\s\S]*)$/;
164
+ const ifPayloadValid = (payload, then) => {
165
+ const splitAt = payload.indexOf(MESSAGE_SEPARATOR);
166
+ if (splitAt !== -1) {
167
+ then(slice(payload, 0, splitAt), slice(payload, splitAt + 1));
168
+ }
169
+ };
170
+ const receivePayloadRemainder = (fromClientId, remainder, receive) =>
171
+ receive(fromClientId, ...jsonParseWithUndefined(remainder));
172
+ const createPayloadReceiver = (receive, fragmentTimeoutSeconds = 1) => {
173
+ const buffer = mapNew();
174
+ const setPendingTimeout = (bufferKey) =>
175
+ startTimeout(() => collDel(buffer, bufferKey), fragmentTimeoutSeconds);
176
+ const delPending = (bufferKey, pending) => {
177
+ stopTimeout(pending[3]);
178
+ collDel(buffer, bufferKey);
179
+ };
180
+ return (payload) =>
181
+ ifPayloadValid(payload, (fromClientId, remainder) => {
182
+ const [, messageId, indexStr, totalStr, fragment] =
183
+ strMatch(remainder, FRAGMENT) ?? [];
184
+ if (messageId) {
185
+ const index = parseInt(indexStr);
186
+ const total = parseInt(totalStr);
187
+ if (total > 0 && index >= 0 && index < total) {
188
+ const bufferKey = fromClientId + MESSAGE_SEPARATOR + messageId;
189
+ const pending = mapEnsure(buffer, bufferKey, () => [
190
+ [],
191
+ total,
192
+ total,
193
+ setPendingTimeout(bufferKey),
194
+ ]);
195
+ const [fragments] = pending;
196
+ if (total == pending[2] && isUndefined(fragments[index])) {
197
+ stopTimeout(pending[3]);
198
+ pending[3] = setPendingTimeout(bufferKey);
199
+ fragments[index] = fragment;
200
+ pending[1]--;
201
+ }
202
+ if (pending[1] == 0) {
203
+ delPending(bufferKey, pending);
204
+ receivePayloadRemainder(
205
+ fromClientId,
206
+ arrayJoin(fragments),
207
+ receive,
208
+ );
209
+ }
210
+ }
211
+ } else {
212
+ receivePayloadRemainder(fromClientId, remainder, receive);
213
+ }
214
+ });
215
+ };
216
+ const createRawPayload = (clientId, remainder) =>
217
+ clientId + MESSAGE_SEPARATOR + remainder;
218
+ const createPayloads = (toClientId, requestId, message, body, fragmentSize) => {
219
+ const clientId = toClientId ?? EMPTY_STRING;
220
+ const remainder = jsonStringWithUndefined([requestId, message, body]);
221
+ const maxFragmentSize = mathFloor(fragmentSize ?? 0);
222
+ if (
223
+ isUndefined(fragmentSize) ||
224
+ maxFragmentSize < 1 ||
225
+ size(remainder) <= maxFragmentSize
226
+ ) {
227
+ return [createRawPayload(clientId, remainder)];
228
+ }
229
+ const fragments = strMatch(
230
+ remainder,
231
+ new RegExp(`.{1,${maxFragmentSize}}`, 'g'),
232
+ );
233
+ const total = size(fragments ?? []);
234
+ const messageId = getUniqueId();
235
+ return arrayMap(fragments ?? [], (fragment, index) =>
236
+ createRawPayload(
237
+ clientId,
238
+ arrayJoin([messageId, index, total, fragment], MESSAGE_SEPARATOR),
239
+ ),
240
+ );
241
+ };
242
+
175
243
  const stampNew = (value, hlc) => (hlc ? [value, hlc] : [value]);
176
244
  const getLatestHlc = (hlc1, hlc2) =>
177
245
  /* istanbul ignore next */
@@ -745,17 +813,23 @@ const createWsSynchronizer = async (
745
813
  onSend,
746
814
  onReceive,
747
815
  onIgnoredError,
816
+ fragmentSize,
748
817
  ) => {
749
818
  const addEventListener = (event, handler) => {
750
819
  webSocket.addEventListener(event, handler);
751
820
  return () => webSocket.removeEventListener(event, handler);
752
821
  };
753
- const registerReceive = (receive) =>
754
- addEventListener(MESSAGE, ({data}) =>
755
- receivePayload(data.toString(UTF8), receive),
822
+ const registerReceive = (receive) => {
823
+ const receivePayload = createPayloadReceiver(
824
+ receive,
825
+ requestTimeoutSeconds,
756
826
  );
827
+ addEventListener(MESSAGE, ({data}) => receivePayload(data.toString(UTF8)));
828
+ };
757
829
  const send = (toClientId, ...args) =>
758
- webSocket.send(createPayload(toClientId, ...args));
830
+ arrayForEach(createPayloads(toClientId, ...args, fragmentSize), (payload) =>
831
+ webSocket.send(payload),
832
+ );
759
833
  const destroy = () => {
760
834
  webSocket.close();
761
835
  };
@@ -5,6 +5,7 @@ const OPEN = 'open';
5
5
  const MESSAGE = 'message';
6
6
  const ERROR = 'error';
7
7
  const UNDEFINED = '\uFFFC';
8
+ const strMatch = (str, regex) => str?.match(regex);
8
9
  const strSplit = (str, separator = EMPTY_STRING, limit) =>
9
10
  str.split(separator, limit);
10
11
 
@@ -19,6 +20,7 @@ const GLOBAL = globalThis;
19
20
  const THOUSAND = 1e3;
20
21
  const startTimeout = (callback, sec = 0) =>
21
22
  setTimeout(callback, sec * THOUSAND);
23
+ const stopTimeout = clearTimeout;
22
24
  const mathFloor = math.floor;
23
25
  const mathRandom = math.random;
24
26
  const isNullish = (thing) => thing == null;
@@ -44,12 +46,19 @@ const tryCatch = async (action, then1, then2) => {
44
46
  };
45
47
 
46
48
  const arrayForEach = (array, cb) => array.forEach(cb);
49
+ const arrayJoin = (array, sep = EMPTY_STRING) => array.join(sep);
47
50
  const arrayMap = (array, cb) => array.map(cb);
48
51
  const arrayReduce = (array, cb, initial) => array.reduce(cb, initial);
49
52
  const arrayClear = (array, to) => array.splice(0, to);
50
53
  const arrayPush = (array, ...values) => array.push(...values);
51
54
  const arrayShift = (array) => array.shift();
52
55
 
56
+ const collSize = (coll) => coll?.size ?? 0;
57
+ const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
58
+ const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
59
+ const collForEach = (coll, cb) => coll?.forEach(cb);
60
+ const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
61
+
53
62
  const object = Object;
54
63
  const getPrototypeOf = (obj) => object.getPrototypeOf(obj);
55
64
  const objEntries = object.entries;
@@ -83,44 +92,6 @@ const objEnsure = (obj, id, getDefaultValue) => {
83
92
  return obj[id];
84
93
  };
85
94
 
86
- const jsonString = JSON.stringify;
87
- const jsonParse = JSON.parse;
88
- const jsonStringWithUndefined = (obj) =>
89
- jsonString(obj, (_key, value) => (isUndefined(value) ? UNDEFINED : value));
90
- const jsonParseWithUndefined = (str) =>
91
- // JSON.parse reviver removes properties with undefined values
92
- replaceUndefinedString(jsonParse(str));
93
- const replaceUndefinedString = (obj) =>
94
- obj === UNDEFINED
95
- ? void 0
96
- : isArray(obj)
97
- ? arrayMap(obj, replaceUndefinedString)
98
- : isObject(obj)
99
- ? objMap(obj, replaceUndefinedString)
100
- : obj;
101
-
102
- const MESSAGE_SEPARATOR = '\n';
103
- const ifPayloadValid = (payload, then) => {
104
- const splitAt = payload.indexOf(MESSAGE_SEPARATOR);
105
- if (splitAt !== -1) {
106
- then(slice(payload, 0, splitAt), slice(payload, splitAt + 1));
107
- }
108
- };
109
- const receivePayload = (payload, receive) =>
110
- ifPayloadValid(payload, (fromClientId, remainder) =>
111
- receive(fromClientId, ...jsonParseWithUndefined(remainder)),
112
- );
113
- const createPayload = (toClientId, ...args) =>
114
- createRawPayload(toClientId ?? EMPTY_STRING, jsonStringWithUndefined(args));
115
- const createRawPayload = (clientId, remainder) =>
116
- clientId + MESSAGE_SEPARATOR + remainder;
117
-
118
- const collSize = (coll) => coll?.size ?? 0;
119
- const collHas = (coll, keyOrValue) => coll?.has(keyOrValue) ?? false;
120
- const collIsEmpty = (coll) => isUndefined(coll) || collSize(coll) == 0;
121
- const collForEach = (coll, cb) => coll?.forEach(cb);
122
- const collDel = (coll, keyOrValue) => coll?.delete(keyOrValue);
123
-
124
95
  const map = Map;
125
96
  const mapNew = (entries) => new map(entries);
126
97
  const mapGet = (map2, key) => map2?.get(key);
@@ -172,6 +143,103 @@ const getUniqueId = (length = 16) =>
172
143
  EMPTY_STRING,
173
144
  );
174
145
 
146
+ const jsonString = JSON.stringify;
147
+ const jsonParse = JSON.parse;
148
+ const jsonStringWithUndefined = (obj) =>
149
+ jsonString(obj, (_key, value) => (isUndefined(value) ? UNDEFINED : value));
150
+ const jsonParseWithUndefined = (str) =>
151
+ // JSON.parse reviver removes properties with undefined values
152
+ replaceUndefinedString(jsonParse(str));
153
+ const replaceUndefinedString = (obj) =>
154
+ obj === UNDEFINED
155
+ ? void 0
156
+ : isArray(obj)
157
+ ? arrayMap(obj, replaceUndefinedString)
158
+ : isObject(obj)
159
+ ? objMap(obj, replaceUndefinedString)
160
+ : obj;
161
+
162
+ const MESSAGE_SEPARATOR = '\n';
163
+ const FRAGMENT = /^(.+)\n(\d+)\n(\d+)\n([\s\S]*)$/;
164
+ const ifPayloadValid = (payload, then) => {
165
+ const splitAt = payload.indexOf(MESSAGE_SEPARATOR);
166
+ if (splitAt !== -1) {
167
+ then(slice(payload, 0, splitAt), slice(payload, splitAt + 1));
168
+ }
169
+ };
170
+ const receivePayloadRemainder = (fromClientId, remainder, receive) =>
171
+ receive(fromClientId, ...jsonParseWithUndefined(remainder));
172
+ const createPayloadReceiver = (receive, fragmentTimeoutSeconds = 1) => {
173
+ const buffer = mapNew();
174
+ const setPendingTimeout = (bufferKey) =>
175
+ startTimeout(() => collDel(buffer, bufferKey), fragmentTimeoutSeconds);
176
+ const delPending = (bufferKey, pending) => {
177
+ stopTimeout(pending[3]);
178
+ collDel(buffer, bufferKey);
179
+ };
180
+ return (payload) =>
181
+ ifPayloadValid(payload, (fromClientId, remainder) => {
182
+ const [, messageId, indexStr, totalStr, fragment] =
183
+ strMatch(remainder, FRAGMENT) ?? [];
184
+ if (messageId) {
185
+ const index = parseInt(indexStr);
186
+ const total = parseInt(totalStr);
187
+ if (total > 0 && index >= 0 && index < total) {
188
+ const bufferKey = fromClientId + MESSAGE_SEPARATOR + messageId;
189
+ const pending = mapEnsure(buffer, bufferKey, () => [
190
+ [],
191
+ total,
192
+ total,
193
+ setPendingTimeout(bufferKey),
194
+ ]);
195
+ const [fragments] = pending;
196
+ if (total == pending[2] && isUndefined(fragments[index])) {
197
+ stopTimeout(pending[3]);
198
+ pending[3] = setPendingTimeout(bufferKey);
199
+ fragments[index] = fragment;
200
+ pending[1]--;
201
+ }
202
+ if (pending[1] == 0) {
203
+ delPending(bufferKey, pending);
204
+ receivePayloadRemainder(
205
+ fromClientId,
206
+ arrayJoin(fragments),
207
+ receive,
208
+ );
209
+ }
210
+ }
211
+ } else {
212
+ receivePayloadRemainder(fromClientId, remainder, receive);
213
+ }
214
+ });
215
+ };
216
+ const createRawPayload = (clientId, remainder) =>
217
+ clientId + MESSAGE_SEPARATOR + remainder;
218
+ const createPayloads = (toClientId, requestId, message, body, fragmentSize) => {
219
+ const clientId = toClientId ?? EMPTY_STRING;
220
+ const remainder = jsonStringWithUndefined([requestId, message, body]);
221
+ const maxFragmentSize = mathFloor(fragmentSize ?? 0);
222
+ if (
223
+ isUndefined(fragmentSize) ||
224
+ maxFragmentSize < 1 ||
225
+ size(remainder) <= maxFragmentSize
226
+ ) {
227
+ return [createRawPayload(clientId, remainder)];
228
+ }
229
+ const fragments = strMatch(
230
+ remainder,
231
+ new RegExp(`.{1,${maxFragmentSize}}`, 'g'),
232
+ );
233
+ const total = size(fragments ?? []);
234
+ const messageId = getUniqueId();
235
+ return arrayMap(fragments ?? [], (fragment, index) =>
236
+ createRawPayload(
237
+ clientId,
238
+ arrayJoin([messageId, index, total, fragment], MESSAGE_SEPARATOR),
239
+ ),
240
+ );
241
+ };
242
+
175
243
  const stampNew = (value, hlc) => (hlc ? [value, hlc] : [value]);
176
244
  const getLatestHlc = (hlc1, hlc2) =>
177
245
  /* istanbul ignore next */
@@ -745,17 +813,23 @@ const createWsSynchronizer = async (
745
813
  onSend,
746
814
  onReceive,
747
815
  onIgnoredError,
816
+ fragmentSize,
748
817
  ) => {
749
818
  const addEventListener = (event, handler) => {
750
819
  webSocket.addEventListener(event, handler);
751
820
  return () => webSocket.removeEventListener(event, handler);
752
821
  };
753
- const registerReceive = (receive) =>
754
- addEventListener(MESSAGE, ({data}) =>
755
- receivePayload(data.toString(UTF8), receive),
822
+ const registerReceive = (receive) => {
823
+ const receivePayload = createPayloadReceiver(
824
+ receive,
825
+ requestTimeoutSeconds,
756
826
  );
827
+ addEventListener(MESSAGE, ({data}) => receivePayload(data.toString(UTF8)));
828
+ };
757
829
  const send = (toClientId, ...args) =>
758
- webSocket.send(createPayload(toClientId, ...args));
830
+ arrayForEach(createPayloads(toClientId, ...args, fragmentSize), (payload) =>
831
+ webSocket.send(payload),
832
+ );
759
833
  const destroy = () => {
760
834
  webSocket.close();
761
835
  };
@@ -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();