@ravi-hq/ravi 0.6.1 → 0.6.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 +8 -9
- package/dist/channels/email-trusted.d.ts +5 -5
- package/dist/channels/email-trusted.js +18 -18
- package/dist/channels/email-trusted.js.map +1 -1
- package/dist/channels/email.d.ts +12 -12
- package/dist/channels/email.js +22 -22
- package/dist/channels/email.js.map +1 -1
- package/dist/client.d.ts +18 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +22 -1
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/polling-pool.d.ts +17 -0
- package/dist/polling-pool.d.ts.map +1 -0
- package/dist/polling-pool.js +42 -0
- package/dist/polling-pool.js.map +1 -0
- package/dist/polling.d.ts +70 -0
- package/dist/polling.d.ts.map +1 -0
- package/dist/polling.js +208 -0
- package/dist/polling.js.map +1 -0
- package/dist/tools/contacts.d.ts.map +1 -1
- package/dist/tools/contacts.js +3 -4
- package/dist/tools/contacts.js.map +1 -1
- package/dist/tools/identity.d.ts.map +1 -1
- package/dist/tools/identity.js +4 -3
- package/dist/tools/identity.js.map +1 -1
- package/dist/types.d.ts +6 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/channels/email-untrusted.d.ts +0 -133
- package/dist/channels/email-untrusted.d.ts.map +0 -1
- package/dist/channels/email-untrusted.js +0 -283
- package/dist/channels/email-untrusted.js.map +0 -1
- package/dist/sse-pool.d.ts +0 -18
- package/dist/sse-pool.d.ts.map +0 -1
- package/dist/sse-pool.js +0 -43
- package/dist/sse-pool.js.map +0 -1
- package/dist/sse.d.ts +0 -117
- package/dist/sse.d.ts.map +0 -1
- package/dist/sse.js +0 -301
- package/dist/sse.js.map +0 -1
package/dist/sse.d.ts
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import type { EmailEvent } from "./types.js";
|
|
2
|
-
/** Callback invoked when a typed SSE event is received. */
|
|
3
|
-
export type SSEEventHandler<T> = (event: T) => void;
|
|
4
|
-
/** Options for constructing a {@link RaviSSEClient}. */
|
|
5
|
-
export interface RaviSSEClientConfig {
|
|
6
|
-
/** Base URL of the Ravi API (e.g. "https://api.ravi.dev"). */
|
|
7
|
-
apiUrl: string;
|
|
8
|
-
/** JWT bearer token for authentication (identity is embedded in JWT claims). */
|
|
9
|
-
token: string;
|
|
10
|
-
/**
|
|
11
|
-
* Optional callback invoked when SSE receives a 401/403 response.
|
|
12
|
-
* Return a fresh token to retry the connection, or `null` to stop.
|
|
13
|
-
*/
|
|
14
|
-
onAuthFailure?: () => Promise<string | null>;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Server-Sent Events client for the Ravi real-time event stream.
|
|
18
|
-
*
|
|
19
|
-
* Connects to `GET /api/events/stream/` and dispatches typed events
|
|
20
|
-
* (email_owner, email_trusted, email_untrusted) to registered handlers. Identity scoping is embedded
|
|
21
|
-
* in the JWT token (obtained via bind-identity). Automatically reconnects
|
|
22
|
-
* with exponential backoff on connection loss and monitors server
|
|
23
|
-
* heartbeats to detect stale connections.
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* const sse = new RaviSSEClient({
|
|
28
|
-
* apiUrl: "https://api.ravi.dev",
|
|
29
|
-
* token: "ey...", // bound token with identity in JWT claims
|
|
30
|
-
* });
|
|
31
|
-
*
|
|
32
|
-
* sse.onEmailOwner((event) => console.log("New email:", event.subject));
|
|
33
|
-
* sse.connect();
|
|
34
|
-
*
|
|
35
|
-
* // Later...
|
|
36
|
-
* sse.disconnect();
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
export declare class RaviSSEClient {
|
|
40
|
-
private apiUrl;
|
|
41
|
-
private token;
|
|
42
|
-
private controller;
|
|
43
|
-
private reconnectDelay;
|
|
44
|
-
private maxReconnectDelay;
|
|
45
|
-
private lastEventId;
|
|
46
|
-
private emailOwnerHandlers;
|
|
47
|
-
private emailTrustedHandlers;
|
|
48
|
-
private emailUntrustedHandlers;
|
|
49
|
-
private reconnectHandlers;
|
|
50
|
-
private running;
|
|
51
|
-
private heartbeatTimeout;
|
|
52
|
-
/** Expect a keepalive or data every 60s (server sends keepalive every 30s). */
|
|
53
|
-
private heartbeatInterval;
|
|
54
|
-
/** Flag set when the heartbeat timer aborts the connection. */
|
|
55
|
-
private isHeartbeatAbort;
|
|
56
|
-
/** Optional callback to attempt token refresh on 401/403. */
|
|
57
|
-
private onAuthFailure?;
|
|
58
|
-
constructor(config: RaviSSEClientConfig);
|
|
59
|
-
/** Register a handler for email events from the identity owner. */
|
|
60
|
-
onEmailOwner(handler: SSEEventHandler<EmailEvent>): void;
|
|
61
|
-
/** Register a handler for email events from trusted senders. */
|
|
62
|
-
onEmailTrusted(handler: SSEEventHandler<EmailEvent>): void;
|
|
63
|
-
/** Register a handler for email events from untrusted senders. */
|
|
64
|
-
onEmailUntrusted(handler: SSEEventHandler<EmailEvent>): void;
|
|
65
|
-
/**
|
|
66
|
-
* Register a handler that fires whenever the client reconnects after a
|
|
67
|
-
* connection drop. Useful for re-fetching state that may have been missed
|
|
68
|
-
* during the disconnection window.
|
|
69
|
-
*/
|
|
70
|
-
onReconnect(handler: () => void): void;
|
|
71
|
-
/**
|
|
72
|
-
* Open the SSE connection and begin streaming events.
|
|
73
|
-
*
|
|
74
|
-
* The connection loop runs in the background and will automatically
|
|
75
|
-
* reconnect on failure. Call {@link disconnect} to stop.
|
|
76
|
-
*/
|
|
77
|
-
connect(): void;
|
|
78
|
-
/**
|
|
79
|
-
* Update the bearer token used for SSE connections.
|
|
80
|
-
*
|
|
81
|
-
* Takes effect on the next reconnection — the current connection
|
|
82
|
-
* continues until it naturally drops or is aborted.
|
|
83
|
-
*/
|
|
84
|
-
updateToken(token: string): void;
|
|
85
|
-
/**
|
|
86
|
-
* Gracefully close the SSE connection.
|
|
87
|
-
*
|
|
88
|
-
* Aborts any in-flight fetch, stops the heartbeat timer, and prevents
|
|
89
|
-
* further reconnection attempts.
|
|
90
|
-
*/
|
|
91
|
-
disconnect(): void;
|
|
92
|
-
/**
|
|
93
|
-
* Core connection loop. Opens a streaming fetch to the SSE endpoint,
|
|
94
|
-
* parses the event stream, and dispatches events. On failure, waits
|
|
95
|
-
* with exponential backoff before retrying.
|
|
96
|
-
*/
|
|
97
|
-
private startConnection;
|
|
98
|
-
/**
|
|
99
|
-
* Parse the JSON data payload and dispatch to the appropriate typed handlers.
|
|
100
|
-
*
|
|
101
|
-
* Logs and skips events with malformed JSON. Handler errors are caught
|
|
102
|
-
* individually so one broken handler does not prevent others from running.
|
|
103
|
-
*/
|
|
104
|
-
private dispatchEvent;
|
|
105
|
-
/**
|
|
106
|
-
* Reset the heartbeat watchdog timer.
|
|
107
|
-
*
|
|
108
|
-
* Called whenever we receive any data from the server (keepalive comments,
|
|
109
|
-
* event data, etc.). If no data arrives within {@link heartbeatInterval}ms,
|
|
110
|
-
* the connection is assumed stale and the fetch is aborted to trigger
|
|
111
|
-
* reconnection.
|
|
112
|
-
*/
|
|
113
|
-
private resetHeartbeatTimer;
|
|
114
|
-
/** Clear the heartbeat watchdog timer. */
|
|
115
|
-
private clearHeartbeatTimer;
|
|
116
|
-
}
|
|
117
|
-
//# sourceMappingURL=sse.d.ts.map
|
package/dist/sse.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,2DAA2D;AAC3D,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AAIpD,wDAAwD;AACxD,MAAM,WAAW,mBAAmB;IAClC,8DAA8D;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,gFAAgF;IAChF,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9C;AAID;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,kBAAkB,CAAqC;IAC/D,OAAO,CAAC,oBAAoB,CAAqC;IACjE,OAAO,CAAC,sBAAsB,CAAqC;IACnE,OAAO,CAAC,iBAAiB,CAAyB;IAClD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAA8C;IACtE,+EAA+E;IAC/E,OAAO,CAAC,iBAAiB,CAAU;IACnC,+DAA+D;IAC/D,OAAO,CAAC,gBAAgB,CAAS;IACjC,6DAA6D;IAC7D,OAAO,CAAC,aAAa,CAAC,CAA+B;gBAEzC,MAAM,EAAE,mBAAmB;IAMvC,mEAAmE;IACnE,YAAY,CAAC,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,GAAG,IAAI;IAIxD,gEAAgE;IAChE,cAAc,CAAC,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,GAAG,IAAI;IAI1D,kEAAkE;IAClE,gBAAgB,CAAC,OAAO,EAAE,eAAe,CAAC,UAAU,CAAC,GAAG,IAAI;IAI5D;;;;OAIG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAItC;;;;;OAKG;IACH,OAAO,IAAI,IAAI;IAOf;;;;;OAKG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAIhC;;;;;OAKG;IACH,UAAU,IAAI,IAAI;IAOlB;;;;OAIG;YACW,eAAe;IA+I7B;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IA6BrB;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IAS3B,0CAA0C;IAC1C,OAAO,CAAC,mBAAmB;CAM5B"}
|
package/dist/sse.js
DELETED
|
@@ -1,301 +0,0 @@
|
|
|
1
|
-
// ─── SSE Client ──────────────────────────────────────────────────────────────
|
|
2
|
-
/**
|
|
3
|
-
* Server-Sent Events client for the Ravi real-time event stream.
|
|
4
|
-
*
|
|
5
|
-
* Connects to `GET /api/events/stream/` and dispatches typed events
|
|
6
|
-
* (email_owner, email_trusted, email_untrusted) to registered handlers. Identity scoping is embedded
|
|
7
|
-
* in the JWT token (obtained via bind-identity). Automatically reconnects
|
|
8
|
-
* with exponential backoff on connection loss and monitors server
|
|
9
|
-
* heartbeats to detect stale connections.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* const sse = new RaviSSEClient({
|
|
14
|
-
* apiUrl: "https://api.ravi.dev",
|
|
15
|
-
* token: "ey...", // bound token with identity in JWT claims
|
|
16
|
-
* });
|
|
17
|
-
*
|
|
18
|
-
* sse.onEmailOwner((event) => console.log("New email:", event.subject));
|
|
19
|
-
* sse.connect();
|
|
20
|
-
*
|
|
21
|
-
* // Later...
|
|
22
|
-
* sse.disconnect();
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
export class RaviSSEClient {
|
|
26
|
-
apiUrl;
|
|
27
|
-
token;
|
|
28
|
-
controller = null;
|
|
29
|
-
reconnectDelay = 1000;
|
|
30
|
-
maxReconnectDelay = 30000;
|
|
31
|
-
lastEventId = null;
|
|
32
|
-
emailOwnerHandlers = [];
|
|
33
|
-
emailTrustedHandlers = [];
|
|
34
|
-
emailUntrustedHandlers = [];
|
|
35
|
-
reconnectHandlers = [];
|
|
36
|
-
running = false;
|
|
37
|
-
heartbeatTimeout = null;
|
|
38
|
-
/** Expect a keepalive or data every 60s (server sends keepalive every 30s). */
|
|
39
|
-
heartbeatInterval = 60_000;
|
|
40
|
-
/** Flag set when the heartbeat timer aborts the connection. */
|
|
41
|
-
isHeartbeatAbort = false;
|
|
42
|
-
/** Optional callback to attempt token refresh on 401/403. */
|
|
43
|
-
onAuthFailure;
|
|
44
|
-
constructor(config) {
|
|
45
|
-
this.apiUrl = config.apiUrl.replace(/\/$/, "");
|
|
46
|
-
this.token = config.token;
|
|
47
|
-
this.onAuthFailure = config.onAuthFailure;
|
|
48
|
-
}
|
|
49
|
-
/** Register a handler for email events from the identity owner. */
|
|
50
|
-
onEmailOwner(handler) {
|
|
51
|
-
this.emailOwnerHandlers.push(handler);
|
|
52
|
-
}
|
|
53
|
-
/** Register a handler for email events from trusted senders. */
|
|
54
|
-
onEmailTrusted(handler) {
|
|
55
|
-
this.emailTrustedHandlers.push(handler);
|
|
56
|
-
}
|
|
57
|
-
/** Register a handler for email events from untrusted senders. */
|
|
58
|
-
onEmailUntrusted(handler) {
|
|
59
|
-
this.emailUntrustedHandlers.push(handler);
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Register a handler that fires whenever the client reconnects after a
|
|
63
|
-
* connection drop. Useful for re-fetching state that may have been missed
|
|
64
|
-
* during the disconnection window.
|
|
65
|
-
*/
|
|
66
|
-
onReconnect(handler) {
|
|
67
|
-
this.reconnectHandlers.push(handler);
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Open the SSE connection and begin streaming events.
|
|
71
|
-
*
|
|
72
|
-
* The connection loop runs in the background and will automatically
|
|
73
|
-
* reconnect on failure. Call {@link disconnect} to stop.
|
|
74
|
-
*/
|
|
75
|
-
connect() {
|
|
76
|
-
this.running = true;
|
|
77
|
-
this.startConnection().catch((err) => {
|
|
78
|
-
console.error("[ravi] SSE connection loop terminated unexpectedly:", err);
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* Update the bearer token used for SSE connections.
|
|
83
|
-
*
|
|
84
|
-
* Takes effect on the next reconnection — the current connection
|
|
85
|
-
* continues until it naturally drops or is aborted.
|
|
86
|
-
*/
|
|
87
|
-
updateToken(token) {
|
|
88
|
-
this.token = token;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Gracefully close the SSE connection.
|
|
92
|
-
*
|
|
93
|
-
* Aborts any in-flight fetch, stops the heartbeat timer, and prevents
|
|
94
|
-
* further reconnection attempts.
|
|
95
|
-
*/
|
|
96
|
-
disconnect() {
|
|
97
|
-
this.running = false;
|
|
98
|
-
this.controller?.abort();
|
|
99
|
-
this.controller = null;
|
|
100
|
-
this.clearHeartbeatTimer();
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* Core connection loop. Opens a streaming fetch to the SSE endpoint,
|
|
104
|
-
* parses the event stream, and dispatches events. On failure, waits
|
|
105
|
-
* with exponential backoff before retrying.
|
|
106
|
-
*/
|
|
107
|
-
async startConnection() {
|
|
108
|
-
let isReconnect = false;
|
|
109
|
-
let consecutiveFailures = 0;
|
|
110
|
-
let authRetries = 0;
|
|
111
|
-
while (this.running) {
|
|
112
|
-
try {
|
|
113
|
-
this.controller = new AbortController();
|
|
114
|
-
const headers = {
|
|
115
|
-
Authorization: `Bearer ${this.token}`,
|
|
116
|
-
Accept: "text/event-stream",
|
|
117
|
-
};
|
|
118
|
-
if (this.lastEventId) {
|
|
119
|
-
headers["Last-Event-ID"] = this.lastEventId;
|
|
120
|
-
}
|
|
121
|
-
const response = await fetch(`${this.apiUrl}/api/events/stream/`, {
|
|
122
|
-
headers,
|
|
123
|
-
signal: this.controller.signal,
|
|
124
|
-
});
|
|
125
|
-
if (!response.ok) {
|
|
126
|
-
const status = response.status;
|
|
127
|
-
if (status === 401 || status === 403) {
|
|
128
|
-
if (this.onAuthFailure && authRetries < 2) {
|
|
129
|
-
authRetries++;
|
|
130
|
-
let newToken = null;
|
|
131
|
-
try {
|
|
132
|
-
newToken = await this.onAuthFailure();
|
|
133
|
-
}
|
|
134
|
-
catch (refreshErr) {
|
|
135
|
-
const detail = refreshErr instanceof Error ? refreshErr.message : String(refreshErr);
|
|
136
|
-
console.error(`[ravi] SSE auth refresh callback failed: ${detail}`);
|
|
137
|
-
}
|
|
138
|
-
if (newToken) {
|
|
139
|
-
this.token = newToken;
|
|
140
|
-
continue; // retry with refreshed token
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
console.error(`[ravi] SSE auth failed (${status}) — stopping`);
|
|
144
|
-
this.running = false;
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
throw new Error(`SSE connection failed: HTTP ${status}`);
|
|
148
|
-
}
|
|
149
|
-
// Successful connection — reset backoff, failure counter, and auth retries
|
|
150
|
-
this.reconnectDelay = 1000;
|
|
151
|
-
consecutiveFailures = 0;
|
|
152
|
-
authRetries = 0;
|
|
153
|
-
this.resetHeartbeatTimer();
|
|
154
|
-
// Fire reconnect handlers AFTER successful connection
|
|
155
|
-
if (isReconnect) {
|
|
156
|
-
for (const handler of this.reconnectHandlers) {
|
|
157
|
-
try {
|
|
158
|
-
handler();
|
|
159
|
-
}
|
|
160
|
-
catch (err) {
|
|
161
|
-
console.error("[ravi] Reconnect handler threw:", err);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
isReconnect = true;
|
|
166
|
-
const reader = response.body?.getReader();
|
|
167
|
-
if (!reader)
|
|
168
|
-
throw new Error("No response body");
|
|
169
|
-
const decoder = new TextDecoder();
|
|
170
|
-
let buffer = "";
|
|
171
|
-
// Per-event accumulators (SSE spec: fields build up until blank line)
|
|
172
|
-
let currentId = null;
|
|
173
|
-
let currentEvent = null;
|
|
174
|
-
let currentData = "";
|
|
175
|
-
while (this.running) {
|
|
176
|
-
const { done, value } = await reader.read();
|
|
177
|
-
if (done)
|
|
178
|
-
break;
|
|
179
|
-
buffer += decoder.decode(value, { stream: true });
|
|
180
|
-
const lines = buffer.split("\n");
|
|
181
|
-
// Keep the last incomplete line in the buffer
|
|
182
|
-
buffer = lines.pop() ?? "";
|
|
183
|
-
for (const rawLine of lines) {
|
|
184
|
-
const line = rawLine.replace(/\r$/, "");
|
|
185
|
-
if (line.startsWith(":")) {
|
|
186
|
-
// Comment line — server keepalive
|
|
187
|
-
this.resetHeartbeatTimer();
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
if (line.startsWith("id:")) {
|
|
191
|
-
currentId = line.slice(line[3] === " " ? 4 : 3).trim();
|
|
192
|
-
}
|
|
193
|
-
else if (line.startsWith("event:")) {
|
|
194
|
-
currentEvent = line.slice(line[6] === " " ? 7 : 6).trim();
|
|
195
|
-
}
|
|
196
|
-
else if (line.startsWith("data:")) {
|
|
197
|
-
const value = line.slice(line[5] === " " ? 6 : 5);
|
|
198
|
-
if (currentData)
|
|
199
|
-
currentData += "\n";
|
|
200
|
-
currentData += value;
|
|
201
|
-
}
|
|
202
|
-
else if (line === "") {
|
|
203
|
-
// Blank line = end of event dispatch
|
|
204
|
-
this.resetHeartbeatTimer();
|
|
205
|
-
if (currentEvent && currentData) {
|
|
206
|
-
if (currentId)
|
|
207
|
-
this.lastEventId = currentId;
|
|
208
|
-
this.dispatchEvent(currentEvent, currentData);
|
|
209
|
-
}
|
|
210
|
-
currentId = null;
|
|
211
|
-
currentEvent = null;
|
|
212
|
-
currentData = "";
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
// Stream closed cleanly — clear heartbeat
|
|
217
|
-
this.clearHeartbeatTimer();
|
|
218
|
-
}
|
|
219
|
-
catch (error) {
|
|
220
|
-
this.clearHeartbeatTimer();
|
|
221
|
-
// If disconnect() was called, exit cleanly
|
|
222
|
-
if (!this.running)
|
|
223
|
-
return;
|
|
224
|
-
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
225
|
-
console.error(`[ravi] SSE connection error: ${errorMsg}`);
|
|
226
|
-
// Heartbeat-triggered aborts are expected — don't count as failures
|
|
227
|
-
if (this.isHeartbeatAbort) {
|
|
228
|
-
this.isHeartbeatAbort = false;
|
|
229
|
-
}
|
|
230
|
-
else {
|
|
231
|
-
consecutiveFailures++;
|
|
232
|
-
}
|
|
233
|
-
if (consecutiveFailures >= 50) {
|
|
234
|
-
console.error(`[ravi] SSE giving up after ${consecutiveFailures} consecutive failures`);
|
|
235
|
-
this.running = false;
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
// Exponential backoff
|
|
239
|
-
await new Promise((resolve) => setTimeout(resolve, this.reconnectDelay));
|
|
240
|
-
this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
|
|
241
|
-
isReconnect = true;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Parse the JSON data payload and dispatch to the appropriate typed handlers.
|
|
247
|
-
*
|
|
248
|
-
* Logs and skips events with malformed JSON. Handler errors are caught
|
|
249
|
-
* individually so one broken handler does not prevent others from running.
|
|
250
|
-
*/
|
|
251
|
-
dispatchEvent(eventType, data) {
|
|
252
|
-
let parsed;
|
|
253
|
-
try {
|
|
254
|
-
parsed = JSON.parse(data);
|
|
255
|
-
}
|
|
256
|
-
catch {
|
|
257
|
-
console.error(`[ravi] Failed to parse SSE event (type=${eventType}): ${data.slice(0, 200)}`);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
const handlers = eventType === "email_owner" ? this.emailOwnerHandlers
|
|
261
|
-
: eventType === "email_trusted" ? this.emailTrustedHandlers
|
|
262
|
-
: eventType === "email_untrusted" ? this.emailUntrustedHandlers
|
|
263
|
-
: null;
|
|
264
|
-
if (!handlers) {
|
|
265
|
-
console.warn(`[ravi] Received unhandled SSE event type: ${eventType}`);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
for (const handler of handlers) {
|
|
269
|
-
try {
|
|
270
|
-
handler(parsed);
|
|
271
|
-
}
|
|
272
|
-
catch (err) {
|
|
273
|
-
console.error(`[ravi] SSE ${eventType} handler threw:`, err);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Reset the heartbeat watchdog timer.
|
|
279
|
-
*
|
|
280
|
-
* Called whenever we receive any data from the server (keepalive comments,
|
|
281
|
-
* event data, etc.). If no data arrives within {@link heartbeatInterval}ms,
|
|
282
|
-
* the connection is assumed stale and the fetch is aborted to trigger
|
|
283
|
-
* reconnection.
|
|
284
|
-
*/
|
|
285
|
-
resetHeartbeatTimer() {
|
|
286
|
-
this.clearHeartbeatTimer();
|
|
287
|
-
this.heartbeatTimeout = setTimeout(() => {
|
|
288
|
-
// No heartbeat received — force reconnect by aborting the fetch
|
|
289
|
-
this.isHeartbeatAbort = true;
|
|
290
|
-
this.controller?.abort();
|
|
291
|
-
}, this.heartbeatInterval);
|
|
292
|
-
}
|
|
293
|
-
/** Clear the heartbeat watchdog timer. */
|
|
294
|
-
clearHeartbeatTimer() {
|
|
295
|
-
if (this.heartbeatTimeout) {
|
|
296
|
-
clearTimeout(this.heartbeatTimeout);
|
|
297
|
-
this.heartbeatTimeout = null;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
//# sourceMappingURL=sse.js.map
|
package/dist/sse.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"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"}
|