@semiont/http-transport 0.5.29 → 0.5.31

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 CHANGED
@@ -42,6 +42,7 @@ import {
42
42
  type BusEvent,
43
43
  type ActorStateUnitOptions,
44
44
  DEGRADED_THRESHOLD_MS,
45
+ SseConnectError,
45
46
  } from '@semiont/http-transport';
46
47
  ```
47
48
 
package/dist/index.d.ts CHANGED
@@ -1,6 +1,30 @@
1
1
  import { KyInstance } from 'ky';
2
2
  import { Observable, BehaviorSubject } from 'rxjs';
3
- import { StateUnit, ConnectionState, SemiontError, TransportErrorCode, ITransport, IGatewayOperations, BaseUrl, AccessToken, Logger, EventMap, ResourceId, EventBus, Email, components, GoogleCredential, RefreshToken, UserResponse, ListUsersResponse, UserDID, UpdateUserRequest, UpdateUserResponse, HealthCheckResponse, StatusResponse, IContentTransport, PutBinaryRequest, PutBinaryOptions, ExtractionOutcome } from '@semiont/core';
3
+ import { SemiontError, StateUnit, ConnectionState, ITransport, IGatewayOperations, BaseUrl, AccessToken, Logger, EventMap, ResourceId, EventBus, Email, components, GoogleCredential, RefreshToken, UserResponse, ListUsersResponse, UserDID, UpdateUserRequest, UpdateUserResponse, HealthCheckResponse, StatusResponse, TransportErrorCode, IContentTransport, PutBinaryRequest, PutBinaryOptions } from '@semiont/core';
4
+
5
+ /**
6
+ * A refused SSE connect — `POST /bus/subscribe` answered non-2xx (or 2xx
7
+ * with no body). The status is CARRIED, not interpolated into the message:
8
+ * P3's backoff/terminal split reads it (auth-refused is terminal in kind;
9
+ * 5xx is transient), and it surfaces on `ActorStateUnit.errors$` so a
10
+ * refused client is observable instead of a silent retry loop
11
+ * (SSE-AUTH-RESILIENCE P2, shape B).
12
+ *
13
+ * Extends `SemiontError` (core) rather than reusing `APIError`: APIError
14
+ * lives in http-transport.ts, which imports the actor — reaching for it
15
+ * there would buy one inherited field with an import cycle.
16
+ *
17
+ * Its OWN module, not `actor-state-unit.ts`, because `*-state-unit.ts` files
18
+ * carry no class declarations: state units are plain-object factories, and
19
+ * `audit-state-unit-no-class.sh` is the static half of the A1 axiom whose
20
+ * allowlist is deliberately empty. An error type is not state-unit
21
+ * machinery, so it moves rather than earning that gate its first exception.
22
+ * A leaf module importing only core also keeps the cycle shut.
23
+ */
24
+ declare class SseConnectError extends SemiontError {
25
+ readonly status: number;
26
+ constructor(status: number);
27
+ }
4
28
 
5
29
  interface BusEvent {
6
30
  channel: string;
@@ -11,7 +35,23 @@ interface ActorStateUnitOptions {
11
35
  baseUrl: string;
12
36
  token: string | (() => string);
13
37
  channels: string[];
38
+ /**
39
+ * Base of the failure-retry backoff ladder AND the flat cadence of the
40
+ * `unauthenticated` waiting tick (which polls the token getter, no
41
+ * network). A failure retry waits jitter(min(reconnectMs·2ⁿ, 60 s));
42
+ * n resets on a successful open. Default 5 s.
43
+ */
14
44
  reconnectMs?: number;
45
+ /**
46
+ * The SAME hook `HttpTransportConfig.tokenRefresher` wires into the HTTP
47
+ * beforeRetry path (SSE-AUTH-RESILIENCE P4, D2 — no second refresh
48
+ * mechanism). Consulted ONCE per outage when a connect is refused 401,
49
+ * before parking `unauthenticated`; a successful open re-arms it. The
50
+ * refresher's owner rotates the token SOURCE (sessions push the new token
51
+ * into `token$`); the actor then reconnects through the getter, so the
52
+ * token stays single-sourced. A throwing refresher is treated as `null`.
53
+ */
54
+ tokenRefresher?: () => Promise<string | null>;
15
55
  /**
16
56
  * Remove-side reconnect hysteresis (MULTI-RESOURCE-SCOPE). Scope
17
57
  * additions need liveness quickly (100 ms debounce), but a removal only
@@ -41,8 +81,22 @@ interface ActorStateUnit extends StateUnit {
41
81
  on$<T = Record<string, unknown>>(channel: string): Observable<T>;
42
82
  emit(channel: string, payload: Record<string, unknown>, emitScope?: string): Promise<number>;
43
83
  state$: Observable<ConnectionState>;
84
+ /**
85
+ * Refused connects (SSE-AUTH-RESILIENCE P2). One `SseConnectError` per
86
+ * non-2xx `/bus/subscribe` answer, carrying the HTTP status as
87
+ * structured data. Network-level failures (fetch rejections) have no
88
+ * status and do not emit here — they stay on the reconnect path.
89
+ */
90
+ errors$: Observable<SseConnectError>;
44
91
  /** With `scope`: upsert channels into that scope's matrix entry. Without: global channels. */
45
92
  addChannels(channels: string[], scope?: string): void;
93
+ /**
94
+ * Whether `channel` is in the current GLOBAL subscription set — i.e. the
95
+ * gateway delivers it on this connection. Correlated replies always ride
96
+ * global channels, so this is `busRequest`'s fail-fast probe on a
97
+ * narrowed-subscription transport (see `BusRequestPrimitive.isSubscribed`).
98
+ */
99
+ isSubscribed(channel: string): boolean;
46
100
  /** With `scope`: remove channels from that scope's entry (empty entry drops the scope). Without: global channels. */
47
101
  removeChannels(channels: string[], scope?: string): void;
48
102
  /**
@@ -75,12 +129,6 @@ type AuthResponse = components['schemas']['AuthResponse'];
75
129
  type TokenRefreshResponse = components['schemas']['TokenRefreshResponse'];
76
130
  type AdminUserStatsResponse = components['schemas']['AdminUserStatsResponse'];
77
131
  type OAuthConfigResponse = components['schemas']['OAuthConfigResponse'];
78
- declare class APIError extends SemiontError {
79
- code: TransportErrorCode;
80
- readonly status: number;
81
- readonly statusText: string;
82
- constructor(message: string, status: number, statusText: string, body?: unknown);
83
- }
84
132
  type TokenRefresher = () => Promise<string | null>;
85
133
  interface HttpTransportConfig {
86
134
  baseUrl: BaseUrl;
@@ -98,6 +146,19 @@ interface HttpTransportConfig {
98
146
  */
99
147
  loadLastEventIds?: () => Record<string, string> | null;
100
148
  saveLastEventId?: (scope: string, id: string) => void;
149
+ /**
150
+ * The global SSE channel set this transport subscribes. Absent means the
151
+ * full `BRIDGED_CHANNELS` — a full client must receive every operation's
152
+ * reply channel, or its `busRequest`s time out. A narrow-profile process
153
+ * (the worker) passes exactly the reply channels for the operations it
154
+ * awaits: reply channels are global fan-out on the gateway, so a full
155
+ * subscription receives every OTHER client's replies too — measured at
156
+ * ~85 multi-MB `browse:annotations-result` frames/min during the
157
+ * 2026-09-03 worker OOM, all parsed and dropped by cid filtering.
158
+ * A `busRequest` on an operation whose replies are outside this set
159
+ * fails fast with `bus.unsubscribed` (see `BusRequestPrimitive`).
160
+ */
161
+ channels?: readonly string[];
101
162
  }
102
163
  declare class HttpTransport implements ITransport, IGatewayOperations {
103
164
  readonly baseUrl: BaseUrl;
@@ -148,6 +209,14 @@ declare class HttpTransport implements ITransport, IGatewayOperations {
148
209
  * replays from the server's retention buffer on reconnect.
149
210
  */
150
211
  trackReply(correlationId: string): () => void;
212
+ /**
213
+ * `busRequest`'s fail-fast probe (`BusRequestPrimitive.isSubscribed`):
214
+ * whether the actor's global subscription set delivers `channel`. On a
215
+ * full client (no `channels` config) every bridged reply channel is
216
+ * subscribed and this never gates; on a narrowed transport it turns a
217
+ * doomed request into an immediate `bus.unsubscribed` error.
218
+ */
219
+ isSubscribed(channel: string): boolean;
151
220
  dispose(): void;
152
221
  /**
153
222
  * Route a transport-level error onto `errors$`. Used by sibling adapters
@@ -188,6 +257,25 @@ declare class HttpTransport implements ITransport, IGatewayOperations {
188
257
  getToken(): AccessToken | undefined;
189
258
  }
190
259
 
260
+ /**
261
+ * `APIError` — the transport's HTTP failure, carrying the status it came from.
262
+ *
263
+ * Lives in its own module rather than in `http-transport.ts` because
264
+ * `actor-state-unit.ts` throws it too (SIDECAR-BOOT-RESILIENCE P2) and
265
+ * `http-transport.ts` imports `actor-state-unit.ts` — so the obvious home is a
266
+ * cycle. The alternative, a second status-bearing error class beside this one,
267
+ * is the duplicated shape the house rules forbid: there would then be two
268
+ * answers to "how does an HTTP failure carry its status", and the retry
269
+ * predicate could only agree with one of them.
270
+ */
271
+
272
+ declare class APIError extends SemiontError {
273
+ code: TransportErrorCode;
274
+ readonly status: number;
275
+ readonly statusText: string;
276
+ constructor(message: string, status: number, statusText: string, body?: unknown);
277
+ }
278
+
191
279
  /**
192
280
  * HttpContentTransport — binary I/O over HTTP.
193
281
  *
@@ -257,50 +345,10 @@ declare class HttpContentTransport implements IContentTransport {
257
345
  getResourceGraph(resourceId: ResourceId, options?: {
258
346
  auth?: AccessToken;
259
347
  }): Promise<GetResourceResponse>;
260
- /**
261
- * Store a resource's derived coordinate map (ANCHORED-TEXT-CACHE Lane 5).
262
- *
263
- * The Smelter is the producer and runs as its own process, which is why this
264
- * crosses the wire at all: the map goes to the one store the KnowledgeSystem
265
- * owns, rather than to a volume shared between service images.
266
- */
267
- putAnchoredText(checksum: string, outcome: ExtractionOutcome, options?: {
268
- auth?: AccessToken;
269
- }): Promise<void>;
270
- /**
271
- * The resource's coordinate map, or `null` when none has been derived —
272
- * which is the common case and not an error: callers degrade to no quoted
273
- * text.
274
- *
275
- * 204 is that answer, and the body is empty, so it must be taken before
276
- * `.json()` is reached — parsing an empty body throws, which would turn the
277
- * ordinary case into a failure. A 404 degrades the same way, though it is a
278
- * different fact: the resource itself is absent, and a resource that does
279
- * not exist has no map either.
280
- */
281
- getAnchoredText(resourceId: ResourceId, options?: {
282
- auth?: AccessToken;
283
- }): Promise<ExtractionOutcome | null>;
284
- /**
285
- * The cache-consult read (PERSIST-ANCHORS P2c) — checksum-addressed and
286
- * barrier-free; 204 is the ordinary miss. This is how an out-of-process
287
- * extraction seam hits the cache at all.
288
- */
289
- getAnchoredTextByChecksum(checksum: string, options?: {
290
- auth?: AccessToken;
291
- }): Promise<ExtractionOutcome | null>;
292
- /**
293
- * The store's would-hit keys — the reconcile planner's bulk existence read
294
- * (PERSIST-ANCHORS P0). One request per reconcile; keys only, never the
295
- * maps themselves, which is the point of the dedicated route.
296
- */
297
- listAnchoredTextKeys(options?: {
298
- auth?: AccessToken;
299
- }): Promise<string[]>;
300
348
  dispose(): void;
301
349
  /** Auth header + W3C trace propagation for the active span. */
302
350
  private requestHeaders;
303
351
  }
304
352
 
305
- export { APIError, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport, createActorStateUnit };
353
+ export { APIError, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport, SseConnectError, createActorStateUnit };
306
354
  export type { ActorStateUnit, ActorStateUnitOptions, BusEvent, HttpTransportConfig, TokenRefresher };
package/dist/index.js CHANGED
@@ -1,26 +1,66 @@
1
1
  import ky, { HTTPError } from 'ky';
2
2
  import { Subject, BehaviorSubject } from 'rxjs';
3
- import { RESOURCE_BROADCAST_TYPES, PERSISTED_EVENT_TYPES, BRIDGED_CHANNELS, SemiontError, busLog, busLogEnabled } from '@semiont/core';
3
+ import { RESOURCE_BROADCAST_TYPES, PERSISTED_EVENT_TYPES, BRIDGED_CHANNELS, SemiontError, uuidV4, retryWithBackoff, isRetryableRequestError, busLog, busLogEnabled } from '@semiont/core';
4
4
  import { getActiveTraceparent, recordBusEmit, withSpan, SpanKind, extractTraceparent, withTraceparent } from '@semiont/observability';
5
5
  import { share, filter, map } from 'rxjs/operators';
6
6
 
7
7
  // src/transport/http-transport.ts
8
+ var SseConnectError = class extends SemiontError {
9
+ status;
10
+ constructor(status) {
11
+ super(`SSE connect failed: ${status}`, "SSE_CONNECT_FAILED", { status });
12
+ this.name = "SseConnectError";
13
+ this.status = status;
14
+ }
15
+ };
16
+ function classifyApiCode(status) {
17
+ if (status === 400) return "bad-request";
18
+ if (status === 401) return "unauthorized";
19
+ if (status === 403) return "forbidden";
20
+ if (status === 404) return "not-found";
21
+ if (status === 409) return "conflict";
22
+ if (status >= 500) return "unavailable";
23
+ return "error";
24
+ }
25
+ var APIError = class extends SemiontError {
26
+ status;
27
+ statusText;
28
+ constructor(message, status, statusText, body) {
29
+ super(message, classifyApiCode(status), { status, statusText, body });
30
+ this.name = "APIError";
31
+ this.status = status;
32
+ this.statusText = statusText;
33
+ }
34
+ };
35
+ new APIError("", 0, "");
36
+
37
+ // src/transport/actor-state-unit.ts
8
38
  var DEGRADED_THRESHOLD_MS = 3e3;
39
+ var MAX_RECONNECT_MS = 6e4;
40
+ var EMIT_TIMEOUT_MS = 3e4;
41
+ var EMIT_RETRY = {
42
+ attempts: 4,
43
+ initialDelayMs: 1e3,
44
+ maxDelayMs: 4e3
45
+ };
9
46
  var LINGER_MS = 1e3;
10
47
  var ALLOWED_TRANSITIONS = {
11
- initial: ["connecting", "closed"],
12
- connecting: ["open", "reconnecting", "closed"],
48
+ initial: ["connecting", "unauthenticated", "closed"],
49
+ connecting: ["open", "reconnecting", "unauthenticated", "closed"],
13
50
  open: ["reconnecting", "closed"],
14
- reconnecting: ["connecting", "degraded", "closed"],
51
+ reconnecting: ["connecting", "degraded", "unauthenticated", "closed"],
15
52
  // `degraded → reconnecting` is a legitimate recovery edge: a channel-set
16
53
  // change (`addChannels`/`removeChannels`) schedules a reconnect that can
17
54
  // fire while the connection is degraded. Omitting it made `reconnect()`
18
55
  // throw a fatal, uncaught exception from the reconnect timer (#844).
19
- degraded: ["connecting", "reconnecting", "closed"],
56
+ degraded: ["connecting", "reconnecting", "unauthenticated", "closed"],
57
+ // Leaving `unauthenticated` takes a usable credential (the gate saw a
58
+ // different, non-empty token) → straight to `connecting`; or teardown.
59
+ unauthenticated: ["connecting", "closed"],
20
60
  closed: []
21
61
  };
22
62
  function createActorStateUnit(options) {
23
- const { baseUrl, token: tokenOrGetter, channels: initialChannels, reconnectMs = 5e3, lazyRemoveMs = 5e3 } = options;
63
+ const { baseUrl, token: tokenOrGetter, channels: initialChannels, reconnectMs = 5e3, lazyRemoveMs = 5e3, tokenRefresher } = options;
24
64
  const getToken = typeof tokenOrGetter === "function" ? tokenOrGetter : () => tokenOrGetter;
25
65
  const globalChannels = new Set(initialChannels);
26
66
  const scopedSubscriptions = /* @__PURE__ */ new Map();
@@ -28,8 +68,10 @@ function createActorStateUnit(options) {
28
68
  Object.entries(options.loadLastEventIds?.() ?? {})
29
69
  );
30
70
  const pendingReplies = /* @__PURE__ */ new Set();
71
+ const clientId = uuidV4();
31
72
  const events$ = new Subject();
32
73
  const state$ = new BehaviorSubject("initial");
74
+ const errors$ = new Subject();
33
75
  let currentState = "initial";
34
76
  let degradedTimer = null;
35
77
  const transition = (next) => {
@@ -88,7 +130,31 @@ function createActorStateUnit(options) {
88
130
  for (const t of lingerTimers) clearTimeout(t);
89
131
  lingerTimers.clear();
90
132
  };
133
+ let retryAttempt = 0;
134
+ let refusedToken = null;
135
+ let refreshBurned = false;
136
+ const scheduleRetry = (delayMs, keepPrevious = false) => {
137
+ if (!running) return;
138
+ if (reconnectTimer) clearTimeout(reconnectTimer);
139
+ reconnectTimer = setTimeout(() => {
140
+ if (running) connect(keepPrevious);
141
+ }, delayMs);
142
+ };
143
+ const backoffDelay = () => {
144
+ const cap = Math.min(reconnectMs * 2 ** retryAttempt, MAX_RECONNECT_MS);
145
+ retryAttempt++;
146
+ return cap / 2 + Math.random() * (cap / 2);
147
+ };
91
148
  const connect = async (keepPrevious = false) => {
149
+ const token = getToken();
150
+ if (!token || token === refusedToken) {
151
+ if (running) {
152
+ if (currentState !== "unauthenticated") transition("unauthenticated");
153
+ scheduleRetry(reconnectMs, keepPrevious);
154
+ }
155
+ return;
156
+ }
157
+ refusedToken = null;
92
158
  transition("connecting");
93
159
  const previous = [...inflightControllers];
94
160
  if (!keepPrevious) {
@@ -110,19 +176,22 @@ function createActorStateUnit(options) {
110
176
  ...watermark !== void 0 ? { lastEventId: watermark } : {}
111
177
  };
112
178
  }),
113
- ...pendingReplies.size > 0 ? { pendingReplies: [...pendingReplies] } : {}
179
+ ...pendingReplies.size > 0 ? { pendingReplies: [...pendingReplies] } : {},
180
+ clientId
114
181
  });
115
182
  const url = `${baseUrl}/bus/subscribe`;
116
183
  const controller = new AbortController();
117
184
  inflightControllers.add(controller);
118
185
  try {
119
186
  const headers = {
120
- Authorization: `Bearer ${getToken()}`,
187
+ // The gate's single read: `token` is what this ATTEMPT sends, and
188
+ // exactly what `refusedToken` records if the gateway says 401.
189
+ Authorization: `Bearer ${token}`,
121
190
  "Content-Type": "application/json"
122
191
  };
123
192
  const response = await fetch(url, { method: "POST", headers, body, signal: controller.signal });
124
193
  if (!response.ok || !response.body) {
125
- throw new Error(`SSE connect failed: ${response.status}`);
194
+ throw new SseConnectError(response.status);
126
195
  }
127
196
  if (!running) return;
128
197
  if (keepPrevious) {
@@ -140,19 +209,34 @@ function createActorStateUnit(options) {
140
209
  lingerTimers.add(lingerTimer);
141
210
  }
142
211
  transition("open");
212
+ retryAttempt = 0;
213
+ refreshBurned = false;
143
214
  const reader = response.body.getReader();
144
215
  const decoder = new TextDecoder();
145
- let buffer = "";
216
+ let lineSegments = [];
146
217
  let currentEvent = "";
147
218
  let currentData = "";
148
219
  let currentId;
149
220
  while (running && inflightControllers.has(controller)) {
150
221
  const { done, value } = await reader.read();
151
222
  if (done) break;
152
- buffer += decoder.decode(value, { stream: true });
153
- const lines = buffer.split("\n");
154
- buffer = lines.pop() ?? "";
155
- for (const line of lines) {
223
+ const text = decoder.decode(value, { stream: true });
224
+ let searchFrom = 0;
225
+ while (searchFrom <= text.length) {
226
+ const nl = text.indexOf("\n", searchFrom);
227
+ if (nl === -1) {
228
+ if (searchFrom < text.length) {
229
+ lineSegments.push(searchFrom === 0 ? text : text.slice(searchFrom));
230
+ }
231
+ break;
232
+ }
233
+ let line = text.slice(searchFrom, nl);
234
+ searchFrom = nl + 1;
235
+ if (lineSegments.length > 0) {
236
+ lineSegments.push(line);
237
+ line = lineSegments.join("");
238
+ lineSegments = [];
239
+ }
156
240
  if (line.startsWith("event: ")) {
157
241
  currentEvent = line.slice(7);
158
242
  } else if (line.startsWith("data: ")) {
@@ -205,14 +289,34 @@ function createActorStateUnit(options) {
205
289
  }
206
290
  } catch (err) {
207
291
  if (err.name === "AbortError") return;
292
+ if (err instanceof SseConnectError) {
293
+ errors$.next(err);
294
+ if (err.status === 401 && running && !superseded.has(controller)) {
295
+ refusedToken = token;
296
+ retryAttempt = 0;
297
+ if (currentState !== "unauthenticated") transition("unauthenticated");
298
+ if (tokenRefresher && !refreshBurned) {
299
+ refreshBurned = true;
300
+ let refreshed = null;
301
+ try {
302
+ refreshed = await tokenRefresher();
303
+ } catch {
304
+ }
305
+ if (running && refreshed && refreshed !== refusedToken) {
306
+ scheduleRetry(0, keepPrevious);
307
+ return;
308
+ }
309
+ }
310
+ scheduleRetry(reconnectMs, keepPrevious);
311
+ return;
312
+ }
313
+ }
208
314
  } finally {
209
315
  inflightControllers.delete(controller);
210
316
  }
211
317
  if (running && !superseded.has(controller)) {
212
318
  transition("reconnecting");
213
- reconnectTimer = setTimeout(() => {
214
- if (running) connect();
215
- }, reconnectMs);
319
+ scheduleRetry(backoffDelay());
216
320
  }
217
321
  };
218
322
  const reconnect = () => {
@@ -255,7 +359,7 @@ function createActorStateUnit(options) {
255
359
  );
256
360
  },
257
361
  emit: async (channel, payload, emitScope) => {
258
- const body = { channel, payload };
362
+ const body = { channel, payload, clientId };
259
363
  if (emitScope) body.scope = emitScope;
260
364
  const headers = {
261
365
  "Content-Type": "application/json",
@@ -266,19 +370,28 @@ function createActorStateUnit(options) {
266
370
  headers["traceparent"] = trace.traceparent;
267
371
  if (trace.tracestate) headers["tracestate"] = trace.tracestate;
268
372
  }
269
- const res = await fetch(`${baseUrl}/bus/emit`, {
270
- method: "POST",
271
- headers,
272
- body: JSON.stringify(body)
273
- });
274
- if (!res.ok) {
275
- let detail = "";
276
- try {
277
- detail = (await res.text()).slice(0, 500);
278
- } catch {
373
+ const res = await retryWithBackoff(async () => {
374
+ const attempt = await fetch(`${baseUrl}/bus/emit`, {
375
+ method: "POST",
376
+ headers,
377
+ body: JSON.stringify(body),
378
+ signal: AbortSignal.timeout(EMIT_TIMEOUT_MS)
379
+ });
380
+ if (!attempt.ok) {
381
+ let detail = "";
382
+ try {
383
+ detail = (await attempt.text()).slice(0, 500);
384
+ } catch {
385
+ }
386
+ throw new APIError(
387
+ `/bus/emit ${attempt.status}${detail ? `: ${detail}` : ""}`,
388
+ attempt.status,
389
+ attempt.statusText,
390
+ detail || void 0
391
+ );
279
392
  }
280
- throw new Error(`/bus/emit ${res.status}${detail ? `: ${detail}` : ""}`);
281
- }
393
+ return attempt;
394
+ }, isRetryableRequestError, EMIT_RETRY);
282
395
  try {
283
396
  const reply = await res.json();
284
397
  return typeof reply.subscribers === "number" ? reply.subscribers : -1;
@@ -287,6 +400,8 @@ function createActorStateUnit(options) {
287
400
  }
288
401
  },
289
402
  state$: state$.asObservable(),
403
+ errors$: errors$.asObservable(),
404
+ isSubscribed: (channel) => globalChannels.has(channel),
290
405
  addChannels: (channels, scope) => {
291
406
  let changed = false;
292
407
  if (scope !== void 0) {
@@ -377,6 +492,7 @@ function createActorStateUnit(options) {
377
492
  disconnect();
378
493
  events$.complete();
379
494
  state$.complete();
495
+ errors$.complete();
380
496
  }
381
497
  };
382
498
  }
@@ -389,25 +505,6 @@ var RESOURCE_SCOPED_CHANNELS = [
389
505
  ...PERSISTED_EVENT_TYPES.filter((t) => !BRIDGED_CHANNELS.includes(t)),
390
506
  ...RESOURCE_BROADCAST_TYPES
391
507
  ];
392
- function classifyApiCode(status) {
393
- if (status === 400) return "bad-request";
394
- if (status === 401) return "unauthorized";
395
- if (status === 403) return "forbidden";
396
- if (status === 404) return "not-found";
397
- if (status === 409) return "conflict";
398
- if (status >= 500) return "unavailable";
399
- return "error";
400
- }
401
- var APIError = class extends SemiontError {
402
- status;
403
- statusText;
404
- constructor(message, status, statusText, body) {
405
- super(message, classifyApiCode(status), { status, statusText, body });
406
- this.name = "APIError";
407
- this.status = status;
408
- this.statusText = statusText;
409
- }
410
- };
411
508
  var HttpTransport = class {
412
509
  baseUrl;
413
510
  http;
@@ -536,14 +633,20 @@ var HttpTransport = class {
536
633
  // private again — external callers should use emit/on/stream/state$.
537
634
  get actor() {
538
635
  if (!this._actor) {
636
+ const globalChannels = this.config.channels ?? BRIDGED_CHANNELS;
539
637
  this._actor = createActorStateUnit({
540
638
  baseUrl: this.baseUrl,
541
639
  token: () => this.token$.getValue() ?? "",
542
- channels: [...BRIDGED_CHANNELS],
640
+ channels: [...globalChannels],
543
641
  ...this.config.loadLastEventIds ? { loadLastEventIds: this.config.loadLastEventIds } : {},
544
- ...this.config.saveLastEventId ? { saveLastEventId: this.config.saveLastEventId } : {}
642
+ ...this.config.saveLastEventId ? { saveLastEventId: this.config.saveLastEventId } : {},
643
+ // The SAME hook the ky beforeRetry path uses (SSE-AUTH-RESILIENCE
644
+ // P4, D2) — the SSE connect path refreshes once before parking
645
+ // `unauthenticated`, and no second refresh mechanism exists.
646
+ ...this.config.tokenRefresher ? { tokenRefresher: this.config.tokenRefresher } : {}
545
647
  });
546
- for (const channel of [...BRIDGED_CHANNELS, ...RESOURCE_SCOPED_CHANNELS]) {
648
+ this._actor.errors$.subscribe((e) => this.errorsSubject.next(e));
649
+ for (const channel of [...globalChannels, ...RESOURCE_SCOPED_CHANNELS]) {
547
650
  this._actor.on$(channel).subscribe((payload) => {
548
651
  for (const bus of this.bridges) {
549
652
  bus.get(channel).next(payload);
@@ -630,6 +733,16 @@ var HttpTransport = class {
630
733
  trackReply(correlationId) {
631
734
  return this.actor.trackReply(correlationId);
632
735
  }
736
+ /**
737
+ * `busRequest`'s fail-fast probe (`BusRequestPrimitive.isSubscribed`):
738
+ * whether the actor's global subscription set delivers `channel`. On a
739
+ * full client (no `channels` config) every bridged reply channel is
740
+ * subscribed and this never gates; on a narrowed transport it turns a
741
+ * doomed request into an immediate `bus.unsubscribed` error.
742
+ */
743
+ isSubscribed(channel) {
744
+ return this.actor.isSubscribed(channel);
745
+ }
633
746
  dispose() {
634
747
  if (this.disposed) return;
635
748
  this.disposed = true;
@@ -841,90 +954,6 @@ var HttpContentTransport = class {
841
954
  { kind: SpanKind.CLIENT, attrs: { "resource.id": resourceId, "content.graph": true } }
842
955
  );
843
956
  }
844
- /**
845
- * Store a resource's derived coordinate map (ANCHORED-TEXT-CACHE Lane 5).
846
- *
847
- * The Smelter is the producer and runs as its own process, which is why this
848
- * crosses the wire at all: the map goes to the one store the KnowledgeSystem
849
- * owns, rather than to a volume shared between service images.
850
- */
851
- async putAnchoredText(checksum, outcome, options) {
852
- busLog("PUT", "anchored-text", { checksum });
853
- await withSpan(
854
- "content.put_anchored_text",
855
- () => this.transport.rawHttp.put(`${this.transport.baseUrl}/anchored-text/${checksum}`, {
856
- headers: this.requestHeaders(options?.auth),
857
- json: outcome
858
- }).json(),
859
- { kind: SpanKind.CLIENT, attrs: { "content.checksum": checksum } }
860
- );
861
- }
862
- /**
863
- * The resource's coordinate map, or `null` when none has been derived —
864
- * which is the common case and not an error: callers degrade to no quoted
865
- * text.
866
- *
867
- * 204 is that answer, and the body is empty, so it must be taken before
868
- * `.json()` is reached — parsing an empty body throws, which would turn the
869
- * ordinary case into a failure. A 404 degrades the same way, though it is a
870
- * different fact: the resource itself is absent, and a resource that does
871
- * not exist has no map either.
872
- */
873
- async getAnchoredText(resourceId, options) {
874
- busLog("GET", "anchored-text", { resourceId });
875
- return withSpan(
876
- "content.get_anchored_text",
877
- async () => {
878
- const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/resources/${resourceId}/anchored-text`, {
879
- headers: this.requestHeaders(options?.auth),
880
- throwHttpErrors: false
881
- });
882
- if (response.status === 204 || response.status === 404) return null;
883
- if (!response.ok) throw new Error(`anchored-text read failed: ${response.status}`);
884
- return response.json();
885
- },
886
- { kind: SpanKind.CLIENT, attrs: { "resource.id": resourceId } }
887
- );
888
- }
889
- /**
890
- * The cache-consult read (PERSIST-ANCHORS P2c) — checksum-addressed and
891
- * barrier-free; 204 is the ordinary miss. This is how an out-of-process
892
- * extraction seam hits the cache at all.
893
- */
894
- async getAnchoredTextByChecksum(checksum, options) {
895
- busLog("GET", "anchored-text-by-checksum", { checksum });
896
- return withSpan(
897
- "content.get_anchored_text_by_checksum",
898
- async () => {
899
- const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/anchored-text/${checksum}`, {
900
- headers: this.requestHeaders(options?.auth),
901
- throwHttpErrors: false
902
- });
903
- if (response.status === 204) return null;
904
- if (!response.ok) throw new Error(`anchored-text checksum read failed: ${response.status}`);
905
- return response.json();
906
- },
907
- { kind: SpanKind.CLIENT, attrs: { "content.checksum": checksum } }
908
- );
909
- }
910
- /**
911
- * The store's would-hit keys — the reconcile planner's bulk existence read
912
- * (PERSIST-ANCHORS P0). One request per reconcile; keys only, never the
913
- * maps themselves, which is the point of the dedicated route.
914
- */
915
- async listAnchoredTextKeys(options) {
916
- busLog("GET", "anchored-text-keys", {});
917
- return withSpan(
918
- "content.list_anchored_text_keys",
919
- async () => {
920
- const { keys } = await this.transport.rawHttp.get(`${this.transport.baseUrl}/anchored-text/keys`, {
921
- headers: this.requestHeaders(options?.auth)
922
- }).json();
923
- return keys;
924
- },
925
- { kind: SpanKind.CLIENT }
926
- );
927
- }
928
957
  dispose() {
929
958
  }
930
959
  /** Auth header + W3C trace propagation for the active span. */
@@ -1035,6 +1064,6 @@ function uploadViaXhr(opts) {
1035
1064
  });
1036
1065
  }
1037
1066
 
1038
- export { APIError, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport, createActorStateUnit };
1067
+ export { APIError, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport, SseConnectError, createActorStateUnit };
1039
1068
  //# sourceMappingURL=index.js.map
1040
1069
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/transport/actor-state-unit.ts","../src/transport/http-transport.ts","../src/transport/http-content-transport.ts"],"names":["Subject","BehaviorSubject","busLog","withSpan","SpanKind","getActiveTraceparent","body","err"],"mappings":"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","file":"index.js","sourcesContent":["import { BehaviorSubject, Observable, Subject } from 'rxjs';\nimport { filter, map, share } from 'rxjs/operators';\nimport { busLog, busLogEnabled, type ConnectionState, type StateUnit } from '@semiont/core';\nimport {\n SpanKind,\n extractTraceparent,\n getActiveTraceparent,\n withSpan,\n withTraceparent,\n} from '@semiont/observability';\n\nexport type { ConnectionState };\n\nexport interface BusEvent {\n channel: string;\n payload: Record<string, unknown>;\n scope?: string;\n}\n\nexport interface ActorStateUnitOptions {\n baseUrl: string;\n token: string | (() => string);\n channels: string[];\n reconnectMs?: number;\n /**\n * Remove-side reconnect hysteresis (MULTI-RESOURCE-SCOPE). Scope\n * additions need liveness quickly (100 ms debounce), but a removal only\n * narrows delivery — extra events for a just-released scope are\n * idempotent locally — so remove-only changes wait this long before\n * reconnecting. Keeps hover-churn (transient per-citation previews)\n * from turning every mouse pass into a reconnect storm; any addition\n * flushes pending removals with it on the fast path.\n */\n lazyRemoveMs?: number;\n /**\n * B17 (LOCAL-STORAGE) — IO-abstracted persistence of the last seen\n * PERSISTED event id PER SCOPE, so a reloaded client resumes each\n * scope's replay instead of gapping. `load` runs once at construction;\n * `save` fires per persisted (`p-*`) id with that frame's scope —\n * ephemeral (`e-*`) ids are never saved: they carry no replay meaning,\n * and letting them displace a scope's watermark was exactly the silent\n * replay-loss hole the single-id design had. The transport stays\n * storage-free; callers wrap their own adapter in these thunks.\n */\n loadLastEventIds?: () => Record<string, string> | null;\n saveLastEventId?: (scope: string, id: string) => void;\n}\n\n/** Time in the `reconnecting` state before transitioning to `degraded`. */\nexport const DEGRADED_THRESHOLD_MS = 3_000;\n\n/**\n * How long a superseded connection keeps DRAINING after a make-before-break\n * handoff before being aborted. Aborting the old connection the instant the\n * new one opened discarded replies already written to the old socket but not\n * yet read by the client (a freshly-hydrating page's main thread is busy —\n * exactly the N-concurrent-loaders-at-connect repro in\n * .plans/bugs/concurrent-browse-resource-starvation.md). The overlap is safe:\n * persisted ids are stable and correlated-reply ids are deterministic\n * (`e-<channel>:<cid>`, routes/bus.ts), so `seenEventIds` dedups double\n * delivery.\n */\nexport const LINGER_MS = 1_000;\n\nexport interface ActorStateUnit extends StateUnit {\n on$<T = Record<string, unknown>>(channel: string): Observable<T>;\n emit(channel: string, payload: Record<string, unknown>, emitScope?: string): Promise<number>;\n state$: Observable<ConnectionState>;\n /** With `scope`: upsert channels into that scope's matrix entry. Without: global channels. */\n addChannels(channels: string[], scope?: string): void;\n /** With `scope`: remove channels from that scope's entry (empty entry drops the scope). Without: global channels. */\n removeChannels(channels: string[], scope?: string): void;\n /**\n * Correlated-reply retention, client side (BUS-RESUMPTION Phase 2 /\n * SDK-DEBT S1): register a busRequest correlationId as awaiting its\n * reply. Every connect body includes the currently-tracked set as\n * `pendingReplies`, so a reply published while the connection was down\n * is replayed from the server's retention buffer. The returned disposer\n * (idempotent) removes the id on settle.\n */\n trackReply(correlationId: string): () => void;\n start(): void;\n stop(): void;\n}\n\n/** Allowed transitions in the connection state machine. */\nconst ALLOWED_TRANSITIONS: Record<ConnectionState, ReadonlyArray<ConnectionState>> = {\n initial: ['connecting', 'closed'],\n connecting: ['open', 'reconnecting', 'closed'],\n open: ['reconnecting', 'closed'],\n reconnecting: ['connecting', 'degraded', 'closed'],\n // `degraded → reconnecting` is a legitimate recovery edge: a channel-set\n // change (`addChannels`/`removeChannels`) schedules a reconnect that can\n // fire while the connection is degraded. Omitting it made `reconnect()`\n // throw a fatal, uncaught exception from the reconnect timer (#844).\n degraded: ['connecting', 'reconnecting', 'closed'],\n closed: [],\n};\n\nexport function createActorStateUnit(options: ActorStateUnitOptions): ActorStateUnit {\n const { baseUrl, token: tokenOrGetter, channels: initialChannels, reconnectMs = 5_000, lazyRemoveMs = 5_000 } = options;\n const getToken = typeof tokenOrGetter === 'function' ? tokenOrGetter : () => tokenOrGetter;\n\n const globalChannels = new Set(initialChannels);\n /** The subscription matrix's scoped half: scope → channels (MULTI-RESOURCE-SCOPE). */\n const scopedSubscriptions = new Map<string, Set<string>>();\n /**\n * Per-scope resumption watermarks: the last PERSISTED (`p-*`) id seen for\n * each scope. Sent as `lastEventId` on that scope's matrix entry so the\n * server replays each scope's own gap. A scope keeps its watermark after\n * its channels are removed — re-subscribing later replays what was missed\n * in between. Ephemeral ids never touch this map.\n */\n const scopeWatermarks = new Map<string, string>(\n Object.entries(options.loadLastEventIds?.() ?? {}),\n );\n /** Outstanding busRequest correlationIds — ride every connect body (S1). */\n const pendingReplies = new Set<string>();\n\n const events$ = new Subject<BusEvent>();\n const state$ = new BehaviorSubject<ConnectionState>('initial');\n let currentState: ConnectionState = 'initial';\n let degradedTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Move the state machine to `next`. An unexpected edge is logged and\n * ignored — NOT thrown. `transition()` runs inside timer callbacks (the\n * reconnect and degraded timers), so a throw here is an uncaught exception\n * that takes down the host process (#844). A bad edge means a bug in the\n * reconnect loop, but degrading gracefully (keep the current state, warn)\n * is strictly better than killing a long-running job. The permitted edges\n * — including the `degraded → reconnecting` recovery edge — are in\n * `ALLOWED_TRANSITIONS`.\n *\n * Side effect: manages the `degraded` timer. Enters on\n * `reconnecting`, cleared on exit.\n */\n const transition = (next: ConnectionState): void => {\n if (currentState === next) return;\n const allowed = ALLOWED_TRANSITIONS[currentState];\n if (!allowed.includes(next)) {\n console.warn(`[actor] ignoring invalid connection state transition: ${currentState} → ${next}`);\n return;\n }\n const prev = currentState;\n currentState = next;\n\n if (next === 'reconnecting' && prev !== 'reconnecting') {\n // Starting a reconnect cycle — arm the degraded-threshold timer.\n if (degradedTimer) clearTimeout(degradedTimer);\n degradedTimer = setTimeout(() => {\n if (currentState === 'reconnecting') transition('degraded');\n }, DEGRADED_THRESHOLD_MS);\n }\n if (prev === 'reconnecting' && next !== 'reconnecting') {\n // Leaving reconnecting (to connecting, degraded, or closed) —\n // the timer is either no longer relevant or has just fired.\n if (degradedTimer) { clearTimeout(degradedTimer); degradedTimer = null; }\n }\n\n state$.next(next);\n };\n\n let running = false;\n /**\n * All in-flight SSE fetch controllers. Tracked as a Set because\n * connect() may race with itself under mount-churn or rapid channel-\n * set changes — whenever a new connect() starts we abort ALL previous\n * in-flight fetches rather than only the last-tracked one. A previous\n * single-slot implementation leaked orphaned streams (diagnosed by\n * observing 3 concurrent SSE subscribes in the /bus/subscribe network\n * log, each delivering duplicate RECV frames). Using a Set guarantees\n * at most one live stream post-reconnect regardless of race order.\n */\n const inflightControllers = new Set<AbortController>();\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Connections retired by a make-before-break handoff. A superseded\n * connection lingers (still draining) for LINGER_MS before its abort; its\n * read loop ending — naturally or via that abort — must NOT drive the\n * actor-wide reconnect logic, which belongs to the live connection only.\n */\n const superseded = new WeakSet<AbortController>();\n /** Pending linger-abort timers, cleared on stop/dispose. */\n const lingerTimers = new Set<ReturnType<typeof setTimeout>>();\n\n /**\n * Recently-delivered event ids, to dedup the make-before-break overlap: the\n * brief window where the old and new connection both deliver the same live\n * event during a scope-change handoff. Persisted ids (`p-<scope>-<seq>`) are\n * stable across connections, so this collapses such an overlap to a single\n * emission. Ephemeral ids (`e-<connectionId>-<counter>`) are per-connection,\n * so a cross-connection ephemeral duplicate is NOT caught here — its\n * consumers tolerate the rare double (a correlation reply is taken with\n * `take(1)`; cache invalidations and job-completion are idempotent/terminal).\n * Bounded FIFO (insertion-ordered Set) to cap memory.\n *\n * Cost note: this is *always-on* — every delivered event does a has/add here\n * — yet a duplicate is only possible during a handoff overlap; in steady\n * state there's a single connection and nothing can collide. So every\n * consumer of this transport carries a small standing structure for a path\n * that fires only on (now-rare) scope changes. It's left unconditional\n * because the per-event cost is negligible next to the JSON.parse + trace\n * span already on this path. If that ever stops being true, scope it to the\n * overlap (build on handoff start, drop once the old read loop exits) or\n * track a high-water `Map<scope, maxSeq>` instead of every id.\n */\n const seenEventIds = new Set<string>();\n const SEEN_EVENT_IDS_MAX = 512;\n const rememberEventId = (id: string): void => {\n seenEventIds.add(id);\n if (seenEventIds.size > SEEN_EVENT_IDS_MAX) {\n const oldest = seenEventIds.values().next().value;\n if (oldest !== undefined) seenEventIds.delete(oldest);\n }\n };\n /** Release a claim whose apply threw, so a redelivery is re-processed\n * rather than swallowed by its own dedup entry. */\n const forgetEventId = (id: string): void => {\n seenEventIds.delete(id);\n };\n\n const shared$ = events$.pipe(share());\n\n const disconnect = () => {\n for (const c of inflightControllers) {\n try { c.abort(); } catch { /* noop */ }\n }\n inflightControllers.clear();\n if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }\n for (const t of lingerTimers) clearTimeout(t);\n lingerTimers.clear();\n };\n\n const connect = async (keepPrevious = false) => {\n // Transition to `connecting` from whichever reconnect-ish state\n // we're currently in (`initial`, `reconnecting`, `degraded`).\n transition('connecting');\n\n // Snapshot the connections this connect() supersedes.\n // - keepPrevious=false (initial connect / drop-recovery): there is no\n // live connection worth preserving, so abort up front — this closes\n // the orphan-stream leak described above.\n // - keepPrevious=true (scope-change reconnect): MAKE-BEFORE-BREAK. Keep\n // the previous connection(s) ALIVE until the new one is `open`, then\n // abort them (below, after the fetch resolves), so an in-flight\n // ephemeral result isn't dropped in a reconnect gap (#847). The brief\n // window where old and new both deliver is deduped by event id.\n const previous = [...inflightControllers];\n if (!keepPrevious) {\n for (const c of previous) {\n try { c.abort(); } catch { /* noop */ }\n }\n inflightControllers.clear();\n }\n\n // POST subscription matrix (MULTI-RESOURCE-SCOPE): global channels plus\n // one entry per scope, each carrying its own resumption watermark.\n const body = JSON.stringify({\n global: [...globalChannels],\n scoped: [...scopedSubscriptions.entries()].map(([scope, chans]) => {\n const watermark = scopeWatermarks.get(scope);\n return {\n scope,\n channels: [...chans],\n ...(watermark !== undefined ? { lastEventId: watermark } : {}),\n };\n }),\n ...(pendingReplies.size > 0 ? { pendingReplies: [...pendingReplies] } : {}),\n });\n const url = `${baseUrl}/bus/subscribe`;\n\n const controller = new AbortController();\n inflightControllers.add(controller);\n\n try {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${getToken()}`,\n 'Content-Type': 'application/json',\n };\n const response = await fetch(url, { method: 'POST', headers, body, signal: controller.signal });\n\n if (!response.ok || !response.body) {\n throw new Error(`SSE connect failed: ${response.status}`);\n }\n\n // Stopped/disposed while the fetch was in flight — don't proceed to open\n // (and retire the old connection on) a stream we've been told to tear\n // down. `stop()`/`dispose()` already aborted this controller.\n if (!running) return;\n\n // Make-before-break handoff: the new connection is established (the\n // gateway has subscribed it and any `Last-Event-ID` replay is flowing),\n // so mark the previous connection(s) superseded and LINGER them — keep\n // them draining for LINGER_MS before the abort. Aborting immediately\n // here discarded replies already written to the old socket but not yet\n // read (the buffered-bytes loss in\n // .plans/bugs/concurrent-browse-resource-starvation.md); an event\n // delivered by both connections during the overlap is deduped by id in\n // the read loop below (persisted ids are stable; correlated-reply ids\n // are deterministic per routes/bus.ts). Had the fetch failed, we'd have\n // thrown above and never reached here, leaving the old connection live\n // (no gap).\n if (keepPrevious) {\n for (const c of previous) superseded.add(c);\n const lingerTimer = setTimeout(() => {\n lingerTimers.delete(lingerTimer);\n for (const c of previous) {\n try { c.abort(); } catch { /* noop */ }\n inflightControllers.delete(c);\n }\n }, LINGER_MS);\n lingerTimers.add(lingerTimer);\n }\n\n transition('open');\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n // SSE parse state is declared OUTSIDE the read loop: a single\n // event can span many `reader.read()` chunks when the payload is\n // large (a full resource-result with annotations can easily exceed\n // one TCP segment). Resetting these on every read would silently\n // drop any event whose `event:`/`id:` headers land in one chunk\n // and whose terminating blank line lands in the next.\n let currentEvent = '';\n let currentData = '';\n let currentId: string | undefined;\n\n while (running && inflightControllers.has(controller)) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7);\n } else if (line.startsWith('data: ')) {\n currentData = line.slice(6);\n } else if (line.startsWith('id: ')) {\n currentId = line.slice(4);\n } else if (line === '') {\n // Skip an overlap duplicate — the same stable-id event delivered\n // by both the old and new connection during a make-before-break\n // handoff (#847). Ephemeral ids are unique per connection, so this\n // never spuriously drops a distinct event.\n const isDuplicate = currentId !== undefined && seenEventIds.has(currentId);\n if (currentEvent === 'bus-event' && currentData && !isDuplicate) {\n const parsed = JSON.parse(currentData) as BusEvent;\n busLog('RECV', parsed.channel, parsed.payload, parsed.scope);\n // Drain-window forensics: an event delivered by a SUPERSEDED\n // (lingering) connection is one that an immediate handover abort\n // would have discarded — the loss mode of\n // .plans/bugs/concurrent-browse-resource-starvation.md. Gated\n // (per-event, bursty during overlap); flip bus logging on to\n // see how real the window is.\n if (busLogEnabled() && superseded.has(controller)) {\n // eslint-disable-next-line no-console\n console.debug(`[bus LINGER] ${parsed.channel} delivered on superseded connection`);\n }\n // Tier 2: lift trace context off the SSE payload (the\n // gateway's writeBusEvent puts it there). The synchronous\n // fan-out to subscribers happens inside the bus.recv span,\n // so handlers see the parent trace.\n const carrier = extractTraceparent(\n parsed.payload as Record<string, unknown>,\n );\n // The two kinds of id bookkeeping sit on OPPOSITE sides of the\n // awaited fan-out, because they answer different questions.\n //\n // `seenEventIds` answers \"has this frame been claimed?\" and must\n // be recorded BEFORE the await: the await yields the event loop,\n // so during a make-before-break overlap the sibling connection\n // can read the same stable-id frame, find the set still missing\n // it, and deliver it a second time — defeating the overlap dedup\n // (#847) that .plans/bugs/BRIDGE-GAPS.md exists to protect.\n // Rolled back if the apply throws, so a redelivery after a\n // dropped read loop is re-processed rather than silently\n // swallowed by its own claim.\n if (currentId !== undefined) rememberEventId(currentId);\n try {\n await withTraceparent(carrier, () =>\n withSpan(\n `bus.recv:${parsed.channel}`,\n () => { events$.next(parsed); },\n {\n kind: SpanKind.CONSUMER,\n attrs: {\n 'bus.channel': parsed.channel,\n ...(parsed.scope ? { 'bus.scope': parsed.scope } : {}),\n },\n },\n ),\n );\n } catch (err) {\n if (currentId !== undefined) forgetEventId(currentId);\n throw err;\n }\n // The resume watermark and the persisted bookmark answer \"have\n // this event's effects been absorbed?\" and stay AFTER the apply.\n // The pre-fix order (stash first, then an AWAITED apply) opened a\n // gap where a bystander cache's debounced save could fire\n // mid-await, find every cache quiet, and flush a bookmark whose\n // event nothing had absorbed — the fast-path reload loss\n // (.plans/bugs/annotation-lost-on-immediate-reload-after-create.md).\n // Both stay on the LAGGING side, which is safe: a reconnect or\n // crash mid-apply resumes from the previous id and redelivers,\n // and re-invalidation is idempotent.\n //\n // Watermarks are PER SCOPE and persisted-ids-only: a `p-*` id is\n // stamped only on scoped deliveries (the frame always carries\n // `scope`), and ephemeral ids never displace a scope's watermark\n // — the silent replay-loss hole the old single-id design had.\n if (currentId !== undefined && currentId.startsWith('p-') && parsed.scope) {\n scopeWatermarks.set(parsed.scope, currentId);\n // B17: persist per scope — see ActorStateUnitOptions.\n options.saveLastEventId?.(parsed.scope, currentId);\n }\n }\n currentEvent = '';\n currentData = '';\n currentId = undefined;\n }\n }\n }\n } catch (err) {\n if ((err as Error).name === 'AbortError') return;\n // Any non-abort error falls through to the reconnect-retry block.\n } finally {\n inflightControllers.delete(controller);\n }\n\n // If we reached here without an AbortError, the connection dropped\n // or the fetch failed. Transition to reconnecting and schedule a\n // retry after `reconnectMs` — unless this was a SUPERSEDED (lingering)\n // connection ending: its termination is expected teardown, not a drop\n // of the live stream, and must not restart the reconnect machinery.\n if (running && !superseded.has(controller)) {\n transition('reconnecting');\n reconnectTimer = setTimeout(() => {\n if (running) connect();\n }, reconnectMs);\n }\n };\n\n const reconnect = () => {\n if (!running) return;\n // Transition to `reconnecting` BEFORE aborting the current\n // connection. This matches the pre-state-machine contract where\n // gap-detection relied on seeing a \"dropped\" signal before a\n // subsequent \"connected\" signal; with the state machine, the\n // transition sequence `open → reconnecting → connecting → open`\n // is what BrowseNamespace's gap-detection (pre-BUS-RESUMPTION\n // code path) watches for.\n if (currentState === 'open' || currentState === 'connecting' || currentState === 'degraded') {\n transition('reconnecting');\n }\n // Make-before-break: do NOT abort the live connection here. Cancel only a\n // pending drop-recovery retry, then connect — `connect(keepPrevious=true)`\n // retires the old connection after the new one is open (no gap).\n if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }\n connect(true);\n };\n\n // Debounce channel-set-change reconnects. React StrictMode in dev\n // produces mount → cleanup → mount synchronously, which previously\n // translated into three back-to-back reconnects — enough to tear down\n // in-flight responses, fire gap detection, refetch, tear that down\n // again, and leave the page stuck in \"Loading...\" while caches\n // thrashed. With a short debounce the whole sequence collapses into\n // one reconnect after the final channel-set is stable.\n //\n // Two cadences (MULTI-RESOURCE-SCOPE remove-side hysteresis): additions\n // take the fast 100 ms path (a new scope needs liveness now); remove-only\n // changes wait `lazyRemoveMs` — removal merely narrows delivery, and the\n // consumer's hover churn would otherwise reconnect on every mouse pass.\n // The connect body reads current state, so whichever timer fires first\n // carries ALL pending changes; a fast schedule therefore supersedes any\n // pending lazy one, and a lazy schedule never preempts a pending fast one.\n let reconnectTimer2: ReturnType<typeof setTimeout> | null = null;\n let lazyReconnectTimer: ReturnType<typeof setTimeout> | null = null;\n const RECONNECT_DEBOUNCE_MS = 100;\n const scheduleReconnect = () => {\n if (lazyReconnectTimer) { clearTimeout(lazyReconnectTimer); lazyReconnectTimer = null; }\n if (reconnectTimer2) clearTimeout(reconnectTimer2);\n reconnectTimer2 = setTimeout(() => {\n reconnectTimer2 = null;\n reconnect();\n }, RECONNECT_DEBOUNCE_MS);\n };\n const scheduleLazyReconnect = () => {\n if (reconnectTimer2 || lazyReconnectTimer) return; // a pending flush already covers this change\n lazyReconnectTimer = setTimeout(() => {\n lazyReconnectTimer = null;\n reconnect();\n }, lazyRemoveMs);\n };\n\n return {\n on$<T = Record<string, unknown>>(channel: string): Observable<T> {\n return shared$.pipe(\n filter((e) => e.channel === channel),\n map((e) => e.payload as T),\n );\n },\n\n emit: async (channel: string, payload: Record<string, unknown>, emitScope?: string): Promise<number> => {\n // EMIT logging + bus.emit span live at the transport contract layer\n // (`HttpTransport.emit`). ActorStateUnit is plumbing. We do propagate the\n // active span's W3C traceparent on the outbound POST so the gateway\n // can stitch the bus.dispatch server span as a child.\n const body: Record<string, unknown> = { channel, payload };\n if (emitScope) body.scope = emitScope;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${getToken()}`,\n };\n const trace = getActiveTraceparent();\n if (trace) {\n headers['traceparent'] = trace.traceparent;\n if (trace.tracestate) headers['tracestate'] = trace.tracestate;\n }\n const res = await fetch(`${baseUrl}/bus/emit`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n // A refused emit (validation 400, auth 401…) must REJECT — busRequest's\n // contract detaches its doomed reply and propagates this to the caller.\n // Resolving a sentinel here instead leaves that caller waiting for a\n // reply the gateway will never send.\n if (!res.ok) {\n let detail = '';\n try {\n detail = (await res.text()).slice(0, 500);\n } catch {\n // status alone\n }\n throw new Error(`/bus/emit ${res.status}${detail ? `: ${detail}` : ''}`);\n }\n // `-1` = count unknown (older gateway / unreadable body) — never let a\n // parse failure read as an empty room. Same sentinel as the Go client.\n try {\n const reply = (await res.json()) as { subscribers?: unknown };\n return typeof reply.subscribers === 'number' ? reply.subscribers : -1;\n } catch {\n return -1;\n }\n },\n\n state$: state$.asObservable(),\n\n addChannels: (channels: string[], scope?: string) => {\n let changed = false;\n if (scope !== undefined) {\n let entry = scopedSubscriptions.get(scope);\n if (!entry) {\n entry = new Set<string>();\n scopedSubscriptions.set(scope, entry);\n }\n for (const ch of channels) {\n if (!entry.has(ch)) { entry.add(ch); changed = true; }\n }\n } else {\n for (const ch of channels) {\n if (!globalChannels.has(ch)) { globalChannels.add(ch); changed = true; }\n }\n }\n if (changed) scheduleReconnect();\n },\n\n removeChannels: (channels: string[], scope?: string) => {\n let changed = false;\n if (scope !== undefined) {\n const entry = scopedSubscriptions.get(scope);\n if (entry) {\n for (const ch of channels) {\n if (entry.delete(ch)) changed = true;\n }\n // The watermark survives the scope's removal deliberately: a later\n // re-subscribe replays what was missed in between.\n if (entry.size === 0) scopedSubscriptions.delete(scope);\n }\n } else {\n for (const ch of channels) {\n if (globalChannels.delete(ch)) changed = true;\n }\n }\n if (changed) scheduleLazyReconnect();\n },\n\n trackReply: (correlationId: string) => {\n pendingReplies.add(correlationId);\n let released = false;\n return () => {\n if (released) return;\n released = true;\n pendingReplies.delete(correlationId);\n };\n },\n\n start: () => {\n if (running) return;\n running = true;\n connect();\n },\n\n stop: () => {\n running = false;\n if (currentState !== 'closed') transition('closed');\n if (reconnectTimer2) { clearTimeout(reconnectTimer2); reconnectTimer2 = null; }\n if (lazyReconnectTimer) { clearTimeout(lazyReconnectTimer); lazyReconnectTimer = null; }\n if (degradedTimer) { clearTimeout(degradedTimer); degradedTimer = null; }\n disconnect();\n },\n\n dispose: () => {\n running = false;\n if (currentState !== 'closed') transition('closed');\n if (reconnectTimer2) { clearTimeout(reconnectTimer2); reconnectTimer2 = null; }\n if (lazyReconnectTimer) { clearTimeout(lazyReconnectTimer); lazyReconnectTimer = null; }\n if (degradedTimer) { clearTimeout(degradedTimer); degradedTimer = null; }\n disconnect();\n events$.complete();\n state$.complete();\n },\n };\n}\n","/**\n * HttpTransport — the HTTP/SSE implementation of ITransport.\n *\n * Phase 1 of TRANSPORT-ABSTRACTION. Owns everything that crosses the wire\n * in remote mode: the bus actor (SSE + POST /bus/emit), auth/admin/exchange/\n * system HTTP endpoints, and connection-state plumbing.\n *\n * Does NOT own the local coordination bus — that lives on `SemiontClient`.\n * `bridgeInto(bus)` wires SSE-received events into the caller-supplied bus\n * once at construction.\n */\n\nimport ky, { HTTPError, type KyInstance } from 'ky';\nimport { BehaviorSubject, Observable, Subject } from 'rxjs';\nimport type {\n AccessToken,\n BaseUrl,\n Email,\n EventBus,\n EventMap,\n GoogleCredential,\n Logger,\n RefreshToken,\n ResourceId,\n UserDID,\n components,\n} from '@semiont/core';\nimport {\n PERSISTED_EVENT_TYPES,\n RESOURCE_BROADCAST_TYPES,\n SemiontError,\n busLog,\n} from '@semiont/core';\nimport type { TransportErrorCode } from '@semiont/core';\nimport { SpanKind, recordBusEmit, withSpan } from '@semiont/observability';\nimport { createActorStateUnit, type ActorStateUnit } from './actor-state-unit';\nimport type {\n ConnectionState,\n IGatewayOperations,\n ITransport,\n HealthCheckResponse,\n StatusResponse,\n UserResponse,\n UpdateUserRequest,\n UpdateUserResponse,\n ListUsersResponse,\n} from '@semiont/core';\nimport { BRIDGED_CHANNELS } from '@semiont/core';\n\ntype AuthResponse = components['schemas']['AuthResponse'];\ntype TokenRefreshResponse = components['schemas']['TokenRefreshResponse'];\ntype AdminUserStatsResponse = components['schemas']['AdminUserStatsResponse'];\ntype OAuthConfigResponse = components['schemas']['OAuthConfigResponse'];\n\n// ── Channel constants (mirror client.ts) ────────────────────────────────\n\nexport const RESOURCE_SCOPED_CHANNELS = [\n // Exclude channels already globally bridged: a channel in both lists is\n // forwarded twice on a scoped connection (global copy → ephemeral id, scoped\n // copy → persisted id) with different SSE ids, escaping the client dedup\n // (.plans/bugs/BRIDGE-GAPS.md). Generalizes the former one-off\n // `frame:entity-type-added` exclusion.\n ...PERSISTED_EVENT_TYPES.filter((t) => !(BRIDGED_CHANNELS as readonly string[]).includes(t)),\n ...RESOURCE_BROADCAST_TYPES,\n];\n\nfunction classifyApiCode(status: number): TransportErrorCode {\n if (status === 400) return 'bad-request';\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not-found';\n if (status === 409) return 'conflict';\n if (status >= 500) return 'unavailable';\n return 'error';\n}\n\nexport class APIError extends SemiontError {\n declare code: TransportErrorCode;\n readonly status: number;\n readonly statusText: string;\n\n constructor(message: string, status: number, statusText: string, body?: unknown) {\n super(message, classifyApiCode(status), { status, statusText, body });\n this.name = 'APIError';\n this.status = status;\n this.statusText = statusText;\n }\n}\n\nexport type TokenRefresher = () => Promise<string | null>;\n\nexport interface HttpTransportConfig {\n baseUrl: BaseUrl;\n /** Observable token source; headers read the current value. */\n token$?: BehaviorSubject<AccessToken | null>;\n timeout?: number;\n retry?: number;\n logger?: Logger;\n /** Optional 401-recovery hook. See {@link TokenRefresher}. */\n tokenRefresher?: TokenRefresher;\n /**\n * B17 — persistence thunks for the last seen persisted SSE id PER\n * SCOPE, passed through to the actor state unit. See\n * {@link ActorStateUnitOptions}.\n */\n loadLastEventIds?: () => Record<string, string> | null;\n saveLastEventId?: (scope: string, id: string) => void;\n}\n\nexport class HttpTransport implements ITransport, IGatewayOperations {\n readonly baseUrl: BaseUrl;\n private readonly http: KyInstance;\n private readonly token$: BehaviorSubject<AccessToken | null>;\n private readonly logger?: Logger;\n private readonly errorsSubject: Subject<SemiontError> = new Subject<SemiontError>();\n /**\n * Stream of `APIError` instances surfaced from any HTTP request just\n * before the transport throws to the caller. Satisfies the `ITransport`\n * `errors$` contract — see `@semiont/core/transport.ts`.\n */\n readonly errors$: Observable<SemiontError> = this.errorsSubject.asObservable();\n\n private _actor: ActorStateUnit | null = null;\n private _actorStarted = false;\n private disposed = false;\n\n /**\n * Per-resource subscription ref-counts (MULTI-RESOURCE-SCOPE). Distinct\n * resources COMPOSE — each key's first subscribe adds its scoped channels\n * to the actor's matrix, its last release removes them; keys are fully\n * independent. Local fan-out for scoped channels is a SINGLETON wired in\n * the actor getter (one delivery per event regardless of how many scopes\n * are held), so entries here are counts only.\n */\n private readonly scopeRefCounts = new Map<string, number>();\n\n /** Buses we've been asked to bridge wire events into. */\n private readonly bridges: EventBus[] = [];\n\n private readonly config: HttpTransportConfig;\n\n constructor(config: HttpTransportConfig) {\n const { baseUrl, timeout = 30000, retry = 2, logger, tokenRefresher } = config;\n this.config = config;\n\n this.baseUrl = (baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl) as BaseUrl;\n this.token$ = config.token$ ?? new BehaviorSubject<AccessToken | null>(null);\n this.logger = logger;\n\n // Retry policy: when a refresher is configured, expand retry to also\n // cover 401 (one attempt). Otherwise use the plain `retry` number.\n const retryConfig = tokenRefresher\n ? {\n limit: 1,\n methods: ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'],\n statusCodes: [401, 408, 413, 429, 500, 502, 503, 504],\n }\n : retry;\n\n this.http = ky.create({\n timeout,\n retry: retryConfig,\n hooks: {\n beforeRequest: [\n ({ request }) => {\n if (this.logger) {\n this.logger.debug('HTTP Request', {\n type: 'http_request',\n url: request.url,\n method: request.method,\n timestamp: Date.now(),\n hasAuth: request.headers.has('Authorization'),\n });\n }\n },\n ],\n beforeRetry: tokenRefresher\n ? [\n async ({ request, error }) => {\n if (!(error instanceof HTTPError) || error.response.status !== 401) {\n return undefined;\n }\n try {\n const newToken = await tokenRefresher();\n if (!newToken) return ky.stop;\n request.headers.set('Authorization', `Bearer ${newToken}`);\n return undefined;\n } catch {\n return ky.stop;\n }\n },\n ]\n : [],\n afterResponse: [\n ({ request, response }) => {\n if (this.logger) {\n this.logger.debug('HTTP Response', {\n type: 'http_response',\n url: request.url,\n method: request.method,\n status: response.status,\n statusText: response.statusText,\n });\n }\n return response;\n },\n ],\n beforeError: [\n async ({ request, error }) => {\n const response = error instanceof HTTPError ? error.response : undefined;\n if (response) {\n const body = await response.json().catch(() => ({})) as { message?: string };\n if (this.logger) {\n this.logger.error('HTTP Request Failed', {\n type: 'http_error',\n url: request.url,\n method: request.method,\n status: response.status,\n statusText: response.statusText,\n error: body.message || `HTTP ${response.status}: ${response.statusText}`,\n });\n }\n const apiError = new APIError(\n body.message || `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n response.statusText,\n body,\n );\n this.errorsSubject.next(apiError);\n throw apiError;\n }\n return error;\n },\n ],\n },\n });\n\n // Auto-start the bus actor once a token arrives.\n this.token$.subscribe((token) => {\n if (token && !this._actorStarted && !this.disposed) {\n this._actorStarted = true;\n this.actor.start();\n }\n });\n }\n\n // ── Lazy actor construction + per-channel fan-in to bridges ───────────\n //\n // `actor` is exposed so the legacy `SemiontClient` can keep `.actor`\n // pointing at the same ActorStateUnit during the transport-abstraction\n // migration. Once SemiontClient is removed, this should be made\n // private again — external callers should use emit/on/stream/state$.\n\n get actor(): ActorStateUnit {\n if (!this._actor) {\n this._actor = createActorStateUnit({\n baseUrl: this.baseUrl,\n token: () => this.token$.getValue() ?? '',\n channels: [...BRIDGED_CHANNELS],\n ...(this.config.loadLastEventIds ? { loadLastEventIds: this.config.loadLastEventIds } : {}),\n ...(this.config.saveLastEventId ? { saveLastEventId: this.config.saveLastEventId } : {}),\n });\n // One fan-in per channel, wired once for the actor's lifetime — the\n // globally-bridged set AND the resource-scoped set (disjoint by the\n // bus-invariants guard). Scoped events only arrive for scopes in the\n // actor's matrix (gateway-authoritative filtering), so an always-on\n // scoped fan-in delivers nothing while no scope is held — and exactly\n // ONCE per event however many scopes are held (the per-scope\n // bridge-subs design would have duplicated delivery N×).\n for (const channel of [...BRIDGED_CHANNELS, ...RESOURCE_SCOPED_CHANNELS]) {\n this._actor.on$<Record<string, unknown>>(channel).subscribe((payload) => {\n for (const bus of this.bridges) {\n (bus.get(channel as keyof EventMap) as { next(v: unknown): void }).next(payload);\n }\n });\n }\n }\n return this._actor;\n }\n\n // ── ITransport — bus primitives ───────────────────────────────────────\n\n async emit<K extends keyof EventMap>(\n channel: K,\n payload: EventMap[K],\n resourceScope?: ResourceId,\n ): Promise<number> {\n busLog('EMIT', channel as string, payload, resourceScope as string | undefined);\n recordBusEmit(channel as string, resourceScope as string | undefined);\n return withSpan(\n `bus.emit:${channel as string}`,\n async () => {\n if (resourceScope !== undefined) {\n return this.actor.emit(\n channel as string,\n payload as unknown as Record<string, unknown>,\n resourceScope as string,\n );\n }\n return this.actor.emit(\n channel as string,\n payload as unknown as Record<string, unknown>,\n );\n },\n {\n kind: SpanKind.PRODUCER,\n attrs: {\n 'bus.channel': channel as string,\n ...(resourceScope ? { 'bus.scope': resourceScope as string } : {}),\n },\n },\n );\n }\n\n on<K extends keyof EventMap>(\n channel: K,\n handler: (payload: EventMap[K]) => void,\n ): () => void {\n const sub = this.actor.on$<EventMap[K]>(channel as string).subscribe(handler);\n return () => sub.unsubscribe();\n }\n\n stream<K extends keyof EventMap>(channel: K): Observable<EventMap[K]> {\n return this.actor.on$<EventMap[K]>(channel as string);\n }\n\n /**\n * Wire this transport's SSE fan-in into the given bus. Every channel\n * in `BRIDGED_CHANNELS` (and subsequently per-resource scoped channels\n * opened by `subscribeToResource`) is published on the bus. Safe to\n * call multiple times — each bus is added to the fan-out list.\n */\n bridgeInto(bus: EventBus): void {\n this.bridges.push(bus);\n }\n\n subscribeToResource(resourceId: ResourceId): () => void {\n const key = resourceId as string;\n const count = this.scopeRefCounts.get(key) ?? 0;\n this.scopeRefCounts.set(key, count + 1);\n if (count === 0) {\n this.actor.addChannels([...RESOURCE_SCOPED_CHANNELS], key);\n }\n\n let called = false;\n return () => {\n if (called) return;\n called = true;\n const remaining = (this.scopeRefCounts.get(key) ?? 0) - 1;\n if (remaining > 0) {\n this.scopeRefCounts.set(key, remaining);\n return;\n }\n this.scopeRefCounts.delete(key);\n this.actor.removeChannels([...RESOURCE_SCOPED_CHANNELS], key);\n };\n }\n\n get state$(): Observable<ConnectionState> {\n return this.actor.state$;\n }\n\n /**\n * Correlated-reply retention, client side (BUS-RESUMPTION Phase 2 /\n * SDK-DEBT S1): `busRequest` registers its cid here before emitting;\n * the actor carries the tracked set as `pendingReplies` on every\n * subscribe body, so a reply published while the connection was down\n * replays from the server's retention buffer on reconnect.\n */\n trackReply(correlationId: string): () => void {\n return this.actor.trackReply(correlationId);\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.scopeRefCounts.clear();\n if (this._actor) {\n this._actor.dispose();\n this._actor = null;\n }\n this.errorsSubject.complete();\n }\n\n /**\n * Route a transport-level error onto `errors$`. Used by sibling adapters\n * (e.g. `HttpContentTransport`'s XHR upload path) that don't go through\n * the `ky` `beforeError` hook and need to surface failures on the same\n * stream the rest of the transport publishes to.\n */\n pushError(error: SemiontError): void {\n if (this.disposed) return;\n this.errorsSubject.next(error);\n }\n\n // ── Auth ──────────────────────────────────────────────────────────────\n\n private authHeaders(): Record<string, string> {\n const token = this.token$.getValue() ?? undefined;\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n\n async authenticatePassword(email: Email, password: string): Promise<AuthResponse> {\n return this.http.post(`${this.baseUrl}/api/tokens/password`, {\n json: { email, password },\n headers: this.authHeaders(),\n }).json();\n }\n\n async authenticateGoogle(credential: GoogleCredential): Promise<AuthResponse> {\n return this.http.post(`${this.baseUrl}/api/tokens/google`, {\n json: { credential },\n headers: this.authHeaders(),\n }).json();\n }\n\n async refreshAccessToken(token: RefreshToken): Promise<TokenRefreshResponse> {\n return this.http.post(`${this.baseUrl}/api/tokens/refresh`, {\n json: { refreshToken: token },\n headers: this.authHeaders(),\n }).json();\n }\n\n async logout(): Promise<void> {\n await this.http.post(`${this.baseUrl}/api/users/logout`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async acceptTerms(): Promise<void> {\n await this.http.post(`${this.baseUrl}/api/users/accept-terms`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async getCurrentUser(): Promise<UserResponse> {\n return this.http.get(`${this.baseUrl}/api/users/me`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async getMediaToken(resourceId: ResourceId): Promise<{ token: string }> {\n return this.http.post(`${this.baseUrl}/api/tokens/media`, {\n json: { resourceId },\n headers: this.authHeaders(),\n }).json();\n }\n\n // ── Admin ─────────────────────────────────────────────────────────────\n\n async listUsers(): Promise<ListUsersResponse> {\n return this.http.get(`${this.baseUrl}/api/admin/users`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async getUserStats(): Promise<AdminUserStatsResponse> {\n return this.http.get(`${this.baseUrl}/api/admin/users/stats`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async updateUser(id: UserDID, data: UpdateUserRequest): Promise<UpdateUserResponse> {\n return this.http.patch(`${this.baseUrl}/api/admin/users/${id}`, {\n json: data,\n headers: this.authHeaders(),\n }).json();\n }\n\n async getOAuthConfig(): Promise<OAuthConfigResponse> {\n return this.http.get(`${this.baseUrl}/api/admin/oauth/config`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n // ── System status ─────────────────────────────────────────────────────\n\n async healthCheck(): Promise<HealthCheckResponse> {\n return this.http.get(`${this.baseUrl}/api/health`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async getStatus(): Promise<StatusResponse> {\n return this.http.get(`${this.baseUrl}/api/status`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n // ── Internal: ky accessor for legacy passthroughs (temporary) ─────────\n\n /**\n * Temporary escape hatch for the ongoing transport migration: namespaces\n * that still need to issue ad-hoc HTTP calls (e.g. legacy browse/mark\n * HTTP fallbacks) can borrow the configured `ky` instance here. Will be\n * deleted once all namespaces route through bus channels or through\n * typed methods on this transport.\n */\n get rawHttp(): KyInstance {\n return this.http;\n }\n\n /**\n * Current access token (synchronously read from the BehaviorSubject).\n * Used by content-transport and legacy namespace HTTP fallbacks that\n * need to pass `auth: token` through some code paths.\n */\n getToken(): AccessToken | undefined {\n return this.token$.getValue() ?? undefined;\n }\n}\n\n// Re-export for convenience\nexport type { ConnectionState } from '@semiont/core';\n","/**\n * HttpContentTransport — binary I/O over HTTP.\n *\n * Phase 1 of TRANSPORT-ABSTRACTION. Narrow by design because binary has\n * different backpressure and streaming characteristics than typed command\n * payloads. Uses the HttpTransport's underlying ky instance + token, so\n * retries, logging, and auth behave identically to the rest of the wire.\n *\n * Two `putBinary` paths live side by side, selected by runtime\n * environment + caller intent:\n * - **ky path (default + Node)** — the original `ky.post(...)` path.\n * Keeps retry-with-refresh, beforeError → APIError, observability\n * spans intact. Hits when no `onProgress`/`signal` is passed, OR\n * when `XMLHttpRequest` isn't available in the runtime (Node\n * workers, the CLI). On Node-side `signal`-aborts: the in-flight\n * `fetch` continues in the background and the `cancelled` flag in\n * `yield.resource` suppresses the resolve/reject callbacks.\n * - **XHR path (browsers with `onProgress` or `signal`)** — hand-rolled\n * because `ky` wraps `fetch` which can't observe upload byte-\n * progress today (`Request({ duplex: 'half' })` is the long-term\n * direction; not yet widely available across the webviews this\n * codepath needs to run in). Threads auth + traceparent headers,\n * emits `onProgress` from `xhr.upload.onprogress`, supports\n * cancellation via the `signal` option (calling `xhr.abort()`),\n * and routes failures onto the same `transport.errors$` stream\n * the ky path uses.\n *\n * The runtime check on `XMLHttpRequest` is the load-bearing seam: a\n * Node worker calling `client.yield.resource(...)` (which always passes\n * a `signal` for unsubscribe-aborts) must NOT take the XHR path —\n * `XMLHttpRequest` is undefined and the upload throws synchronously.\n * Browsers always have it; Node does not.\n *\n * v1 limitation: the XHR path does NOT auto-refresh on 401. Mitigation:\n * the session's proactive refresh fires before token expiry, so an\n * upload that *starts* with a fresh token usually completes. An upload\n * spanning the narrow window between expiry and proactive-refresh would\n * fail; the existing `errors$` → modal path surfaces it as session-\n * expired. If retry-with-refresh on the upload path becomes a real\n * complaint, wire a manual retry loop here that reads `token$` afresh.\n */\n\nimport type { AccessToken, ExtractionOutcome, ResourceId, PutBinaryOptions, components } from '@semiont/core';\nimport { busLog } from '@semiont/core';\nimport { SpanKind, getActiveTraceparent, withSpan } from '@semiont/observability';\nimport type { HttpTransport } from './http-transport';\nimport { APIError } from './http-transport';\nimport type { IContentTransport, PutBinaryRequest } from '@semiont/core';\n\ntype GetResourceResponse = components['schemas']['GetResourceResponse'];\n\nexport class HttpContentTransport implements IContentTransport {\n constructor(private readonly transport: HttpTransport) {}\n\n async putBinary(\n request: PutBinaryRequest,\n options?: PutBinaryOptions,\n ): Promise<{ resourceId: ResourceId }> {\n const sizeBytes = request.file instanceof File ? request.file.size : request.file.length;\n busLog('PUT', 'content', {\n name: request.name,\n format: request.format,\n storageUri: request.storageUri,\n sizeBytes,\n });\n return withSpan(\n 'content.put',\n async () => {\n const formData = buildFormData(request);\n const headers = this.requestHeaders(options?.auth);\n\n // Branch on caller intent AND runtime support. The ky path is\n // the well-trodden default; the XHR path lights up only when a\n // caller wants byte progress or cancellation AND the runtime\n // has `XMLHttpRequest` (browsers do; Node does not). Without\n // the runtime guard, every Node-side `yield.resource(...)`\n // call (which always passes `signal`) would throw\n // `XMLHttpRequest is not defined`.\n const xhrAvailable = typeof XMLHttpRequest !== 'undefined';\n if (xhrAvailable && (options?.onProgress || options?.signal)) {\n return uploadViaXhr({\n url: `${this.transport.baseUrl}/resources`,\n formData,\n headers,\n onProgress: options.onProgress,\n signal: options.signal,\n onApiError: (err) => this.transport.pushError(err),\n });\n }\n\n const result = await this.transport.rawHttp\n .post(`${this.transport.baseUrl}/resources`, {\n body: formData,\n headers,\n })\n .json<{ resourceId: string }>();\n\n return { resourceId: result.resourceId as ResourceId };\n },\n {\n kind: SpanKind.CLIENT,\n attrs: {\n 'content.format': request.format,\n 'content.size_bytes': sizeBytes,\n },\n },\n );\n }\n\n async getBinary(\n resourceId: ResourceId,\n options?: { auth?: AccessToken },\n ): Promise<{ data: ArrayBuffer; contentType: string }> {\n busLog('GET', 'content', { resourceId });\n return withSpan(\n 'content.get',\n async () => {\n // Pure pipe: no Accept header — the route serves the stored bytes\n // verbatim with their real Content-Type (SIMPLER-JSON-LD.md).\n const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/resources/${resourceId}`, {\n headers: this.requestHeaders(options?.auth),\n });\n const contentType = response.headers.get('content-type') || 'application/octet-stream';\n const data = await response.arrayBuffer();\n return { data, contentType };\n },\n { kind: SpanKind.CLIENT, attrs: { 'resource.id': resourceId as unknown as string } },\n );\n }\n\n async getBinaryStream(\n resourceId: ResourceId,\n options?: { auth?: AccessToken },\n ): Promise<{ stream: ReadableStream<Uint8Array>; contentType: string }> {\n busLog('GET', 'content', { resourceId, stream: true });\n return withSpan(\n 'content.get',\n async () => {\n // Pure pipe: no Accept header (see getBinary).\n const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/resources/${resourceId}`, {\n headers: this.requestHeaders(options?.auth),\n });\n const contentType = response.headers.get('content-type') || 'application/octet-stream';\n if (!response.body) {\n throw new Error('Response body is null - cannot create stream');\n }\n return { stream: response.body, contentType };\n },\n {\n kind: SpanKind.CLIENT,\n attrs: { 'resource.id': resourceId as unknown as string, 'content.stream': true },\n },\n );\n }\n\n /**\n * Dereference the resource's JSON-LD graph over HTTP — the LD face an\n * external linked-data client sees. Deliberately HTTP, not the bus\n * (SIMPLER-JSON-LD.md §5).\n */\n async getResourceGraph(\n resourceId: ResourceId,\n options?: { auth?: AccessToken },\n ): Promise<GetResourceResponse> {\n busLog('GET', 'content', { resourceId, graph: true });\n return withSpan(\n 'content.get_graph',\n () =>\n this.transport.rawHttp\n .get(`${this.transport.baseUrl}/resources/${resourceId}/jsonld`, {\n headers: this.requestHeaders(options?.auth),\n })\n .json<GetResourceResponse>(),\n { kind: SpanKind.CLIENT, attrs: { 'resource.id': resourceId as unknown as string, 'content.graph': true } },\n );\n }\n\n /**\n * Store a resource's derived coordinate map (ANCHORED-TEXT-CACHE Lane 5).\n *\n * The Smelter is the producer and runs as its own process, which is why this\n * crosses the wire at all: the map goes to the one store the KnowledgeSystem\n * owns, rather than to a volume shared between service images.\n */\n async putAnchoredText(\n checksum: string,\n outcome: ExtractionOutcome,\n options?: { auth?: AccessToken },\n ): Promise<void> {\n busLog('PUT', 'anchored-text', { checksum });\n await withSpan(\n 'content.put_anchored_text',\n () =>\n this.transport.rawHttp\n .put(`${this.transport.baseUrl}/anchored-text/${checksum}`, {\n headers: this.requestHeaders(options?.auth),\n json: outcome,\n })\n .json<unknown>(),\n { kind: SpanKind.CLIENT, attrs: { 'content.checksum': checksum } },\n );\n }\n\n /**\n * The resource's coordinate map, or `null` when none has been derived —\n * which is the common case and not an error: callers degrade to no quoted\n * text.\n *\n * 204 is that answer, and the body is empty, so it must be taken before\n * `.json()` is reached — parsing an empty body throws, which would turn the\n * ordinary case into a failure. A 404 degrades the same way, though it is a\n * different fact: the resource itself is absent, and a resource that does\n * not exist has no map either.\n */\n async getAnchoredText(\n resourceId: ResourceId,\n options?: { auth?: AccessToken },\n ): Promise<ExtractionOutcome | null> {\n busLog('GET', 'anchored-text', { resourceId });\n return withSpan(\n 'content.get_anchored_text',\n async () => {\n const response = await this.transport.rawHttp\n .get(`${this.transport.baseUrl}/resources/${resourceId}/anchored-text`, {\n headers: this.requestHeaders(options?.auth),\n throwHttpErrors: false,\n });\n if (response.status === 204 || response.status === 404) return null;\n if (!response.ok) throw new Error(`anchored-text read failed: ${response.status}`);\n return response.json<ExtractionOutcome>();\n },\n { kind: SpanKind.CLIENT, attrs: { 'resource.id': resourceId as unknown as string } },\n );\n }\n\n /**\n * The cache-consult read (PERSIST-ANCHORS P2c) — checksum-addressed and\n * barrier-free; 204 is the ordinary miss. This is how an out-of-process\n * extraction seam hits the cache at all.\n */\n async getAnchoredTextByChecksum(\n checksum: string,\n options?: { auth?: AccessToken },\n ): Promise<ExtractionOutcome | null> {\n busLog('GET', 'anchored-text-by-checksum', { checksum });\n return withSpan(\n 'content.get_anchored_text_by_checksum',\n async () => {\n const response = await this.transport.rawHttp\n .get(`${this.transport.baseUrl}/anchored-text/${checksum}`, {\n headers: this.requestHeaders(options?.auth),\n throwHttpErrors: false,\n });\n if (response.status === 204) return null;\n if (!response.ok) throw new Error(`anchored-text checksum read failed: ${response.status}`);\n return response.json<ExtractionOutcome>();\n },\n { kind: SpanKind.CLIENT, attrs: { 'content.checksum': checksum } },\n );\n }\n\n /**\n * The store's would-hit keys — the reconcile planner's bulk existence read\n * (PERSIST-ANCHORS P0). One request per reconcile; keys only, never the\n * maps themselves, which is the point of the dedicated route.\n */\n async listAnchoredTextKeys(options?: { auth?: AccessToken }): Promise<string[]> {\n busLog('GET', 'anchored-text-keys', {});\n return withSpan(\n 'content.list_anchored_text_keys',\n async () => {\n const { keys } = await this.transport.rawHttp\n .get(`${this.transport.baseUrl}/anchored-text/keys`, {\n headers: this.requestHeaders(options?.auth),\n })\n .json<{ keys: string[] }>();\n return keys;\n },\n { kind: SpanKind.CLIENT },\n );\n }\n\n dispose(): void {\n // HttpContentTransport has no resources of its own; HttpTransport owns\n // the ky instance and token subject. No-op is correct here.\n }\n\n /** Auth header + W3C trace propagation for the active span. */\n private requestHeaders(override?: AccessToken): Record<string, string> {\n const token = override ?? this.transport.getToken();\n const headers: Record<string, string> = token ? { Authorization: `Bearer ${token}` } : {};\n const trace = getActiveTraceparent();\n if (trace) {\n headers['traceparent'] = trace.traceparent;\n if (trace.tracestate) headers['tracestate'] = trace.tracestate;\n }\n return headers;\n }\n}\n\nfunction buildFormData(request: PutBinaryRequest): FormData {\n const formData = new FormData();\n formData.append('name', request.name);\n formData.append('format', request.format);\n formData.append('storageUri', request.storageUri);\n\n if (request.file instanceof File) {\n formData.append('file', request.file);\n } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(request.file)) {\n // `Buffer` is a Node global; referencing it bare in the browser throws\n // ReferenceError before the isBuffer call. Browser uploads always hit\n // the File branch above; this branch is for Node-side workers.\n const blob = new Blob([new Uint8Array(request.file)], { type: request.format });\n formData.append('file', blob, request.name);\n } else {\n throw new Error('file must be a File or Buffer');\n }\n\n if (request.entityTypes && request.entityTypes.length > 0) {\n formData.append('entityTypes', JSON.stringify(request.entityTypes));\n }\n if (request.language) formData.append('language', request.language);\n if (request.sourceAnnotationId) formData.append('sourceAnnotationId', String(request.sourceAnnotationId));\n if (request.sourceResourceId) formData.append('sourceResourceId', String(request.sourceResourceId));\n if (request.generationPrompt) formData.append('generationPrompt', request.generationPrompt);\n if (request.generator) formData.append('generator', JSON.stringify(request.generator));\n if (request.cloneToken) formData.append('cloneToken', request.cloneToken);\n if (request.archiveOriginal !== undefined) formData.append('archiveOriginal', String(request.archiveOriginal));\n if (request.isDraft !== undefined) formData.append('isDraft', String(request.isDraft));\n\n return formData;\n}\n\ninterface XhrUploadOptions {\n url: string;\n formData: FormData;\n headers: Record<string, string>;\n onProgress?: (event: { bytesUploaded: number; totalBytes: number }) => void;\n signal?: AbortSignal;\n onApiError: (error: APIError) => void;\n}\n\n/**\n * XHR-based POST that exposes `xhr.upload.onprogress` byte counts and\n * supports cancellation via `AbortSignal`. Mirrors the ky path's error\n * shape: 4xx/5xx and network-level failures both surface as `APIError`,\n * and every error is routed onto `transport.errors$` before the promise\n * rejects.\n */\nfunction uploadViaXhr(opts: XhrUploadOptions): Promise<{ resourceId: ResourceId }> {\n const { url, formData, headers, onProgress, signal, onApiError } = opts;\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n if (signal?.aborted) {\n const err = new APIError('Upload aborted', 0, 'aborted');\n onApiError(err);\n reject(err);\n return;\n }\n\n xhr.open('POST', url);\n for (const [name, value] of Object.entries(headers)) {\n xhr.setRequestHeader(name, value);\n }\n\n if (onProgress) {\n xhr.upload.onprogress = (e: ProgressEvent) => {\n // `lengthComputable` is true when Content-Length is known. For\n // FormData posts the browser computes it, so this is true in\n // practice; the false branch handles the rare chunked-encoding\n // / gzip-while-uploading case.\n const totalBytes = e.lengthComputable ? e.total : 0;\n onProgress({ bytesUploaded: e.loaded, totalBytes });\n };\n }\n\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const body = JSON.parse(xhr.responseText) as { resourceId: string };\n resolve({ resourceId: body.resourceId as ResourceId });\n } catch (parseErr) {\n const err = new APIError(\n `Upload succeeded but response was not valid JSON: ${(parseErr as Error).message}`,\n xhr.status,\n xhr.statusText,\n xhr.responseText,\n );\n onApiError(err);\n reject(err);\n }\n return;\n }\n let body: unknown = xhr.responseText;\n try { body = JSON.parse(xhr.responseText); } catch { /* keep as text */ }\n const message = (body && typeof body === 'object' && 'message' in body && typeof (body as { message: unknown }).message === 'string')\n ? (body as { message: string }).message\n : `HTTP ${xhr.status}: ${xhr.statusText}`;\n const err = new APIError(message, xhr.status, xhr.statusText, body);\n onApiError(err);\n reject(err);\n };\n\n xhr.onerror = () => {\n // Network-level failure (DNS, TCP reset, CORS). XHR doesn't give\n // us a useful status here; classify as `unavailable` via 0 status\n // mapping in classifyApiCode.\n const err = new APIError('Network error during upload', 0, 'network-error');\n onApiError(err);\n reject(err);\n };\n\n xhr.ontimeout = () => {\n const err = new APIError('Upload timed out', 0, 'timeout');\n onApiError(err);\n reject(err);\n };\n\n xhr.onabort = () => {\n // Caller-initiated abort via `signal`. Emit a single APIError so the\n // shape matches the other failure paths; consumers can disambiguate\n // via `signal.aborted` if they need to.\n const err = new APIError('Upload aborted', 0, 'aborted');\n onApiError(err);\n reject(err);\n };\n\n if (signal) {\n const onAbort = () => xhr.abort();\n signal.addEventListener('abort', onAbort, { once: true });\n // No teardown for the listener — once xhr fires onabort/onerror/onload\n // the signal is no longer relevant; the listener is GC'd with the xhr.\n }\n\n xhr.send(formData);\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/transport/sse-connect-error.ts","../src/transport/api-error.ts","../src/transport/actor-state-unit.ts","../src/transport/http-transport.ts","../src/transport/http-content-transport.ts"],"names":["SemiontError","Subject","BehaviorSubject","busLog","withSpan","SpanKind","getActiveTraceparent","body","err"],"mappings":"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","file":"index.js","sourcesContent":["import { SemiontError } from '@semiont/core';\n\n/**\n * A refused SSE connect — `POST /bus/subscribe` answered non-2xx (or 2xx\n * with no body). The status is CARRIED, not interpolated into the message:\n * P3's backoff/terminal split reads it (auth-refused is terminal in kind;\n * 5xx is transient), and it surfaces on `ActorStateUnit.errors$` so a\n * refused client is observable instead of a silent retry loop\n * (SSE-AUTH-RESILIENCE P2, shape B).\n *\n * Extends `SemiontError` (core) rather than reusing `APIError`: APIError\n * lives in http-transport.ts, which imports the actor — reaching for it\n * there would buy one inherited field with an import cycle.\n *\n * Its OWN module, not `actor-state-unit.ts`, because `*-state-unit.ts` files\n * carry no class declarations: state units are plain-object factories, and\n * `audit-state-unit-no-class.sh` is the static half of the A1 axiom whose\n * allowlist is deliberately empty. An error type is not state-unit\n * machinery, so it moves rather than earning that gate its first exception.\n * A leaf module importing only core also keeps the cycle shut.\n */\nexport class SseConnectError extends SemiontError {\n readonly status: number;\n constructor(status: number) {\n super(`SSE connect failed: ${status}`, 'SSE_CONNECT_FAILED', { status });\n this.name = 'SseConnectError';\n this.status = status;\n }\n}\n","/**\n * `APIError` — the transport's HTTP failure, carrying the status it came from.\n *\n * Lives in its own module rather than in `http-transport.ts` because\n * `actor-state-unit.ts` throws it too (SIDECAR-BOOT-RESILIENCE P2) and\n * `http-transport.ts` imports `actor-state-unit.ts` — so the obvious home is a\n * cycle. The alternative, a second status-bearing error class beside this one,\n * is the duplicated shape the house rules forbid: there would then be two\n * answers to \"how does an HTTP failure carry its status\", and the retry\n * predicate could only agree with one of them.\n */\n\nimport { SemiontError, type TransportErrorCode, type HttpStatusError } from '@semiont/core';\n\nfunction classifyApiCode(status: number): TransportErrorCode {\n if (status === 400) return 'bad-request';\n if (status === 401) return 'unauthorized';\n if (status === 403) return 'forbidden';\n if (status === 404) return 'not-found';\n if (status === 409) return 'conflict';\n if (status >= 500) return 'unavailable';\n return 'error';\n}\n\nexport class APIError extends SemiontError {\n declare code: TransportErrorCode;\n readonly status: number;\n readonly statusText: string;\n\n constructor(message: string, status: number, statusText: string, body?: unknown) {\n super(message, classifyApiCode(status), { status, statusText, body });\n this.name = 'APIError';\n this.status = status;\n this.statusText = statusText;\n }\n}\n\n/**\n * The contract between this class and core's `isRetryableRequestError`, asserted\n * at compile time (SIDECAR-BOOT-RESILIENCE P1/P2).\n *\n * Core cannot name `APIError` — http-transport depends on core, not the reverse —\n * so the predicate reads the `status` field structurally. Without this line that\n * agreement is a coincidence: rename `status` here and every 429 silently stops\n * being retryable, with no test failing, because a predicate that finds no status\n * simply answers `false`. **A silent loss of retry is exactly the failure this\n * plan exists to prevent**, so it is pinned by the compiler rather than by a test.\n */\nconst _conformsToRetryContract: HttpStatusError = new APIError('', 0, '');\nvoid _conformsToRetryContract;\n","import { BehaviorSubject, Observable, Subject } from 'rxjs';\nimport { filter, map, share } from 'rxjs/operators';\nimport { busLog, busLogEnabled, uuidV4, retryWithBackoff, isRetryableRequestError, type components, type ConnectionState, type StateUnit, type RetryPolicy } from '@semiont/core';\nimport {\n SpanKind,\n extractTraceparent,\n getActiveTraceparent,\n withSpan,\n withTraceparent,\n} from '@semiont/observability';\nimport { SseConnectError } from './sse-connect-error';\nimport { APIError } from './api-error';\n\nexport type { ConnectionState };\n\nexport interface BusEvent {\n channel: string;\n payload: Record<string, unknown>;\n scope?: string;\n}\n\n\nexport interface ActorStateUnitOptions {\n baseUrl: string;\n token: string | (() => string);\n channels: string[];\n /**\n * Base of the failure-retry backoff ladder AND the flat cadence of the\n * `unauthenticated` waiting tick (which polls the token getter, no\n * network). A failure retry waits jitter(min(reconnectMs·2ⁿ, 60 s));\n * n resets on a successful open. Default 5 s.\n */\n reconnectMs?: number;\n /**\n * The SAME hook `HttpTransportConfig.tokenRefresher` wires into the HTTP\n * beforeRetry path (SSE-AUTH-RESILIENCE P4, D2 — no second refresh\n * mechanism). Consulted ONCE per outage when a connect is refused 401,\n * before parking `unauthenticated`; a successful open re-arms it. The\n * refresher's owner rotates the token SOURCE (sessions push the new token\n * into `token$`); the actor then reconnects through the getter, so the\n * token stays single-sourced. A throwing refresher is treated as `null`.\n */\n tokenRefresher?: () => Promise<string | null>;\n /**\n * Remove-side reconnect hysteresis (MULTI-RESOURCE-SCOPE). Scope\n * additions need liveness quickly (100 ms debounce), but a removal only\n * narrows delivery — extra events for a just-released scope are\n * idempotent locally — so remove-only changes wait this long before\n * reconnecting. Keeps hover-churn (transient per-citation previews)\n * from turning every mouse pass into a reconnect storm; any addition\n * flushes pending removals with it on the fast path.\n */\n lazyRemoveMs?: number;\n /**\n * B17 (LOCAL-STORAGE) — IO-abstracted persistence of the last seen\n * PERSISTED event id PER SCOPE, so a reloaded client resumes each\n * scope's replay instead of gapping. `load` runs once at construction;\n * `save` fires per persisted (`p-*`) id with that frame's scope —\n * ephemeral (`e-*`) ids are never saved: they carry no replay meaning,\n * and letting them displace a scope's watermark was exactly the silent\n * replay-loss hole the single-id design had. The transport stays\n * storage-free; callers wrap their own adapter in these thunks.\n */\n loadLastEventIds?: () => Record<string, string> | null;\n saveLastEventId?: (scope: string, id: string) => void;\n}\n\n/** Time in the `reconnecting` state before transitioning to `degraded`. */\nexport const DEGRADED_THRESHOLD_MS = 3_000;\n\n/**\n * Ceiling for the failure-retry backoff (SSE-AUTH-RESILIENCE P3, D1a): the\n * exponential ladder from `reconnectMs` caps here, so a long outage settles\n * into roughly one attempt per minute rather than growing without bound.\n */\nconst MAX_RECONNECT_MS = 60_000;\n\n/**\n * Deadline on the `/bus/emit` POST (JOB-RESTART-SAFETY P7). The gateway\n * accepts an emit and returns 202 promptly; a POST that has not resolved by\n * here means the gateway is unresponsive (mid-restart, overwhelmed), and the\n * emit is rejected rather than awaited forever. This is the transport-level\n * bound behind the 2026-09-03 finalization hang: a worker's mark:create /\n * job:complete emit to a wedged gateway used to hang the worker loop with no\n * timeout of its own. The rejection surfaces as a job failure the queue\n * classifies transient (an unreachable gateway is not the request's fault),\n * so the work retries instead of wedging. Covers EVERY emit and every job\n * type — not just the reference-annotation persist P6 bounded.\n */\nexport const EMIT_TIMEOUT_MS = 30_000;\n\n/**\n * Retry budget for ONE `/bus/emit` POST (SIDECAR-BOOT-RESILIENCE D3).\n *\n * Per request, deliberately — not per boot pass. \"Retry when one settles\" is\n * advice about a single request; re-running a whole catch-up pass to recover\n * from one refusal re-sends hundreds of already-successful emits, which is the\n * amplification that wedged the weaver in the first place.\n *\n * Small on purpose. `EMIT_TIMEOUT_MS` bounds each attempt, so the worst case\n * here is 4 attempts plus up to ~7s of jittered waiting, and an emit that a\n * caller is awaiting must fail while the caller still cares. The patience for a\n * gateway that is genuinely down belongs to the boot pass above it, not here.\n */\nexport const EMIT_RETRY: RetryPolicy = {\n attempts: 4,\n initialDelayMs: 1_000,\n maxDelayMs: 4_000,\n};\n\n/**\n * How long a superseded connection keeps DRAINING after a make-before-break\n * handoff before being aborted. Aborting the old connection the instant the\n * new one opened discarded replies already written to the old socket but not\n * yet read by the client (a freshly-hydrating page's main thread is busy —\n * exactly the N-concurrent-loaders-at-connect repro in\n * .plans/bugs/concurrent-browse-resource-starvation.md). The overlap is safe:\n * persisted ids are stable and correlated-reply ids are deterministic\n * (`e-<channel>:<cid>`, routes/bus.ts), so `seenEventIds` dedups double\n * delivery.\n */\nexport const LINGER_MS = 1_000;\n\nexport interface ActorStateUnit extends StateUnit {\n on$<T = Record<string, unknown>>(channel: string): Observable<T>;\n emit(channel: string, payload: Record<string, unknown>, emitScope?: string): Promise<number>;\n state$: Observable<ConnectionState>;\n /**\n * Refused connects (SSE-AUTH-RESILIENCE P2). One `SseConnectError` per\n * non-2xx `/bus/subscribe` answer, carrying the HTTP status as\n * structured data. Network-level failures (fetch rejections) have no\n * status and do not emit here — they stay on the reconnect path.\n */\n errors$: Observable<SseConnectError>;\n /** With `scope`: upsert channels into that scope's matrix entry. Without: global channels. */\n addChannels(channels: string[], scope?: string): void;\n /**\n * Whether `channel` is in the current GLOBAL subscription set — i.e. the\n * gateway delivers it on this connection. Correlated replies always ride\n * global channels, so this is `busRequest`'s fail-fast probe on a\n * narrowed-subscription transport (see `BusRequestPrimitive.isSubscribed`).\n */\n isSubscribed(channel: string): boolean;\n /** With `scope`: remove channels from that scope's entry (empty entry drops the scope). Without: global channels. */\n removeChannels(channels: string[], scope?: string): void;\n /**\n * Correlated-reply retention, client side (BUS-RESUMPTION Phase 2 /\n * SDK-DEBT S1): register a busRequest correlationId as awaiting its\n * reply. Every connect body includes the currently-tracked set as\n * `pendingReplies`, so a reply published while the connection was down\n * is replayed from the server's retention buffer. The returned disposer\n * (idempotent) removes the id on settle.\n */\n trackReply(correlationId: string): () => void;\n start(): void;\n stop(): void;\n}\n\n/** Allowed transitions in the connection state machine. */\nconst ALLOWED_TRANSITIONS: Record<ConnectionState, ReadonlyArray<ConnectionState>> = {\n initial: ['connecting', 'unauthenticated', 'closed'],\n connecting: ['open', 'reconnecting', 'unauthenticated', 'closed'],\n open: ['reconnecting', 'closed'],\n reconnecting: ['connecting', 'degraded', 'unauthenticated', 'closed'],\n // `degraded → reconnecting` is a legitimate recovery edge: a channel-set\n // change (`addChannels`/`removeChannels`) schedules a reconnect that can\n // fire while the connection is degraded. Omitting it made `reconnect()`\n // throw a fatal, uncaught exception from the reconnect timer (#844).\n degraded: ['connecting', 'reconnecting', 'unauthenticated', 'closed'],\n // Leaving `unauthenticated` takes a usable credential (the gate saw a\n // different, non-empty token) → straight to `connecting`; or teardown.\n unauthenticated: ['connecting', 'closed'],\n closed: [],\n};\n\nexport function createActorStateUnit(options: ActorStateUnitOptions): ActorStateUnit {\n const { baseUrl, token: tokenOrGetter, channels: initialChannels, reconnectMs = 5_000, lazyRemoveMs = 5_000, tokenRefresher } = options;\n const getToken = typeof tokenOrGetter === 'function' ? tokenOrGetter : () => tokenOrGetter;\n\n const globalChannels = new Set(initialChannels);\n /** The subscription matrix's scoped half: scope → channels (MULTI-RESOURCE-SCOPE). */\n const scopedSubscriptions = new Map<string, Set<string>>();\n /**\n * Per-scope resumption watermarks: the last PERSISTED (`p-*`) id seen for\n * each scope. Sent as `lastEventId` on that scope's matrix entry so the\n * server replays each scope's own gap. A scope keeps its watermark after\n * its channels are removed — re-subscribing later replays what was missed\n * in between. Ephemeral ids never touch this map.\n */\n const scopeWatermarks = new Map<string, string>(\n Object.entries(options.loadLastEventIds?.() ?? {}),\n );\n /** Outstanding busRequest correlationIds — ride every connect body (S1). */\n const pendingReplies = new Set<string>();\n /**\n * This bus client's routing address for correlated replies\n * (CORRELATED-REPLY-ROUTING D1). Minted once per ACTOR — deliberately not\n * per connection: a make-before-break handover runs two connections at\n * once and a reconnect replaces one, so a per-connection id would strand\n * the reply on the dying socket. Both overlap connections present this\n * same address, and the deterministic `e-<channel>:<cid>` id dedups the\n * double delivery exactly as it does today.\n *\n * Not persisted: \"stable\" means across transport reconnects, not across\n * page reloads. A reload builds a new actor, and its `pendingReplies`\n * replay is what recovers in-flight replies (S1).\n */\n const clientId = uuidV4();\n\n const events$ = new Subject<BusEvent>();\n const state$ = new BehaviorSubject<ConnectionState>('initial');\n const errors$ = new Subject<SseConnectError>();\n let currentState: ConnectionState = 'initial';\n let degradedTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Move the state machine to `next`. An unexpected edge is logged and\n * ignored — NOT thrown. `transition()` runs inside timer callbacks (the\n * reconnect and degraded timers), so a throw here is an uncaught exception\n * that takes down the host process (#844). A bad edge means a bug in the\n * reconnect loop, but degrading gracefully (keep the current state, warn)\n * is strictly better than killing a long-running job. The permitted edges\n * — including the `degraded → reconnecting` recovery edge — are in\n * `ALLOWED_TRANSITIONS`.\n *\n * Side effect: manages the `degraded` timer. Enters on\n * `reconnecting`, cleared on exit.\n */\n const transition = (next: ConnectionState): void => {\n if (currentState === next) return;\n const allowed = ALLOWED_TRANSITIONS[currentState];\n if (!allowed.includes(next)) {\n console.warn(`[actor] ignoring invalid connection state transition: ${currentState} → ${next}`);\n return;\n }\n const prev = currentState;\n currentState = next;\n\n if (next === 'reconnecting' && prev !== 'reconnecting') {\n // Starting a reconnect cycle — arm the degraded-threshold timer.\n if (degradedTimer) clearTimeout(degradedTimer);\n degradedTimer = setTimeout(() => {\n if (currentState === 'reconnecting') transition('degraded');\n }, DEGRADED_THRESHOLD_MS);\n }\n if (prev === 'reconnecting' && next !== 'reconnecting') {\n // Leaving reconnecting (to connecting, degraded, or closed) —\n // the timer is either no longer relevant or has just fired.\n if (degradedTimer) { clearTimeout(degradedTimer); degradedTimer = null; }\n }\n\n state$.next(next);\n };\n\n let running = false;\n /**\n * All in-flight SSE fetch controllers. Tracked as a Set because\n * connect() may race with itself under mount-churn or rapid channel-\n * set changes — whenever a new connect() starts we abort ALL previous\n * in-flight fetches rather than only the last-tracked one. A previous\n * single-slot implementation leaked orphaned streams (diagnosed by\n * observing 3 concurrent SSE subscribes in the /bus/subscribe network\n * log, each delivering duplicate RECV frames). Using a Set guarantees\n * at most one live stream post-reconnect regardless of race order.\n */\n const inflightControllers = new Set<AbortController>();\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n\n /**\n * Connections retired by a make-before-break handoff. A superseded\n * connection lingers (still draining) for LINGER_MS before its abort; its\n * read loop ending — naturally or via that abort — must NOT drive the\n * actor-wide reconnect logic, which belongs to the live connection only.\n */\n const superseded = new WeakSet<AbortController>();\n /** Pending linger-abort timers, cleared on stop/dispose. */\n const lingerTimers = new Set<ReturnType<typeof setTimeout>>();\n\n /**\n * Recently-delivered event ids, to dedup the make-before-break overlap: the\n * brief window where the old and new connection both deliver the same live\n * event during a scope-change handoff. Persisted ids (`p-<scope>-<seq>`) are\n * stable across connections, so this collapses such an overlap to a single\n * emission. Ephemeral ids (`e-<connectionId>-<counter>`) are per-connection,\n * so a cross-connection ephemeral duplicate is NOT caught here — its\n * consumers tolerate the rare double (a correlation reply is taken with\n * `take(1)`; cache invalidations and job-completion are idempotent/terminal).\n * Bounded FIFO (insertion-ordered Set) to cap memory.\n *\n * Cost note: this is *always-on* — every delivered event does a has/add here\n * — yet a duplicate is only possible during a handoff overlap; in steady\n * state there's a single connection and nothing can collide. So every\n * consumer of this transport carries a small standing structure for a path\n * that fires only on (now-rare) scope changes. It's left unconditional\n * because the per-event cost is negligible next to the JSON.parse + trace\n * span already on this path. If that ever stops being true, scope it to the\n * overlap (build on handoff start, drop once the old read loop exits) or\n * track a high-water `Map<scope, maxSeq>` instead of every id.\n */\n const seenEventIds = new Set<string>();\n const SEEN_EVENT_IDS_MAX = 512;\n const rememberEventId = (id: string): void => {\n seenEventIds.add(id);\n if (seenEventIds.size > SEEN_EVENT_IDS_MAX) {\n const oldest = seenEventIds.values().next().value;\n if (oldest !== undefined) seenEventIds.delete(oldest);\n }\n };\n /** Release a claim whose apply threw, so a redelivery is re-processed\n * rather than swallowed by its own dedup entry. */\n const forgetEventId = (id: string): void => {\n seenEventIds.delete(id);\n };\n\n const shared$ = events$.pipe(share());\n\n const disconnect = () => {\n for (const c of inflightControllers) {\n try { c.abort(); } catch { /* noop */ }\n }\n inflightControllers.clear();\n if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }\n for (const t of lingerTimers) clearTimeout(t);\n lingerTimers.clear();\n };\n\n /**\n * Drop-recovery scheduling (SSE-AUTH-RESILIENCE P3). One place arms\n * `reconnectTimer`, so the gate's waiting tick and the failure retry can\n * never disagree on cadence (the split-brain the P1 handoff warned about).\n * Failure retries BACK OFF (D1a): equal-jitter exponential — delay ∈\n * [cap/2, cap], cap = min(reconnectMs·2ⁿ, MAX_RECONNECT_MS) — so a downed\n * gateway sees a thinning trickle instead of 12 requests/min from every\n * client, jitter keeps N clients out of lockstep, and a transient failure\n * still recovers (backoff delays recovery; it never prevents it). The\n * WAITING tick stays flat at `reconnectMs`: it polls the token getter and\n * makes no requests, so there is no load to bound — and backing it off\n * would only slow recovery after a re-login. Scope-churn reconnects\n * (debounce/hysteresis timers) are a separate mechanism, untouched.\n */\n let retryAttempt = 0;\n let refusedToken: string | null = null;\n /**\n * One refresher consult per outage (SSE-AUTH-RESILIENCE P4). Burned on\n * the first 401 that consults; re-armed only by a successful open. Without\n * this, a gateway refusing every token turns refresh-then-401 into a loop\n * at connect cadence — the storm with extra steps.\n */\n let refreshBurned = false;\n const scheduleRetry = (delayMs: number, keepPrevious = false) => {\n if (!running) return;\n if (reconnectTimer) clearTimeout(reconnectTimer);\n reconnectTimer = setTimeout(() => {\n if (running) connect(keepPrevious);\n }, delayMs);\n };\n const backoffDelay = () => {\n const cap = Math.min(reconnectMs * 2 ** retryAttempt, MAX_RECONNECT_MS);\n retryAttempt++;\n return cap / 2 + Math.random() * (cap / 2);\n };\n\n const connect = async (keepPrevious = false) => {\n // ── The credential gate (SSE-AUTH-RESILIENCE P1 + P3, shapes A + B) ─\n //\n // A connect with no bearer CANNOT succeed (shape A: `HttpTransport`\n // renders an exhausted session's null `token$` as `''`). A connect\n // re-sending a bearer the gateway just REFUSED cannot succeed either\n // (shape B: the same credential gets the same 401). Both are\n // guaranteed-failing requests generated on a timer — the storm in\n // .plans/bugs/stale-sse-actor-401-loops-after-token-expiry.md — so\n // neither is attempted.\n //\n // This is D3 and D6a answered together: ONE `unauthenticated` state for\n // \"no credential yet\" and \"credential refused\", because they are the\n // same operational fact — not attempting, and only a usable DIFFERENT\n // credential changes anything. The finer cause (the 401) is on\n // `errors$`.\n //\n // We WAIT rather than give up: the tick polls the GETTER — no network,\n // so there is no load to bound, and the tick stays at the flat base.\n // Recovery is automatic: a re-login or session refresh rotates the\n // getter's value and the next tick connects. That makes a 401 terminal\n // PER-CREDENTIAL, not per-actor — a hard stop here would kill the SPA's\n // bus for good on an out-of-band revocation (logout elsewhere bumping\n // `tokenVersion`) that the session's next proactive refresh heals.\n const token = getToken();\n if (!token || token === refusedToken) {\n if (running) {\n if (currentState !== 'unauthenticated') transition('unauthenticated');\n scheduleRetry(reconnectMs, keepPrevious);\n }\n return;\n }\n refusedToken = null; // a different credential — worth attempting\n\n // Transition to `connecting` from whichever reconnect-ish state\n // we're currently in (`initial`, `reconnecting`, `degraded`).\n transition('connecting');\n\n // Snapshot the connections this connect() supersedes.\n // - keepPrevious=false (initial connect / drop-recovery): there is no\n // live connection worth preserving, so abort up front — this closes\n // the orphan-stream leak described above.\n // - keepPrevious=true (scope-change reconnect): MAKE-BEFORE-BREAK. Keep\n // the previous connection(s) ALIVE until the new one is `open`, then\n // abort them (below, after the fetch resolves), so an in-flight\n // ephemeral result isn't dropped in a reconnect gap (#847). The brief\n // window where old and new both deliver is deduped by event id.\n const previous = [...inflightControllers];\n if (!keepPrevious) {\n for (const c of previous) {\n try { c.abort(); } catch { /* noop */ }\n }\n inflightControllers.clear();\n }\n\n // POST subscription matrix (MULTI-RESOURCE-SCOPE): global channels plus\n // one entry per scope, each carrying its own resumption watermark.\n // `satisfies` is the drift-lock (the MEDIA_TYPES idiom): the body is\n // hand-written while the schema owns the shape, so the compiler — not a\n // reviewer — is what notices a required field going missing. That is\n // what makes `clientId` required in BusSubscribeRequest worth anything\n // on this side of the wire (CORRELATED-REPLY-ROUTING P2).\n const body = JSON.stringify({\n global: [...globalChannels],\n scoped: [...scopedSubscriptions.entries()].map(([scope, chans]) => {\n const watermark = scopeWatermarks.get(scope);\n return {\n scope,\n channels: [...chans],\n ...(watermark !== undefined ? { lastEventId: watermark } : {}),\n };\n }),\n ...(pendingReplies.size > 0 ? { pendingReplies: [...pendingReplies] } : {}),\n clientId,\n } satisfies components['schemas']['BusSubscribeRequest']);\n const url = `${baseUrl}/bus/subscribe`;\n\n const controller = new AbortController();\n inflightControllers.add(controller);\n\n try {\n const headers: Record<string, string> = {\n // The gate's single read: `token` is what this ATTEMPT sends, and\n // exactly what `refusedToken` records if the gateway says 401.\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n };\n const response = await fetch(url, { method: 'POST', headers, body, signal: controller.signal });\n\n if (!response.ok || !response.body) {\n throw new SseConnectError(response.status);\n }\n\n // Stopped/disposed while the fetch was in flight — don't proceed to open\n // (and retire the old connection on) a stream we've been told to tear\n // down. `stop()`/`dispose()` already aborted this controller.\n if (!running) return;\n\n // Make-before-break handoff: the new connection is established (the\n // gateway has subscribed it and any `Last-Event-ID` replay is flowing),\n // so mark the previous connection(s) superseded and LINGER them — keep\n // them draining for LINGER_MS before the abort. Aborting immediately\n // here discarded replies already written to the old socket but not yet\n // read (the buffered-bytes loss in\n // .plans/bugs/concurrent-browse-resource-starvation.md); an event\n // delivered by both connections during the overlap is deduped by id in\n // the read loop below (persisted ids are stable; correlated-reply ids\n // are deterministic per routes/bus.ts). Had the fetch failed, we'd have\n // thrown above and never reached here, leaving the old connection live\n // (no gap).\n if (keepPrevious) {\n for (const c of previous) superseded.add(c);\n const lingerTimer = setTimeout(() => {\n lingerTimers.delete(lingerTimer);\n for (const c of previous) {\n try { c.abort(); } catch { /* noop */ }\n inflightControllers.delete(c);\n }\n }, LINGER_MS);\n lingerTimers.add(lingerTimer);\n }\n\n transition('open');\n retryAttempt = 0; // a success resets the backoff ladder\n refreshBurned = false; // …and re-arms the refresh-once (P4)\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n\n /**\n * Segments of the current, still-incomplete line. A single `data:`\n * line carries a whole JSON payload — a browse reply can run to\n * megabytes — delivered across many `reader.read()` chunks. The\n * previous `buffer += chunk` + `buffer.split('\\n')` re-flattened and\n * re-scanned the ENTIRE accumulated buffer on every read: O(frame² /\n * chunkSize) bytes of large-string allocation per frame, all landing\n * in V8's large-object space, which only major GC reclaims. Under the\n * reply fan-out burst (~85 multi-MB `browse:*-result` frames/min)\n * that allocation rate outran mark-compact and OOM'd the worker\n * (2026-09-03, DoD #7). Segments are joined exactly once, when the\n * line's newline arrives; each read scans only its own chunk.\n */\n let lineSegments: string[] = [];\n\n // SSE parse state is declared OUTSIDE the read loop: a single\n // event can span many `reader.read()` chunks when the payload is\n // large (a full resource-result with annotations can easily exceed\n // one TCP segment). Resetting these on every read would silently\n // drop any event whose `event:`/`id:` headers land in one chunk\n // and whose terminating blank line lands in the next.\n let currentEvent = '';\n let currentData = '';\n let currentId: string | undefined;\n\n while (running && inflightControllers.has(controller)) {\n const { done, value } = await reader.read();\n if (done) break;\n\n const text = decoder.decode(value, { stream: true });\n\n let searchFrom = 0;\n while (searchFrom <= text.length) {\n const nl = text.indexOf('\\n', searchFrom);\n if (nl === -1) {\n if (searchFrom < text.length) {\n lineSegments.push(searchFrom === 0 ? text : text.slice(searchFrom));\n }\n break;\n }\n let line = text.slice(searchFrom, nl);\n searchFrom = nl + 1;\n if (lineSegments.length > 0) {\n lineSegments.push(line);\n line = lineSegments.join('');\n lineSegments = [];\n }\n if (line.startsWith('event: ')) {\n currentEvent = line.slice(7);\n } else if (line.startsWith('data: ')) {\n currentData = line.slice(6);\n } else if (line.startsWith('id: ')) {\n currentId = line.slice(4);\n } else if (line === '') {\n // Skip an overlap duplicate — the same stable-id event delivered\n // by both the old and new connection during a make-before-break\n // handoff (#847). Ephemeral ids are unique per connection, so this\n // never spuriously drops a distinct event.\n const isDuplicate = currentId !== undefined && seenEventIds.has(currentId);\n if (currentEvent === 'bus-event' && currentData && !isDuplicate) {\n const parsed = JSON.parse(currentData) as BusEvent;\n busLog('RECV', parsed.channel, parsed.payload, parsed.scope);\n // Drain-window forensics: an event delivered by a SUPERSEDED\n // (lingering) connection is one that an immediate handover abort\n // would have discarded — the loss mode of\n // .plans/bugs/concurrent-browse-resource-starvation.md. Gated\n // (per-event, bursty during overlap); flip bus logging on to\n // see how real the window is.\n if (busLogEnabled() && superseded.has(controller)) {\n // eslint-disable-next-line no-console\n console.debug(`[bus LINGER] ${parsed.channel} delivered on superseded connection`);\n }\n // Tier 2: lift trace context off the SSE payload (the\n // gateway's writeBusEvent puts it there). The synchronous\n // fan-out to subscribers happens inside the bus.recv span,\n // so handlers see the parent trace.\n const carrier = extractTraceparent(\n parsed.payload as Record<string, unknown>,\n );\n // The two kinds of id bookkeeping sit on OPPOSITE sides of the\n // awaited fan-out, because they answer different questions.\n //\n // `seenEventIds` answers \"has this frame been claimed?\" and must\n // be recorded BEFORE the await: the await yields the event loop,\n // so during a make-before-break overlap the sibling connection\n // can read the same stable-id frame, find the set still missing\n // it, and deliver it a second time — defeating the overlap dedup\n // (#847) that .plans/bugs/BRIDGE-GAPS.md exists to protect.\n // Rolled back if the apply throws, so a redelivery after a\n // dropped read loop is re-processed rather than silently\n // swallowed by its own claim.\n if (currentId !== undefined) rememberEventId(currentId);\n try {\n await withTraceparent(carrier, () =>\n withSpan(\n `bus.recv:${parsed.channel}`,\n () => { events$.next(parsed); },\n {\n kind: SpanKind.CONSUMER,\n attrs: {\n 'bus.channel': parsed.channel,\n ...(parsed.scope ? { 'bus.scope': parsed.scope } : {}),\n },\n },\n ),\n );\n } catch (err) {\n if (currentId !== undefined) forgetEventId(currentId);\n throw err;\n }\n // The resume watermark and the persisted bookmark answer \"have\n // this event's effects been absorbed?\" and stay AFTER the apply.\n // The pre-fix order (stash first, then an AWAITED apply) opened a\n // gap where a bystander cache's debounced save could fire\n // mid-await, find every cache quiet, and flush a bookmark whose\n // event nothing had absorbed — the fast-path reload loss\n // (.plans/bugs/annotation-lost-on-immediate-reload-after-create.md).\n // Both stay on the LAGGING side, which is safe: a reconnect or\n // crash mid-apply resumes from the previous id and redelivers,\n // and re-invalidation is idempotent.\n //\n // Watermarks are PER SCOPE and persisted-ids-only: a `p-*` id is\n // stamped only on scoped deliveries (the frame always carries\n // `scope`), and ephemeral ids never displace a scope's watermark\n // — the silent replay-loss hole the old single-id design had.\n if (currentId !== undefined && currentId.startsWith('p-') && parsed.scope) {\n scopeWatermarks.set(parsed.scope, currentId);\n // B17: persist per scope — see ActorStateUnitOptions.\n options.saveLastEventId?.(parsed.scope, currentId);\n }\n }\n currentEvent = '';\n currentData = '';\n currentId = undefined;\n }\n }\n }\n } catch (err) {\n if ((err as Error).name === 'AbortError') return;\n // A refused connect carries its status out (P2). Anything else — a\n // network failure, a dropped stream — has no status to carry and\n // stays off errors$.\n if (err instanceof SseConnectError) {\n errors$.next(err);\n // 401: re-sending THIS bearer is deterministic, so park instead of\n // retrying (P3). The gate re-admits the actor the moment the getter\n // yields a different token; until then, no requests at all. Non-401\n // refusals (5xx, proxies) are transient in kind and take the\n // backoff path below.\n if (err.status === 401 && running && !superseded.has(controller)) {\n refusedToken = token;\n retryAttempt = 0;\n if (currentState !== 'unauthenticated') transition('unauthenticated');\n // P4: refresh ONCE before staying parked — the same hook the HTTP\n // beforeRetry path uses (D2). Parked state is truthful while the\n // refresh call is in flight. On success the refresher's owner has\n // rotated the token source, so an immediate tick reconnects\n // through the gate's getter read; on null/throw (or an unchanged\n // token) the flat waiting tick stands.\n if (tokenRefresher && !refreshBurned) {\n refreshBurned = true;\n let refreshed: string | null = null;\n try {\n refreshed = await tokenRefresher();\n } catch {\n // A throwing refresher is a failed refresh, not a crash.\n }\n if (running && refreshed && refreshed !== refusedToken) {\n scheduleRetry(0, keepPrevious);\n return;\n }\n }\n scheduleRetry(reconnectMs, keepPrevious);\n return;\n }\n }\n } finally {\n inflightControllers.delete(controller);\n }\n\n // If we reached here without an AbortError, the connection dropped\n // or the fetch failed (a 401 already diverted to `unauthenticated`\n // above). Transition to reconnecting and schedule a retry on the\n // backoff ladder — unless this was a SUPERSEDED (lingering)\n // connection ending: its termination is expected teardown, not a drop\n // of the live stream, and must not restart the reconnect machinery.\n if (running && !superseded.has(controller)) {\n transition('reconnecting');\n scheduleRetry(backoffDelay());\n }\n };\n\n const reconnect = () => {\n if (!running) return;\n // Transition to `reconnecting` BEFORE aborting the current\n // connection. This matches the pre-state-machine contract where\n // gap-detection relied on seeing a \"dropped\" signal before a\n // subsequent \"connected\" signal; with the state machine, the\n // transition sequence `open → reconnecting → connecting → open`\n // is what BrowseNamespace's gap-detection (pre-BUS-RESUMPTION\n // code path) watches for.\n if (currentState === 'open' || currentState === 'connecting' || currentState === 'degraded') {\n transition('reconnecting');\n }\n // Make-before-break: do NOT abort the live connection here. Cancel only a\n // pending drop-recovery retry, then connect — `connect(keepPrevious=true)`\n // retires the old connection after the new one is open (no gap).\n if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }\n connect(true);\n };\n\n // Debounce channel-set-change reconnects. React StrictMode in dev\n // produces mount → cleanup → mount synchronously, which previously\n // translated into three back-to-back reconnects — enough to tear down\n // in-flight responses, fire gap detection, refetch, tear that down\n // again, and leave the page stuck in \"Loading...\" while caches\n // thrashed. With a short debounce the whole sequence collapses into\n // one reconnect after the final channel-set is stable.\n //\n // Two cadences (MULTI-RESOURCE-SCOPE remove-side hysteresis): additions\n // take the fast 100 ms path (a new scope needs liveness now); remove-only\n // changes wait `lazyRemoveMs` — removal merely narrows delivery, and the\n // consumer's hover churn would otherwise reconnect on every mouse pass.\n // The connect body reads current state, so whichever timer fires first\n // carries ALL pending changes; a fast schedule therefore supersedes any\n // pending lazy one, and a lazy schedule never preempts a pending fast one.\n let reconnectTimer2: ReturnType<typeof setTimeout> | null = null;\n let lazyReconnectTimer: ReturnType<typeof setTimeout> | null = null;\n const RECONNECT_DEBOUNCE_MS = 100;\n const scheduleReconnect = () => {\n if (lazyReconnectTimer) { clearTimeout(lazyReconnectTimer); lazyReconnectTimer = null; }\n if (reconnectTimer2) clearTimeout(reconnectTimer2);\n reconnectTimer2 = setTimeout(() => {\n reconnectTimer2 = null;\n reconnect();\n }, RECONNECT_DEBOUNCE_MS);\n };\n const scheduleLazyReconnect = () => {\n if (reconnectTimer2 || lazyReconnectTimer) return; // a pending flush already covers this change\n lazyReconnectTimer = setTimeout(() => {\n lazyReconnectTimer = null;\n reconnect();\n }, lazyRemoveMs);\n };\n\n return {\n on$<T = Record<string, unknown>>(channel: string): Observable<T> {\n return shared$.pipe(\n filter((e) => e.channel === channel),\n map((e) => e.payload as T),\n );\n },\n\n emit: async (channel: string, payload: Record<string, unknown>, emitScope?: string): Promise<number> => {\n // EMIT logging + bus.emit span live at the transport contract layer\n // (`HttpTransport.emit`). ActorStateUnit is plumbing. We do propagate the\n // active span's W3C traceparent on the outbound POST so the gateway\n // can stitch the bus.dispatch server span as a child.\n const body: Record<string, unknown> = { channel, payload, clientId };\n if (emitScope) body.scope = emitScope;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${getToken()}`,\n };\n const trace = getActiveTraceparent();\n if (trace) {\n headers['traceparent'] = trace.traceparent;\n if (trace.tracestate) headers['tracestate'] = trace.tracestate;\n }\n // Retried per request (SIDECAR-BOOT-RESILIENCE D3): a 429/503/504 or an\n // expired deadline gets another attempt; a 400/401/403 rejects on the\n // first, unretried. The predicate is core's, shared with the boot passes\n // above, so \"retryable\" means one thing across the fleet.\n //\n // The whole attempt — POST, status check, and the error it throws — is\n // inside the retried unit, because the refusal IS the failure being\n // classified. Retrying only the fetch would re-run the request and then\n // hand back the same unexamined response.\n const res = await retryWithBackoff(async () => {\n // Bounded (JOB-RESTART-SAFETY P7): an unresponsive gateway must not hang\n // the caller's loop forever. AbortSignal.timeout rejects with a\n // DOMException named TimeoutError — which the predicate treats as\n // retryable, since a deadline is the definition of \"try again\".\n const attempt = await fetch(`${baseUrl}/bus/emit`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(EMIT_TIMEOUT_MS),\n });\n // A refused emit (validation 400, auth 401…) must REJECT — busRequest's\n // contract detaches its doomed reply and propagates this to the caller.\n // Resolving a sentinel here instead leaves that caller waiting for a\n // reply the gateway will never send.\n if (!attempt.ok) {\n let detail = '';\n try {\n detail = (await attempt.text()).slice(0, 500);\n } catch {\n // status alone\n }\n // APIError, not a bare Error: the status rides as a FIELD (D1), which\n // is what makes it classifiable without parsing it back out of prose.\n // The message keeps its shape, so callers matching on it are unaffected.\n throw new APIError(\n `/bus/emit ${attempt.status}${detail ? `: ${detail}` : ''}`,\n attempt.status,\n attempt.statusText,\n detail || undefined,\n );\n }\n return attempt;\n }, isRetryableRequestError, EMIT_RETRY);\n // `-1` = count unknown (older gateway / unreadable body) — never let a\n // parse failure read as an empty room. Same sentinel as the Go client.\n try {\n const reply = (await res.json()) as { subscribers?: unknown };\n return typeof reply.subscribers === 'number' ? reply.subscribers : -1;\n } catch {\n return -1;\n }\n },\n\n state$: state$.asObservable(),\n\n errors$: errors$.asObservable(),\n\n isSubscribed: (channel: string) => globalChannels.has(channel),\n\n addChannels: (channels: string[], scope?: string) => {\n let changed = false;\n if (scope !== undefined) {\n let entry = scopedSubscriptions.get(scope);\n if (!entry) {\n entry = new Set<string>();\n scopedSubscriptions.set(scope, entry);\n }\n for (const ch of channels) {\n if (!entry.has(ch)) { entry.add(ch); changed = true; }\n }\n } else {\n for (const ch of channels) {\n if (!globalChannels.has(ch)) { globalChannels.add(ch); changed = true; }\n }\n }\n if (changed) scheduleReconnect();\n },\n\n removeChannels: (channels: string[], scope?: string) => {\n let changed = false;\n if (scope !== undefined) {\n const entry = scopedSubscriptions.get(scope);\n if (entry) {\n for (const ch of channels) {\n if (entry.delete(ch)) changed = true;\n }\n // The watermark survives the scope's removal deliberately: a later\n // re-subscribe replays what was missed in between.\n if (entry.size === 0) scopedSubscriptions.delete(scope);\n }\n } else {\n for (const ch of channels) {\n if (globalChannels.delete(ch)) changed = true;\n }\n }\n if (changed) scheduleLazyReconnect();\n },\n\n trackReply: (correlationId: string) => {\n pendingReplies.add(correlationId);\n let released = false;\n return () => {\n if (released) return;\n released = true;\n pendingReplies.delete(correlationId);\n };\n },\n\n start: () => {\n if (running) return;\n running = true;\n connect();\n },\n\n stop: () => {\n running = false;\n if (currentState !== 'closed') transition('closed');\n if (reconnectTimer2) { clearTimeout(reconnectTimer2); reconnectTimer2 = null; }\n if (lazyReconnectTimer) { clearTimeout(lazyReconnectTimer); lazyReconnectTimer = null; }\n if (degradedTimer) { clearTimeout(degradedTimer); degradedTimer = null; }\n disconnect();\n },\n\n dispose: () => {\n running = false;\n if (currentState !== 'closed') transition('closed');\n if (reconnectTimer2) { clearTimeout(reconnectTimer2); reconnectTimer2 = null; }\n if (lazyReconnectTimer) { clearTimeout(lazyReconnectTimer); lazyReconnectTimer = null; }\n if (degradedTimer) { clearTimeout(degradedTimer); degradedTimer = null; }\n disconnect();\n events$.complete();\n state$.complete();\n errors$.complete();\n },\n };\n}\n","/**\n * HttpTransport — the HTTP/SSE implementation of ITransport.\n *\n * Phase 1 of TRANSPORT-ABSTRACTION. Owns everything that crosses the wire\n * in remote mode: the bus actor (SSE + POST /bus/emit), auth/admin/exchange/\n * system HTTP endpoints, and connection-state plumbing.\n *\n * Does NOT own the local coordination bus — that lives on `SemiontClient`.\n * `bridgeInto(bus)` wires SSE-received events into the caller-supplied bus\n * once at construction.\n */\n\nimport ky, { HTTPError, type KyInstance } from 'ky';\nimport { BehaviorSubject, Observable, Subject } from 'rxjs';\nimport type {\n AccessToken,\n BaseUrl,\n Email,\n EventBus,\n EventMap,\n GoogleCredential,\n Logger,\n RefreshToken,\n ResourceId,\n UserDID,\n components,\n} from '@semiont/core';\nimport {\n PERSISTED_EVENT_TYPES,\n RESOURCE_BROADCAST_TYPES,\n SemiontError,\n busLog,\n} from '@semiont/core';\nimport { SpanKind, recordBusEmit, withSpan } from '@semiont/observability';\nimport { createActorStateUnit, type ActorStateUnit } from './actor-state-unit';\nimport { APIError } from './api-error';\nimport type {\n ConnectionState,\n IGatewayOperations,\n ITransport,\n HealthCheckResponse,\n StatusResponse,\n UserResponse,\n UpdateUserRequest,\n UpdateUserResponse,\n ListUsersResponse,\n} from '@semiont/core';\nimport { BRIDGED_CHANNELS } from '@semiont/core';\n\ntype AuthResponse = components['schemas']['AuthResponse'];\ntype TokenRefreshResponse = components['schemas']['TokenRefreshResponse'];\ntype AdminUserStatsResponse = components['schemas']['AdminUserStatsResponse'];\ntype OAuthConfigResponse = components['schemas']['OAuthConfigResponse'];\n\n// ── Channel constants (mirror client.ts) ────────────────────────────────\n\nexport const RESOURCE_SCOPED_CHANNELS = [\n // Exclude channels already globally bridged: a channel in both lists is\n // forwarded twice on a scoped connection (global copy → ephemeral id, scoped\n // copy → persisted id) with different SSE ids, escaping the client dedup\n // (.plans/bugs/BRIDGE-GAPS.md). Generalizes the former one-off\n // `frame:entity-type-added` exclusion.\n ...PERSISTED_EVENT_TYPES.filter((t) => !(BRIDGED_CHANNELS as readonly string[]).includes(t)),\n ...RESOURCE_BROADCAST_TYPES,\n];\n\nexport type TokenRefresher = () => Promise<string | null>;\n\nexport interface HttpTransportConfig {\n baseUrl: BaseUrl;\n /** Observable token source; headers read the current value. */\n token$?: BehaviorSubject<AccessToken | null>;\n timeout?: number;\n retry?: number;\n logger?: Logger;\n /** Optional 401-recovery hook. See {@link TokenRefresher}. */\n tokenRefresher?: TokenRefresher;\n /**\n * B17 — persistence thunks for the last seen persisted SSE id PER\n * SCOPE, passed through to the actor state unit. See\n * {@link ActorStateUnitOptions}.\n */\n loadLastEventIds?: () => Record<string, string> | null;\n saveLastEventId?: (scope: string, id: string) => void;\n /**\n * The global SSE channel set this transport subscribes. Absent means the\n * full `BRIDGED_CHANNELS` — a full client must receive every operation's\n * reply channel, or its `busRequest`s time out. A narrow-profile process\n * (the worker) passes exactly the reply channels for the operations it\n * awaits: reply channels are global fan-out on the gateway, so a full\n * subscription receives every OTHER client's replies too — measured at\n * ~85 multi-MB `browse:annotations-result` frames/min during the\n * 2026-09-03 worker OOM, all parsed and dropped by cid filtering.\n * A `busRequest` on an operation whose replies are outside this set\n * fails fast with `bus.unsubscribed` (see `BusRequestPrimitive`).\n */\n channels?: readonly string[];\n}\n\nexport class HttpTransport implements ITransport, IGatewayOperations {\n readonly baseUrl: BaseUrl;\n private readonly http: KyInstance;\n private readonly token$: BehaviorSubject<AccessToken | null>;\n private readonly logger?: Logger;\n private readonly errorsSubject: Subject<SemiontError> = new Subject<SemiontError>();\n /**\n * Stream of `APIError` instances surfaced from any HTTP request just\n * before the transport throws to the caller. Satisfies the `ITransport`\n * `errors$` contract — see `@semiont/core/transport.ts`.\n */\n readonly errors$: Observable<SemiontError> = this.errorsSubject.asObservable();\n\n private _actor: ActorStateUnit | null = null;\n private _actorStarted = false;\n private disposed = false;\n\n /**\n * Per-resource subscription ref-counts (MULTI-RESOURCE-SCOPE). Distinct\n * resources COMPOSE — each key's first subscribe adds its scoped channels\n * to the actor's matrix, its last release removes them; keys are fully\n * independent. Local fan-out for scoped channels is a SINGLETON wired in\n * the actor getter (one delivery per event regardless of how many scopes\n * are held), so entries here are counts only.\n */\n private readonly scopeRefCounts = new Map<string, number>();\n\n /** Buses we've been asked to bridge wire events into. */\n private readonly bridges: EventBus[] = [];\n\n private readonly config: HttpTransportConfig;\n\n constructor(config: HttpTransportConfig) {\n const { baseUrl, timeout = 30000, retry = 2, logger, tokenRefresher } = config;\n this.config = config;\n\n this.baseUrl = (baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl) as BaseUrl;\n this.token$ = config.token$ ?? new BehaviorSubject<AccessToken | null>(null);\n this.logger = logger;\n\n // Retry policy: when a refresher is configured, expand retry to also\n // cover 401 (one attempt). Otherwise use the plain `retry` number.\n const retryConfig = tokenRefresher\n ? {\n limit: 1,\n methods: ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'],\n statusCodes: [401, 408, 413, 429, 500, 502, 503, 504],\n }\n : retry;\n\n this.http = ky.create({\n timeout,\n retry: retryConfig,\n hooks: {\n beforeRequest: [\n ({ request }) => {\n if (this.logger) {\n this.logger.debug('HTTP Request', {\n type: 'http_request',\n url: request.url,\n method: request.method,\n timestamp: Date.now(),\n hasAuth: request.headers.has('Authorization'),\n });\n }\n },\n ],\n beforeRetry: tokenRefresher\n ? [\n async ({ request, error }) => {\n if (!(error instanceof HTTPError) || error.response.status !== 401) {\n return undefined;\n }\n try {\n const newToken = await tokenRefresher();\n if (!newToken) return ky.stop;\n request.headers.set('Authorization', `Bearer ${newToken}`);\n return undefined;\n } catch {\n return ky.stop;\n }\n },\n ]\n : [],\n afterResponse: [\n ({ request, response }) => {\n if (this.logger) {\n this.logger.debug('HTTP Response', {\n type: 'http_response',\n url: request.url,\n method: request.method,\n status: response.status,\n statusText: response.statusText,\n });\n }\n return response;\n },\n ],\n beforeError: [\n async ({ request, error }) => {\n const response = error instanceof HTTPError ? error.response : undefined;\n if (response) {\n const body = await response.json().catch(() => ({})) as { message?: string };\n if (this.logger) {\n this.logger.error('HTTP Request Failed', {\n type: 'http_error',\n url: request.url,\n method: request.method,\n status: response.status,\n statusText: response.statusText,\n error: body.message || `HTTP ${response.status}: ${response.statusText}`,\n });\n }\n const apiError = new APIError(\n body.message || `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n response.statusText,\n body,\n );\n this.errorsSubject.next(apiError);\n throw apiError;\n }\n return error;\n },\n ],\n },\n });\n\n // Auto-start the bus actor once a token arrives.\n this.token$.subscribe((token) => {\n if (token && !this._actorStarted && !this.disposed) {\n this._actorStarted = true;\n this.actor.start();\n }\n });\n }\n\n // ── Lazy actor construction + per-channel fan-in to bridges ───────────\n //\n // `actor` is exposed so the legacy `SemiontClient` can keep `.actor`\n // pointing at the same ActorStateUnit during the transport-abstraction\n // migration. Once SemiontClient is removed, this should be made\n // private again — external callers should use emit/on/stream/state$.\n\n get actor(): ActorStateUnit {\n if (!this._actor) {\n const globalChannels = this.config.channels ?? BRIDGED_CHANNELS;\n this._actor = createActorStateUnit({\n baseUrl: this.baseUrl,\n token: () => this.token$.getValue() ?? '',\n channels: [...globalChannels],\n ...(this.config.loadLastEventIds ? { loadLastEventIds: this.config.loadLastEventIds } : {}),\n ...(this.config.saveLastEventId ? { saveLastEventId: this.config.saveLastEventId } : {}),\n // The SAME hook the ky beforeRetry path uses (SSE-AUTH-RESILIENCE\n // P4, D2) — the SSE connect path refreshes once before parking\n // `unauthenticated`, and no second refresh mechanism exists.\n ...(this.config.tokenRefresher ? { tokenRefresher: this.config.tokenRefresher } : {}),\n });\n // Refused connects surface on the transport's contract stream too —\n // an SSE subscribe IS an HTTP request, and `SseConnectError` is a\n // `SemiontError` (SSE-AUTH-RESILIENCE P4, closing P2's deferred\n // bridge question).\n this._actor.errors$.subscribe((e) => this.errorsSubject.next(e));\n // One fan-in per channel, wired once for the actor's lifetime — the\n // globally-subscribed set AND the resource-scoped set (disjoint by the\n // bus-invariants guard). Scoped events only arrive for scopes in the\n // actor's matrix (gateway-authoritative filtering), so an always-on\n // scoped fan-in delivers nothing while no scope is held — and exactly\n // ONCE per event however many scopes are held (the per-scope\n // bridge-subs design would have duplicated delivery N×).\n for (const channel of [...globalChannels, ...RESOURCE_SCOPED_CHANNELS]) {\n this._actor.on$<Record<string, unknown>>(channel).subscribe((payload) => {\n for (const bus of this.bridges) {\n (bus.get(channel as keyof EventMap) as { next(v: unknown): void }).next(payload);\n }\n });\n }\n }\n return this._actor;\n }\n\n // ── ITransport — bus primitives ───────────────────────────────────────\n\n async emit<K extends keyof EventMap>(\n channel: K,\n payload: EventMap[K],\n resourceScope?: ResourceId,\n ): Promise<number> {\n busLog('EMIT', channel as string, payload, resourceScope as string | undefined);\n recordBusEmit(channel as string, resourceScope as string | undefined);\n return withSpan(\n `bus.emit:${channel as string}`,\n async () => {\n if (resourceScope !== undefined) {\n return this.actor.emit(\n channel as string,\n payload as unknown as Record<string, unknown>,\n resourceScope as string,\n );\n }\n return this.actor.emit(\n channel as string,\n payload as unknown as Record<string, unknown>,\n );\n },\n {\n kind: SpanKind.PRODUCER,\n attrs: {\n 'bus.channel': channel as string,\n ...(resourceScope ? { 'bus.scope': resourceScope as string } : {}),\n },\n },\n );\n }\n\n on<K extends keyof EventMap>(\n channel: K,\n handler: (payload: EventMap[K]) => void,\n ): () => void {\n const sub = this.actor.on$<EventMap[K]>(channel as string).subscribe(handler);\n return () => sub.unsubscribe();\n }\n\n stream<K extends keyof EventMap>(channel: K): Observable<EventMap[K]> {\n return this.actor.on$<EventMap[K]>(channel as string);\n }\n\n /**\n * Wire this transport's SSE fan-in into the given bus. Every channel\n * in `BRIDGED_CHANNELS` (and subsequently per-resource scoped channels\n * opened by `subscribeToResource`) is published on the bus. Safe to\n * call multiple times — each bus is added to the fan-out list.\n */\n bridgeInto(bus: EventBus): void {\n this.bridges.push(bus);\n }\n\n subscribeToResource(resourceId: ResourceId): () => void {\n const key = resourceId as string;\n const count = this.scopeRefCounts.get(key) ?? 0;\n this.scopeRefCounts.set(key, count + 1);\n if (count === 0) {\n this.actor.addChannels([...RESOURCE_SCOPED_CHANNELS], key);\n }\n\n let called = false;\n return () => {\n if (called) return;\n called = true;\n const remaining = (this.scopeRefCounts.get(key) ?? 0) - 1;\n if (remaining > 0) {\n this.scopeRefCounts.set(key, remaining);\n return;\n }\n this.scopeRefCounts.delete(key);\n this.actor.removeChannels([...RESOURCE_SCOPED_CHANNELS], key);\n };\n }\n\n get state$(): Observable<ConnectionState> {\n return this.actor.state$;\n }\n\n /**\n * Correlated-reply retention, client side (BUS-RESUMPTION Phase 2 /\n * SDK-DEBT S1): `busRequest` registers its cid here before emitting;\n * the actor carries the tracked set as `pendingReplies` on every\n * subscribe body, so a reply published while the connection was down\n * replays from the server's retention buffer on reconnect.\n */\n trackReply(correlationId: string): () => void {\n return this.actor.trackReply(correlationId);\n }\n\n /**\n * `busRequest`'s fail-fast probe (`BusRequestPrimitive.isSubscribed`):\n * whether the actor's global subscription set delivers `channel`. On a\n * full client (no `channels` config) every bridged reply channel is\n * subscribed and this never gates; on a narrowed transport it turns a\n * doomed request into an immediate `bus.unsubscribed` error.\n */\n isSubscribed(channel: string): boolean {\n return this.actor.isSubscribed(channel);\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n this.scopeRefCounts.clear();\n if (this._actor) {\n this._actor.dispose();\n this._actor = null;\n }\n this.errorsSubject.complete();\n }\n\n /**\n * Route a transport-level error onto `errors$`. Used by sibling adapters\n * (e.g. `HttpContentTransport`'s XHR upload path) that don't go through\n * the `ky` `beforeError` hook and need to surface failures on the same\n * stream the rest of the transport publishes to.\n */\n pushError(error: SemiontError): void {\n if (this.disposed) return;\n this.errorsSubject.next(error);\n }\n\n // ── Auth ──────────────────────────────────────────────────────────────\n\n private authHeaders(): Record<string, string> {\n const token = this.token$.getValue() ?? undefined;\n return token ? { Authorization: `Bearer ${token}` } : {};\n }\n\n async authenticatePassword(email: Email, password: string): Promise<AuthResponse> {\n return this.http.post(`${this.baseUrl}/api/tokens/password`, {\n json: { email, password },\n headers: this.authHeaders(),\n }).json();\n }\n\n async authenticateGoogle(credential: GoogleCredential): Promise<AuthResponse> {\n return this.http.post(`${this.baseUrl}/api/tokens/google`, {\n json: { credential },\n headers: this.authHeaders(),\n }).json();\n }\n\n async refreshAccessToken(token: RefreshToken): Promise<TokenRefreshResponse> {\n return this.http.post(`${this.baseUrl}/api/tokens/refresh`, {\n json: { refreshToken: token },\n headers: this.authHeaders(),\n }).json();\n }\n\n async logout(): Promise<void> {\n await this.http.post(`${this.baseUrl}/api/users/logout`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async acceptTerms(): Promise<void> {\n await this.http.post(`${this.baseUrl}/api/users/accept-terms`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async getCurrentUser(): Promise<UserResponse> {\n return this.http.get(`${this.baseUrl}/api/users/me`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async getMediaToken(resourceId: ResourceId): Promise<{ token: string }> {\n return this.http.post(`${this.baseUrl}/api/tokens/media`, {\n json: { resourceId },\n headers: this.authHeaders(),\n }).json();\n }\n\n // ── Admin ─────────────────────────────────────────────────────────────\n\n async listUsers(): Promise<ListUsersResponse> {\n return this.http.get(`${this.baseUrl}/api/admin/users`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async getUserStats(): Promise<AdminUserStatsResponse> {\n return this.http.get(`${this.baseUrl}/api/admin/users/stats`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async updateUser(id: UserDID, data: UpdateUserRequest): Promise<UpdateUserResponse> {\n return this.http.patch(`${this.baseUrl}/api/admin/users/${id}`, {\n json: data,\n headers: this.authHeaders(),\n }).json();\n }\n\n async getOAuthConfig(): Promise<OAuthConfigResponse> {\n return this.http.get(`${this.baseUrl}/api/admin/oauth/config`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n // ── System status ─────────────────────────────────────────────────────\n\n async healthCheck(): Promise<HealthCheckResponse> {\n return this.http.get(`${this.baseUrl}/api/health`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n async getStatus(): Promise<StatusResponse> {\n return this.http.get(`${this.baseUrl}/api/status`, {\n headers: this.authHeaders(),\n }).json();\n }\n\n // ── Internal: ky accessor for legacy passthroughs (temporary) ─────────\n\n /**\n * Temporary escape hatch for the ongoing transport migration: namespaces\n * that still need to issue ad-hoc HTTP calls (e.g. legacy browse/mark\n * HTTP fallbacks) can borrow the configured `ky` instance here. Will be\n * deleted once all namespaces route through bus channels or through\n * typed methods on this transport.\n */\n get rawHttp(): KyInstance {\n return this.http;\n }\n\n /**\n * Current access token (synchronously read from the BehaviorSubject).\n * Used by content-transport and legacy namespace HTTP fallbacks that\n * need to pass `auth: token` through some code paths.\n */\n getToken(): AccessToken | undefined {\n return this.token$.getValue() ?? undefined;\n }\n}\n\n// Re-export for convenience\nexport type { ConnectionState } from '@semiont/core';\n","/**\n * HttpContentTransport — binary I/O over HTTP.\n *\n * Phase 1 of TRANSPORT-ABSTRACTION. Narrow by design because binary has\n * different backpressure and streaming characteristics than typed command\n * payloads. Uses the HttpTransport's underlying ky instance + token, so\n * retries, logging, and auth behave identically to the rest of the wire.\n *\n * Two `putBinary` paths live side by side, selected by runtime\n * environment + caller intent:\n * - **ky path (default + Node)** — the original `ky.post(...)` path.\n * Keeps retry-with-refresh, beforeError → APIError, observability\n * spans intact. Hits when no `onProgress`/`signal` is passed, OR\n * when `XMLHttpRequest` isn't available in the runtime (Node\n * workers, the CLI). On Node-side `signal`-aborts: the in-flight\n * `fetch` continues in the background and the `cancelled` flag in\n * `yield.resource` suppresses the resolve/reject callbacks.\n * - **XHR path (browsers with `onProgress` or `signal`)** — hand-rolled\n * because `ky` wraps `fetch` which can't observe upload byte-\n * progress today (`Request({ duplex: 'half' })` is the long-term\n * direction; not yet widely available across the webviews this\n * codepath needs to run in). Threads auth + traceparent headers,\n * emits `onProgress` from `xhr.upload.onprogress`, supports\n * cancellation via the `signal` option (calling `xhr.abort()`),\n * and routes failures onto the same `transport.errors$` stream\n * the ky path uses.\n *\n * The runtime check on `XMLHttpRequest` is the load-bearing seam: a\n * Node worker calling `client.yield.resource(...)` (which always passes\n * a `signal` for unsubscribe-aborts) must NOT take the XHR path —\n * `XMLHttpRequest` is undefined and the upload throws synchronously.\n * Browsers always have it; Node does not.\n *\n * v1 limitation: the XHR path does NOT auto-refresh on 401. Mitigation:\n * the session's proactive refresh fires before token expiry, so an\n * upload that *starts* with a fresh token usually completes. An upload\n * spanning the narrow window between expiry and proactive-refresh would\n * fail; the existing `errors$` → modal path surfaces it as session-\n * expired. If retry-with-refresh on the upload path becomes a real\n * complaint, wire a manual retry loop here that reads `token$` afresh.\n */\n\nimport type { AccessToken, ResourceId, PutBinaryOptions, components } from '@semiont/core';\nimport { busLog } from '@semiont/core';\nimport { SpanKind, getActiveTraceparent, withSpan } from '@semiont/observability';\nimport type { HttpTransport } from './http-transport';\nimport { APIError } from './api-error';\nimport type { IContentTransport, PutBinaryRequest } from '@semiont/core';\n\ntype GetResourceResponse = components['schemas']['GetResourceResponse'];\n\nexport class HttpContentTransport implements IContentTransport {\n constructor(private readonly transport: HttpTransport) {}\n\n async putBinary(\n request: PutBinaryRequest,\n options?: PutBinaryOptions,\n ): Promise<{ resourceId: ResourceId }> {\n const sizeBytes = request.file instanceof File ? request.file.size : request.file.length;\n busLog('PUT', 'content', {\n name: request.name,\n format: request.format,\n storageUri: request.storageUri,\n sizeBytes,\n });\n return withSpan(\n 'content.put',\n async () => {\n const formData = buildFormData(request);\n const headers = this.requestHeaders(options?.auth);\n\n // Branch on caller intent AND runtime support. The ky path is\n // the well-trodden default; the XHR path lights up only when a\n // caller wants byte progress or cancellation AND the runtime\n // has `XMLHttpRequest` (browsers do; Node does not). Without\n // the runtime guard, every Node-side `yield.resource(...)`\n // call (which always passes `signal`) would throw\n // `XMLHttpRequest is not defined`.\n const xhrAvailable = typeof XMLHttpRequest !== 'undefined';\n if (xhrAvailable && (options?.onProgress || options?.signal)) {\n return uploadViaXhr({\n url: `${this.transport.baseUrl}/resources`,\n formData,\n headers,\n onProgress: options.onProgress,\n signal: options.signal,\n onApiError: (err) => this.transport.pushError(err),\n });\n }\n\n const result = await this.transport.rawHttp\n .post(`${this.transport.baseUrl}/resources`, {\n body: formData,\n headers,\n })\n .json<{ resourceId: string }>();\n\n return { resourceId: result.resourceId as ResourceId };\n },\n {\n kind: SpanKind.CLIENT,\n attrs: {\n 'content.format': request.format,\n 'content.size_bytes': sizeBytes,\n },\n },\n );\n }\n\n async getBinary(\n resourceId: ResourceId,\n options?: { auth?: AccessToken },\n ): Promise<{ data: ArrayBuffer; contentType: string }> {\n busLog('GET', 'content', { resourceId });\n return withSpan(\n 'content.get',\n async () => {\n // Pure pipe: no Accept header — the route serves the stored bytes\n // verbatim with their real Content-Type (SIMPLER-JSON-LD.md).\n const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/resources/${resourceId}`, {\n headers: this.requestHeaders(options?.auth),\n });\n const contentType = response.headers.get('content-type') || 'application/octet-stream';\n const data = await response.arrayBuffer();\n return { data, contentType };\n },\n { kind: SpanKind.CLIENT, attrs: { 'resource.id': resourceId as unknown as string } },\n );\n }\n\n async getBinaryStream(\n resourceId: ResourceId,\n options?: { auth?: AccessToken },\n ): Promise<{ stream: ReadableStream<Uint8Array>; contentType: string }> {\n busLog('GET', 'content', { resourceId, stream: true });\n return withSpan(\n 'content.get',\n async () => {\n // Pure pipe: no Accept header (see getBinary).\n const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/resources/${resourceId}`, {\n headers: this.requestHeaders(options?.auth),\n });\n const contentType = response.headers.get('content-type') || 'application/octet-stream';\n if (!response.body) {\n throw new Error('Response body is null - cannot create stream');\n }\n return { stream: response.body, contentType };\n },\n {\n kind: SpanKind.CLIENT,\n attrs: { 'resource.id': resourceId as unknown as string, 'content.stream': true },\n },\n );\n }\n\n /**\n * Dereference the resource's JSON-LD graph over HTTP — the LD face an\n * external linked-data client sees. Deliberately HTTP, not the bus\n * (SIMPLER-JSON-LD.md §5).\n */\n async getResourceGraph(\n resourceId: ResourceId,\n options?: { auth?: AccessToken },\n ): Promise<GetResourceResponse> {\n busLog('GET', 'content', { resourceId, graph: true });\n return withSpan(\n 'content.get_graph',\n () =>\n this.transport.rawHttp\n .get(`${this.transport.baseUrl}/resources/${resourceId}/jsonld`, {\n headers: this.requestHeaders(options?.auth),\n })\n .json<GetResourceResponse>(),\n { kind: SpanKind.CLIENT, attrs: { 'resource.id': resourceId as unknown as string, 'content.graph': true } },\n );\n }\n\n\n\n\n\n dispose(): void {\n // HttpContentTransport has no resources of its own; HttpTransport owns\n // the ky instance and token subject. No-op is correct here.\n }\n\n /** Auth header + W3C trace propagation for the active span. */\n private requestHeaders(override?: AccessToken): Record<string, string> {\n const token = override ?? this.transport.getToken();\n const headers: Record<string, string> = token ? { Authorization: `Bearer ${token}` } : {};\n const trace = getActiveTraceparent();\n if (trace) {\n headers['traceparent'] = trace.traceparent;\n if (trace.tracestate) headers['tracestate'] = trace.tracestate;\n }\n return headers;\n }\n}\n\nfunction buildFormData(request: PutBinaryRequest): FormData {\n const formData = new FormData();\n formData.append('name', request.name);\n formData.append('format', request.format);\n formData.append('storageUri', request.storageUri);\n\n if (request.file instanceof File) {\n formData.append('file', request.file);\n } else if (typeof Buffer !== 'undefined' && Buffer.isBuffer(request.file)) {\n // `Buffer` is a Node global; referencing it bare in the browser throws\n // ReferenceError before the isBuffer call. Browser uploads always hit\n // the File branch above; this branch is for Node-side workers.\n const blob = new Blob([new Uint8Array(request.file)], { type: request.format });\n formData.append('file', blob, request.name);\n } else {\n throw new Error('file must be a File or Buffer');\n }\n\n if (request.entityTypes && request.entityTypes.length > 0) {\n formData.append('entityTypes', JSON.stringify(request.entityTypes));\n }\n if (request.language) formData.append('language', request.language);\n if (request.sourceAnnotationId) formData.append('sourceAnnotationId', String(request.sourceAnnotationId));\n if (request.sourceResourceId) formData.append('sourceResourceId', String(request.sourceResourceId));\n if (request.generationPrompt) formData.append('generationPrompt', request.generationPrompt);\n if (request.generator) formData.append('generator', JSON.stringify(request.generator));\n if (request.cloneToken) formData.append('cloneToken', request.cloneToken);\n if (request.archiveOriginal !== undefined) formData.append('archiveOriginal', String(request.archiveOriginal));\n if (request.isDraft !== undefined) formData.append('isDraft', String(request.isDraft));\n\n return formData;\n}\n\ninterface XhrUploadOptions {\n url: string;\n formData: FormData;\n headers: Record<string, string>;\n onProgress?: (event: { bytesUploaded: number; totalBytes: number }) => void;\n signal?: AbortSignal;\n onApiError: (error: APIError) => void;\n}\n\n/**\n * XHR-based POST that exposes `xhr.upload.onprogress` byte counts and\n * supports cancellation via `AbortSignal`. Mirrors the ky path's error\n * shape: 4xx/5xx and network-level failures both surface as `APIError`,\n * and every error is routed onto `transport.errors$` before the promise\n * rejects.\n */\nfunction uploadViaXhr(opts: XhrUploadOptions): Promise<{ resourceId: ResourceId }> {\n const { url, formData, headers, onProgress, signal, onApiError } = opts;\n\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n if (signal?.aborted) {\n const err = new APIError('Upload aborted', 0, 'aborted');\n onApiError(err);\n reject(err);\n return;\n }\n\n xhr.open('POST', url);\n for (const [name, value] of Object.entries(headers)) {\n xhr.setRequestHeader(name, value);\n }\n\n if (onProgress) {\n xhr.upload.onprogress = (e: ProgressEvent) => {\n // `lengthComputable` is true when Content-Length is known. For\n // FormData posts the browser computes it, so this is true in\n // practice; the false branch handles the rare chunked-encoding\n // / gzip-while-uploading case.\n const totalBytes = e.lengthComputable ? e.total : 0;\n onProgress({ bytesUploaded: e.loaded, totalBytes });\n };\n }\n\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n try {\n const body = JSON.parse(xhr.responseText) as { resourceId: string };\n resolve({ resourceId: body.resourceId as ResourceId });\n } catch (parseErr) {\n const err = new APIError(\n `Upload succeeded but response was not valid JSON: ${(parseErr as Error).message}`,\n xhr.status,\n xhr.statusText,\n xhr.responseText,\n );\n onApiError(err);\n reject(err);\n }\n return;\n }\n let body: unknown = xhr.responseText;\n try { body = JSON.parse(xhr.responseText); } catch { /* keep as text */ }\n const message = (body && typeof body === 'object' && 'message' in body && typeof (body as { message: unknown }).message === 'string')\n ? (body as { message: string }).message\n : `HTTP ${xhr.status}: ${xhr.statusText}`;\n const err = new APIError(message, xhr.status, xhr.statusText, body);\n onApiError(err);\n reject(err);\n };\n\n xhr.onerror = () => {\n // Network-level failure (DNS, TCP reset, CORS). XHR doesn't give\n // us a useful status here; classify as `unavailable` via 0 status\n // mapping in classifyApiCode.\n const err = new APIError('Network error during upload', 0, 'network-error');\n onApiError(err);\n reject(err);\n };\n\n xhr.ontimeout = () => {\n const err = new APIError('Upload timed out', 0, 'timeout');\n onApiError(err);\n reject(err);\n };\n\n xhr.onabort = () => {\n // Caller-initiated abort via `signal`. Emit a single APIError so the\n // shape matches the other failure paths; consumers can disambiguate\n // via `signal.aborted` if they need to.\n const err = new APIError('Upload aborted', 0, 'aborted');\n onApiError(err);\n reject(err);\n };\n\n if (signal) {\n const onAbort = () => xhr.abort();\n signal.addEventListener('abort', onAbort, { once: true });\n // No teardown for the listener — once xhr fires onabort/onerror/onload\n // the signal is no longer relevant; the listener is GC'd with the xhr.\n }\n\n xhr.send(formData);\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@semiont/http-transport",
3
- "version": "0.5.29",
3
+ "version": "0.5.31",
4
4
  "description": "HTTP transport adapters for the Semiont SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -39,9 +39,9 @@
39
39
  "test:coverage": "vitest run --coverage"
40
40
  },
41
41
  "dependencies": {
42
- "@semiont/core": "0.5.29",
43
- "@semiont/observability": "0.5.29",
44
- "ky": "^2.0.2",
42
+ "@semiont/core": "0.5.31",
43
+ "@semiont/observability": "0.5.31",
44
+ "ky": "^2.1.0",
45
45
  "rxjs": "^7.8.1"
46
46
  },
47
47
  "devDependencies": {