@kadoa/node-sdk 0.24.0 → 0.24.2
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 +2 -0
- package/dist/browser/index.global.js +8 -8
- package/dist/browser/index.global.js.map +1 -1
- package/dist/index.d.mts +23 -1
- package/dist/index.d.ts +23 -1
- package/dist/index.js +259 -76
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +259 -76
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -6035,7 +6035,7 @@ var NotificationSetupService = class {
|
|
|
6035
6035
|
(s) => s.eventType === eventType
|
|
6036
6036
|
);
|
|
6037
6037
|
if (existing?.id) {
|
|
6038
|
-
const existingChannelIds = (existing.channels || []).map((
|
|
6038
|
+
const existingChannelIds = (existing.channels || []).map((channel) => channel.id).filter(Boolean);
|
|
6039
6039
|
const mergedChannelIds = [
|
|
6040
6040
|
.../* @__PURE__ */ new Set([...existingChannelIds, ...channelIds])
|
|
6041
6041
|
];
|
|
@@ -6201,7 +6201,7 @@ process.env.KADOA_WSS_NEO_API_URI ?? "wss://events.kadoa.com/events/ws";
|
|
|
6201
6201
|
var REALTIME_API_URI = process.env.KADOA_REALTIME_API_URI ?? "https://realtime.kadoa.com";
|
|
6202
6202
|
|
|
6203
6203
|
// src/version.ts
|
|
6204
|
-
var SDK_VERSION = "0.24.
|
|
6204
|
+
var SDK_VERSION = "0.24.2";
|
|
6205
6205
|
var SDK_NAME = "kadoa-node-sdk";
|
|
6206
6206
|
var SDK_LANGUAGE = "node";
|
|
6207
6207
|
|
|
@@ -6210,89 +6210,256 @@ var debug5 = logger.wss;
|
|
|
6210
6210
|
if (typeof WebSocket === "undefined") {
|
|
6211
6211
|
global.WebSocket = __require("ws");
|
|
6212
6212
|
}
|
|
6213
|
-
var
|
|
6213
|
+
var isDrainControlMessage = (message) => message.type === "control.draining";
|
|
6214
|
+
var isRealtimeEvent = (message) => message.type !== "heartbeat" && message.type !== "control.draining";
|
|
6215
|
+
var _Realtime = class _Realtime {
|
|
6214
6216
|
constructor(config) {
|
|
6217
|
+
this.drainingSockets = /* @__PURE__ */ new Set();
|
|
6215
6218
|
this.lastHeartbeat = Date.now();
|
|
6216
6219
|
this.isConnecting = false;
|
|
6217
6220
|
this.eventListeners = /* @__PURE__ */ new Set();
|
|
6218
6221
|
this.connectionListeners = /* @__PURE__ */ new Set();
|
|
6219
6222
|
this.errorListeners = /* @__PURE__ */ new Set();
|
|
6223
|
+
this.isClosed = false;
|
|
6224
|
+
this.hasConnectedOnce = false;
|
|
6225
|
+
this.recentEventIds = /* @__PURE__ */ new Set();
|
|
6226
|
+
this.recentEventIdQueue = [];
|
|
6227
|
+
this.maxRecentEventIds = 1e3;
|
|
6220
6228
|
this.apiKey = config.apiKey;
|
|
6221
6229
|
this.heartbeatInterval = config.heartbeatInterval || 1e4;
|
|
6222
|
-
this.reconnectDelay = config.reconnectDelay
|
|
6230
|
+
this.reconnectDelay = this.normalizeReconnectDelay(config.reconnectDelay);
|
|
6223
6231
|
this.missedHeartbeatsLimit = config.missedHeartbeatsLimit || 3e4;
|
|
6224
6232
|
}
|
|
6225
6233
|
async connect() {
|
|
6226
|
-
if (this.isConnecting)
|
|
6234
|
+
if (this.isClosed || this.isConnecting || this.activeSocket) {
|
|
6235
|
+
return;
|
|
6236
|
+
}
|
|
6227
6237
|
this.isConnecting = true;
|
|
6228
6238
|
try {
|
|
6229
|
-
const
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6239
|
+
const { access_token, team_id } = await this.getOAuthToken();
|
|
6240
|
+
await this.openSocket(access_token, team_id, "active");
|
|
6241
|
+
this.hasConnectedOnce = true;
|
|
6242
|
+
} catch (err) {
|
|
6243
|
+
debug5("Failed to connect: %O", err);
|
|
6244
|
+
this.isConnecting = false;
|
|
6245
|
+
this.notifyErrorListeners(err);
|
|
6246
|
+
if (!this.hasConnectedOnce) {
|
|
6247
|
+
throw err;
|
|
6248
|
+
}
|
|
6249
|
+
this.scheduleReconnect();
|
|
6250
|
+
}
|
|
6251
|
+
}
|
|
6252
|
+
async getOAuthToken() {
|
|
6253
|
+
const response = await fetch(`${PUBLIC_API_URI}/v4/oauth2/token`, {
|
|
6254
|
+
method: "POST",
|
|
6255
|
+
headers: {
|
|
6256
|
+
"Content-Type": "application/json",
|
|
6257
|
+
"x-api-key": `${this.apiKey}`,
|
|
6258
|
+
"x-sdk-version": SDK_VERSION
|
|
6259
|
+
}
|
|
6260
|
+
});
|
|
6261
|
+
return await response.json();
|
|
6262
|
+
}
|
|
6263
|
+
async openSocket(accessToken, teamId, role) {
|
|
6264
|
+
await new Promise((resolve, reject) => {
|
|
6265
|
+
const socket = new WebSocket(
|
|
6266
|
+
`${WSS_API_URI}?access_token=${accessToken}`
|
|
6267
|
+
);
|
|
6268
|
+
let settled = false;
|
|
6269
|
+
socket.onopen = () => {
|
|
6270
|
+
const subscribeMessage = {
|
|
6271
|
+
action: "subscribe",
|
|
6272
|
+
channel: teamId
|
|
6273
|
+
};
|
|
6274
|
+
if (this.lastCursor) {
|
|
6275
|
+
subscribeMessage.lastCursor = this.lastCursor;
|
|
6235
6276
|
}
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
this.
|
|
6240
|
-
|
|
6241
|
-
);
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
this.lastHeartbeat = Date.now();
|
|
6245
|
-
if (this.socket?.readyState === WebSocket.OPEN) {
|
|
6246
|
-
this.socket.send(
|
|
6247
|
-
JSON.stringify({
|
|
6248
|
-
action: "subscribe",
|
|
6249
|
-
channel: team_id
|
|
6250
|
-
})
|
|
6251
|
-
);
|
|
6252
|
-
debug5("Connected to WebSocket");
|
|
6253
|
-
this.notifyConnectionListeners(true);
|
|
6254
|
-
}
|
|
6255
|
-
this.startHeartbeatCheck();
|
|
6277
|
+
socket.send(JSON.stringify(subscribeMessage));
|
|
6278
|
+
this.promoteSocket(socket, role);
|
|
6279
|
+
this.isConnecting = false;
|
|
6280
|
+
this.lastHeartbeat = Date.now();
|
|
6281
|
+
this.startHeartbeatCheck();
|
|
6282
|
+
debug5("Connected to WebSocket");
|
|
6283
|
+
if (!settled) {
|
|
6284
|
+
settled = true;
|
|
6256
6285
|
resolve();
|
|
6257
|
-
}
|
|
6258
|
-
|
|
6259
|
-
|
|
6260
|
-
|
|
6261
|
-
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
} catch (err) {
|
|
6274
|
-
debug5("Failed to parse incoming message: %O", err);
|
|
6275
|
-
}
|
|
6276
|
-
};
|
|
6277
|
-
this.socket.onclose = () => {
|
|
6278
|
-
debug5("WebSocket disconnected. Attempting to reconnect...");
|
|
6279
|
-
this.isConnecting = false;
|
|
6280
|
-
this.stopHeartbeatCheck();
|
|
6281
|
-
this.notifyConnectionListeners(false, "Connection closed");
|
|
6282
|
-
setTimeout(() => this.connect(), this.reconnectDelay);
|
|
6283
|
-
};
|
|
6284
|
-
this.socket.onerror = (error) => {
|
|
6285
|
-
debug5("WebSocket error: %O", error);
|
|
6286
|
-
this.isConnecting = false;
|
|
6287
|
-
this.notifyErrorListeners(error);
|
|
6286
|
+
}
|
|
6287
|
+
};
|
|
6288
|
+
socket.onmessage = (event) => {
|
|
6289
|
+
this.handleSocketMessage(socket, event.data);
|
|
6290
|
+
};
|
|
6291
|
+
socket.onclose = () => {
|
|
6292
|
+
this.handleSocketClose(socket);
|
|
6293
|
+
if (!settled) {
|
|
6294
|
+
settled = true;
|
|
6295
|
+
reject(new Error("WebSocket closed before opening"));
|
|
6296
|
+
}
|
|
6297
|
+
};
|
|
6298
|
+
socket.onerror = (error) => {
|
|
6299
|
+
this.notifyErrorListeners(error);
|
|
6300
|
+
if (!settled) {
|
|
6301
|
+
settled = true;
|
|
6288
6302
|
reject(error);
|
|
6289
|
-
|
|
6290
|
-
|
|
6303
|
+
return;
|
|
6304
|
+
}
|
|
6305
|
+
if (socket === this.activeSocket) {
|
|
6306
|
+
this.handleUnexpectedDisconnect("Socket error");
|
|
6307
|
+
}
|
|
6308
|
+
};
|
|
6309
|
+
});
|
|
6310
|
+
}
|
|
6311
|
+
promoteSocket(socket, role) {
|
|
6312
|
+
if (role === "replacement" && this.activeSocket && this.activeSocket !== socket) {
|
|
6313
|
+
this.drainingSockets.add(this.activeSocket);
|
|
6314
|
+
}
|
|
6315
|
+
this.activeSocket = socket;
|
|
6316
|
+
this.drainingSockets.delete(socket);
|
|
6317
|
+
if (role === "active" || !this.hasConnectedOnce) {
|
|
6318
|
+
this.notifyConnectionListeners(true);
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
handleSocketMessage(socket, rawData) {
|
|
6322
|
+
try {
|
|
6323
|
+
const payload = typeof rawData === "string" ? rawData : rawData.toString?.() ?? "";
|
|
6324
|
+
const data = JSON.parse(payload);
|
|
6325
|
+
if (data.type === "heartbeat") {
|
|
6326
|
+
if (socket === this.activeSocket) {
|
|
6327
|
+
this.handleHeartbeat();
|
|
6328
|
+
}
|
|
6329
|
+
return;
|
|
6330
|
+
}
|
|
6331
|
+
if (isDrainControlMessage(data)) {
|
|
6332
|
+
this.handleDrainSignal(socket, data);
|
|
6333
|
+
return;
|
|
6334
|
+
}
|
|
6335
|
+
if (!isRealtimeEvent(data)) {
|
|
6336
|
+
return;
|
|
6337
|
+
}
|
|
6338
|
+
if (typeof data._cursor === "string") {
|
|
6339
|
+
this.lastCursor = data._cursor;
|
|
6340
|
+
}
|
|
6341
|
+
if (typeof data.id === "string") {
|
|
6342
|
+
fetch(`${REALTIME_API_URI}/api/v1/events/ack`, {
|
|
6343
|
+
method: "POST",
|
|
6344
|
+
headers: { "Content-Type": "application/json" },
|
|
6345
|
+
body: JSON.stringify({ id: data.id })
|
|
6346
|
+
}).catch((error) => {
|
|
6347
|
+
debug5("Failed to acknowledge event %s: %O", data.id, error);
|
|
6348
|
+
});
|
|
6349
|
+
}
|
|
6350
|
+
if (this.isDuplicateEvent(data.id)) {
|
|
6351
|
+
return;
|
|
6352
|
+
}
|
|
6353
|
+
this.notifyEventListeners(data);
|
|
6291
6354
|
} catch (err) {
|
|
6292
|
-
debug5("Failed to
|
|
6293
|
-
|
|
6294
|
-
|
|
6355
|
+
debug5("Failed to parse incoming message: %O", err);
|
|
6356
|
+
}
|
|
6357
|
+
}
|
|
6358
|
+
handleDrainSignal(socket, message) {
|
|
6359
|
+
if (socket !== this.activeSocket || this.isClosed) {
|
|
6360
|
+
return;
|
|
6361
|
+
}
|
|
6362
|
+
debug5("Received drain signal, preparing replacement socket");
|
|
6363
|
+
this.drainingSockets.add(socket);
|
|
6364
|
+
this.scheduleDrainReconnect(message.retryAfterMs);
|
|
6365
|
+
}
|
|
6366
|
+
handleSocketClose(socket) {
|
|
6367
|
+
const wasActiveSocket = socket === this.activeSocket;
|
|
6368
|
+
this.drainingSockets.delete(socket);
|
|
6369
|
+
if (!wasActiveSocket) {
|
|
6370
|
+
return;
|
|
6371
|
+
}
|
|
6372
|
+
this.activeSocket = void 0;
|
|
6373
|
+
this.stopHeartbeatCheck();
|
|
6374
|
+
if (this.isClosed) {
|
|
6375
|
+
return;
|
|
6376
|
+
}
|
|
6377
|
+
if (this.drainingSockets.size > 0) {
|
|
6378
|
+
debug5("Draining socket closed after replacement was scheduled");
|
|
6379
|
+
return;
|
|
6380
|
+
}
|
|
6381
|
+
this.handleUnexpectedDisconnect("Connection closed");
|
|
6382
|
+
}
|
|
6383
|
+
handleUnexpectedDisconnect(reason) {
|
|
6384
|
+
this.isConnecting = false;
|
|
6385
|
+
this.notifyConnectionListeners(false, reason);
|
|
6386
|
+
this.scheduleReconnect();
|
|
6387
|
+
}
|
|
6388
|
+
scheduleReconnect(replacement = false) {
|
|
6389
|
+
if (this.isClosed || this.reconnectTimer) {
|
|
6390
|
+
return;
|
|
6391
|
+
}
|
|
6392
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
6393
|
+
this.reconnectTimer = void 0;
|
|
6394
|
+
if (this.isClosed || this.isConnecting || !replacement && this.activeSocket) {
|
|
6395
|
+
return;
|
|
6396
|
+
}
|
|
6397
|
+
this.isConnecting = true;
|
|
6398
|
+
try {
|
|
6399
|
+
const { access_token, team_id } = await this.getOAuthToken();
|
|
6400
|
+
await this.openSocket(
|
|
6401
|
+
access_token,
|
|
6402
|
+
team_id,
|
|
6403
|
+
replacement ? "replacement" : "active"
|
|
6404
|
+
);
|
|
6405
|
+
} catch (err) {
|
|
6406
|
+
debug5("Reconnect failed: %O", err);
|
|
6407
|
+
this.isConnecting = false;
|
|
6408
|
+
this.notifyErrorListeners(err);
|
|
6409
|
+
this.scheduleReconnect(replacement);
|
|
6410
|
+
}
|
|
6411
|
+
}, this.reconnectDelay);
|
|
6412
|
+
}
|
|
6413
|
+
scheduleDrainReconnect(retryAfterMs) {
|
|
6414
|
+
if (this.isClosed || this.reconnectTimer) {
|
|
6415
|
+
return;
|
|
6295
6416
|
}
|
|
6417
|
+
let safeDelayMs = this.reconnectDelay;
|
|
6418
|
+
if (typeof retryAfterMs === "number" && Number.isFinite(retryAfterMs) && retryAfterMs >= 0 && retryAfterMs <= _Realtime.MAX_RECONNECT_DELAY_MS) {
|
|
6419
|
+
safeDelayMs = Math.trunc(retryAfterMs);
|
|
6420
|
+
}
|
|
6421
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
6422
|
+
this.reconnectTimer = void 0;
|
|
6423
|
+
if (this.isClosed || this.isConnecting) {
|
|
6424
|
+
return;
|
|
6425
|
+
}
|
|
6426
|
+
this.isConnecting = true;
|
|
6427
|
+
try {
|
|
6428
|
+
const { access_token, team_id } = await this.getOAuthToken();
|
|
6429
|
+
await this.openSocket(access_token, team_id, "replacement");
|
|
6430
|
+
} catch (err) {
|
|
6431
|
+
debug5("Reconnect failed: %O", err);
|
|
6432
|
+
this.isConnecting = false;
|
|
6433
|
+
this.notifyErrorListeners(err);
|
|
6434
|
+
this.scheduleReconnect(true);
|
|
6435
|
+
}
|
|
6436
|
+
}, safeDelayMs);
|
|
6437
|
+
}
|
|
6438
|
+
normalizeReconnectDelay(delay) {
|
|
6439
|
+
if (typeof delay !== "number" || !Number.isFinite(delay)) {
|
|
6440
|
+
return _Realtime.DEFAULT_RECONNECT_DELAY_MS;
|
|
6441
|
+
}
|
|
6442
|
+
return Math.min(
|
|
6443
|
+
Math.max(0, Math.trunc(delay)),
|
|
6444
|
+
_Realtime.MAX_RECONNECT_DELAY_MS
|
|
6445
|
+
);
|
|
6446
|
+
}
|
|
6447
|
+
isDuplicateEvent(eventId) {
|
|
6448
|
+
if (!eventId) {
|
|
6449
|
+
return false;
|
|
6450
|
+
}
|
|
6451
|
+
if (this.recentEventIds.has(eventId)) {
|
|
6452
|
+
return true;
|
|
6453
|
+
}
|
|
6454
|
+
this.recentEventIds.add(eventId);
|
|
6455
|
+
this.recentEventIdQueue.push(eventId);
|
|
6456
|
+
if (this.recentEventIdQueue.length > this.maxRecentEventIds) {
|
|
6457
|
+
const expiredId = this.recentEventIdQueue.shift();
|
|
6458
|
+
if (expiredId) {
|
|
6459
|
+
this.recentEventIds.delete(expiredId);
|
|
6460
|
+
}
|
|
6461
|
+
}
|
|
6462
|
+
return false;
|
|
6296
6463
|
}
|
|
6297
6464
|
handleHeartbeat() {
|
|
6298
6465
|
debug5("Heartbeat received");
|
|
@@ -6320,22 +6487,24 @@ var Realtime = class {
|
|
|
6320
6487
|
this.errorListeners.forEach((listener) => {
|
|
6321
6488
|
try {
|
|
6322
6489
|
listener(error);
|
|
6323
|
-
} catch (
|
|
6324
|
-
debug5("Error in error listener: %O",
|
|
6490
|
+
} catch (listenerError) {
|
|
6491
|
+
debug5("Error in error listener: %O", listenerError);
|
|
6325
6492
|
}
|
|
6326
6493
|
});
|
|
6327
6494
|
}
|
|
6328
6495
|
startHeartbeatCheck() {
|
|
6496
|
+
this.stopHeartbeatCheck();
|
|
6329
6497
|
this.missedHeartbeatCheckTimer = setInterval(() => {
|
|
6330
|
-
if (Date.now() - this.lastHeartbeat > this.missedHeartbeatsLimit) {
|
|
6498
|
+
if (this.activeSocket && Date.now() - this.lastHeartbeat > this.missedHeartbeatsLimit) {
|
|
6331
6499
|
debug5("No heartbeat received in 30 seconds! Closing connection.");
|
|
6332
|
-
this.
|
|
6500
|
+
this.activeSocket.close();
|
|
6333
6501
|
}
|
|
6334
6502
|
}, this.heartbeatInterval);
|
|
6335
6503
|
}
|
|
6336
6504
|
stopHeartbeatCheck() {
|
|
6337
6505
|
if (this.missedHeartbeatCheckTimer) {
|
|
6338
6506
|
clearInterval(this.missedHeartbeatCheckTimer);
|
|
6507
|
+
this.missedHeartbeatCheckTimer = void 0;
|
|
6339
6508
|
}
|
|
6340
6509
|
}
|
|
6341
6510
|
/**
|
|
@@ -6356,6 +6525,9 @@ var Realtime = class {
|
|
|
6356
6525
|
*/
|
|
6357
6526
|
onConnection(listener) {
|
|
6358
6527
|
this.connectionListeners.add(listener);
|
|
6528
|
+
if (this.isConnected()) {
|
|
6529
|
+
listener(true);
|
|
6530
|
+
}
|
|
6359
6531
|
return () => {
|
|
6360
6532
|
this.connectionListeners.delete(listener);
|
|
6361
6533
|
};
|
|
@@ -6372,19 +6544,30 @@ var Realtime = class {
|
|
|
6372
6544
|
};
|
|
6373
6545
|
}
|
|
6374
6546
|
close() {
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
this.
|
|
6378
|
-
this.
|
|
6547
|
+
this.isClosed = true;
|
|
6548
|
+
if (this.reconnectTimer) {
|
|
6549
|
+
clearTimeout(this.reconnectTimer);
|
|
6550
|
+
this.reconnectTimer = void 0;
|
|
6379
6551
|
}
|
|
6552
|
+
this.stopHeartbeatCheck();
|
|
6553
|
+
this.activeSocket?.close();
|
|
6554
|
+
this.activeSocket = void 0;
|
|
6555
|
+
this.drainingSockets.forEach((socket) => {
|
|
6556
|
+
socket.close();
|
|
6557
|
+
});
|
|
6558
|
+
this.drainingSockets.clear();
|
|
6559
|
+
this.isConnecting = false;
|
|
6380
6560
|
this.eventListeners.clear();
|
|
6381
6561
|
this.connectionListeners.clear();
|
|
6382
6562
|
this.errorListeners.clear();
|
|
6383
6563
|
}
|
|
6384
6564
|
isConnected() {
|
|
6385
|
-
return this.
|
|
6565
|
+
return this.activeSocket?.readyState === WebSocket.OPEN;
|
|
6386
6566
|
}
|
|
6387
6567
|
};
|
|
6568
|
+
_Realtime.DEFAULT_RECONNECT_DELAY_MS = 5e3;
|
|
6569
|
+
_Realtime.MAX_RECONNECT_DELAY_MS = 6e4;
|
|
6570
|
+
var Realtime = _Realtime;
|
|
6388
6571
|
|
|
6389
6572
|
// src/domains/user/user.service.ts
|
|
6390
6573
|
var UserService = class {
|