@calimero-network/mero-js 19.14.0 → 19.15.0
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/events/sse.js +45 -6
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +73 -6
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +35 -33
- package/dist/index.js +20 -19
- package/dist/index.mjs +73 -6
- package/dist/index.mjs.map +4 -4
- package/dist/request/index.d.ts +1 -0
- package/dist/request/index.js +1 -0
- package/dist/request/request-sig.d.ts +99 -0
- package/dist/request/request-sig.js +96 -0
- package/package.json +1 -1
package/dist/events/sse.js
CHANGED
|
@@ -6,6 +6,32 @@ import { AuthRevokedError, HTTPError } from '../http-client/index.js';
|
|
|
6
6
|
* all — is recoverable by refreshing and reconnecting.
|
|
7
7
|
*/
|
|
8
8
|
const TERMINAL_AUTH_ERRORS = new Set(['token_reuse', 'token_revoked']);
|
|
9
|
+
/**
|
|
10
|
+
* Statuses a terminal `x-auth-error` can arrive on.
|
|
11
|
+
*
|
|
12
|
+
* Measured against merod 0.11.0-rc.41: a revoked token family answers
|
|
13
|
+
* `GET /sse` and `POST /sse/subscription` with **403**, `x-auth-error:
|
|
14
|
+
* token_revoked` and `content-length: 0` — `token_expired` is the 401.
|
|
15
|
+
* Keying the check on 401 alone made the branch below unreachable for the one
|
|
16
|
+
* reason a stream actually sees, so a dead token family surfaced as a bare
|
|
17
|
+
* `HTTPError(403)` and reconnected on a timer forever.
|
|
18
|
+
*/
|
|
19
|
+
const TERMINAL_AUTH_STATUSES = new Set([401, 403]);
|
|
20
|
+
/**
|
|
21
|
+
* Whether a failed response means the whole token family is gone, so the caller
|
|
22
|
+
* must re-authenticate and the client must stop reconnecting.
|
|
23
|
+
*
|
|
24
|
+
* The body carries nothing on this path (`content-length: 0`), so the header is
|
|
25
|
+
* the only evidence there is.
|
|
26
|
+
*/
|
|
27
|
+
function isTerminalAuthFailure(response) {
|
|
28
|
+
const authError = response.headers.get('x-auth-error');
|
|
29
|
+
if (!authError)
|
|
30
|
+
return null;
|
|
31
|
+
if (!TERMINAL_AUTH_STATUSES.has(response.status))
|
|
32
|
+
return null;
|
|
33
|
+
return TERMINAL_AUTH_ERRORS.has(authError) ? authError : null;
|
|
34
|
+
}
|
|
9
35
|
export class SseClient {
|
|
10
36
|
constructor(opts) {
|
|
11
37
|
this.sessionId = null;
|
|
@@ -110,12 +136,10 @@ export class SseClient {
|
|
|
110
136
|
// 401 as fatal — logs the user out on a routine reconnect after the
|
|
111
137
|
// access token ages out, discarding a refresh token that was still
|
|
112
138
|
// good. The distinction is in the header; surface it.
|
|
113
|
-
const
|
|
139
|
+
const terminal = isTerminalAuthFailure(response);
|
|
114
140
|
const bodyText = await response.text().catch(() => undefined);
|
|
115
|
-
if (
|
|
116
|
-
|
|
117
|
-
TERMINAL_AUTH_ERRORS.has(authError)) {
|
|
118
|
-
throw new AuthRevokedError(authError, response.status, response.statusText, url, response.headers, bodyText);
|
|
141
|
+
if (terminal) {
|
|
142
|
+
throw new AuthRevokedError(terminal, response.status, response.statusText, url, response.headers, bodyText);
|
|
119
143
|
}
|
|
120
144
|
throw new HTTPError(response.status, response.statusText, url, response.headers, bodyText);
|
|
121
145
|
}
|
|
@@ -135,6 +159,11 @@ export class SseClient {
|
|
|
135
159
|
return;
|
|
136
160
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
137
161
|
this.emit('error', error);
|
|
162
|
+
// Reconnecting against a revoked token family is a timer that never
|
|
163
|
+
// succeeds: every attempt re-sends the same dead credential. The caller
|
|
164
|
+
// has to re-authenticate, and an AuthRevokedError is how it is told.
|
|
165
|
+
if (error instanceof AuthRevokedError)
|
|
166
|
+
return;
|
|
138
167
|
this.scheduleReconnect();
|
|
139
168
|
}
|
|
140
169
|
}
|
|
@@ -284,7 +313,17 @@ export class SseClient {
|
|
|
284
313
|
}),
|
|
285
314
|
});
|
|
286
315
|
if (!response.ok) {
|
|
287
|
-
|
|
316
|
+
// The subscribe path used to report only the status. On the one failure
|
|
317
|
+
// that matters it is the whole of what the caller got: a revoked family
|
|
318
|
+
// answers 403 with an empty body, so a bare `SSE subscribe failed: 403`
|
|
319
|
+
// discarded the only evidence of what went wrong — and a 403 here reads
|
|
320
|
+
// identically to the unrelated "not the session owner" refusal, which
|
|
321
|
+
// does carry a body.
|
|
322
|
+
const terminal = isTerminalAuthFailure(response);
|
|
323
|
+
const bodyText = await response.text().catch(() => undefined);
|
|
324
|
+
this.emit('error', terminal
|
|
325
|
+
? new AuthRevokedError(terminal, response.status, response.statusText, `${this.baseUrl}/sse/subscription`, response.headers, bodyText)
|
|
326
|
+
: new HTTPError(response.status, response.statusText, `${this.baseUrl}/sse/subscription`, response.headers, bodyText));
|
|
288
327
|
}
|
|
289
328
|
}
|
|
290
329
|
catch (err) {
|