@pooflabs/core 0.0.28 → 0.0.30
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/dist/client/operations.d.ts +7 -2
- package/dist/index.js +213 -33
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +213 -33
- package/dist/index.mjs.map +1 -1
- package/dist/utils/utils.d.ts +1 -1
- package/package.json +1 -1
|
@@ -22,6 +22,11 @@ export type GetOptions = {
|
|
|
22
22
|
headers?: Record<string, string>;
|
|
23
23
|
};
|
|
24
24
|
};
|
|
25
|
+
export type RunQueryOptions = {
|
|
26
|
+
_overrides?: {
|
|
27
|
+
headers?: Record<string, string>;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
25
30
|
export declare function get(path: string, opts?: GetOptions): Promise<any>;
|
|
26
31
|
export type RunExpressionOptions = {
|
|
27
32
|
returnType?: 'Bool' | 'String' | 'Int' | 'UInt';
|
|
@@ -38,12 +43,12 @@ export type RunExpressionResult = {
|
|
|
38
43
|
result?: any;
|
|
39
44
|
}[];
|
|
40
45
|
};
|
|
41
|
-
export declare function runQuery(absolutePath: string, queryName: string, queryArgs: any): Promise<any>;
|
|
46
|
+
export declare function runQuery(absolutePath: string, queryName: string, queryArgs: any, opts?: RunQueryOptions): Promise<any>;
|
|
42
47
|
export declare function runQueryMany(many: {
|
|
43
48
|
absolutePath: string;
|
|
44
49
|
queryName: string;
|
|
45
50
|
queryArgs: any;
|
|
46
|
-
}[]): Promise<any>;
|
|
51
|
+
}[], opts?: RunQueryOptions): Promise<any>;
|
|
47
52
|
export declare function runExpression(expression: string, queryArgs: any, options?: RunExpressionOptions): Promise<RunExpressionResult>;
|
|
48
53
|
export declare function runExpressionMany(many: {
|
|
49
54
|
expression: string;
|
package/dist/index.js
CHANGED
|
@@ -204,6 +204,11 @@ class WebSessionManager {
|
|
|
204
204
|
}
|
|
205
205
|
WebSessionManager.TAROBASE_SESSION_STORAGE_KEY = "tarobase_session_storage";
|
|
206
206
|
|
|
207
|
+
var webSessionManager = /*#__PURE__*/Object.freeze({
|
|
208
|
+
__proto__: null,
|
|
209
|
+
WebSessionManager: WebSessionManager
|
|
210
|
+
});
|
|
211
|
+
|
|
207
212
|
function getDefaultExportFromCjs (x) {
|
|
208
213
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
209
214
|
}
|
|
@@ -3101,11 +3106,19 @@ class ServerSessionManager {
|
|
|
3101
3106
|
/* The single, shared instance */
|
|
3102
3107
|
ServerSessionManager.instance = new ServerSessionManager();
|
|
3103
3108
|
|
|
3109
|
+
var serverSessionManager = /*#__PURE__*/Object.freeze({
|
|
3110
|
+
__proto__: null,
|
|
3111
|
+
ServerSessionManager: ServerSessionManager
|
|
3112
|
+
});
|
|
3113
|
+
|
|
3104
3114
|
async function createBearerToken(isServer) {
|
|
3105
3115
|
if (isServer) {
|
|
3106
3116
|
const sessionMgr = ServerSessionManager.instance;
|
|
3107
3117
|
const session = await sessionMgr.getSession();
|
|
3108
|
-
|
|
3118
|
+
if (!(session === null || session === void 0 ? void 0 : session.idToken)) {
|
|
3119
|
+
return null;
|
|
3120
|
+
}
|
|
3121
|
+
return `Bearer ${session.idToken}`;
|
|
3109
3122
|
}
|
|
3110
3123
|
const idToken = WebSessionManager.getIdToken();
|
|
3111
3124
|
if (!idToken) {
|
|
@@ -3138,13 +3151,72 @@ async function getRefreshToken(isServer) {
|
|
|
3138
3151
|
}
|
|
3139
3152
|
return WebSessionManager.getRefreshToken();
|
|
3140
3153
|
}
|
|
3141
|
-
function updateIdTokenAndAccessToken(idToken, accessToken) {
|
|
3142
|
-
|
|
3154
|
+
async function updateIdTokenAndAccessToken(idToken, accessToken, isServer = false) {
|
|
3155
|
+
if (isServer) {
|
|
3156
|
+
const sessionMgr = ServerSessionManager.instance;
|
|
3157
|
+
// Ensure we have a session to update; if missing, let lazy creation happen on next request.
|
|
3158
|
+
let session = null;
|
|
3159
|
+
try {
|
|
3160
|
+
session = await sessionMgr.getSession();
|
|
3161
|
+
}
|
|
3162
|
+
catch (_a) {
|
|
3163
|
+
return;
|
|
3164
|
+
}
|
|
3165
|
+
if (!session) {
|
|
3166
|
+
return;
|
|
3167
|
+
}
|
|
3168
|
+
sessionMgr.setSession(Object.assign(Object.assign({}, session), { idToken,
|
|
3169
|
+
accessToken }));
|
|
3170
|
+
return;
|
|
3171
|
+
}
|
|
3172
|
+
await WebSessionManager.updateIdTokenAndAccessToken(idToken, accessToken);
|
|
3143
3173
|
}
|
|
3144
3174
|
|
|
3175
|
+
const refreshInFlight = new Map();
|
|
3176
|
+
async function refreshAuthSessionOnce(appId, isServer) {
|
|
3177
|
+
const key = `${isServer ? "server" : "web"}:${appId}`;
|
|
3178
|
+
const existing = refreshInFlight.get(key);
|
|
3179
|
+
if (existing) {
|
|
3180
|
+
return existing;
|
|
3181
|
+
}
|
|
3182
|
+
const refreshPromise = (async () => {
|
|
3183
|
+
var _a, _b;
|
|
3184
|
+
try {
|
|
3185
|
+
const refreshToken = await getRefreshToken(isServer);
|
|
3186
|
+
if (!refreshToken) {
|
|
3187
|
+
return false;
|
|
3188
|
+
}
|
|
3189
|
+
const refreshData = await refreshSession(refreshToken);
|
|
3190
|
+
if (!(refreshData === null || refreshData === void 0 ? void 0 : refreshData.idToken) || !(refreshData === null || refreshData === void 0 ? void 0 : refreshData.accessToken)) {
|
|
3191
|
+
return false;
|
|
3192
|
+
}
|
|
3193
|
+
await updateIdTokenAndAccessToken(refreshData.idToken, refreshData.accessToken, isServer);
|
|
3194
|
+
return true;
|
|
3195
|
+
}
|
|
3196
|
+
catch (error) {
|
|
3197
|
+
if (!isServer && (((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) === 401 || ((_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.status) === 403)) {
|
|
3198
|
+
const { WebSessionManager } = await Promise.resolve().then(function () { return webSessionManager; });
|
|
3199
|
+
WebSessionManager.clearSession();
|
|
3200
|
+
}
|
|
3201
|
+
return false;
|
|
3202
|
+
}
|
|
3203
|
+
finally {
|
|
3204
|
+
refreshInFlight.delete(key);
|
|
3205
|
+
}
|
|
3206
|
+
})();
|
|
3207
|
+
refreshInFlight.set(key, refreshPromise);
|
|
3208
|
+
return refreshPromise;
|
|
3209
|
+
}
|
|
3145
3210
|
async function makeApiRequest(method, urlPath, data, _overrides) {
|
|
3146
|
-
var _a, _b, _c, _d
|
|
3211
|
+
var _a, _b, _c, _d;
|
|
3147
3212
|
const config = await getConfig();
|
|
3213
|
+
let hasRetriedAfterServerSessionReset = false;
|
|
3214
|
+
const clearServerSession = async () => {
|
|
3215
|
+
if (!config.isServer)
|
|
3216
|
+
return;
|
|
3217
|
+
const { ServerSessionManager } = await Promise.resolve().then(function () { return serverSessionManager; });
|
|
3218
|
+
ServerSessionManager.instance.clearSession();
|
|
3219
|
+
};
|
|
3148
3220
|
async function executeRequest() {
|
|
3149
3221
|
const authHeader = await createAuthHeader(config.isServer);
|
|
3150
3222
|
const headers = Object.assign({ "Content-Type": "application/json", "X-Public-App-Id": config.appId, "X-App-Id": config.appId }, authHeader);
|
|
@@ -3176,27 +3248,34 @@ async function makeApiRequest(method, urlPath, data, _overrides) {
|
|
|
3176
3248
|
catch (error) {
|
|
3177
3249
|
if (((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 401) {
|
|
3178
3250
|
try {
|
|
3179
|
-
const
|
|
3180
|
-
if (!
|
|
3181
|
-
throw new Error("
|
|
3182
|
-
}
|
|
3183
|
-
const refreshData = await refreshSession(refreshToken);
|
|
3184
|
-
if (refreshData &&
|
|
3185
|
-
refreshData.idToken &&
|
|
3186
|
-
refreshData.accessToken) {
|
|
3187
|
-
updateIdTokenAndAccessToken(refreshData.idToken, refreshData.accessToken);
|
|
3251
|
+
const refreshed = await refreshAuthSessionOnce(config.appId, config.isServer);
|
|
3252
|
+
if (!refreshed) {
|
|
3253
|
+
throw new Error("Unable to refresh auth session");
|
|
3188
3254
|
}
|
|
3189
3255
|
return await executeRequest();
|
|
3190
3256
|
}
|
|
3191
|
-
catch (
|
|
3257
|
+
catch (_refreshError) {
|
|
3258
|
+
// Server-side fallback: clear cached session and retry once to force
|
|
3259
|
+
// createSession() with a fresh Cognito session.
|
|
3260
|
+
if (config.isServer && !hasRetriedAfterServerSessionReset) {
|
|
3261
|
+
hasRetriedAfterServerSessionReset = true;
|
|
3262
|
+
await clearServerSession();
|
|
3263
|
+
return await executeRequest();
|
|
3264
|
+
}
|
|
3192
3265
|
throw error;
|
|
3193
3266
|
}
|
|
3194
3267
|
}
|
|
3195
3268
|
if (((_b = error.response) === null || _b === void 0 ? void 0 : _b.status) == 404) {
|
|
3196
3269
|
return { data: null, status: 404 };
|
|
3197
3270
|
}
|
|
3198
|
-
if ((
|
|
3199
|
-
|
|
3271
|
+
if ((_c = error.response) === null || _c === void 0 ? void 0 : _c.data) {
|
|
3272
|
+
const responseData = typeof error.response.data === 'object'
|
|
3273
|
+
? error.response.data
|
|
3274
|
+
: { message: String(error.response.data) };
|
|
3275
|
+
const apiError = new Error(responseData.message || error.message || 'Request failed');
|
|
3276
|
+
Object.assign(apiError, responseData);
|
|
3277
|
+
apiError.statusCode = (_d = error.response) === null || _d === void 0 ? void 0 : _d.status;
|
|
3278
|
+
throw apiError;
|
|
3200
3279
|
}
|
|
3201
3280
|
throw error;
|
|
3202
3281
|
}
|
|
@@ -3261,6 +3340,40 @@ async function getConfig() {
|
|
|
3261
3340
|
return clientConfig;
|
|
3262
3341
|
}
|
|
3263
3342
|
|
|
3343
|
+
/******************************************************************************
|
|
3344
|
+
Copyright (c) Microsoft Corporation.
|
|
3345
|
+
|
|
3346
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
3347
|
+
purpose with or without fee is hereby granted.
|
|
3348
|
+
|
|
3349
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
3350
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
3351
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
3352
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
3353
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
3354
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
3355
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
3356
|
+
***************************************************************************** */
|
|
3357
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
3358
|
+
|
|
3359
|
+
|
|
3360
|
+
function __rest(s, e) {
|
|
3361
|
+
var t = {};
|
|
3362
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
3363
|
+
t[p] = s[p];
|
|
3364
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
3365
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
3366
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
3367
|
+
t[p[i]] = s[p[i]];
|
|
3368
|
+
}
|
|
3369
|
+
return t;
|
|
3370
|
+
}
|
|
3371
|
+
|
|
3372
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
3373
|
+
var e = new Error(message);
|
|
3374
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
3375
|
+
};
|
|
3376
|
+
|
|
3264
3377
|
// Cache for get responses
|
|
3265
3378
|
const getCache = {};
|
|
3266
3379
|
// Track in-flight requests to coalesce multiple identical requests
|
|
@@ -3358,11 +3471,11 @@ function cleanupExpiredCache() {
|
|
|
3358
3471
|
});
|
|
3359
3472
|
lastCacheCleanup = now;
|
|
3360
3473
|
}
|
|
3361
|
-
async function runQuery(absolutePath, queryName, queryArgs) {
|
|
3362
|
-
const result = await runQueryMany([{ absolutePath, queryName, queryArgs }]);
|
|
3474
|
+
async function runQuery(absolutePath, queryName, queryArgs, opts) {
|
|
3475
|
+
const result = await runQueryMany([{ absolutePath, queryName, queryArgs }], opts);
|
|
3363
3476
|
return result[0];
|
|
3364
3477
|
}
|
|
3365
|
-
async function runQueryMany(many) {
|
|
3478
|
+
async function runQueryMany(many, opts) {
|
|
3366
3479
|
try {
|
|
3367
3480
|
let queries = [];
|
|
3368
3481
|
for (const { absolutePath, queryName, queryArgs } of many) {
|
|
@@ -3373,7 +3486,7 @@ async function runQueryMany(many) {
|
|
|
3373
3486
|
}
|
|
3374
3487
|
queries.push({ path: normalizedPath, queryName, queryArgs });
|
|
3375
3488
|
}
|
|
3376
|
-
const response = await makeApiRequest('POST', 'queries', { queries },
|
|
3489
|
+
const response = await makeApiRequest('POST', 'queries', { queries }, opts === null || opts === void 0 ? void 0 : opts._overrides);
|
|
3377
3490
|
return response.data.queries.map((result) => result.result);
|
|
3378
3491
|
}
|
|
3379
3492
|
catch (error) {
|
|
@@ -3490,6 +3603,12 @@ async function setMany(many, options) {
|
|
|
3490
3603
|
if (setResponse.data === true) {
|
|
3491
3604
|
return Object.assign(Object.assign({}, documents.map(d => d.document)), { transactionId: null });
|
|
3492
3605
|
}
|
|
3606
|
+
else if (setResponse.data &&
|
|
3607
|
+
typeof setResponse.data === 'object' &&
|
|
3608
|
+
setResponse.data.success === true) {
|
|
3609
|
+
const _a = setResponse.data, { success: _success } = _a, rest = __rest(_a, ["success"]);
|
|
3610
|
+
return Object.assign(Object.assign(Object.assign({}, documents.map(d => d.document)), rest), { transactionId: null });
|
|
3611
|
+
}
|
|
3493
3612
|
else {
|
|
3494
3613
|
return Object.assign(Object.assign({}, setResponse.data), { transactionId: null });
|
|
3495
3614
|
}
|
|
@@ -3720,14 +3839,26 @@ const responseCache = new Map();
|
|
|
3720
3839
|
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
|
3721
3840
|
const TOKEN_REFRESH_BUFFER = 5 * 60 * 1000; // Refresh token 5 minutes before expiry
|
|
3722
3841
|
// ============ WebSocket Config ============
|
|
3842
|
+
const BASE_MIN_RECONNECT_DELAY_MS = 1000;
|
|
3843
|
+
const MIN_RECONNECT_DELAY_JITTER_MS = 1000;
|
|
3844
|
+
const MAX_RECONNECT_DELAY_MS = 300000;
|
|
3845
|
+
const RECONNECT_DELAY_GROW_FACTOR = 1.8;
|
|
3846
|
+
const MIN_BROWSER_RECONNECT_INTERVAL_MS = 30000;
|
|
3723
3847
|
const WS_CONFIG = {
|
|
3724
|
-
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3848
|
+
// Keep retrying indefinitely so long outages recover without page refresh.
|
|
3849
|
+
maxRetries: Infinity,
|
|
3850
|
+
// Conservative starting delay with jitter spreads reconnects across clients.
|
|
3851
|
+
minReconnectionDelay: BASE_MIN_RECONNECT_DELAY_MS + Math.floor(Math.random() * MIN_RECONNECT_DELAY_JITTER_MS),
|
|
3852
|
+
// Stretch backoff to 5 minutes during prolonged outages to protect backend pools.
|
|
3853
|
+
maxReconnectionDelay: MAX_RECONNECT_DELAY_MS,
|
|
3854
|
+
// Moderate growth to avoid rapid retry bursts while still converging to max delay.
|
|
3855
|
+
reconnectionDelayGrowFactor: RECONNECT_DELAY_GROW_FACTOR,
|
|
3856
|
+
// Give handshakes more time before considering the attempt failed.
|
|
3857
|
+
connectionTimeout: 10000,
|
|
3729
3858
|
};
|
|
3730
3859
|
const WS_V2_PATH = '/ws/v2';
|
|
3860
|
+
let browserReconnectHooksAttached = false;
|
|
3861
|
+
let lastBrowserTriggeredReconnectAt = 0;
|
|
3731
3862
|
// ============ Helper Functions ============
|
|
3732
3863
|
function generateSubscriptionId() {
|
|
3733
3864
|
return `sub_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
|
|
@@ -3797,7 +3928,7 @@ async function getFreshAuthToken(isServer) {
|
|
|
3797
3928
|
}
|
|
3798
3929
|
const refreshData = await refreshSession(refreshToken);
|
|
3799
3930
|
if (refreshData && refreshData.idToken && refreshData.accessToken) {
|
|
3800
|
-
updateIdTokenAndAccessToken(refreshData.idToken, refreshData.accessToken);
|
|
3931
|
+
await updateIdTokenAndAccessToken(refreshData.idToken, refreshData.accessToken, isServer);
|
|
3801
3932
|
return refreshData.idToken;
|
|
3802
3933
|
}
|
|
3803
3934
|
}
|
|
@@ -3806,8 +3937,54 @@ async function getFreshAuthToken(isServer) {
|
|
|
3806
3937
|
}
|
|
3807
3938
|
return currentToken;
|
|
3808
3939
|
}
|
|
3940
|
+
function hasDisconnectedActiveConnections() {
|
|
3941
|
+
for (const connection of connections.values()) {
|
|
3942
|
+
if (!connection.ws) {
|
|
3943
|
+
continue;
|
|
3944
|
+
}
|
|
3945
|
+
// Only nudge reconnection when socket is fully closed.
|
|
3946
|
+
// If ReconnectingWebSocket is already in CONNECTING/backoff state, don't interfere.
|
|
3947
|
+
if (connection.ws.readyState === WS_READY_STATE_CLOSED) {
|
|
3948
|
+
return true;
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
return false;
|
|
3952
|
+
}
|
|
3953
|
+
function maybeReconnectFromBrowserSignal() {
|
|
3954
|
+
const now = Date.now();
|
|
3955
|
+
if (now - lastBrowserTriggeredReconnectAt < MIN_BROWSER_RECONNECT_INTERVAL_MS) {
|
|
3956
|
+
return;
|
|
3957
|
+
}
|
|
3958
|
+
if (!hasDisconnectedActiveConnections()) {
|
|
3959
|
+
return;
|
|
3960
|
+
}
|
|
3961
|
+
lastBrowserTriggeredReconnectAt = now;
|
|
3962
|
+
reconnectWithNewAuthV2().catch((error) => {
|
|
3963
|
+
console.error('[WS v2] Browser-triggered reconnect failed:', error);
|
|
3964
|
+
});
|
|
3965
|
+
}
|
|
3966
|
+
function attachBrowserReconnectHooksOnce() {
|
|
3967
|
+
if (browserReconnectHooksAttached) {
|
|
3968
|
+
return;
|
|
3969
|
+
}
|
|
3970
|
+
if (typeof window === 'undefined') {
|
|
3971
|
+
return;
|
|
3972
|
+
}
|
|
3973
|
+
browserReconnectHooksAttached = true;
|
|
3974
|
+
window.addEventListener('online', () => {
|
|
3975
|
+
maybeReconnectFromBrowserSignal();
|
|
3976
|
+
});
|
|
3977
|
+
if (typeof document !== 'undefined') {
|
|
3978
|
+
document.addEventListener('visibilitychange', () => {
|
|
3979
|
+
if (document.visibilityState === 'visible') {
|
|
3980
|
+
maybeReconnectFromBrowserSignal();
|
|
3981
|
+
}
|
|
3982
|
+
});
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3809
3985
|
// ============ Connection Management ============
|
|
3810
3986
|
async function getOrCreateConnection(appId, isServer) {
|
|
3987
|
+
attachBrowserReconnectHooksOnce();
|
|
3811
3988
|
let connection = connections.get(appId);
|
|
3812
3989
|
if (connection && connection.ws) {
|
|
3813
3990
|
return connection;
|
|
@@ -3900,13 +4077,15 @@ function handleServerMessage(connection, message) {
|
|
|
3900
4077
|
case 'subscribed': {
|
|
3901
4078
|
const subscription = connection.subscriptions.get(message.subscriptionId);
|
|
3902
4079
|
if (subscription) {
|
|
3903
|
-
//
|
|
3904
|
-
|
|
3905
|
-
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3909
|
-
|
|
4080
|
+
// A data update can race ahead of subscribed during reconnect.
|
|
4081
|
+
// If we already received data for this subscription, treat subscribed
|
|
4082
|
+
// as an ack only and avoid regressing to an older snapshot.
|
|
4083
|
+
if (subscription.lastData === undefined) {
|
|
4084
|
+
const cacheKey = getCacheKey(subscription.path, subscription.prompt, subscription.shape);
|
|
4085
|
+
responseCache.set(cacheKey, { data: message.data, timestamp: Date.now() });
|
|
4086
|
+
subscription.lastData = message.data;
|
|
4087
|
+
notifyCallbacks(subscription, message.data);
|
|
4088
|
+
}
|
|
3910
4089
|
}
|
|
3911
4090
|
// Resolve pending subscription promise
|
|
3912
4091
|
const pending = connection.pendingSubscriptions.get(message.subscriptionId);
|
|
@@ -3972,6 +4151,7 @@ function notifyCallbacks(subscription, data) {
|
|
|
3972
4151
|
}
|
|
3973
4152
|
// WebSocket readyState constants
|
|
3974
4153
|
const WS_READY_STATE_OPEN = 1;
|
|
4154
|
+
const WS_READY_STATE_CLOSED = 3;
|
|
3975
4155
|
function sendSubscribe(connection, subscription) {
|
|
3976
4156
|
if (!connection.ws || connection.ws.readyState !== WS_READY_STATE_OPEN) {
|
|
3977
4157
|
return;
|