UNPKG

@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
"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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