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.
- package/@types/persisters/index.d.ts +4 -0
- package/@types/persisters/persister-durable-object-sql-storage/index.d.ts +9 -2
- package/@types/persisters/persister-durable-object-sql-storage/with-schemas/index.d.ts +9 -2
- package/@types/persisters/with-schemas/index.d.ts +4 -0
- package/@types/queries/index.d.ts +4 -0
- package/@types/queries/with-schemas/index.d.ts +4 -0
- package/@types/synchronizers/synchronizer-ws-client/index.d.ts +7 -3
- package/@types/synchronizers/synchronizer-ws-client/with-schemas/index.d.ts +7 -3
- package/@types/synchronizers/synchronizer-ws-server/index.d.ts +9 -3
- package/@types/synchronizers/synchronizer-ws-server/with-schemas/index.d.ts +9 -3
- package/@types/synchronizers/synchronizer-ws-server-simple/index.d.ts +6 -3
- package/@types/synchronizers/synchronizer-ws-server-simple/with-schemas/index.d.ts +6 -3
- package/@types/ui-react-dom/index.d.ts +10 -10
- package/@types/ui-react-dom/with-schemas/index.d.ts +10 -10
- package/@types/ui-react-dom-charts/index.d.ts +7 -7
- package/@types/ui-react-dom-charts/with-schemas/index.d.ts +7 -7
- package/@types/ui-react-inspector/index.d.ts +1 -1
- package/@types/ui-react-inspector/with-schemas/index.d.ts +1 -1
- package/@types/ui-solid-dom/index.d.ts +10 -10
- package/@types/ui-solid-dom/with-schemas/index.d.ts +10 -10
- package/@types/ui-solid-inspector/index.d.ts +1 -1
- package/@types/ui-solid-inspector/with-schemas/index.d.ts +1 -1
- package/@types/ui-svelte-dom/index.d.ts +10 -10
- package/@types/ui-svelte-dom/with-schemas/index.d.ts +10 -10
- package/@types/ui-svelte-inspector/index.d.ts +1 -1
- package/@types/ui-svelte-inspector/with-schemas/index.d.ts +1 -1
- package/index.js +191 -74
- package/min/index.js +1 -1
- package/min/index.js.gz +0 -0
- package/min/omni/index.js +1 -1
- package/min/omni/index.js.gz +0 -0
- package/min/omni/with-schemas/index.js +1 -1
- package/min/omni/with-schemas/index.js.gz +0 -0
- package/min/persisters/index.js +1 -1
- package/min/persisters/index.js.gz +0 -0
- package/min/persisters/persister-automerge/index.js +1 -1
- package/min/persisters/persister-automerge/index.js.gz +0 -0
- package/min/persisters/persister-automerge/with-schemas/index.js +1 -1
- package/min/persisters/persister-automerge/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-browser/index.js +1 -1
- package/min/persisters/persister-browser/index.js.gz +0 -0
- package/min/persisters/persister-browser/with-schemas/index.js +1 -1
- package/min/persisters/persister-browser/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-cr-sqlite-wasm/index.js +1 -1
- package/min/persisters/persister-cr-sqlite-wasm/index.js.gz +0 -0
- package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +1 -1
- package/min/persisters/persister-cr-sqlite-wasm/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-durable-object-sql-storage/index.js +1 -1
- package/min/persisters/persister-durable-object-sql-storage/index.js.gz +0 -0
- package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js +1 -1
- package/min/persisters/persister-durable-object-sql-storage/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-durable-object-storage/index.js +1 -1
- package/min/persisters/persister-durable-object-storage/index.js.gz +0 -0
- package/min/persisters/persister-durable-object-storage/with-schemas/index.js +1 -1
- package/min/persisters/persister-durable-object-storage/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-electric-sql/index.js +1 -1
- package/min/persisters/persister-electric-sql/index.js.gz +0 -0
- package/min/persisters/persister-electric-sql/with-schemas/index.js +1 -1
- package/min/persisters/persister-electric-sql/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-expo-sqlite/index.js +1 -1
- package/min/persisters/persister-expo-sqlite/index.js.gz +0 -0
- package/min/persisters/persister-expo-sqlite/with-schemas/index.js +1 -1
- package/min/persisters/persister-expo-sqlite/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-file/index.js +1 -1
- package/min/persisters/persister-file/index.js.gz +0 -0
- package/min/persisters/persister-file/with-schemas/index.js +1 -1
- package/min/persisters/persister-file/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-indexed-db/index.js +1 -1
- package/min/persisters/persister-indexed-db/index.js.gz +0 -0
- package/min/persisters/persister-indexed-db/with-schemas/index.js +1 -1
- package/min/persisters/persister-indexed-db/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-libsql/index.js +1 -1
- package/min/persisters/persister-libsql/index.js.gz +0 -0
- package/min/persisters/persister-libsql/with-schemas/index.js +1 -1
- package/min/persisters/persister-libsql/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-partykit-client/index.js +1 -1
- package/min/persisters/persister-partykit-client/index.js.gz +0 -0
- package/min/persisters/persister-partykit-client/with-schemas/index.js +1 -1
- package/min/persisters/persister-partykit-client/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-pglite/index.js +1 -1
- package/min/persisters/persister-pglite/index.js.gz +0 -0
- package/min/persisters/persister-pglite/with-schemas/index.js +1 -1
- package/min/persisters/persister-pglite/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-postgres/index.js +1 -1
- package/min/persisters/persister-postgres/index.js.gz +0 -0
- package/min/persisters/persister-postgres/with-schemas/index.js +1 -1
- package/min/persisters/persister-postgres/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-powersync/index.js +1 -1
- package/min/persisters/persister-powersync/index.js.gz +0 -0
- package/min/persisters/persister-powersync/with-schemas/index.js +1 -1
- package/min/persisters/persister-powersync/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-react-native-mmkv/index.js +1 -1
- package/min/persisters/persister-react-native-mmkv/index.js.gz +0 -0
- package/min/persisters/persister-react-native-mmkv/with-schemas/index.js +1 -1
- package/min/persisters/persister-react-native-mmkv/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-react-native-sqlite/index.js +1 -1
- package/min/persisters/persister-react-native-sqlite/index.js.gz +0 -0
- package/min/persisters/persister-react-native-sqlite/with-schemas/index.js +1 -1
- package/min/persisters/persister-react-native-sqlite/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-remote/index.js +1 -1
- package/min/persisters/persister-remote/index.js.gz +0 -0
- package/min/persisters/persister-remote/with-schemas/index.js +1 -1
- package/min/persisters/persister-remote/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-sqlite-bun/index.js +1 -1
- package/min/persisters/persister-sqlite-bun/index.js.gz +0 -0
- package/min/persisters/persister-sqlite-bun/with-schemas/index.js +1 -1
- package/min/persisters/persister-sqlite-bun/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-sqlite-wasm/index.js +1 -1
- package/min/persisters/persister-sqlite-wasm/index.js.gz +0 -0
- package/min/persisters/persister-sqlite-wasm/with-schemas/index.js +1 -1
- package/min/persisters/persister-sqlite-wasm/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-sqlite3/index.js +1 -1
- package/min/persisters/persister-sqlite3/index.js.gz +0 -0
- package/min/persisters/persister-sqlite3/with-schemas/index.js +1 -1
- package/min/persisters/persister-sqlite3/with-schemas/index.js.gz +0 -0
- package/min/persisters/persister-yjs/index.js +1 -1
- package/min/persisters/persister-yjs/index.js.gz +0 -0
- package/min/persisters/persister-yjs/with-schemas/index.js +1 -1
- package/min/persisters/persister-yjs/with-schemas/index.js.gz +0 -0
- package/min/persisters/with-schemas/index.js +1 -1
- package/min/persisters/with-schemas/index.js.gz +0 -0
- package/min/queries/index.js +1 -1
- package/min/queries/index.js.gz +0 -0
- package/min/queries/with-schemas/index.js +1 -1
- package/min/queries/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/index.js +1 -1
- package/min/synchronizers/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-broadcast-channel/index.js +1 -1
- package/min/synchronizers/synchronizer-broadcast-channel/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-local/index.js +1 -1
- package/min/synchronizers/synchronizer-local/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-local/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-local/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-client/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-client/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-client/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server-durable-object/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server-durable-object/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server-simple/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server-simple/index.js.gz +0 -0
- package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +1 -1
- package/min/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js.gz +0 -0
- package/min/synchronizers/with-schemas/index.js +1 -1
- package/min/synchronizers/with-schemas/index.js.gz +0 -0
- package/min/ui-react-inspector/index.js +1 -1
- package/min/ui-react-inspector/index.js.gz +0 -0
- package/min/ui-react-inspector/with-schemas/index.js +1 -1
- package/min/ui-react-inspector/with-schemas/index.js.gz +0 -0
- package/min/ui-solid-inspector/index.js +1 -1
- package/min/ui-solid-inspector/index.js.gz +0 -0
- package/min/ui-solid-inspector/with-schemas/index.js +1 -1
- package/min/ui-solid-inspector/with-schemas/index.js.gz +0 -0
- package/min/ui-svelte-inspector/index.js +1 -1
- package/min/ui-svelte-inspector/index.js.gz +0 -0
- package/min/ui-svelte-inspector/with-schemas/index.js +1 -1
- package/min/ui-svelte-inspector/with-schemas/index.js.gz +0 -0
- package/min/with-schemas/index.js +1 -1
- package/min/with-schemas/index.js.gz +0 -0
- package/omni/index.js +606 -179
- package/omni/with-schemas/index.js +606 -179
- package/package.json +12 -8
- package/persisters/index.js +49 -10
- package/persisters/persister-automerge/index.js +29 -5
- package/persisters/persister-automerge/with-schemas/index.js +29 -5
- package/persisters/persister-browser/index.js +29 -5
- package/persisters/persister-browser/with-schemas/index.js +29 -5
- package/persisters/persister-cr-sqlite-wasm/index.js +29 -5
- package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +29 -5
- package/persisters/persister-durable-object-sql-storage/index.js +44 -8
- package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +44 -8
- package/persisters/persister-durable-object-storage/index.js +31 -5
- package/persisters/persister-durable-object-storage/with-schemas/index.js +31 -5
- package/persisters/persister-electric-sql/index.js +29 -5
- package/persisters/persister-electric-sql/with-schemas/index.js +29 -5
- package/persisters/persister-expo-sqlite/index.js +29 -5
- package/persisters/persister-expo-sqlite/with-schemas/index.js +29 -5
- package/persisters/persister-file/index.js +31 -5
- package/persisters/persister-file/with-schemas/index.js +31 -5
- package/persisters/persister-indexed-db/index.js +29 -5
- package/persisters/persister-indexed-db/with-schemas/index.js +29 -5
- package/persisters/persister-libsql/index.js +29 -5
- package/persisters/persister-libsql/with-schemas/index.js +29 -5
- package/persisters/persister-partykit-client/index.js +31 -5
- package/persisters/persister-partykit-client/with-schemas/index.js +31 -5
- package/persisters/persister-pglite/index.js +49 -10
- package/persisters/persister-pglite/with-schemas/index.js +49 -10
- package/persisters/persister-postgres/index.js +49 -10
- package/persisters/persister-postgres/with-schemas/index.js +49 -10
- package/persisters/persister-powersync/index.js +29 -5
- package/persisters/persister-powersync/with-schemas/index.js +29 -5
- package/persisters/persister-react-native-mmkv/index.js +31 -5
- package/persisters/persister-react-native-mmkv/with-schemas/index.js +31 -5
- package/persisters/persister-react-native-sqlite/index.js +29 -5
- package/persisters/persister-react-native-sqlite/with-schemas/index.js +29 -5
- package/persisters/persister-remote/index.js +31 -5
- package/persisters/persister-remote/with-schemas/index.js +31 -5
- package/persisters/persister-sqlite-bun/index.js +29 -5
- package/persisters/persister-sqlite-bun/with-schemas/index.js +29 -5
- package/persisters/persister-sqlite-wasm/index.js +29 -5
- package/persisters/persister-sqlite-wasm/with-schemas/index.js +29 -5
- package/persisters/persister-sqlite3/index.js +29 -5
- package/persisters/persister-sqlite3/with-schemas/index.js +29 -5
- package/persisters/persister-yjs/index.js +29 -5
- package/persisters/persister-yjs/with-schemas/index.js +29 -5
- package/persisters/with-schemas/index.js +49 -10
- package/queries/index.js +205 -74
- package/queries/with-schemas/index.js +205 -74
- package/readme.md +14 -14
- package/releases.md +84 -67
- package/synchronizers/index.js +31 -5
- package/synchronizers/synchronizer-broadcast-channel/index.js +39 -13
- package/synchronizers/synchronizer-broadcast-channel/with-schemas/index.js +39 -13
- package/synchronizers/synchronizer-local/index.js +31 -5
- package/synchronizers/synchronizer-local/with-schemas/index.js +31 -5
- package/synchronizers/synchronizer-ws-client/index.js +220 -61
- package/synchronizers/synchronizer-ws-client/with-schemas/index.js +220 -61
- package/synchronizers/synchronizer-ws-server/index.js +253 -73
- package/synchronizers/synchronizer-ws-server/with-schemas/index.js +253 -73
- package/synchronizers/synchronizer-ws-server-durable-object/index.js +88 -40
- package/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +88 -40
- package/synchronizers/synchronizer-ws-server-simple/index.js +184 -60
- package/synchronizers/synchronizer-ws-server-simple/with-schemas/index.js +184 -60
- package/synchronizers/with-schemas/index.js +31 -5
- package/ui-react-inspector/index.js +29 -5
- package/ui-react-inspector/with-schemas/index.js +29 -5
- package/ui-solid-inspector/index.js +29 -5
- package/ui-solid-inspector/with-schemas/index.js +29 -5
- package/ui-svelte-inspector/index.js +29 -5
- package/ui-svelte-inspector/with-schemas/index.js +29 -5
- 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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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] +=
|
|
459
|
-
bufferedSize +=
|
|
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 (
|
|
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
|
-
|
|
559
|
+
(isMultipleChannelIdTooLarge(body) ||
|
|
560
|
+
isMultipleChannelIdStructureValid(body))
|
|
521
561
|
: control == 2 /* Unsubscribe */ &&
|
|
522
562
|
isNull(requestId) &&
|
|
523
563
|
isString(body) &&
|
|
524
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
719
|
+
invalidate(errorNew(ERROR_SYNC_MESSAGE));
|
|
618
720
|
}
|
|
619
721
|
};
|
|
620
|
-
const destroy = () =>
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
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
|
-
|
|
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);
|