@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.cjs
CHANGED
|
@@ -978,7 +978,7 @@ function createMultiplexer(observability = {}) {
|
|
|
978
978
|
const span = tracer?.startSpan("fitz.request", attributes);
|
|
979
979
|
let spanEnded = false;
|
|
980
980
|
if (signal?.aborted) {
|
|
981
|
-
const error = abortError$
|
|
981
|
+
const error = abortError$3();
|
|
982
982
|
span?.recordException(error);
|
|
983
983
|
span?.end();
|
|
984
984
|
meter?.counter("fitz.request.failed", 1, {
|
|
@@ -1052,7 +1052,7 @@ function createMultiplexer(observability = {}) {
|
|
|
1052
1052
|
heapifyUp(requestEntry.timeoutIndex);
|
|
1053
1053
|
if (signal) {
|
|
1054
1054
|
onAbort = () => {
|
|
1055
|
-
failRequest(abortError$
|
|
1055
|
+
failRequest(abortError$3());
|
|
1056
1056
|
};
|
|
1057
1057
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
1058
1058
|
}
|
|
@@ -1072,7 +1072,7 @@ function createMultiplexer(observability = {}) {
|
|
|
1072
1072
|
return deferred.promise;
|
|
1073
1073
|
}, (err) => {
|
|
1074
1074
|
if (!finalize()) {
|
|
1075
|
-
if (signal?.aborted) throw abortError$
|
|
1075
|
+
if (signal?.aborted) throw abortError$3();
|
|
1076
1076
|
throw err;
|
|
1077
1077
|
}
|
|
1078
1078
|
unregisterRequest(messageType, requestEntry);
|
|
@@ -1085,7 +1085,7 @@ function createMultiplexer(observability = {}) {
|
|
|
1085
1085
|
span?.end();
|
|
1086
1086
|
spanEnded = true;
|
|
1087
1087
|
}
|
|
1088
|
-
if (signal?.aborted) throw abortError$
|
|
1088
|
+
if (signal?.aborted) throw abortError$3();
|
|
1089
1089
|
throw err;
|
|
1090
1090
|
});
|
|
1091
1091
|
};
|
|
@@ -1197,7 +1197,7 @@ function createMultiplexer(observability = {}) {
|
|
|
1197
1197
|
const Multiplexer = function(observability = {}) {
|
|
1198
1198
|
return createMultiplexer(observability);
|
|
1199
1199
|
};
|
|
1200
|
-
function abortError$
|
|
1200
|
+
function abortError$3() {
|
|
1201
1201
|
const error = /* @__PURE__ */ new Error("The operation was aborted");
|
|
1202
1202
|
error.name = "AbortError";
|
|
1203
1203
|
return error;
|
|
@@ -1281,7 +1281,7 @@ function shouldRetryOperation(retryClass, error) {
|
|
|
1281
1281
|
//#region src/client/connection.ts
|
|
1282
1282
|
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1283
1283
|
const sleepWithAbort = async (ms, signal) => {
|
|
1284
|
-
if (signal?.aborted) throw abortError$
|
|
1284
|
+
if (signal?.aborted) throw abortError$2();
|
|
1285
1285
|
if (ms <= 0) return;
|
|
1286
1286
|
await new Promise((resolve, reject) => {
|
|
1287
1287
|
const timer = setTimeout(() => {
|
|
@@ -1290,7 +1290,7 @@ const sleepWithAbort = async (ms, signal) => {
|
|
|
1290
1290
|
}, ms);
|
|
1291
1291
|
const onAbort = () => {
|
|
1292
1292
|
cleanup();
|
|
1293
|
-
reject(abortError$
|
|
1293
|
+
reject(abortError$2());
|
|
1294
1294
|
};
|
|
1295
1295
|
const cleanup = () => {
|
|
1296
1296
|
clearTimeout(timer);
|
|
@@ -1366,7 +1366,7 @@ function createRequestGate(maxConcurrency, maxQueueSize) {
|
|
|
1366
1366
|
let closed = false;
|
|
1367
1367
|
const queue = [];
|
|
1368
1368
|
const acquire = async (signal) => {
|
|
1369
|
-
if (signal?.aborted) throw abortError$
|
|
1369
|
+
if (signal?.aborted) throw abortError$2();
|
|
1370
1370
|
if (closed) throw connectionClosedError();
|
|
1371
1371
|
return await new Promise((resolve, reject) => {
|
|
1372
1372
|
const grant = () => {
|
|
@@ -1389,7 +1389,7 @@ function createRequestGate(maxConcurrency, maxQueueSize) {
|
|
|
1389
1389
|
waiter.onAbort = () => {
|
|
1390
1390
|
removeWaiter(waiter);
|
|
1391
1391
|
cleanup();
|
|
1392
|
-
reject(abortError$
|
|
1392
|
+
reject(abortError$2());
|
|
1393
1393
|
};
|
|
1394
1394
|
if (signal) signal.addEventListener("abort", waiter.onAbort, { once: true });
|
|
1395
1395
|
if (activeCount < maxConcurrency) {
|
|
@@ -1438,7 +1438,7 @@ function createRequestGate(maxConcurrency, maxQueueSize) {
|
|
|
1438
1438
|
close
|
|
1439
1439
|
};
|
|
1440
1440
|
}
|
|
1441
|
-
function abortError$
|
|
1441
|
+
function abortError$2() {
|
|
1442
1442
|
const error = /* @__PURE__ */ new Error("The operation was aborted");
|
|
1443
1443
|
error.name = "AbortError";
|
|
1444
1444
|
return error;
|
|
@@ -1447,14 +1447,14 @@ function connectionClosedError() {
|
|
|
1447
1447
|
return new ConnectionError("Connection closed", { state: "CLOSED" });
|
|
1448
1448
|
}
|
|
1449
1449
|
function throwIfAborted$1(signal) {
|
|
1450
|
-
if (signal?.aborted) throw abortError$
|
|
1450
|
+
if (signal?.aborted) throw abortError$2();
|
|
1451
1451
|
}
|
|
1452
1452
|
function isAbortError$1(error) {
|
|
1453
1453
|
return error instanceof Error && error.name === "AbortError";
|
|
1454
1454
|
}
|
|
1455
1455
|
const waitForSharedPromise$1 = async (promise, signal) => {
|
|
1456
1456
|
if (!signal) return promise;
|
|
1457
|
-
if (signal.aborted) throw abortError$
|
|
1457
|
+
if (signal.aborted) throw abortError$2();
|
|
1458
1458
|
return await new Promise((resolve, reject) => {
|
|
1459
1459
|
let settled = false;
|
|
1460
1460
|
const cleanup = () => {
|
|
@@ -1467,7 +1467,7 @@ const waitForSharedPromise$1 = async (promise, signal) => {
|
|
|
1467
1467
|
callback();
|
|
1468
1468
|
};
|
|
1469
1469
|
const onAbort = () => {
|
|
1470
|
-
settle(() => reject(abortError$
|
|
1470
|
+
settle(() => reject(abortError$2()));
|
|
1471
1471
|
};
|
|
1472
1472
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
1473
1473
|
promise.then((value) => {
|
|
@@ -1488,6 +1488,9 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1488
1488
|
const retryMaxAttempts = options.retry?.maxAttempts ?? 3;
|
|
1489
1489
|
const retryBackoffMs = options.retry?.backoffMs ?? 100;
|
|
1490
1490
|
const retryMaxBackoffMs = options.retry?.maxBackoffMs ?? 1e3;
|
|
1491
|
+
const heartbeatEnabled = options.heartbeat?.enabled ?? true;
|
|
1492
|
+
const heartbeatIntervalMs = options.heartbeat?.intervalMs ?? 1e4;
|
|
1493
|
+
const heartbeatTimeoutMs = options.heartbeat?.timeoutMs ?? 3e4;
|
|
1491
1494
|
const maxInFlightRequests = options.maxInFlightRequests ?? 256;
|
|
1492
1495
|
const maxRequestQueueSize = options.maxRequestQueueSize ?? 1024;
|
|
1493
1496
|
const observability = options.observability;
|
|
@@ -1504,6 +1507,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1504
1507
|
let permanentlyClosed = false;
|
|
1505
1508
|
let connectPromise = null;
|
|
1506
1509
|
let reconnectPromise = null;
|
|
1510
|
+
let connectionLossPromise = null;
|
|
1507
1511
|
let reconnectRestoreActive = false;
|
|
1508
1512
|
let authOutcome = null;
|
|
1509
1513
|
let authRejected = false;
|
|
@@ -1513,6 +1517,10 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1513
1517
|
const closeAbortController = new AbortController();
|
|
1514
1518
|
const connectionScope = createScope("connection");
|
|
1515
1519
|
const readyListeners = /* @__PURE__ */ new Set();
|
|
1520
|
+
let heartbeatTimer = null;
|
|
1521
|
+
let heartbeatTransport = null;
|
|
1522
|
+
let heartbeatPending = false;
|
|
1523
|
+
let lastActivityAt = Date.now();
|
|
1516
1524
|
const log = (level, event, fields) => {
|
|
1517
1525
|
observability?.logger?.log(level, event, fields);
|
|
1518
1526
|
};
|
|
@@ -1582,6 +1590,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1582
1590
|
permanentlyClosed = true;
|
|
1583
1591
|
closeRequested = true;
|
|
1584
1592
|
receiveLoopAbort = true;
|
|
1593
|
+
stopHeartbeat();
|
|
1585
1594
|
closeAbortController.abort();
|
|
1586
1595
|
asyncHandlerDispatcher.close();
|
|
1587
1596
|
const scopeDisposePromise = connectionScope.dispose();
|
|
@@ -1763,7 +1772,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1763
1772
|
cb();
|
|
1764
1773
|
};
|
|
1765
1774
|
const onAbort = () => {
|
|
1766
|
-
settle(() => reject(abortError$
|
|
1775
|
+
settle(() => reject(abortError$2()));
|
|
1767
1776
|
};
|
|
1768
1777
|
const onStateChange = () => {
|
|
1769
1778
|
const failure = readyFailure();
|
|
@@ -1846,6 +1855,75 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1846
1855
|
}
|
|
1847
1856
|
}
|
|
1848
1857
|
};
|
|
1858
|
+
const withWriteLock = async (operation) => {
|
|
1859
|
+
const prior = writeChain;
|
|
1860
|
+
let release;
|
|
1861
|
+
writeChain = new Promise((resolve) => {
|
|
1862
|
+
release = resolve;
|
|
1863
|
+
});
|
|
1864
|
+
await prior;
|
|
1865
|
+
try {
|
|
1866
|
+
return await operation();
|
|
1867
|
+
} finally {
|
|
1868
|
+
release();
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
const markOutboundActivity = () => {
|
|
1872
|
+
lastActivityAt = Date.now();
|
|
1873
|
+
};
|
|
1874
|
+
const markRemoteActivity = () => {
|
|
1875
|
+
lastActivityAt = Date.now();
|
|
1876
|
+
};
|
|
1877
|
+
const stopHeartbeat = () => {
|
|
1878
|
+
if (heartbeatTimer) {
|
|
1879
|
+
clearTimeout(heartbeatTimer);
|
|
1880
|
+
heartbeatTimer = null;
|
|
1881
|
+
}
|
|
1882
|
+
heartbeatTransport = null;
|
|
1883
|
+
heartbeatPending = false;
|
|
1884
|
+
};
|
|
1885
|
+
const startHeartbeat = (activeTransport) => {
|
|
1886
|
+
if (!heartbeatEnabled) return;
|
|
1887
|
+
stopHeartbeat();
|
|
1888
|
+
activeTransport.enableKeepAlive?.(heartbeatIntervalMs);
|
|
1889
|
+
heartbeatTransport = activeTransport;
|
|
1890
|
+
markRemoteActivity();
|
|
1891
|
+
const scheduleNext = () => {
|
|
1892
|
+
if (closeRequested || receiveLoopAbort || heartbeatTransport !== activeTransport) return;
|
|
1893
|
+
heartbeatTimer = setTimeout(tick, heartbeatIntervalMs);
|
|
1894
|
+
};
|
|
1895
|
+
const tick = () => {
|
|
1896
|
+
heartbeatTimer = null;
|
|
1897
|
+
if (closeRequested || receiveLoopAbort || heartbeatTransport !== activeTransport) return;
|
|
1898
|
+
const now = Date.now();
|
|
1899
|
+
if (now - lastActivityAt < heartbeatIntervalMs) {
|
|
1900
|
+
scheduleNext();
|
|
1901
|
+
return;
|
|
1902
|
+
}
|
|
1903
|
+
const supportsHeartbeat = activeTransport.supportsHeartbeat?.() ?? true;
|
|
1904
|
+
if (!heartbeatPending && supportsHeartbeat && activeTransport.sendHeartbeat) {
|
|
1905
|
+
heartbeatPending = true;
|
|
1906
|
+
const heartbeatSentAt = now;
|
|
1907
|
+
const dispatchHeartbeat = (heartbeat) => activeTransport.sendHeartbeat(heartbeat);
|
|
1908
|
+
withWriteLock(async () => {
|
|
1909
|
+
await dispatchHeartbeat({ timeoutMs: heartbeatTimeoutMs });
|
|
1910
|
+
}).then(() => {
|
|
1911
|
+
if (heartbeatTransport !== activeTransport) return;
|
|
1912
|
+
heartbeatPending = false;
|
|
1913
|
+
markRemoteActivity();
|
|
1914
|
+
}).catch((error) => {
|
|
1915
|
+
if (heartbeatTransport !== activeTransport) return;
|
|
1916
|
+
heartbeatPending = false;
|
|
1917
|
+
if (lastActivityAt > heartbeatSentAt) return;
|
|
1918
|
+
const heartbeatError = new TransportError(`Heartbeat failed: ${describeError(error)}`);
|
|
1919
|
+
activeTransport.close().catch(() => void 0);
|
|
1920
|
+
handleConnectionLoss(heartbeatError);
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
scheduleNext();
|
|
1924
|
+
};
|
|
1925
|
+
scheduleNext();
|
|
1926
|
+
};
|
|
1849
1927
|
const openAndAuthenticate = async (isReconnect, signal) => {
|
|
1850
1928
|
throwIfAborted$1(signal);
|
|
1851
1929
|
receiveLoopAbort = false;
|
|
@@ -1853,10 +1931,13 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1853
1931
|
requestGate = createRequestGate(maxInFlightRequests, maxRequestQueueSize);
|
|
1854
1932
|
const activeTransport = transportFactory();
|
|
1855
1933
|
transport = activeTransport;
|
|
1934
|
+
stopHeartbeat();
|
|
1856
1935
|
setState(isReconnect ? "RECONNECTING" : "CONNECTING");
|
|
1857
1936
|
emitLifecycleEvent(isReconnect ? "reconnect_start" : "connect_start");
|
|
1858
1937
|
await activeTransport.connect();
|
|
1938
|
+
markRemoteActivity();
|
|
1859
1939
|
if (closeRequested) {
|
|
1940
|
+
stopHeartbeat();
|
|
1860
1941
|
await activeTransport.close().catch(() => void 0);
|
|
1861
1942
|
if (transport === activeTransport) transport = null;
|
|
1862
1943
|
throw connectionClosedError();
|
|
@@ -1889,6 +1970,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1889
1970
|
hasEstablishedSession = true;
|
|
1890
1971
|
reconnectExhausted = false;
|
|
1891
1972
|
setState("AUTHENTICATED");
|
|
1973
|
+
startHeartbeat(activeTransport);
|
|
1892
1974
|
if (!isReconnect) multiplexer.setConnected();
|
|
1893
1975
|
emitLifecycleEvent(isReconnect ? "reconnect_succeeded" : "connect_succeeded");
|
|
1894
1976
|
} catch (error) {
|
|
@@ -1896,6 +1978,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1896
1978
|
reconnectRestoreActive = false;
|
|
1897
1979
|
multiplexer.setDisconnected();
|
|
1898
1980
|
emitDisconnect();
|
|
1981
|
+
stopHeartbeat();
|
|
1899
1982
|
if (activeTransport) await activeTransport.close().catch(() => void 0);
|
|
1900
1983
|
if (transport === activeTransport) transport = null;
|
|
1901
1984
|
const rejectedAuth = error instanceof AuthenticationError;
|
|
@@ -1903,7 +1986,7 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1903
1986
|
if (closeRequested) setState("CLOSED");
|
|
1904
1987
|
else setState(rejectedAuth ? "CLOSED" : "DISCONNECTED");
|
|
1905
1988
|
emitLifecycleEvent(isReconnect ? "reconnect_failed" : "connect_failed", error);
|
|
1906
|
-
if (isAbortError$1(error)) throw abortError$
|
|
1989
|
+
if (isAbortError$1(error)) throw abortError$2();
|
|
1907
1990
|
throw error;
|
|
1908
1991
|
}
|
|
1909
1992
|
};
|
|
@@ -1911,10 +1994,12 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1911
1994
|
const token = await tokenProvider();
|
|
1912
1995
|
const frame = FrameCodec.encodeFrame(1, utf8Encoder.encode(token));
|
|
1913
1996
|
await ensureTransport().send(frame);
|
|
1997
|
+
markOutboundActivity();
|
|
1914
1998
|
};
|
|
1915
1999
|
const startReceiveLoop = async () => {
|
|
1916
2000
|
while (!receiveLoopAbort && !closeRequested) try {
|
|
1917
2001
|
const data = await ensureTransport().receive();
|
|
2002
|
+
markRemoteActivity();
|
|
1918
2003
|
const frames = frameParser.parseFrames(data);
|
|
1919
2004
|
for (const frame of frames) multiplexer.dispatch(frame.messageType, frame.payload);
|
|
1920
2005
|
} catch (error) {
|
|
@@ -1924,6 +2009,17 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
1924
2009
|
}
|
|
1925
2010
|
};
|
|
1926
2011
|
const handleConnectionLoss = async (error) => {
|
|
2012
|
+
if (connectionLossPromise) {
|
|
2013
|
+
await connectionLossPromise;
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
connectionLossPromise = handleConnectionLossOnce(error).finally(() => {
|
|
2017
|
+
connectionLossPromise = null;
|
|
2018
|
+
});
|
|
2019
|
+
await connectionLossPromise;
|
|
2020
|
+
};
|
|
2021
|
+
const handleConnectionLossOnce = async (error) => {
|
|
2022
|
+
stopHeartbeat();
|
|
1927
2023
|
multiplexer.setDisconnected();
|
|
1928
2024
|
requestGate.close();
|
|
1929
2025
|
emitDisconnect();
|
|
@@ -2010,17 +2106,10 @@ function createConnection(transportFactory, tokenProvider, options = {}) {
|
|
|
2010
2106
|
notifyReadyListeners();
|
|
2011
2107
|
};
|
|
2012
2108
|
const sendSerialized = async (transport, data) => {
|
|
2013
|
-
|
|
2014
|
-
let release;
|
|
2015
|
-
writeChain = new Promise((resolve) => {
|
|
2016
|
-
release = resolve;
|
|
2017
|
-
});
|
|
2018
|
-
await prior;
|
|
2019
|
-
try {
|
|
2109
|
+
await withWriteLock(async () => {
|
|
2020
2110
|
await transport.send(data);
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
}
|
|
2111
|
+
markOutboundActivity();
|
|
2112
|
+
});
|
|
2024
2113
|
};
|
|
2025
2114
|
const emitLifecycleEvent = (event, error, attempt) => {
|
|
2026
2115
|
const payload = {
|
|
@@ -2096,6 +2185,7 @@ function createWebSocketTransport(url, options = {}) {
|
|
|
2096
2185
|
let receiverResolve = null;
|
|
2097
2186
|
const timeout = options.timeout ?? 3e4;
|
|
2098
2187
|
const maxFrameSize = options.maxFrameSize ?? 65535;
|
|
2188
|
+
const receiveTimeoutEnabled = options.receiveTimeout ?? true;
|
|
2099
2189
|
const enqueueMessage = (data) => {
|
|
2100
2190
|
if (receiverResolve) {
|
|
2101
2191
|
receiverResolve(data);
|
|
@@ -2169,6 +2259,56 @@ function createWebSocketTransport(url, options = {}) {
|
|
|
2169
2259
|
}
|
|
2170
2260
|
});
|
|
2171
2261
|
};
|
|
2262
|
+
const sendHeartbeat = async (heartbeatOptions) => {
|
|
2263
|
+
if (!connected || !ws) throw new TransportError("WebSocket is not connected");
|
|
2264
|
+
const activeWs = ws;
|
|
2265
|
+
if (typeof activeWs.ping !== "function" || typeof activeWs.once !== "function" || typeof activeWs.removeListener !== "function") throw new TransportError("WebSocket heartbeat is not supported");
|
|
2266
|
+
const socket = activeWs;
|
|
2267
|
+
return new Promise((resolve, reject) => {
|
|
2268
|
+
let settled = false;
|
|
2269
|
+
let timeoutId = null;
|
|
2270
|
+
const cleanup = () => {
|
|
2271
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2272
|
+
socket.removeListener("pong", onPong);
|
|
2273
|
+
socket.removeListener("close", onClose);
|
|
2274
|
+
socket.removeListener("error", onError);
|
|
2275
|
+
};
|
|
2276
|
+
const settle = (callback) => {
|
|
2277
|
+
if (settled) return;
|
|
2278
|
+
settled = true;
|
|
2279
|
+
cleanup();
|
|
2280
|
+
callback();
|
|
2281
|
+
};
|
|
2282
|
+
const onPong = () => {
|
|
2283
|
+
settle(resolve);
|
|
2284
|
+
};
|
|
2285
|
+
const onClose = () => {
|
|
2286
|
+
settle(() => reject(new TransportError("WebSocket closed during heartbeat")));
|
|
2287
|
+
};
|
|
2288
|
+
const onError = (...args) => {
|
|
2289
|
+
const event = args[0];
|
|
2290
|
+
const message = event instanceof Error ? event.message : event?.message || "unknown error";
|
|
2291
|
+
settle(() => reject(new TransportError(`WebSocket heartbeat failed: ${message}`)));
|
|
2292
|
+
};
|
|
2293
|
+
timeoutId = setTimeout(() => {
|
|
2294
|
+
settle(() => reject(new TimeoutError(`WebSocket heartbeat timeout after ${heartbeatOptions.timeoutMs}ms`)));
|
|
2295
|
+
}, heartbeatOptions.timeoutMs);
|
|
2296
|
+
socket.once("pong", onPong);
|
|
2297
|
+
socket.once("close", onClose);
|
|
2298
|
+
socket.once("error", onError);
|
|
2299
|
+
try {
|
|
2300
|
+
socket.ping(new Uint8Array(), void 0, (err) => {
|
|
2301
|
+
if (err) settle(() => reject(new TransportError(`WebSocket ping failed: ${err.message}`)));
|
|
2302
|
+
});
|
|
2303
|
+
} catch (err) {
|
|
2304
|
+
settle(() => reject(new TransportError(`WebSocket heartbeat error: ${err instanceof Error ? err.message : String(err)}`)));
|
|
2305
|
+
}
|
|
2306
|
+
});
|
|
2307
|
+
};
|
|
2308
|
+
const supportsHeartbeat = () => {
|
|
2309
|
+
return typeof ws?.ping === "function" && typeof ws?.once === "function" && typeof ws?.removeListener === "function";
|
|
2310
|
+
};
|
|
2311
|
+
const enableKeepAlive = () => void 0;
|
|
2172
2312
|
const receive = async () => {
|
|
2173
2313
|
if (receiveQueue.length > 0) {
|
|
2174
2314
|
const message = receiveQueue.shift();
|
|
@@ -2177,12 +2317,12 @@ function createWebSocketTransport(url, options = {}) {
|
|
|
2177
2317
|
}
|
|
2178
2318
|
if (!connected) throw new TransportError("Connection closed");
|
|
2179
2319
|
return new Promise((resolve, reject) => {
|
|
2180
|
-
const timeoutId = setTimeout(() => {
|
|
2320
|
+
const timeoutId = receiveTimeoutEnabled ? setTimeout(() => {
|
|
2181
2321
|
receiverResolve = null;
|
|
2182
2322
|
reject(new TimeoutError(`WebSocket receive timeout after ${timeout}ms`));
|
|
2183
|
-
}, timeout);
|
|
2323
|
+
}, timeout) : null;
|
|
2184
2324
|
receiverResolve = (data) => {
|
|
2185
|
-
clearTimeout(timeoutId);
|
|
2325
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2186
2326
|
receiverResolve = null;
|
|
2187
2327
|
if (data === null) {
|
|
2188
2328
|
reject(new TransportError("Connection closed"));
|
|
@@ -2217,6 +2357,9 @@ function createWebSocketTransport(url, options = {}) {
|
|
|
2217
2357
|
connect,
|
|
2218
2358
|
send,
|
|
2219
2359
|
receive,
|
|
2360
|
+
sendHeartbeat,
|
|
2361
|
+
supportsHeartbeat,
|
|
2362
|
+
enableKeepAlive,
|
|
2220
2363
|
close,
|
|
2221
2364
|
getUrl,
|
|
2222
2365
|
isConnected
|
|
@@ -2242,6 +2385,7 @@ function createTcpTransport(url, options = {}) {
|
|
|
2242
2385
|
let receiverResolve = null;
|
|
2243
2386
|
const timeout = options.timeout ?? 3e4;
|
|
2244
2387
|
const maxFrameSize = options.maxFrameSize ?? 65535;
|
|
2388
|
+
const receiveTimeoutEnabled = options.receiveTimeout ?? true;
|
|
2245
2389
|
let lengthBuffer = new Uint8Array(4);
|
|
2246
2390
|
let lengthOffset = 0;
|
|
2247
2391
|
let currentMessageLength = null;
|
|
@@ -2316,7 +2460,7 @@ function createTcpTransport(url, options = {}) {
|
|
|
2316
2460
|
clearTimeout(connectTimeout);
|
|
2317
2461
|
connected = true;
|
|
2318
2462
|
activeSocket.setNoDelay(true);
|
|
2319
|
-
activeSocket.setTimeout(timeout);
|
|
2463
|
+
activeSocket.setTimeout(receiveTimeoutEnabled ? timeout : 0);
|
|
2320
2464
|
resolve();
|
|
2321
2465
|
});
|
|
2322
2466
|
activeSocket.on("data", (chunk) => {
|
|
@@ -2334,8 +2478,10 @@ function createTcpTransport(url, options = {}) {
|
|
|
2334
2478
|
if (receiverResolve) receiverResolve(new Uint8Array(0));
|
|
2335
2479
|
});
|
|
2336
2480
|
activeSocket.on("timeout", () => {
|
|
2337
|
-
|
|
2338
|
-
|
|
2481
|
+
if (receiveTimeoutEnabled) {
|
|
2482
|
+
activeSocket.destroy();
|
|
2483
|
+
connected = false;
|
|
2484
|
+
}
|
|
2339
2485
|
});
|
|
2340
2486
|
} catch (err) {
|
|
2341
2487
|
reject(new TransportError(`Failed to create TCP socket: ${err instanceof Error ? err.message : String(err)}`));
|
|
@@ -2360,6 +2506,10 @@ function createTcpTransport(url, options = {}) {
|
|
|
2360
2506
|
});
|
|
2361
2507
|
});
|
|
2362
2508
|
};
|
|
2509
|
+
const enableKeepAlive = (intervalMs) => {
|
|
2510
|
+
socket?.setKeepAlive(true, intervalMs);
|
|
2511
|
+
};
|
|
2512
|
+
const supportsHeartbeat = () => false;
|
|
2363
2513
|
const receive = async () => {
|
|
2364
2514
|
if (receiveQueue.length > 0) {
|
|
2365
2515
|
const message = receiveQueue.shift();
|
|
@@ -2367,12 +2517,12 @@ function createTcpTransport(url, options = {}) {
|
|
|
2367
2517
|
return message;
|
|
2368
2518
|
}
|
|
2369
2519
|
return new Promise((resolve, reject) => {
|
|
2370
|
-
const timeoutId = setTimeout(() => {
|
|
2520
|
+
const timeoutId = receiveTimeoutEnabled ? setTimeout(() => {
|
|
2371
2521
|
receiverResolve = null;
|
|
2372
2522
|
reject(new TimeoutError(`TCP receive timeout after ${timeout}ms`));
|
|
2373
|
-
}, timeout);
|
|
2523
|
+
}, timeout) : null;
|
|
2374
2524
|
receiverResolve = (data) => {
|
|
2375
|
-
clearTimeout(timeoutId);
|
|
2525
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
2376
2526
|
receiverResolve = null;
|
|
2377
2527
|
if (data.length === 0) reject(new TransportError("Connection closed"));
|
|
2378
2528
|
else resolve(data);
|
|
@@ -2404,6 +2554,8 @@ function createTcpTransport(url, options = {}) {
|
|
|
2404
2554
|
connect,
|
|
2405
2555
|
send,
|
|
2406
2556
|
receive,
|
|
2557
|
+
supportsHeartbeat,
|
|
2558
|
+
enableKeepAlive,
|
|
2407
2559
|
close,
|
|
2408
2560
|
getUrl,
|
|
2409
2561
|
isConnected
|
|
@@ -2782,6 +2934,64 @@ const KvClient = function(connection) {
|
|
|
2782
2934
|
return createKvClient(connection);
|
|
2783
2935
|
};
|
|
2784
2936
|
//#endregion
|
|
2937
|
+
//#region src/core/wake-gate.ts
|
|
2938
|
+
function abortError$1() {
|
|
2939
|
+
const error = /* @__PURE__ */ new Error("The operation was aborted");
|
|
2940
|
+
error.name = "AbortError";
|
|
2941
|
+
return error;
|
|
2942
|
+
}
|
|
2943
|
+
function createWakeGate() {
|
|
2944
|
+
let currentVersion = 0;
|
|
2945
|
+
const waiters = /* @__PURE__ */ new Set();
|
|
2946
|
+
const cleanup = (waiter) => {
|
|
2947
|
+
waiters.delete(waiter);
|
|
2948
|
+
if (waiter.signal && waiter.onAbort) waiter.signal.removeEventListener("abort", waiter.onAbort);
|
|
2949
|
+
};
|
|
2950
|
+
const wake = () => {
|
|
2951
|
+
currentVersion += 1;
|
|
2952
|
+
const version = currentVersion;
|
|
2953
|
+
for (const waiter of Array.from(waiters)) {
|
|
2954
|
+
cleanup(waiter);
|
|
2955
|
+
waiter.resolve(version);
|
|
2956
|
+
}
|
|
2957
|
+
return version;
|
|
2958
|
+
};
|
|
2959
|
+
const waitAfter = async (observedVersion, options = {}) => {
|
|
2960
|
+
if (currentVersion > observedVersion) return currentVersion;
|
|
2961
|
+
if (options.signal?.aborted) throw abortError$1();
|
|
2962
|
+
return await new Promise((resolve, reject) => {
|
|
2963
|
+
const waiter = {
|
|
2964
|
+
observedVersion,
|
|
2965
|
+
resolve,
|
|
2966
|
+
reject,
|
|
2967
|
+
signal: options.signal
|
|
2968
|
+
};
|
|
2969
|
+
waiter.onAbort = () => {
|
|
2970
|
+
cleanup(waiter);
|
|
2971
|
+
reject(abortError$1());
|
|
2972
|
+
};
|
|
2973
|
+
if (options.signal) options.signal.addEventListener("abort", waiter.onAbort, { once: true });
|
|
2974
|
+
if (currentVersion > observedVersion) {
|
|
2975
|
+
cleanup(waiter);
|
|
2976
|
+
resolve(currentVersion);
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
waiters.add(waiter);
|
|
2980
|
+
});
|
|
2981
|
+
};
|
|
2982
|
+
const wait = async (options) => {
|
|
2983
|
+
return await waitAfter(currentVersion, options);
|
|
2984
|
+
};
|
|
2985
|
+
return {
|
|
2986
|
+
get version() {
|
|
2987
|
+
return currentVersion;
|
|
2988
|
+
},
|
|
2989
|
+
wake,
|
|
2990
|
+
waitAfter,
|
|
2991
|
+
wait
|
|
2992
|
+
};
|
|
2993
|
+
}
|
|
2994
|
+
//#endregion
|
|
2785
2995
|
//#region src/domains/queue/codec.ts
|
|
2786
2996
|
/**
|
|
2787
2997
|
* Queue domain codec for encoding and decoding protocol messages.
|
|
@@ -3120,43 +3330,55 @@ function createQueueClient(connection) {
|
|
|
3120
3330
|
let items = await reserveOnce(route, leaseSeconds, batchSize);
|
|
3121
3331
|
if (items.length > 0) return items;
|
|
3122
3332
|
const deadline = Date.now() + waitSeconds * 1e3;
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
pendingNotifications += 1;
|
|
3127
|
-
if (!waiter) return;
|
|
3128
|
-
const resolve = waiter;
|
|
3129
|
-
waiter = void 0;
|
|
3130
|
-
pendingNotifications = 0;
|
|
3131
|
-
resolve();
|
|
3333
|
+
const wakeGate = createWakeGate();
|
|
3334
|
+
const subscription = await subscribe(route, () => {
|
|
3335
|
+
wakeGate.wake();
|
|
3132
3336
|
});
|
|
3133
3337
|
try {
|
|
3134
3338
|
while (true) {
|
|
3339
|
+
const observed = wakeGate.version;
|
|
3135
3340
|
items = await reserveOnce(route, leaseSeconds, batchSize);
|
|
3136
3341
|
if (items.length > 0) return items;
|
|
3137
3342
|
const remainingMs = deadline - Date.now();
|
|
3138
3343
|
if (remainingMs <= 0) return items;
|
|
3139
|
-
|
|
3140
|
-
|
|
3141
|
-
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
}
|
|
3145
|
-
const release = () => {
|
|
3146
|
-
clearTimeout(timeoutId);
|
|
3147
|
-
if (waiter === release) waiter = void 0;
|
|
3148
|
-
resolve();
|
|
3149
|
-
};
|
|
3150
|
-
const timeoutId = setTimeout(release, remainingMs);
|
|
3151
|
-
waiter = release;
|
|
3344
|
+
const waitPromise = wakeGate.waitAfter(observed);
|
|
3345
|
+
let timeoutId = null;
|
|
3346
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
3347
|
+
timeoutId = setTimeout(() => {
|
|
3348
|
+
resolve("timeout");
|
|
3349
|
+
}, remainingMs);
|
|
3152
3350
|
});
|
|
3351
|
+
if (await Promise.race([waitPromise.then(() => {
|
|
3352
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
3353
|
+
return "wake";
|
|
3354
|
+
}), timeoutPromise]) === "timeout") return items;
|
|
3153
3355
|
}
|
|
3154
3356
|
} finally {
|
|
3155
|
-
await subscription.unsubscribe();
|
|
3357
|
+
await subscription.unsubscribe().catch(() => void 0);
|
|
3156
3358
|
}
|
|
3157
3359
|
};
|
|
3158
|
-
const
|
|
3159
|
-
|
|
3360
|
+
const reserveWhenAvailable = async function* (route, options) {
|
|
3361
|
+
assertQueueReserveRoute(route);
|
|
3362
|
+
const wakeGate = createWakeGate();
|
|
3363
|
+
const subscription = await subscribe(route, () => {
|
|
3364
|
+
wakeGate.wake();
|
|
3365
|
+
});
|
|
3366
|
+
try {
|
|
3367
|
+
while (true) {
|
|
3368
|
+
const observed = wakeGate.version;
|
|
3369
|
+
const items = await reserveOnce(route, options.leaseSeconds, options.batchSize ?? 1, options.signal);
|
|
3370
|
+
if (items.length > 0) {
|
|
3371
|
+
yield items;
|
|
3372
|
+
continue;
|
|
3373
|
+
}
|
|
3374
|
+
await wakeGate.waitAfter(observed, { signal: options.signal });
|
|
3375
|
+
}
|
|
3376
|
+
} finally {
|
|
3377
|
+
await subscription.unsubscribe().catch(() => void 0);
|
|
3378
|
+
}
|
|
3379
|
+
};
|
|
3380
|
+
const reserveOnce = async (route, leaseSeconds, batchSize, signal) => {
|
|
3381
|
+
const response = await requestFrame(202, QueueCodec.encodeReserve(route, leaseSeconds, batchSize), signal);
|
|
3160
3382
|
const decoded = QueueCodec.decodeReserveResponse(response);
|
|
3161
3383
|
checkStatus(decoded, "RESERVE");
|
|
3162
3384
|
return (decoded.items ?? []).map((item) => createQueueItem(item.id, item.token, item.body, route, connection));
|
|
@@ -3245,6 +3467,7 @@ function createQueueClient(connection) {
|
|
|
3245
3467
|
return {
|
|
3246
3468
|
enqueue,
|
|
3247
3469
|
reserve,
|
|
3470
|
+
reserveWhenAvailable,
|
|
3248
3471
|
subscribe
|
|
3249
3472
|
};
|
|
3250
3473
|
}
|
|
@@ -4963,6 +5186,33 @@ function createStreamClient(connection) {
|
|
|
4963
5186
|
const page = await readPage(route, startOffset, limit, options);
|
|
4964
5187
|
return StreamCodec.flattenStreamReadItems(page.items);
|
|
4965
5188
|
};
|
|
5189
|
+
const readWhenCommitted = async function* (route, options) {
|
|
5190
|
+
assertStreamPattern(route);
|
|
5191
|
+
const wakeGate = createWakeGate();
|
|
5192
|
+
const subscription = await subscribe(route, () => {
|
|
5193
|
+
wakeGate.wake();
|
|
5194
|
+
});
|
|
5195
|
+
try {
|
|
5196
|
+
let offset = options.offset;
|
|
5197
|
+
while (true) {
|
|
5198
|
+
const observed = wakeGate.version;
|
|
5199
|
+
const page = await readPage(route, offset, options.batchSize ?? 100, {
|
|
5200
|
+
maxBytes: options.maxBytes,
|
|
5201
|
+
filter: options.filter,
|
|
5202
|
+
signal: options.signal
|
|
5203
|
+
});
|
|
5204
|
+
if (page.items.length > 0) {
|
|
5205
|
+
offset = page.cursor.lastResourceOffset + 1n;
|
|
5206
|
+
const records = StreamCodec.flattenStreamReadItems(page.items);
|
|
5207
|
+
if (records.length > 0) yield records;
|
|
5208
|
+
}
|
|
5209
|
+
if (page.cursor.hasMore) continue;
|
|
5210
|
+
await wakeGate.waitAfter(observed, { signal: options.signal });
|
|
5211
|
+
}
|
|
5212
|
+
} finally {
|
|
5213
|
+
await subscription.unsubscribe().catch(() => void 0);
|
|
5214
|
+
}
|
|
5215
|
+
};
|
|
4966
5216
|
const consume = async (route, startOffset, limit = 100, options) => {
|
|
4967
5217
|
return createAsyncIterableIterator(createSliceIterator(await read(route, startOffset, limit, options)));
|
|
4968
5218
|
};
|
|
@@ -5082,6 +5332,7 @@ function createStreamClient(connection) {
|
|
|
5082
5332
|
begin,
|
|
5083
5333
|
readPage,
|
|
5084
5334
|
read,
|
|
5335
|
+
readWhenCommitted,
|
|
5085
5336
|
consume,
|
|
5086
5337
|
peek,
|
|
5087
5338
|
metadata,
|
|
@@ -5262,6 +5513,7 @@ function createScheduleClient(connection) {
|
|
|
5262
5513
|
const { requestFrame, requestReconnectFrame } = createDomainClient(connection);
|
|
5263
5514
|
const subscriptionsByPattern = /* @__PURE__ */ new Map();
|
|
5264
5515
|
const patternsBySubId = /* @__PURE__ */ new Map();
|
|
5516
|
+
const pendingNotificationsBySubId = /* @__PURE__ */ new Map();
|
|
5265
5517
|
let notifyHandlerInitialized = false;
|
|
5266
5518
|
let nextHandlerId = 1;
|
|
5267
5519
|
connection.onReconnect(async () => {
|
|
@@ -5272,6 +5524,7 @@ function createScheduleClient(connection) {
|
|
|
5272
5524
|
}));
|
|
5273
5525
|
subscriptionsByPattern.clear();
|
|
5274
5526
|
patternsBySubId.clear();
|
|
5527
|
+
pendingNotificationsBySubId.clear();
|
|
5275
5528
|
for (const subscription of subscriptions) {
|
|
5276
5529
|
const subId = await subscribeWire(subscription.pattern, requestReconnectFrame);
|
|
5277
5530
|
subscriptionsByPattern.set(subscription.pattern, {
|
|
@@ -5296,6 +5549,29 @@ function createScheduleClient(connection) {
|
|
|
5296
5549
|
const decoded = ScheduleCodec.decodeListResponse(assertSuccess(response, "LIST"));
|
|
5297
5550
|
return [decoded.entries, decoded.totalCount];
|
|
5298
5551
|
};
|
|
5552
|
+
const waitForNotifications = async function* (route, options = {}) {
|
|
5553
|
+
assertConcreteScheduleRoute(route);
|
|
5554
|
+
const wakeGate = createWakeGate();
|
|
5555
|
+
const pendingNotifications = [];
|
|
5556
|
+
const subscription = await subscribe(route, (notification) => {
|
|
5557
|
+
pendingNotifications.push(notification);
|
|
5558
|
+
wakeGate.wake();
|
|
5559
|
+
});
|
|
5560
|
+
try {
|
|
5561
|
+
while (true) {
|
|
5562
|
+
const notification = pendingNotifications.shift();
|
|
5563
|
+
if (notification) {
|
|
5564
|
+
yield notification;
|
|
5565
|
+
continue;
|
|
5566
|
+
}
|
|
5567
|
+
const observed = wakeGate.version;
|
|
5568
|
+
if (pendingNotifications.length > 0) continue;
|
|
5569
|
+
await wakeGate.waitAfter(observed, { signal: options.signal });
|
|
5570
|
+
}
|
|
5571
|
+
} finally {
|
|
5572
|
+
await subscription.unsubscribe().catch(() => void 0);
|
|
5573
|
+
}
|
|
5574
|
+
};
|
|
5299
5575
|
const subscribe = async (pattern, handler) => {
|
|
5300
5576
|
assertConcreteScheduleRoute(pattern);
|
|
5301
5577
|
initNotifyHandler();
|
|
@@ -5319,6 +5595,7 @@ function createScheduleClient(connection) {
|
|
|
5319
5595
|
patternsBySubId.set(subId, pattern);
|
|
5320
5596
|
}
|
|
5321
5597
|
subscription.handlers.set(handlerId, handler);
|
|
5598
|
+
flushPendingNotifications(subId);
|
|
5322
5599
|
return createScheduleSubscription(subId, pattern, async () => {
|
|
5323
5600
|
await unsubscribe(pattern, handlerId);
|
|
5324
5601
|
});
|
|
@@ -5330,6 +5607,7 @@ function createScheduleClient(connection) {
|
|
|
5330
5607
|
if (subscription.handlers.size > 0) return;
|
|
5331
5608
|
subscriptionsByPattern.delete(pattern);
|
|
5332
5609
|
patternsBySubId.delete(subscription.subId);
|
|
5610
|
+
pendingNotificationsBySubId.delete(subscription.subId);
|
|
5333
5611
|
const response = await requestFrame(704, ScheduleCodec.encodeUnsubscribe(pattern));
|
|
5334
5612
|
ScheduleCodec.decodeUnsubscribeResponse(assertSuccess(response, "UNSUBSCRIBE"));
|
|
5335
5613
|
};
|
|
@@ -5340,16 +5618,40 @@ function createScheduleClient(connection) {
|
|
|
5340
5618
|
try {
|
|
5341
5619
|
const decoded = ScheduleCodec.decodeNotification(payload);
|
|
5342
5620
|
const pattern = patternsBySubId.get(decoded.subId);
|
|
5343
|
-
if (!pattern)
|
|
5621
|
+
if (!pattern) {
|
|
5622
|
+
queuePendingNotification(decoded.subId, { payload: decoded.payload });
|
|
5623
|
+
return;
|
|
5624
|
+
}
|
|
5344
5625
|
const subscription = subscriptionsByPattern.get(pattern);
|
|
5345
|
-
if (!subscription)
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
});
|
|
5626
|
+
if (!subscription) {
|
|
5627
|
+
queuePendingNotification(decoded.subId, { payload: decoded.payload });
|
|
5628
|
+
return;
|
|
5629
|
+
}
|
|
5630
|
+
dispatchNotification(subscription, { payload: decoded.payload });
|
|
5350
5631
|
} catch {}
|
|
5351
5632
|
});
|
|
5352
5633
|
};
|
|
5634
|
+
const queuePendingNotification = (subId, notification) => {
|
|
5635
|
+
const existing = pendingNotificationsBySubId.get(subId);
|
|
5636
|
+
if (existing) {
|
|
5637
|
+
existing.push(notification);
|
|
5638
|
+
return;
|
|
5639
|
+
}
|
|
5640
|
+
pendingNotificationsBySubId.set(subId, [notification]);
|
|
5641
|
+
};
|
|
5642
|
+
const flushPendingNotifications = (subId) => {
|
|
5643
|
+
const pending = pendingNotificationsBySubId.get(subId);
|
|
5644
|
+
if (!pending || pending.length === 0) return;
|
|
5645
|
+
pendingNotificationsBySubId.delete(subId);
|
|
5646
|
+
const subscription = Array.from(subscriptionsByPattern.values()).find((entry) => entry.subId === subId);
|
|
5647
|
+
if (!subscription) return;
|
|
5648
|
+
for (const notification of pending) dispatchNotification(subscription, notification);
|
|
5649
|
+
};
|
|
5650
|
+
const dispatchNotification = (subscription, notification) => {
|
|
5651
|
+
for (const handler of subscription.handlers.values()) connection.dispatchAsyncHandler(async () => {
|
|
5652
|
+
await handler(notification);
|
|
5653
|
+
});
|
|
5654
|
+
};
|
|
5353
5655
|
const assertSuccess = (payload, operation) => {
|
|
5354
5656
|
const result = parseStandardResponse(payload);
|
|
5355
5657
|
if (result.success) return result.data;
|
|
@@ -5366,7 +5668,8 @@ function createScheduleClient(connection) {
|
|
|
5366
5668
|
create,
|
|
5367
5669
|
cancel,
|
|
5368
5670
|
list,
|
|
5369
|
-
subscribe
|
|
5671
|
+
subscribe,
|
|
5672
|
+
waitForNotifications
|
|
5370
5673
|
};
|
|
5371
5674
|
}
|
|
5372
5675
|
const ScheduleClient = function(connection) {
|
|
@@ -5434,6 +5737,12 @@ function createClient(config) {
|
|
|
5434
5737
|
maxBackoffMs: 1e3,
|
|
5435
5738
|
...config.retry
|
|
5436
5739
|
},
|
|
5740
|
+
heartbeat: {
|
|
5741
|
+
enabled: true,
|
|
5742
|
+
intervalMs: 1e4,
|
|
5743
|
+
timeoutMs: 3e4,
|
|
5744
|
+
...config.heartbeat
|
|
5745
|
+
},
|
|
5437
5746
|
asyncHandlers: {
|
|
5438
5747
|
maxConcurrency: Infinity,
|
|
5439
5748
|
timeoutMs: 3e4,
|
|
@@ -5465,7 +5774,8 @@ function createClient(config) {
|
|
|
5465
5774
|
if (connection) return connection;
|
|
5466
5775
|
connection = createConnection(() => createTransport(resolvedConfig.url, resolvedConfig.transport, {
|
|
5467
5776
|
timeout: resolvedConfig.timeout,
|
|
5468
|
-
maxFrameSize: resolvedConfig.maxFrameSize
|
|
5777
|
+
maxFrameSize: resolvedConfig.maxFrameSize,
|
|
5778
|
+
receiveTimeout: resolvedConfig.heartbeat?.enabled === false
|
|
5469
5779
|
}), resolveTokenProvider(), {
|
|
5470
5780
|
timeout: resolvedConfig.timeout,
|
|
5471
5781
|
authSettleDelayMs: resolvedConfig.authSettleDelayMs,
|
|
@@ -5473,6 +5783,7 @@ function createClient(config) {
|
|
|
5473
5783
|
maxRequestQueueSize: resolvedConfig.maxRequestQueueSize,
|
|
5474
5784
|
reconnect: resolvedConfig.reconnect,
|
|
5475
5785
|
retry: resolvedConfig.retry,
|
|
5786
|
+
heartbeat: resolvedConfig.heartbeat,
|
|
5476
5787
|
observability,
|
|
5477
5788
|
asyncHandlers: resolvedConfig.asyncHandlers
|
|
5478
5789
|
});
|
|
@@ -5794,6 +6105,7 @@ exports.TimeoutError = TimeoutError;
|
|
|
5794
6105
|
exports.TransportError = TransportError;
|
|
5795
6106
|
exports.createClient = createClient;
|
|
5796
6107
|
exports.createTaskGroup = createTaskGroup;
|
|
6108
|
+
exports.createWakeGate = createWakeGate;
|
|
5797
6109
|
exports.isRetryable = isRetryable;
|
|
5798
6110
|
|
|
5799
6111
|
//# sourceMappingURL=index.cjs.map
|