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.
- package/@types/_internal/queries/with-schemas/index.d.ts +1 -2
- package/@types/_internal/ui/with-schemas/index.d.ts +8 -16
- package/@types/persisters/index.d.ts +32 -12
- package/@types/persisters/persister-durable-object-sql-storage/index.d.ts +9 -8
- package/@types/persisters/persister-durable-object-sql-storage/with-schemas/index.d.ts +10 -10
- package/@types/persisters/with-schemas/index.d.ts +56 -20
- package/@types/queries/index.d.ts +1 -7
- package/@types/queries/with-schemas/index.d.ts +5 -15
- package/@types/store/index.d.ts +1 -2
- package/@types/store/with-schemas/index.d.ts +19 -29
- package/@types/synchronizers/synchronizer-ws-client/index.d.ts +9 -3
- package/@types/synchronizers/synchronizer-ws-client/with-schemas/index.d.ts +10 -3
- package/@types/synchronizers/synchronizer-ws-server/index.d.ts +15 -2
- package/@types/synchronizers/synchronizer-ws-server/with-schemas/index.d.ts +17 -2
- package/@types/synchronizers/synchronizer-ws-server-durable-object/index.d.ts +51 -0
- package/@types/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.d.ts +51 -0
- package/@types/ui-react/with-schemas/index.d.ts +3 -6
- package/@types/ui-react-dom/index.d.ts +10 -10
- package/@types/ui-react-dom/with-schemas/index.d.ts +16 -22
- package/@types/ui-react-dom-charts/index.d.ts +7 -7
- package/@types/ui-react-dom-charts/with-schemas/index.d.ts +10 -11
- 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/with-schemas/index.d.ts +4 -8
- package/@types/ui-solid-dom/index.d.ts +10 -10
- package/@types/ui-solid-dom/with-schemas/index.d.ts +16 -22
- 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 +16 -22
- 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 +21 -19
- package/indexes/index.js +6 -8
- package/indexes/with-schemas/index.js +6 -8
- package/metrics/index.js +6 -8
- package/metrics/with-schemas/index.js +6 -8
- 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-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-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-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-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-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-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/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/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/with-schemas/index.js +1 -1
- package/min/with-schemas/index.js.gz +0 -0
- package/omni/index.js +311 -141
- package/omni/with-schemas/index.js +311 -141
- package/package.json +7 -7
- package/persisters/index.js +129 -37
- package/persisters/persister-cr-sqlite-wasm/index.js +129 -37
- package/persisters/persister-cr-sqlite-wasm/with-schemas/index.js +129 -37
- package/persisters/persister-durable-object-sql-storage/index.js +271 -110
- package/persisters/persister-durable-object-sql-storage/with-schemas/index.js +271 -110
- package/persisters/persister-electric-sql/index.js +129 -37
- package/persisters/persister-electric-sql/with-schemas/index.js +129 -37
- package/persisters/persister-expo-sqlite/index.js +129 -37
- package/persisters/persister-expo-sqlite/with-schemas/index.js +129 -37
- package/persisters/persister-libsql/index.js +129 -37
- package/persisters/persister-libsql/with-schemas/index.js +129 -37
- package/persisters/persister-pglite/index.js +130 -37
- package/persisters/persister-pglite/with-schemas/index.js +130 -37
- package/persisters/persister-postgres/index.js +130 -37
- package/persisters/persister-postgres/with-schemas/index.js +130 -37
- package/persisters/persister-powersync/index.js +182 -117
- package/persisters/persister-powersync/with-schemas/index.js +182 -117
- package/persisters/persister-react-native-sqlite/index.js +129 -37
- package/persisters/persister-react-native-sqlite/with-schemas/index.js +129 -37
- package/persisters/persister-sqlite-bun/index.js +129 -37
- package/persisters/persister-sqlite-bun/with-schemas/index.js +129 -37
- package/persisters/persister-sqlite-wasm/index.js +129 -37
- package/persisters/persister-sqlite-wasm/with-schemas/index.js +129 -37
- package/persisters/persister-sqlite3/index.js +129 -37
- package/persisters/persister-sqlite3/with-schemas/index.js +129 -37
- package/persisters/with-schemas/index.js +129 -37
- package/queries/index.js +21 -19
- package/queries/with-schemas/index.js +21 -19
- package/readme.md +14 -14
- package/relationships/index.js +6 -8
- package/relationships/with-schemas/index.js +6 -8
- package/releases.md +57 -57
- package/synchronizers/synchronizer-ws-client/index.js +116 -42
- package/synchronizers/synchronizer-ws-client/with-schemas/index.js +116 -42
- package/synchronizers/synchronizer-ws-server/index.js +109 -28
- package/synchronizers/synchronizer-ws-server/with-schemas/index.js +109 -28
- package/synchronizers/synchronizer-ws-server-durable-object/index.js +122 -38
- package/synchronizers/synchronizer-ws-server-durable-object/with-schemas/index.js +122 -38
- package/ui-svelte/index.js +1 -1
- package/ui-svelte/with-schemas/index.js +1 -1
- package/ui-svelte-dom/index.js +2 -2
- package/ui-svelte-dom/with-schemas/index.js +2 -2
- package/ui-svelte-inspector/index.js +2 -2
- package/ui-svelte-inspector/with-schemas/index.js +2 -2
- 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
|
-
|
|
755
|
-
|
|
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
|
-
|
|
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
|
-
|
|
755
|
-
|
|
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
|
-
|
|
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
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
);
|
|
260
|
-
const
|
|
261
|
-
|
|
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
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
(
|
|
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
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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
|
-
|
|
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();
|