@microsoft/teams.apps
Version:
<p> <a href="https://www.npmjs.com/package/@microsoft/teams.apps" target="_blank"> <img src="https://img.shields.io/npm/v/@microsoft/teams.apps/latest" /> </a> <a href="https://www.npmjs.com/package/@microsoft/teams.apps?activeTab=code
371 lines • 33.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeoSocket = void 0;
const envelope_1 = require("./envelope");
/** Keep a superseded socket alive while APX connection-ID caches expire. */
const CONNECTION_HANDOFF_MS = 5_000;
/**
* Supervises one Socket Mode connection for a single geo, including readiness,
* reconnection, and proactive token refresh.
*/
class GeoSocket {
deps;
geo;
negotiateUrl;
log;
/** Monotonic connection generation. */
generation = 0;
/** The ready generation receiving new backend traffic. */
active;
/** Prior generations serving cached deliveries and draining SDK replies. */
retiring = new Map();
supervisorLoop;
refreshTimer;
_status = 'idle';
/** Settles when the current connection terminates and drives the supervisor. */
closed = Promise.resolve({ planned: false });
constructor(deps, geo, negotiateUrl, log) {
this.deps = deps;
this.geo = geo;
this.negotiateUrl = negotiateUrl;
this.log = log;
}
get status() {
return this._status;
}
/** Accept frames from the active socket and its draining predecessors. */
canDispatch(gen) {
return (this.deps.isAccepting() &&
(this.active?.gen === gen || this.retiring.has(gen)));
}
/**
* Establish the initial connection within the configured startup budget.
*/
async startInitial() {
this._status = 'connecting';
const deadline = Date.now() + this.deps.startupTimeoutMs;
let attempt = 0;
let lastError;
this.log.debug(`socket-mode[${this.geo}]: starting initial connection with a ${this.deps.startupTimeoutMs}ms startup budget`);
while (this.deps.isAccepting()) {
const gen = ++this.generation;
try {
this.closed = (await this.connectCycle(gen)).closed;
this._status = 'ready';
this.log.debug(`socket-mode[${this.geo}]: initial connection is ready`);
return;
}
catch (err) {
lastError = err;
if (!this.deps.isAccepting())
break;
if (this.deps.isNonRetryable(err)) {
// Thrown to App.start(), which reports it through the app `error` event.
this.log.debug(`socket-mode[${this.geo}]: initial connection rejected with a non-retryable error; not retrying`);
throw err;
}
const delay = this.deps.retryAfterOf(err) ?? this.deps.backoffDelay(attempt);
attempt++;
if (Date.now() + delay >= deadline)
break;
this.log.warn(`socket-mode[${this.geo}]: initial connect attempt ${attempt} failed; retrying in ${delay}ms`, err);
const slept = await this.deps.sleep(delay);
if (!slept)
break;
}
}
this.log.debug(`socket-mode[${this.geo}]: initial connection did not become ready within the startup budget`, lastError);
throw lastError ?? new Error(`Socket Mode failed to establish the initial connection for geo '${this.geo}'.`);
}
superviseInBackground() {
this.supervisorLoop = this.supervise().catch((err) => {
this.log.error(`socket-mode[${this.geo}]: reconnect supervisor stopped unexpectedly`, err);
});
}
/** Stop token refresh, every owned connection, and the reconnect supervisor. */
async stop() {
this.clearRefreshTimer();
const connections = this.releaseConnections();
await Promise.all(connections.map((connection) => connection.stop().catch(() => undefined)));
await this.supervisorLoop?.catch(() => undefined);
this._status = 'stopped';
}
async connectCycle(gen, replaces) {
let settled = false;
let settle;
const closed = new Promise((resolve) => {
settle = (reason) => {
if (!settled) {
settled = true;
resolve(reason);
}
};
});
const handlers = {
onActivity: async (envelope) => {
const arrivedAt = Date.now();
if (!this.canDispatch(gen)) {
this.log.debug('socket-mode: dropping activity received outside the active connection state');
return undefined;
}
const deadlineMs = (0, envelope_1.readField)(envelope, 'deadlineMs');
let deadline = arrivedAt;
if (deadlineMs !== undefined) {
if (typeof deadlineMs === 'number' && Number.isFinite(deadlineMs) && deadlineMs >= 0) {
deadline += deadlineMs;
}
else {
this.log.warn('socket-mode: invalid deadlineMs; no drain budget beyond the cache grace');
}
}
const activity = { deadline };
current.inFlight.add(activity);
this.scheduleDrain(current);
try {
return await this.deps.dispatch(envelope);
}
finally {
current.inFlight.delete(activity);
this.scheduleDrain(current);
}
},
onReady: (frame) => {
if (this.active && this.active.gen !== gen) {
this.retire(this.active);
}
this.active = current;
// Observers reading aggregate status from the ready event must see the
// settled value rather than the previous connecting state.
this._status = 'ready';
this.deps.onReady(frame);
},
onClosed: (error) => {
this.clearDrain(current);
if (this.active?.gen === gen) {
this.clearRefreshTimer();
if (settled && this.deps.isAccepting()) {
// A planned refresh already advanced the supervisor. If the active
// predecessor dies before its replacement is ready, surface the
// resulting delivery outage.
this.active = undefined;
this.reportDisconnected(error);
}
}
this.retiring.delete(gen);
settle({ planned: false, error });
},
};
const connection = this.deps.createConnection(this.negotiateUrl, handlers, replaces
? () => {
if (!this.deps.isAccepting() || this.active !== replaces || !replaces.connection.isOpen) {
return undefined;
}
const expiresAtMs = replaces.connection.expiresAtMs;
if (expiresAtMs !== undefined && Date.now() >= expiresAtMs) {
return undefined;
}
return replaces.connection.connectionId;
}
: undefined);
const current = { gen, connection, inFlight: new Set() };
await connection.start(this.deps.getAbortSignal());
this.scheduleTokenRefresh(gen, connection, () => settle({ planned: true }));
return { closed };
}
async supervise() {
let closed = this.closed;
while (this.deps.isAccepting()) {
const aborted = this.whenAborted();
let reason;
try {
reason = (await Promise.race([closed, aborted.promise])) ?? { planned: false };
}
finally {
// Avoid accumulating one abort listener per reconnect generation.
aborted.dispose();
}
if (!this.deps.isAccepting())
return;
const planned = reason.planned === true;
const error = reason.planned ? undefined : reason.error;
if (planned) {
// Keep the active generation serving while its replacement negotiates
// and waits for SocketReady.
this.log.info(`socket-mode[${this.geo}]: proactively rotating token; renegotiating a fresh connection`);
}
else {
this.reportDisconnected(error);
}
if (!planned) {
const dropped = this.active;
this.active = undefined;
await dropped?.connection.stop().catch(() => undefined);
}
const previous = planned ? this.active : undefined;
const next = await this.reconnect({ keepServing: planned, replaces: previous }, error);
if (!next)
return; // stopped while backing off
closed = next.closed;
this._status = 'ready';
if (planned) {
if (previous?.graceEndsAt !== undefined) {
this.log.info(`socket-mode[${this.geo}]: token rotated; inbound delivery continues for this geo`);
}
else {
this.log.info(`socket-mode[${this.geo}]: reconnected; inbound delivery resumed for this geo`);
this.deps.onReconnected();
}
}
else {
this.log.info(`socket-mode[${this.geo}]: reconnected; inbound delivery resumed for this geo`);
this.deps.onReconnected();
}
}
}
whenAborted() {
const signal = this.deps.getAbortSignal();
if (!signal || signal.aborted) {
return { promise: Promise.resolve(undefined), dispose: () => undefined };
}
let onAbort;
const promise = new Promise((resolve) => {
onAbort = () => resolve(undefined);
signal.addEventListener('abort', onAbort, { once: true });
});
return { promise, dispose: () => signal.removeEventListener('abort', onAbort) };
}
async reconnect(options, prevError) {
let attempt = 0;
let retryAfterMs = this.deps.retryAfterOf(prevError);
while (this.deps.isAccepting()) {
const delay = retryAfterMs ?? this.deps.backoffDelay(attempt);
attempt++;
const slept = await this.deps.sleep(delay);
if (!slept || !this.deps.isAccepting())
return undefined;
if (!options.keepServing) {
this._status = 'connecting';
}
const gen = ++this.generation;
try {
return await this.connectCycle(gen, options.replaces);
}
catch (err) {
// Negotiate isn't abortable, so a rejection can land after stop().
if (!this.deps.isAccepting())
return undefined;
if (this.deps.isNonRetryable(err)) {
await this.stopAfterNonRetryable(err);
return undefined;
}
retryAfterMs = this.deps.retryAfterOf(err);
this.log.warn(`socket-mode[${this.geo}]: reconnect attempt ${attempt} failed; will retry`, err);
}
}
return undefined;
}
/**
* Stop reconnecting after a 401/403: close every connection this geo owns
* (including a predecessor still serving during a planned rotation and any
* sockets in their handoff window), then report the outage with the auth
* error so listeners can see why delivery stopped.
*/
async stopAfterNonRetryable(error) {
this.log.error(`socket-mode[${this.geo}]: reconnect rejected with a non-retryable error; ` +
'inbound delivery for this geo has stopped until the app is restarted', error);
this.clearRefreshTimer();
// Clear before stopping so their onClosed callbacks do not report again.
const connections = this.releaseConnections();
await Promise.all(connections.map((connection) => connection.stop().catch(() => undefined)));
// stop() may have landed while the connections were closing.
if (!this.deps.isAccepting())
return;
this.reportDisconnected(error);
}
/**
* Mark this geo disconnected and emit the event. A non-retryable error means
* the geo has stopped for good; it skips the "paused" warning because
* {@link stopAfterNonRetryable} already logged the permanent stop.
*/
reportDisconnected(error) {
this._status = 'disconnected';
const terminal = error !== undefined && this.deps.isNonRetryable(error);
if (!terminal) {
if (error) {
this.log.warn(`socket-mode[${this.geo}]: disconnected; inbound delivery paused for this geo`, error);
}
else {
this.log.warn(`socket-mode[${this.geo}]: disconnected; inbound delivery paused for this geo`);
}
}
this.deps.onDisconnected(error, terminal);
}
retire(previous) {
previous.graceEndsAt = Date.now() + CONNECTION_HANDOFF_MS;
this.retiring.set(previous.gen, previous);
this.scheduleDrain(previous);
}
scheduleDrain(previous) {
if (this.retiring.get(previous.gen) !== previous || previous.graceEndsAt === undefined)
return;
clearTimeout(previous.drainTimer);
let closeAt = previous.graceEndsAt;
for (const activity of previous.inFlight) {
closeAt = Math.max(closeAt, activity.deadline);
}
// Even an empty drain closes on a timer, outside the SDK reply callback.
// Clamp the timer's range, not the message's deadline.
const delay = Math.min(Math.max(0, closeAt - Date.now()), 2_147_483_647);
previous.drainTimer = setTimeout(() => {
previous.drainTimer = undefined;
if (!this.deps.isAccepting() || this.retiring.get(previous.gen) !== previous)
return;
if (Date.now() < closeAt) {
this.scheduleDrain(previous);
return;
}
this.retiring.delete(previous.gen);
this.clearDrain(previous);
void previous.connection.stop().catch((error) => {
this.log.warn(`socket-mode[${this.geo}]: failed to stop a retired socket`, error);
});
}, delay);
previous.drainTimer.unref?.();
}
clearDrain(current) {
clearTimeout(current.drainTimer);
current.drainTimer = undefined;
current.inFlight.clear();
}
releaseConnections() {
const connections = [this.active, ...this.retiring.values()].filter((current) => current !== undefined);
this.active = undefined;
this.retiring.clear();
for (const current of connections)
this.clearDrain(current);
return connections.map((current) => current.connection);
}
scheduleTokenRefresh(gen, connection, triggerRefresh) {
this.clearRefreshTimer();
const { expiresInSeconds, expiresAtMs } = connection;
if (!expiresInSeconds || expiresInSeconds <= 0)
return;
const remainingMs = expiresAtMs === undefined ? expiresInSeconds * 1000 : expiresAtMs - Date.now();
const delay = Math.max(remainingMs - this.deps.tokenRefreshMarginMs, 1_000);
this.refreshTimer = setTimeout(() => {
if (!this.deps.isAccepting() || gen !== this.active?.gen)
return;
this.log.info(`socket-mode[${this.geo}]: proactively renegotiating before token expiry`);
triggerRefresh();
}, delay);
this.refreshTimer.unref?.();
}
clearRefreshTimer() {
if (this.refreshTimer) {
clearTimeout(this.refreshTimer);
this.refreshTimer = undefined;
}
}
}
exports.GeoSocket = GeoSocket;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VvLXNvY2tldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zb2NrZXQtbW9kZS9nZW8tc29ja2V0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLHlDQUF1QztBQWV2Qyw0RUFBNEU7QUFDNUUsTUFBTSxxQkFBcUIsR0FBRyxLQUFLLENBQUM7QUFtQ3BDOzs7R0FHRztBQUNILE1BQWEsU0FBUztJQWNEO0lBQ1I7SUFDUTtJQUNBO0lBaEJuQix1Q0FBdUM7SUFDL0IsVUFBVSxHQUFHLENBQUMsQ0FBQztJQUN2QiwwREFBMEQ7SUFDbEQsTUFBTSxDQUFvQjtJQUNsQyw0RUFBNEU7SUFDM0QsUUFBUSxHQUFHLElBQUksR0FBRyxFQUE0QixDQUFDO0lBQ3hELGNBQWMsQ0FBaUI7SUFDL0IsWUFBWSxDQUFpQztJQUM3QyxPQUFPLEdBQXFCLE1BQU0sQ0FBQztJQUMzQyxnRkFBZ0Y7SUFDeEUsTUFBTSxHQUF5QixPQUFPLENBQUMsT0FBTyxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7SUFFM0UsWUFDbUIsSUFBbUIsRUFDM0IsR0FBVyxFQUNILFlBQW9CLEVBQ3BCLEdBQVk7UUFIWixTQUFJLEdBQUosSUFBSSxDQUFlO1FBQzNCLFFBQUcsR0FBSCxHQUFHLENBQVE7UUFDSCxpQkFBWSxHQUFaLFlBQVksQ0FBUTtRQUNwQixRQUFHLEdBQUgsR0FBRyxDQUFTO0lBQzVCLENBQUM7SUFFSixJQUFJLE1BQU07UUFDUixPQUFPLElBQUksQ0FBQyxPQUFPLENBQUM7SUFDdEIsQ0FBQztJQUVELDBFQUEwRTtJQUMxRSxXQUFXLENBQUMsR0FBVztRQUNyQixPQUFPLENBQ0wsSUFBSSxDQUFDLElBQUksQ0FBQyxXQUFXLEVBQUU7WUFDdkIsQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsS0FBSyxHQUFHLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FD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