@cntryl/fitz 0.0.3 → 0.0.4
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/README.md +14 -0
- package/dist/index.cjs +379 -67
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +379 -68
- package/dist/index.mjs.map +1 -1
- package/dist/types/client/client.d.ts +1 -1
- package/dist/types/client/client.d.ts.map +1 -1
- package/dist/types/client/connection.d.ts +2 -1
- package/dist/types/client/connection.d.ts.map +1 -1
- package/dist/types/core/types.d.ts +6 -0
- package/dist/types/core/types.d.ts.map +1 -1
- package/dist/types/core/wake-gate.d.ts +11 -0
- package/dist/types/core/wake-gate.d.ts.map +1 -0
- package/dist/types/domains/queue/client.d.ts +5 -0
- package/dist/types/domains/queue/client.d.ts.map +1 -1
- package/dist/types/domains/schedule/client.d.ts +4 -1
- package/dist/types/domains/schedule/client.d.ts.map +1 -1
- package/dist/types/domains/stream/client.d.ts +7 -0
- package/dist/types/domains/stream/client.d.ts.map +1 -1
- package/dist/types/index.d.ts +3 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/transport/tcp.d.ts.map +1 -1
- package/dist/types/transport/types.d.ts +7 -0
- package/dist/types/transport/types.d.ts.map +1 -1
- package/dist/types/transport/websocket.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -981,7 +981,7 @@ function createMultiplexer(observability = {}) {
|
|
|
981
981
|
const span = tracer?.startSpan("fitz.request", attributes);
|
|
982
982
|
let spanEnded = false;
|
|
983
983
|
if (signal?.aborted) {
|
|
984
|
-
const error = abortError$
|
|
984
|
+
const error = abortError$3();
|
|
985
985
|
span?.recordException(error);
|
|
986
986
|
span?.end();
|
|
987
987
|
meter?.counter("fitz.request.failed", 1, {
|
|
@@ -1055,7 +1055,7 @@ function createMultiplexer(observability = {}) {
|
|
|
1055
1055
|
heapifyUp(requestEntry.timeoutIndex);
|
|
1056
1056
|
if (signal) {
|
|
1057
1057
|
onAbort = () => {
|
|
1058
|
-
failRequest(abortError$
|
|
1058
|
+
failRequest(abortError$3());
|
|
1059
1059
|
};
|
|
1060
1060
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
1061
1061
|
}
|
|
@@ -1075,7 +1075,7 @@ function createMultiplexer(observability = {}) {
|
|
|
1075
1075
|
return deferred.promise;
|
|
1076
1076
|
}, (err) => {
|
|
1077
1077
|
if (!finalize()) {
|
|
1078
|
-
if (signal?.aborted) throw abortError$
|
|
1078
|
+
if (signal?.aborted) throw abortError$3();
|
|
1079
1079
|
throw err;
|
|
1080
1080
|
}
|
|
1081
1081
|
unregisterRequest(messageType, requestEntry);
|
|
@@ -1088,7 +1088,7 @@ function createMultiplexer(observability = {}) {
|
|
|
1088
1088
|
span?.end();
|
|
1089
1089
|
spanEnded = true;
|
|
1090
1090
|
}
|
|
1091
|
-
if (signal?.aborted) throw abortError$
|
|
1091
|
+
if (signal?.aborted) throw abortError$3();
|
|
1092
1092
|
throw err;
|
|
1093
1093
|
});
|
|
1094
1094
|
};
|
|
@@ -1200,7 +1200,7 @@ function createMultiplexer(observability = {}) {
|
|
|
1200
1200
|
const Multiplexer = function(observability = {}) {
|
|
1201
1201
|
return createMultiplexer(observability);
|
|
1202
1202
|
};
|
|
1203
|
-
function abortError$
|
|
1203
|
+
function abortError$3() {
|
|
1204
1204
|
const error = /* @__PURE__ */ new Error("The operation was aborted");
|
|
1205
1205
|
error.name = "AbortError";
|
|
1206
1206
|
return error;
|
|
@@ -1284,7 +1284,7 @@ function shouldRetryOperation(retryClass, error) {
|
|
|
1284
1284
|
//#region src/client/connection.ts
|
|
1285
1285
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1286
1286
|
const sleepWithAbort = async (ms, signal) => {
|
|
1287
|
-
if (signal?.aborted) throw abortError$
|
|
1287
|
+
if (signal?.aborted) throw abortError$2();
|
|
1288
1288
|
if (ms <= 0) return;
|
|
1289
1289
|
await new Promise((resolve, reject) => {
|
|
1290
1290
|
const timer = setTimeout(() => {
|
|
@@ -1293,7 +1293,7 @@ const sleepWithAbort = async (ms, signal) => {
|
|
|
1293
1293
|
}, ms);
|
|
1294
1294
|
const onAbort = () => {
|
|
1295
1295
|
cleanup();
|
|
1296
|
-
reject(abortError$
|
|
1296
|
+
reject(abortError$2());
|
|
1297
1297
|
};
|
|
1298
1298
|
const cleanup = () => {
|
|
1299
1299
|
clearTimeout(timer);
|
|
@@ -1369,7 +1369,7 @@ function createRequestGate(maxConcurrency, maxQueueSize) {
|
|
|
1369
1369
|
let closed = false;
|
|
1370
1370
|
const queue = [];
|
|
1371
1371
|
const acquire = async (signal) => {
|
|
1372
|
-
if (signal?.aborted) throw abortError$
|
|
1372
|
+
if (signal?.aborted) throw abortError$2();
|
|
1373
1373
|
if (closed) throw connectionClosedError();
|
|
1374
1374
|
return await new Promise((resolve, reject) => {
|
|
1375
1375
|
const grant = () => {
|
|
@@ -1392,7 +1392,7 @@ function createRequestGate(maxConcurrency, maxQueueSize) {
|
|
|
1392
1392
|
waiter.onAbort = () => {
|
|
1393
1393
|
removeWaiter(waiter);
|
|
1394
1394
|
cleanup();
|
|
1395
|
-
reject(abortError$
|
|
1395
|
+
reject(abortError$2());
|
|
1396
1396
|
};
|
|
1397
1397
|
if (signal) signal.addEventListener("abort", waiter.onAbort, { once: true });
|
|
1398
1398
|
if (activeCount < maxConcurrency) {
|
|
@@ -1441,7 +1441,7 @@ function createRequestGate(maxConcurrency, maxQueueSize) {
|
|
|
1441
1441
|
close
|
|
1442
1442
|
};
|
|
1443
1443
|
}
|
|
1444
|
-
function abortError$
|
|
1444
|
+
function abortError$2() {
|
|
1445
1445
|
const error = /* @__PURE__ */ new Error("The operation was aborted");
|
|
1446
1446
|
error.name = "AbortError";
|
|
1447
1447
|
return error;
|
|
@@ -1450,14 +1450,14 @@ function connectionClosedError() {
|
|
|
1450
1450
|
return new ConnectionError("Connection closed", { state: "CLOSED" });
|
|
1451
1451
|
}
|
|
1452
1452
|
function throwIfAborted$1(signal) {
|
|
1453
|
-
if (signal?.aborted) throw abortError$
|
|
1453
|
+
if (signal?.aborted) throw abortError$2();
|
|
1454
1454
|
}
|
|
1455
1455
|
function isAbortError$1(error) {
|
|
1456
1456
|
return error instanceof Error && error.name === "AbortError";
|
|
1457
1457
|
}
|
|
1458
1458
|
const waitForSharedPromise$1 = async (promise, signal) => {
|
|
1459
1459
|
if (!signal) return promise;
|
|
1460
|
-
if (signal.aborted) throw abortError$
|
|
1460
|
+
if (signal.aborted) throw abortError$2();
|
|
1461
1461
|
return await new Promise((resolve, reject) => {
|
|
1462
1462
|
let settled = false;
|
|
1463
1463
|
const cleanup = () => {
|
|
@@ -1470,7 +1470,7 @@ const waitForSharedPromise$1 = async (promise, signal) => {
|
|
|
1470
1470
|
callback();
|
|
1471
1471
|
};
|
|
1472
1472
|
const onAbort = () => {
|
|
1473
|
-
settle(() => reject(abortError$
|
|
1473
|
+
settle(() => reject(abortError$2()));
|
|
1474
1474
|
};
|
|
1475
1475
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
1476
1476
|
promise.then((value) => {
|
|
@@ -1491,6 +1491,9 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1491
1491
|
const retryMaxAttempts = options.retry?.maxAttempts ?? 3;
|
|
1492
1492
|
const retryBackoffMs = options.retry?.backoffMs ?? 100;
|
|
1493
1493
|
const retryMaxBackoffMs = options.retry?.maxBackoffMs ?? 1e3;
|
|
1494
|
+
const heartbeatEnabled = options.heartbeat?.enabled ?? true;
|
|
1495
|
+
const heartbeatIntervalMs = options.heartbeat?.intervalMs ?? 1e4;
|
|
1496
|
+
const heartbeatTimeoutMs = options.heartbeat?.timeoutMs ?? 3e4;
|
|
1494
1497
|
const maxInFlightRequests = options.maxInFlightRequests ?? 256;
|
|
1495
1498
|
const maxRequestQueueSize = options.maxRequestQueueSize ?? 1024;
|
|
1496
1499
|
const observability = options.observability;
|
|
@@ -1507,6 +1510,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1507
1510
|
let permanentlyClosed = false;
|
|
1508
1511
|
let connectPromise = null;
|
|
1509
1512
|
let reconnectPromise = null;
|
|
1513
|
+
let connectionLossPromise = null;
|
|
1510
1514
|
let reconnectRestoreActive = false;
|
|
1511
1515
|
let authOutcome = null;
|
|
1512
1516
|
let authRejected = false;
|
|
@@ -1516,6 +1520,10 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1516
1520
|
const closeAbortController = new AbortController();
|
|
1517
1521
|
const connectionScope = createScope("connection");
|
|
1518
1522
|
const readyListeners = /* @__PURE__ */ new Set();
|
|
1523
|
+
let heartbeatTimer = null;
|
|
1524
|
+
let heartbeatTransport = null;
|
|
1525
|
+
let heartbeatPending = false;
|
|
1526
|
+
let lastActivityAt = Date.now();
|
|
1519
1527
|
const log = (level, event, fields) => {
|
|
1520
1528
|
observability?.logger?.log(level, event, fields);
|
|
1521
1529
|
};
|
|
@@ -1585,6 +1593,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1585
1593
|
permanentlyClosed = true;
|
|
1586
1594
|
closeRequested = true;
|
|
1587
1595
|
receiveLoopAbort = true;
|
|
1596
|
+
stopHeartbeat();
|
|
1588
1597
|
closeAbortController.abort();
|
|
1589
1598
|
asyncHandlerDispatcher.close();
|
|
1590
1599
|
const scopeDisposePromise = connectionScope.dispose();
|
|
@@ -1766,7 +1775,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1766
1775
|
cb();
|
|
1767
1776
|
};
|
|
1768
1777
|
const onAbort = () => {
|
|
1769
|
-
settle(() => reject(abortError$
|
|
1778
|
+
settle(() => reject(abortError$2()));
|
|
1770
1779
|
};
|
|
1771
1780
|
const onStateChange = () => {
|
|
1772
1781
|
const failure = readyFailure();
|
|
@@ -1849,6 +1858,75 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1849
1858
|
}
|
|
1850
1859
|
}
|
|
1851
1860
|
};
|
|
1861
|
+
const withWriteLock = async (operation) => {
|
|
1862
|
+
const prior = writeChain;
|
|
1863
|
+
let release;
|
|
1864
|
+
writeChain = new Promise((resolve) => {
|
|
1865
|
+
release = resolve;
|
|
1866
|
+
});
|
|
1867
|
+
await prior;
|
|
1868
|
+
try {
|
|
1869
|
+
return await operation();
|
|
1870
|
+
} finally {
|
|
1871
|
+
release();
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
1874
|
+
const markOutboundActivity = () => {
|
|
1875
|
+
lastActivityAt = Date.now();
|
|
1876
|
+
};
|
|
1877
|
+
const markRemoteActivity = () => {
|
|
1878
|
+
lastActivityAt = Date.now();
|
|
1879
|
+
};
|
|
1880
|
+
const stopHeartbeat = () => {
|
|
1881
|
+
if (heartbeatTimer) {
|
|
1882
|
+
clearTimeout(heartbeatTimer);
|
|
1883
|
+
heartbeatTimer = null;
|
|
1884
|
+
}
|
|
1885
|
+
heartbeatTransport = null;
|
|
1886
|
+
heartbeatPending = false;
|
|
1887
|
+
};
|
|
1888
|
+
const startHeartbeat = (activeTransport) => {
|
|
1889
|
+
if (!heartbeatEnabled) return;
|
|
1890
|
+
stopHeartbeat();
|
|
1891
|
+
activeTransport.enableKeepAlive?.(heartbeatIntervalMs);
|
|
1892
|
+
heartbeatTransport = activeTransport;
|
|
1893
|
+
markRemoteActivity();
|
|
1894
|
+
const scheduleNext = () => {
|
|
1895
|
+
if (closeRequested || receiveLoopAbort || heartbeatTransport !== activeTransport) return;
|
|
1896
|
+
heartbeatTimer = setTimeout(tick, heartbeatIntervalMs);
|
|
1897
|
+
};
|
|
1898
|
+
const tick = () => {
|
|
1899
|
+
heartbeatTimer = null;
|
|
1900
|
+
if (closeRequested || receiveLoopAbort || heartbeatTransport !== activeTransport) return;
|
|
1901
|
+
const now = Date.now();
|
|
1902
|
+
if (now - lastActivityAt < heartbeatIntervalMs) {
|
|
1903
|
+
scheduleNext();
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
const supportsHeartbeat = activeTransport.supportsHeartbeat?.() ?? true;
|
|
1907
|
+
if (!heartbeatPending && supportsHeartbeat && activeTransport.sendHeartbeat) {
|
|
1908
|
+
heartbeatPending = true;
|
|
1909
|
+
const heartbeatSentAt = now;
|
|
1910
|
+
const dispatchHeartbeat = (heartbeat) => activeTransport.sendHeartbeat(heartbeat);
|
|
1911
|
+
withWriteLock(async () => {
|
|
1912
|
+
await dispatchHeartbeat({ timeoutMs: heartbeatTimeoutMs });
|
|
1913
|
+
}).then(() => {
|
|
1914
|
+
if (heartbeatTransport !== activeTransport) return;
|
|
1915
|
+
heartbeatPending = false;
|
|
1916
|
+
markRemoteActivity();
|
|
1917
|
+
}).catch((error) => {
|
|
1918
|
+
if (heartbeatTransport !== activeTransport) return;
|
|
1919
|
+
heartbeatPending = false;
|
|
1920
|
+
if (lastActivityAt > heartbeatSentAt) return;
|
|
1921
|
+
const heartbeatError = new TransportError(`Heartbeat failed: ${describeError(error)}`);
|
|
1922
|
+
activeTransport.close().catch(() => void 0);
|
|
1923
|
+
handleConnectionLoss(heartbeatError);
|
|
1924
|
+
});
|
|
1925
|
+
}
|
|
1926
|
+
scheduleNext();
|
|
1927
|
+
};
|
|
1928
|
+
scheduleNext();
|
|
1929
|
+
};
|
|
1852
1930
|
const openAndAuthenticate = async (isReconnect, signal) => {
|
|
1853
1931
|
throwIfAborted$1(signal);
|
|
1854
1932
|
receiveLoopAbort = false;
|
|
@@ -1856,10 +1934,13 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1856
1934
|
requestGate = createRequestGate(maxInFlightRequests, maxRequestQueueSize);
|
|
1857
1935
|
const activeTransport = transportFactory();
|
|
1858
1936
|
transport = activeTransport;
|
|
1937
|
+
stopHeartbeat();
|
|
1859
1938
|
setState(isReconnect ? "RECONNECTING" : "CONNECTING");
|
|
1860
1939
|
emitLifecycleEvent(isReconnect ? "reconnect_start" : "connect_start");
|
|
1861
1940
|
await activeTransport.connect();
|
|
1941
|
+
markRemoteActivity();
|
|
1862
1942
|
if (closeRequested) {
|
|
1943
|
+
stopHeartbeat();
|
|
1863
1944
|
await activeTransport.close().catch(() => void 0);
|
|
1864
1945
|
if (transport === activeTransport) transport = null;
|
|
1865
1946
|
throw connectionClosedError();
|
|
@@ -1892,6 +1973,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1892
1973
|
hasEstablishedSession = true;
|
|
1893
1974
|
reconnectExhausted = false;
|
|
1894
1975
|
setState("AUTHENTICATED");
|
|
1976
|
+
startHeartbeat(activeTransport);
|
|
1895
1977
|
if (!isReconnect) multiplexer.setConnected();
|
|
1896
1978
|
emitLifecycleEvent(isReconnect ? "reconnect_succeeded" : "connect_succeeded");
|
|
1897
1979
|
} catch (error) {
|
|
@@ -1899,6 +1981,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1899
1981
|
reconnectRestoreActive = false;
|
|
1900
1982
|
multiplexer.setDisconnected();
|
|
1901
1983
|
emitDisconnect();
|
|
1984
|
+
stopHeartbeat();
|
|
1902
1985
|
if (activeTransport) await activeTransport.close().catch(() => void 0);
|
|
1903
1986
|
if (transport === activeTransport) transport = null;
|
|
1904
1987
|
const rejectedAuth = error instanceof AuthenticationError;
|
|
@@ -1906,7 +1989,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1906
1989
|
if (closeRequested) setState("CLOSED");
|
|
1907
1990
|
else setState(rejectedAuth ? "CLOSED" : "DISCONNECTED");
|
|
1908
1991
|
emitLifecycleEvent(isReconnect ? "reconnect_failed" : "connect_failed", error);
|
|
1909
|
-
if (isAbortError$1(error)) throw abortError$
|
|
1992
|
+
if (isAbortError$1(error)) throw abortError$2();
|
|
1910
1993
|
throw error;
|
|
1911
1994
|
}
|
|
1912
1995
|
};
|
|
@@ -1914,10 +1997,12 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1914
1997
|
const token = await tokenProvider();
|
|
1915
1998
|
const frame = FrameCodec.encodeFrame(1, utf8Encoder.encode(token));
|
|
1916
1999
|
await ensureTransport().send(frame);
|
|
2000
|
+
markOutboundActivity();
|
|
1917
2001
|
};
|
|
1918
2002
|
const startReceiveLoop = async () => {
|
|
1919
2003
|
while (!receiveLoopAbort && !closeRequested) try {
|
|
1920
2004
|
const data = await ensureTransport().receive();
|
|
2005
|
+
markRemoteActivity();
|
|
1921
2006
|
const frames = frameParser.parseFrames(data);
|
|
1922
2007
|
for (const frame of frames) multiplexer.dispatch(frame.messageType, frame.payload);
|
|
1923
2008
|
} catch (error) {
|
|
@@ -1927,6 +2012,17 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1927
2012
|
}
|
|
1928
2013
|
};
|
|
1929
2014
|
const handleConnectionLoss = async (error) => {
|
|
2015
|
+
if (connectionLossPromise) {
|
|
2016
|
+
await connectionLossPromise;
|
|
2017
|
+
return;
|
|
2018
|
+
}
|
|
2019
|
+
connectionLossPromise = handleConnectionLossOnce(error).finally(() => {
|
|
2020
|
+
connectionLossPromise = null;
|
|
2021
|
+
});
|
|
2022
|
+
await connectionLossPromise;
|
|
2023
|
+
};
|
|
2024
|
+
const handleConnectionLossOnce = async (error) => {
|
|
2025
|
+
stopHeartbeat();
|
|
1930
2026
|
multiplexer.setDisconnected();
|
|
1931
2027
|
requestGate.close();
|
|
1932
2028
|
emitDisconnect();
|
|
@@ -2013,17 +2109,10 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
2013
2109
|
notifyReadyListeners();
|
|
2014
2110
|
};
|
|
2015
2111
|
const sendSerialized = async (transport, data) => {
|
|
2016
|
-
|
|
2017
|
-
let release;
|
|
2018
|
-
writeChain = new Promise((resolve) => {
|
|
2019
|
-
release = resolve;
|
|
2020
|
-
});
|
|
2021
|
-
await prior;
|
|
2022
|
-
try {
|
|
2112
|
+
await withWriteLock(async () => {
|
|
2023
2113
|
await transport.send(data);
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
}
|
|
2114
|
+
markOutboundActivity();
|
|
2115
|
+
});
|
|
2027
2116
|
};
|
|
2028
2117
|
const emitLifecycleEvent = (event, error, attempt) => {
|
|
2029
2118
|
const payload = {
|
|
@@ -2099,6 +2188,7 @@ function createWebSocketTransport(url, options = {}) {
|
|
|
2099
2188
|
let receiverResolve = null;
|
|
2100
2189
|
const timeout = options.timeout ?? 3e4;
|
|
2101
2190
|
const maxFrameSize = options.maxFrameSize ?? 65535;
|
|
2191
|
+
const receiveTimeoutEnabled = options.receiveTimeout ?? true;
|
|
2102
2192
|
const enqueueMessage = (data) => {
|
|
2103
2193
|
if (receiverResolve) {
|
|
2104
2194
|
receiverResolve(data);
|
|
@@ -2172,6 +2262,56 @@ function createWebSocketTransport(url, options = {}) {
|
|
|
2172
2262
|
}
|
|
2173
2263
|
});
|
|
2174
2264
|
};
|
|
2265
|
+
const sendHeartbeat = async (heartbeatOptions) => {
|
|
2266
|
+
if (!connected || !ws) throw new TransportError("WebSocket is not connected");
|
|
2267
|
+
const activeWs = ws;
|
|
2268
|
+
if (typeof activeWs.ping !== "function" || typeof activeWs.once !== "function" || typeof activeWs.removeListener !== "function") throw new TransportError("WebSocket heartbeat is not supported");
|
|
2269
|
+
const socket = activeWs;
|
|
2270
|
+
return new Promise((resolve, reject) => {
|
|
2271
|
+
let settled = false;
|
|
2272
|
+
let timeoutId = null;
|
|
2273
|
+
const cleanup = () => {
|
|
2274
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2275
|
+
socket.removeListener("pong", onPong);
|
|
2276
|
+
socket.removeListener("close", onClose);
|
|
2277
|
+
socket.removeListener("error", onError);
|
|
2278
|
+
};
|
|
2279
|
+
const settle = (callback) => {
|
|
2280
|
+
if (settled) return;
|
|
2281
|
+
settled = true;
|
|
2282
|
+
cleanup();
|
|
2283
|
+
callback();
|
|
2284
|
+
};
|
|
2285
|
+
const onPong = () => {
|
|
2286
|
+
settle(resolve);
|
|
2287
|
+
};
|
|
2288
|
+
const onClose = () => {
|
|
2289
|
+
settle(() => reject(new TransportError("WebSocket closed during heartbeat")));
|
|
2290
|
+
};
|
|
2291
|
+
const onError = (...args) => {
|
|
2292
|
+
const event = args[0];
|
|
2293
|
+
const message = event instanceof Error ? event.message : event?.message || "unknown error";
|
|
2294
|
+
settle(() => reject(new TransportError(`WebSocket heartbeat failed: ${message}`)));
|
|
2295
|
+
};
|
|
2296
|
+
timeoutId = setTimeout(() => {
|
|
2297
|
+
settle(() => reject(new TimeoutError(`WebSocket heartbeat timeout after ${heartbeatOptions.timeoutMs}ms`)));
|
|
2298
|
+
}, heartbeatOptions.timeoutMs);
|
|
2299
|
+
socket.once("pong", onPong);
|
|
2300
|
+
socket.once("close", onClose);
|
|
2301
|
+
socket.once("error", onError);
|
|
2302
|
+
try {
|
|
2303
|
+
socket.ping(new Uint8Array(), void 0, (err) => {
|
|
2304
|
+
if (err) settle(() => reject(new TransportError(`WebSocket ping failed: ${err.message}`)));
|
|
2305
|
+
});
|
|
2306
|
+
} catch (err) {
|
|
2307
|
+
settle(() => reject(new TransportError(`WebSocket heartbeat error: ${err instanceof Error ? err.message : String(err)}`)));
|
|
2308
|
+
}
|
|
2309
|
+
});
|
|
2310
|
+
};
|
|
2311
|
+
const supportsHeartbeat = () => {
|
|
2312
|
+
return typeof ws?.ping === "function" && typeof ws?.once === "function" && typeof ws?.removeListener === "function";
|
|
2313
|
+
};
|
|
2314
|
+
const enableKeepAlive = () => void 0;
|
|
2175
2315
|
const receive = async () => {
|
|
2176
2316
|
if (receiveQueue.length > 0) {
|
|
2177
2317
|
const message = receiveQueue.shift();
|
|
@@ -2180,12 +2320,12 @@ function createWebSocketTransport(url, options = {}) {
|
|
|
2180
2320
|
}
|
|
2181
2321
|
if (!connected) throw new TransportError("Connection closed");
|
|
2182
2322
|
return new Promise((resolve, reject) => {
|
|
2183
|
-
const timeoutId = setTimeout(() => {
|
|
2323
|
+
const timeoutId = receiveTimeoutEnabled ? setTimeout(() => {
|
|
2184
2324
|
receiverResolve = null;
|
|
2185
2325
|
reject(new TimeoutError(`WebSocket receive timeout after ${timeout}ms`));
|
|
2186
|
-
}, timeout);
|
|
2326
|
+
}, timeout) : null;
|
|
2187
2327
|
receiverResolve = (data) => {
|
|
2188
|
-
clearTimeout(timeoutId);
|
|
2328
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2189
2329
|
receiverResolve = null;
|
|
2190
2330
|
if (data === null) {
|
|
2191
2331
|
reject(new TransportError("Connection closed"));
|
|
@@ -2220,6 +2360,9 @@ function createWebSocketTransport(url, options = {}) {
|
|
|
2220
2360
|
connect,
|
|
2221
2361
|
send,
|
|
2222
2362
|
receive,
|
|
2363
|
+
sendHeartbeat,
|
|
2364
|
+
supportsHeartbeat,
|
|
2365
|
+
enableKeepAlive,
|
|
2223
2366
|
close,
|
|
2224
2367
|
getUrl,
|
|
2225
2368
|
isConnected
|
|
@@ -2245,6 +2388,7 @@ function createTcpTransport(url, options = {}) {
|
|
|
2245
2388
|
let receiverResolve = null;
|
|
2246
2389
|
const timeout = options.timeout ?? 3e4;
|
|
2247
2390
|
const maxFrameSize = options.maxFrameSize ?? 65535;
|
|
2391
|
+
const receiveTimeoutEnabled = options.receiveTimeout ?? true;
|
|
2248
2392
|
let lengthBuffer = new Uint8Array(4);
|
|
2249
2393
|
let lengthOffset = 0;
|
|
2250
2394
|
let currentMessageLength = null;
|
|
@@ -2319,7 +2463,7 @@ function createTcpTransport(url, options = {}) {
|
|
|
2319
2463
|
clearTimeout(connectTimeout);
|
|
2320
2464
|
connected = true;
|
|
2321
2465
|
activeSocket.setNoDelay(true);
|
|
2322
|
-
activeSocket.setTimeout(timeout);
|
|
2466
|
+
activeSocket.setTimeout(receiveTimeoutEnabled ? timeout : 0);
|
|
2323
2467
|
resolve();
|
|
2324
2468
|
});
|
|
2325
2469
|
activeSocket.on("data", (chunk) => {
|
|
@@ -2337,8 +2481,10 @@ function createTcpTransport(url, options = {}) {
|
|
|
2337
2481
|
if (receiverResolve) receiverResolve(new Uint8Array(0));
|
|
2338
2482
|
});
|
|
2339
2483
|
activeSocket.on("timeout", () => {
|
|
2340
|
-
|
|
2341
|
-
|
|
2484
|
+
if (receiveTimeoutEnabled) {
|
|
2485
|
+
activeSocket.destroy();
|
|
2486
|
+
connected = false;
|
|
2487
|
+
}
|
|
2342
2488
|
});
|
|
2343
2489
|
} catch (err) {
|
|
2344
2490
|
reject(new TransportError(`Failed to create TCP socket: ${err instanceof Error ? err.message : String(err)}`));
|
|
@@ -2363,6 +2509,10 @@ function createTcpTransport(url, options = {}) {
|
|
|
2363
2509
|
});
|
|
2364
2510
|
});
|
|
2365
2511
|
};
|
|
2512
|
+
const enableKeepAlive = (intervalMs) => {
|
|
2513
|
+
socket?.setKeepAlive(true, intervalMs);
|
|
2514
|
+
};
|
|
2515
|
+
const supportsHeartbeat = () => false;
|
|
2366
2516
|
const receive = async () => {
|
|
2367
2517
|
if (receiveQueue.length > 0) {
|
|
2368
2518
|
const message = receiveQueue.shift();
|
|
@@ -2370,12 +2520,12 @@ function createTcpTransport(url, options = {}) {
|
|
|
2370
2520
|
return message;
|
|
2371
2521
|
}
|
|
2372
2522
|
return new Promise((resolve, reject) => {
|
|
2373
|
-
const timeoutId = setTimeout(() => {
|
|
2523
|
+
const timeoutId = receiveTimeoutEnabled ? setTimeout(() => {
|
|
2374
2524
|
receiverResolve = null;
|
|
2375
2525
|
reject(new TimeoutError(`TCP receive timeout after ${timeout}ms`));
|
|
2376
|
-
}, timeout);
|
|
2526
|
+
}, timeout) : null;
|
|
2377
2527
|
receiverResolve = (data) => {
|
|
2378
|
-
clearTimeout(timeoutId);
|
|
2528
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2379
2529
|
receiverResolve = null;
|
|
2380
2530
|
if (data.length === 0) reject(new TransportError("Connection closed"));
|
|
2381
2531
|
else resolve(data);
|
|
@@ -2407,6 +2557,8 @@ function createTcpTransport(url, options = {}) {
|
|
|
2407
2557
|
connect,
|
|
2408
2558
|
send,
|
|
2409
2559
|
receive,
|
|
2560
|
+
supportsHeartbeat,
|
|
2561
|
+
enableKeepAlive,
|
|
2410
2562
|
close,
|
|
2411
2563
|
getUrl,
|
|
2412
2564
|
isConnected
|
|
@@ -2785,6 +2937,64 @@ const KvClient = function(connection) {
|
|
|
2785
2937
|
return createKvClient(connection);
|
|
2786
2938
|
};
|
|
2787
2939
|
//#endregion
|
|
2940
|
+
//#region src/core/wake-gate.ts
|
|
2941
|
+
function abortError$1() {
|
|
2942
|
+
const error = /* @__PURE__ */ new Error("The operation was aborted");
|
|
2943
|
+
error.name = "AbortError";
|
|
2944
|
+
return error;
|
|
2945
|
+
}
|
|
2946
|
+
function createWakeGate() {
|
|
2947
|
+
let currentVersion = 0;
|
|
2948
|
+
const waiters = /* @__PURE__ */ new Set();
|
|
2949
|
+
const cleanup = (waiter) => {
|
|
2950
|
+
waiters.delete(waiter);
|
|
2951
|
+
if (waiter.signal && waiter.onAbort) waiter.signal.removeEventListener("abort", waiter.onAbort);
|
|
2952
|
+
};
|
|
2953
|
+
const wake = () => {
|
|
2954
|
+
currentVersion += 1;
|
|
2955
|
+
const version = currentVersion;
|
|
2956
|
+
for (const waiter of Array.from(waiters)) {
|
|
2957
|
+
cleanup(waiter);
|
|
2958
|
+
waiter.resolve(version);
|
|
2959
|
+
}
|
|
2960
|
+
return version;
|
|
2961
|
+
};
|
|
2962
|
+
const waitAfter = async (observedVersion, options = {}) => {
|
|
2963
|
+
if (currentVersion > observedVersion) return currentVersion;
|
|
2964
|
+
if (options.signal?.aborted) throw abortError$1();
|
|
2965
|
+
return await new Promise((resolve, reject) => {
|
|
2966
|
+
const waiter = {
|
|
2967
|
+
observedVersion,
|
|
2968
|
+
resolve,
|
|
2969
|
+
reject,
|
|
2970
|
+
signal: options.signal
|
|
2971
|
+
};
|
|
2972
|
+
waiter.onAbort = () => {
|
|
2973
|
+
cleanup(waiter);
|
|
2974
|
+
reject(abortError$1());
|
|
2975
|
+
};
|
|
2976
|
+
if (options.signal) options.signal.addEventListener("abort", waiter.onAbort, { once: true });
|
|
2977
|
+
if (currentVersion > observedVersion) {
|
|
2978
|
+
cleanup(waiter);
|
|
2979
|
+
resolve(currentVersion);
|
|
2980
|
+
return;
|
|
2981
|
+
}
|
|
2982
|
+
waiters.add(waiter);
|
|
2983
|
+
});
|
|
2984
|
+
};
|
|
2985
|
+
const wait = async (options) => {
|
|
2986
|
+
return await waitAfter(currentVersion, options);
|
|
2987
|
+
};
|
|
2988
|
+
return {
|
|
2989
|
+
get version() {
|
|
2990
|
+
return currentVersion;
|
|
2991
|
+
},
|
|
2992
|
+
wake,
|
|
2993
|
+
waitAfter,
|
|
2994
|
+
wait
|
|
2995
|
+
};
|
|
2996
|
+
}
|
|
2997
|
+
//#endregion
|
|
2788
2998
|
//#region src/domains/queue/codec.ts
|
|
2789
2999
|
/**
|
|
2790
3000
|
* Queue domain codec for encoding and decoding protocol messages.
|
|
@@ -3123,43 +3333,55 @@ function createQueueClient(connection) {
|
|
|
3123
3333
|
let items = await reserveOnce(route, leaseSeconds, batchSize);
|
|
3124
3334
|
if (items.length > 0) return items;
|
|
3125
3335
|
const deadline = Date.now() + waitSeconds * 1e3;
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
pendingNotifications += 1;
|
|
3130
|
-
if (!waiter) return;
|
|
3131
|
-
const resolve = waiter;
|
|
3132
|
-
waiter = void 0;
|
|
3133
|
-
pendingNotifications = 0;
|
|
3134
|
-
resolve();
|
|
3336
|
+
const wakeGate = createWakeGate();
|
|
3337
|
+
const subscription = await subscribe(route, () => {
|
|
3338
|
+
wakeGate.wake();
|
|
3135
3339
|
});
|
|
3136
3340
|
try {
|
|
3137
3341
|
while (true) {
|
|
3342
|
+
const observed = wakeGate.version;
|
|
3138
3343
|
items = await reserveOnce(route, leaseSeconds, batchSize);
|
|
3139
3344
|
if (items.length > 0) return items;
|
|
3140
3345
|
const remainingMs = deadline - Date.now();
|
|
3141
3346
|
if (remainingMs <= 0) return items;
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
}
|
|
3148
|
-
const release = () => {
|
|
3149
|
-
clearTimeout(timeoutId);
|
|
3150
|
-
if (waiter === release) waiter = void 0;
|
|
3151
|
-
resolve();
|
|
3152
|
-
};
|
|
3153
|
-
const timeoutId = setTimeout(release, remainingMs);
|
|
3154
|
-
waiter = release;
|
|
3347
|
+
const waitPromise = wakeGate.waitAfter(observed);
|
|
3348
|
+
let timeoutId = null;
|
|
3349
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
3350
|
+
timeoutId = setTimeout(() => {
|
|
3351
|
+
resolve("timeout");
|
|
3352
|
+
}, remainingMs);
|
|
3155
3353
|
});
|
|
3354
|
+
if (await Promise.race([waitPromise.then(() => {
|
|
3355
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
3356
|
+
return "wake";
|
|
3357
|
+
}), timeoutPromise]) === "timeout") return items;
|
|
3156
3358
|
}
|
|
3157
3359
|
} finally {
|
|
3158
|
-
await subscription.unsubscribe();
|
|
3360
|
+
await subscription.unsubscribe().catch(() => void 0);
|
|
3159
3361
|
}
|
|
3160
3362
|
};
|
|
3161
|
-
const
|
|
3162
|
-
|
|
3363
|
+
const reserveWhenAvailable = async function* (route, options) {
|
|
3364
|
+
assertQueueReserveRoute(route);
|
|
3365
|
+
const wakeGate = createWakeGate();
|
|
3366
|
+
const subscription = await subscribe(route, () => {
|
|
3367
|
+
wakeGate.wake();
|
|
3368
|
+
});
|
|
3369
|
+
try {
|
|
3370
|
+
while (true) {
|
|
3371
|
+
const observed = wakeGate.version;
|
|
3372
|
+
const items = await reserveOnce(route, options.leaseSeconds, options.batchSize ?? 1, options.signal);
|
|
3373
|
+
if (items.length > 0) {
|
|
3374
|
+
yield items;
|
|
3375
|
+
continue;
|
|
3376
|
+
}
|
|
3377
|
+
await wakeGate.waitAfter(observed, { signal: options.signal });
|
|
3378
|
+
}
|
|
3379
|
+
} finally {
|
|
3380
|
+
await subscription.unsubscribe().catch(() => void 0);
|
|
3381
|
+
}
|
|
3382
|
+
};
|
|
3383
|
+
const reserveOnce = async (route, leaseSeconds, batchSize, signal) => {
|
|
3384
|
+
const response = await requestFrame(202, QueueCodec.encodeReserve(route, leaseSeconds, batchSize), signal);
|
|
3163
3385
|
const decoded = QueueCodec.decodeReserveResponse(response);
|
|
3164
3386
|
checkStatus(decoded, "RESERVE");
|
|
3165
3387
|
return (decoded.items ?? []).map((item) => createQueueItem(item.id, item.token, item.body, route, connection));
|
|
@@ -3248,6 +3470,7 @@ function createQueueClient(connection) {
|
|
|
3248
3470
|
return {
|
|
3249
3471
|
enqueue,
|
|
3250
3472
|
reserve,
|
|
3473
|
+
reserveWhenAvailable,
|
|
3251
3474
|
subscribe
|
|
3252
3475
|
};
|
|
3253
3476
|
}
|
|
@@ -4966,6 +5189,33 @@ function createStreamClient(connection) {
|
|
|
4966
5189
|
const page = await readPage(route, startOffset, limit, options);
|
|
4967
5190
|
return StreamCodec.flattenStreamReadItems(page.items);
|
|
4968
5191
|
};
|
|
5192
|
+
const readWhenCommitted = async function* (route, options) {
|
|
5193
|
+
assertStreamPattern(route);
|
|
5194
|
+
const wakeGate = createWakeGate();
|
|
5195
|
+
const subscription = await subscribe(route, () => {
|
|
5196
|
+
wakeGate.wake();
|
|
5197
|
+
});
|
|
5198
|
+
try {
|
|
5199
|
+
let offset = options.offset;
|
|
5200
|
+
while (true) {
|
|
5201
|
+
const observed = wakeGate.version;
|
|
5202
|
+
const page = await readPage(route, offset, options.batchSize ?? 100, {
|
|
5203
|
+
maxBytes: options.maxBytes,
|
|
5204
|
+
filter: options.filter,
|
|
5205
|
+
signal: options.signal
|
|
5206
|
+
});
|
|
5207
|
+
if (page.items.length > 0) {
|
|
5208
|
+
offset = page.cursor.lastResourceOffset + 1n;
|
|
5209
|
+
const records = StreamCodec.flattenStreamReadItems(page.items);
|
|
5210
|
+
if (records.length > 0) yield records;
|
|
5211
|
+
}
|
|
5212
|
+
if (page.cursor.hasMore) continue;
|
|
5213
|
+
await wakeGate.waitAfter(observed, { signal: options.signal });
|
|
5214
|
+
}
|
|
5215
|
+
} finally {
|
|
5216
|
+
await subscription.unsubscribe().catch(() => void 0);
|
|
5217
|
+
}
|
|
5218
|
+
};
|
|
4969
5219
|
const consume = async (route, startOffset, limit = 100, options) => {
|
|
4970
5220
|
return createAsyncIterableIterator(createSliceIterator(await read(route, startOffset, limit, options)));
|
|
4971
5221
|
};
|
|
@@ -5085,6 +5335,7 @@ function createStreamClient(connection) {
|
|
|
5085
5335
|
begin,
|
|
5086
5336
|
readPage,
|
|
5087
5337
|
read,
|
|
5338
|
+
readWhenCommitted,
|
|
5088
5339
|
consume,
|
|
5089
5340
|
peek,
|
|
5090
5341
|
metadata,
|
|
@@ -5265,6 +5516,7 @@ function createScheduleClient(connection) {
|
|
|
5265
5516
|
const { requestFrame, requestReconnectFrame } = createDomainClient(connection);
|
|
5266
5517
|
const subscriptionsByPattern = /* @__PURE__ */ new Map();
|
|
5267
5518
|
const patternsBySubId = /* @__PURE__ */ new Map();
|
|
5519
|
+
const pendingNotificationsBySubId = /* @__PURE__ */ new Map();
|
|
5268
5520
|
let notifyHandlerInitialized = false;
|
|
5269
5521
|
let nextHandlerId = 1;
|
|
5270
5522
|
connection.onReconnect(async () => {
|
|
@@ -5275,6 +5527,7 @@ function createScheduleClient(connection) {
|
|
|
5275
5527
|
}));
|
|
5276
5528
|
subscriptionsByPattern.clear();
|
|
5277
5529
|
patternsBySubId.clear();
|
|
5530
|
+
pendingNotificationsBySubId.clear();
|
|
5278
5531
|
for (const subscription of subscriptions) {
|
|
5279
5532
|
const subId = await subscribeWire(subscription.pattern, requestReconnectFrame);
|
|
5280
5533
|
subscriptionsByPattern.set(subscription.pattern, {
|
|
@@ -5299,6 +5552,29 @@ function createScheduleClient(connection) {
|
|
|
5299
5552
|
const decoded = ScheduleCodec.decodeListResponse(assertSuccess(response, "LIST"));
|
|
5300
5553
|
return [decoded.entries, decoded.totalCount];
|
|
5301
5554
|
};
|
|
5555
|
+
const waitForNotifications = async function* (route, options = {}) {
|
|
5556
|
+
assertConcreteScheduleRoute(route);
|
|
5557
|
+
const wakeGate = createWakeGate();
|
|
5558
|
+
const pendingNotifications = [];
|
|
5559
|
+
const subscription = await subscribe(route, (notification) => {
|
|
5560
|
+
pendingNotifications.push(notification);
|
|
5561
|
+
wakeGate.wake();
|
|
5562
|
+
});
|
|
5563
|
+
try {
|
|
5564
|
+
while (true) {
|
|
5565
|
+
const notification = pendingNotifications.shift();
|
|
5566
|
+
if (notification) {
|
|
5567
|
+
yield notification;
|
|
5568
|
+
continue;
|
|
5569
|
+
}
|
|
5570
|
+
const observed = wakeGate.version;
|
|
5571
|
+
if (pendingNotifications.length > 0) continue;
|
|
5572
|
+
await wakeGate.waitAfter(observed, { signal: options.signal });
|
|
5573
|
+
}
|
|
5574
|
+
} finally {
|
|
5575
|
+
await subscription.unsubscribe().catch(() => void 0);
|
|
5576
|
+
}
|
|
5577
|
+
};
|
|
5302
5578
|
const subscribe = async (pattern, handler) => {
|
|
5303
5579
|
assertConcreteScheduleRoute(pattern);
|
|
5304
5580
|
initNotifyHandler();
|
|
@@ -5322,6 +5598,7 @@ function createScheduleClient(connection) {
|
|
|
5322
5598
|
patternsBySubId.set(subId, pattern);
|
|
5323
5599
|
}
|
|
5324
5600
|
subscription.handlers.set(handlerId, handler);
|
|
5601
|
+
flushPendingNotifications(subId);
|
|
5325
5602
|
return createScheduleSubscription(subId, pattern, async () => {
|
|
5326
5603
|
await unsubscribe(pattern, handlerId);
|
|
5327
5604
|
});
|
|
@@ -5333,6 +5610,7 @@ function createScheduleClient(connection) {
|
|
|
5333
5610
|
if (subscription.handlers.size > 0) return;
|
|
5334
5611
|
subscriptionsByPattern.delete(pattern);
|
|
5335
5612
|
patternsBySubId.delete(subscription.subId);
|
|
5613
|
+
pendingNotificationsBySubId.delete(subscription.subId);
|
|
5336
5614
|
const response = await requestFrame(704, ScheduleCodec.encodeUnsubscribe(pattern));
|
|
5337
5615
|
ScheduleCodec.decodeUnsubscribeResponse(assertSuccess(response, "UNSUBSCRIBE"));
|
|
5338
5616
|
};
|
|
@@ -5343,16 +5621,40 @@ function createScheduleClient(connection) {
|
|
|
5343
5621
|
try {
|
|
5344
5622
|
const decoded = ScheduleCodec.decodeNotification(payload);
|
|
5345
5623
|
const pattern = patternsBySubId.get(decoded.subId);
|
|
5346
|
-
if (!pattern)
|
|
5624
|
+
if (!pattern) {
|
|
5625
|
+
queuePendingNotification(decoded.subId, { payload: decoded.payload });
|
|
5626
|
+
return;
|
|
5627
|
+
}
|
|
5347
5628
|
const subscription = subscriptionsByPattern.get(pattern);
|
|
5348
|
-
if (!subscription)
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
});
|
|
5629
|
+
if (!subscription) {
|
|
5630
|
+
queuePendingNotification(decoded.subId, { payload: decoded.payload });
|
|
5631
|
+
return;
|
|
5632
|
+
}
|
|
5633
|
+
dispatchNotification(subscription, { payload: decoded.payload });
|
|
5353
5634
|
} catch {}
|
|
5354
5635
|
});
|
|
5355
5636
|
};
|
|
5637
|
+
const queuePendingNotification = (subId, notification) => {
|
|
5638
|
+
const existing = pendingNotificationsBySubId.get(subId);
|
|
5639
|
+
if (existing) {
|
|
5640
|
+
existing.push(notification);
|
|
5641
|
+
return;
|
|
5642
|
+
}
|
|
5643
|
+
pendingNotificationsBySubId.set(subId, [notification]);
|
|
5644
|
+
};
|
|
5645
|
+
const flushPendingNotifications = (subId) => {
|
|
5646
|
+
const pending = pendingNotificationsBySubId.get(subId);
|
|
5647
|
+
if (!pending || pending.length === 0) return;
|
|
5648
|
+
pendingNotificationsBySubId.delete(subId);
|
|
5649
|
+
const subscription = Array.from(subscriptionsByPattern.values()).find((entry) => entry.subId === subId);
|
|
5650
|
+
if (!subscription) return;
|
|
5651
|
+
for (const notification of pending) dispatchNotification(subscription, notification);
|
|
5652
|
+
};
|
|
5653
|
+
const dispatchNotification = (subscription, notification) => {
|
|
5654
|
+
for (const handler of subscription.handlers.values()) connection.dispatchAsyncHandler(async () => {
|
|
5655
|
+
await handler(notification);
|
|
5656
|
+
});
|
|
5657
|
+
};
|
|
5356
5658
|
const assertSuccess = (payload, operation) => {
|
|
5357
5659
|
const result = parseStandardResponse(payload);
|
|
5358
5660
|
if (result.success) return result.data;
|
|
@@ -5369,7 +5671,8 @@ function createScheduleClient(connection) {
|
|
|
5369
5671
|
create,
|
|
5370
5672
|
cancel,
|
|
5371
5673
|
list,
|
|
5372
|
-
subscribe
|
|
5674
|
+
subscribe,
|
|
5675
|
+
waitForNotifications
|
|
5373
5676
|
};
|
|
5374
5677
|
}
|
|
5375
5678
|
const ScheduleClient = function(connection) {
|
|
@@ -5437,6 +5740,12 @@ function createClient(config) {
|
|
|
5437
5740
|
maxBackoffMs: 1e3,
|
|
5438
5741
|
...config.retry
|
|
5439
5742
|
},
|
|
5743
|
+
heartbeat: {
|
|
5744
|
+
enabled: true,
|
|
5745
|
+
intervalMs: 1e4,
|
|
5746
|
+
timeoutMs: 3e4,
|
|
5747
|
+
...config.heartbeat
|
|
5748
|
+
},
|
|
5440
5749
|
asyncHandlers: {
|
|
5441
5750
|
maxConcurrency: Infinity,
|
|
5442
5751
|
timeoutMs: 3e4,
|
|
@@ -5468,7 +5777,8 @@ function createClient(config) {
|
|
|
5468
5777
|
if (connection) return connection;
|
|
5469
5778
|
connection = createConnection(() => createTransport(resolvedConfig.url, resolvedConfig.transport, {
|
|
5470
5779
|
timeout: resolvedConfig.timeout,
|
|
5471
|
-
maxFrameSize: resolvedConfig.maxFrameSize
|
|
5780
|
+
maxFrameSize: resolvedConfig.maxFrameSize,
|
|
5781
|
+
receiveTimeout: resolvedConfig.heartbeat?.enabled === false
|
|
5472
5782
|
}), resolveTokenProvider(), {
|
|
5473
5783
|
timeout: resolvedConfig.timeout,
|
|
5474
5784
|
authSettleDelayMs: resolvedConfig.authSettleDelayMs,
|
|
@@ -5476,6 +5786,7 @@ function createClient(config) {
|
|
|
5476
5786
|
maxRequestQueueSize: resolvedConfig.maxRequestQueueSize,
|
|
5477
5787
|
reconnect: resolvedConfig.reconnect,
|
|
5478
5788
|
retry: resolvedConfig.retry,
|
|
5789
|
+
heartbeat: resolvedConfig.heartbeat,
|
|
5479
5790
|
observability,
|
|
5480
5791
|
asyncHandlers: resolvedConfig.asyncHandlers
|
|
5481
5792
|
});
|
|
@@ -5731,6 +6042,6 @@ function isAbortError(error) {
|
|
|
5731
6042
|
return error instanceof Error && error.name === "AbortError";
|
|
5732
6043
|
}
|
|
5733
6044
|
//#endregion
|
|
5734
|
-
export { AuthenticationError, Client, CodecError, ConnectionError, ConnectionState, ErrCodeKvBackendError, ErrCodeKvIsolationConflict, ErrCodeLeaseHeld, ErrCodeQueueFull, ErrCodeRpcBackpressure, ErrCodeRpcCorrelationNotFound, ErrCodeRpcRouteNotRegistered, ErrCodeRpcTimeout, ErrCodeRpcUnauthorized, ErrCodeRpcWorkerNotFound, ErrKvConflictingWrite, ErrKvKeyNotFound, ErrKvLeaseExpired, ErrKvOperationNotAllowed, ErrKvTransactionAborted, ErrLeaseHeld, ErrLeaseInvalidToken, ErrLeaseNotFound, ErrNoticeGeneral, ErrQueueFull, ErrQueueInvalidDelay, ErrQueueInvalidToken, ErrQueueMessageNotFound, ErrQueueNotFound, ErrRpcHandlerError, ErrRpcHandlerNotFound, ErrRpcInvalidRequest, ErrRpcTimeout, ErrScheduleInvalidCron, ErrScheduleInvalidDelay, ErrScheduleInvalidTimestamp, ErrScheduleNotFound, ErrScheduleTaskNotFound, ErrStreamExpectedOffsetMismatch, ErrStreamFull, ErrStreamInvalidOffset, ErrStreamNotFound, ErrStreamOffsetOutOfRange, ErrStreamSessionClosed, ErrStreamSessionNotFound, FitzError, KvClient, KvError, LeaseClient, LeaseError, NoticeClient, NoticeError, ProtocolError, QueueClient, QueueError, RequestQueueFullError, RpcClient, RpcError, ScheduleClient, ScheduleError, StreamClient, StreamError, TimeoutError, TransportError, createClient, createTaskGroup, isRetryable };
|
|
6045
|
+
export { AuthenticationError, Client, CodecError, ConnectionError, ConnectionState, ErrCodeKvBackendError, ErrCodeKvIsolationConflict, ErrCodeLeaseHeld, ErrCodeQueueFull, ErrCodeRpcBackpressure, ErrCodeRpcCorrelationNotFound, ErrCodeRpcRouteNotRegistered, ErrCodeRpcTimeout, ErrCodeRpcUnauthorized, ErrCodeRpcWorkerNotFound, ErrKvConflictingWrite, ErrKvKeyNotFound, ErrKvLeaseExpired, ErrKvOperationNotAllowed, ErrKvTransactionAborted, ErrLeaseHeld, ErrLeaseInvalidToken, ErrLeaseNotFound, ErrNoticeGeneral, ErrQueueFull, ErrQueueInvalidDelay, ErrQueueInvalidToken, ErrQueueMessageNotFound, ErrQueueNotFound, ErrRpcHandlerError, ErrRpcHandlerNotFound, ErrRpcInvalidRequest, ErrRpcTimeout, ErrScheduleInvalidCron, ErrScheduleInvalidDelay, ErrScheduleInvalidTimestamp, ErrScheduleNotFound, ErrScheduleTaskNotFound, ErrStreamExpectedOffsetMismatch, ErrStreamFull, ErrStreamInvalidOffset, ErrStreamNotFound, ErrStreamOffsetOutOfRange, ErrStreamSessionClosed, ErrStreamSessionNotFound, FitzError, KvClient, KvError, LeaseClient, LeaseError, NoticeClient, NoticeError, ProtocolError, QueueClient, QueueError, RequestQueueFullError, RpcClient, RpcError, ScheduleClient, ScheduleError, StreamClient, StreamError, TimeoutError, TransportError, createClient, createTaskGroup, createWakeGate, isRetryable };
|
|
5735
6046
|
|
|
5736
6047
|
//# sourceMappingURL=index.mjs.map
|