@semiont/api-client 0.5.1 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/index.d.ts +70 -19
- package/dist/index.js +200 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -35,10 +35,10 @@ import {
|
|
|
35
35
|
type TokenRefresher,
|
|
36
36
|
APIError,
|
|
37
37
|
// SSE-actor machinery used by SDK adapters; not application code:
|
|
38
|
-
|
|
39
|
-
type
|
|
38
|
+
createActorStateUnit,
|
|
39
|
+
type ActorStateUnit,
|
|
40
40
|
type BusEvent,
|
|
41
|
-
type
|
|
41
|
+
type ActorStateUnitOptions,
|
|
42
42
|
DEGRADED_THRESHOLD_MS,
|
|
43
43
|
} from '@semiont/api-client';
|
|
44
44
|
```
|
|
@@ -48,7 +48,7 @@ That's the entire surface. Everything else moved out:
|
|
|
48
48
|
- **`ITransport`, `IContentTransport`, `BRIDGED_CHANNELS`, `ConnectionState`,
|
|
49
49
|
response/progress types** live in [`@semiont/core`](../core/).
|
|
50
50
|
- **`SemiontClient`, namespaces, `SemiontSession`, `SemiontBrowser`,
|
|
51
|
-
|
|
51
|
+
state units, `bus-request`, `cache`** live in [`@semiont/sdk`](../sdk/).
|
|
52
52
|
|
|
53
53
|
## Behavioral contract
|
|
54
54
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { KyInstance } from 'ky';
|
|
2
2
|
import { Observable, BehaviorSubject } from 'rxjs';
|
|
3
|
-
import { ConnectionState, SemiontError, ITransport, BaseUrl, AccessToken, Logger, EventMap, ResourceId, EventBus, Email, components, GoogleCredential, RefreshToken, UserResponse, ListUsersResponse, UserDID, UpdateUserRequest, UpdateUserResponse,
|
|
3
|
+
import { ConnectionState, SemiontError, TransportErrorCode, ITransport, IBackendOperations, BaseUrl, AccessToken, Logger, EventMap, ResourceId, EventBus, Email, components, GoogleCredential, RefreshToken, UserResponse, ListUsersResponse, UserDID, UpdateUserRequest, UpdateUserResponse, BackendDownload, ProgressEvent, HealthCheckResponse, StatusResponse, IContentTransport, PutBinaryRequest, PutBinaryOptions, ContentFormat } from '@semiont/core';
|
|
4
4
|
|
|
5
|
-
/** Minimal
|
|
6
|
-
interface
|
|
5
|
+
/** Minimal StateUnit surface — anything with a `dispose()` method. */
|
|
6
|
+
interface StateUnit {
|
|
7
7
|
dispose(): void;
|
|
8
8
|
}
|
|
9
9
|
|
|
@@ -12,7 +12,7 @@ interface BusEvent {
|
|
|
12
12
|
payload: Record<string, unknown>;
|
|
13
13
|
scope?: string;
|
|
14
14
|
}
|
|
15
|
-
interface
|
|
15
|
+
interface ActorStateUnitOptions {
|
|
16
16
|
baseUrl: string;
|
|
17
17
|
token: string | (() => string);
|
|
18
18
|
channels: string[];
|
|
@@ -21,7 +21,7 @@ interface ActorVMOptions {
|
|
|
21
21
|
}
|
|
22
22
|
/** Time in the `reconnecting` state before transitioning to `degraded`. */
|
|
23
23
|
declare const DEGRADED_THRESHOLD_MS = 3000;
|
|
24
|
-
interface
|
|
24
|
+
interface ActorStateUnit extends StateUnit {
|
|
25
25
|
on$<T = Record<string, unknown>>(channel: string): Observable<T>;
|
|
26
26
|
emit(channel: string, payload: Record<string, unknown>, emitScope?: string): Promise<void>;
|
|
27
27
|
state$: Observable<ConnectionState>;
|
|
@@ -30,7 +30,7 @@ interface ActorVM extends ViewModel {
|
|
|
30
30
|
start(): void;
|
|
31
31
|
stop(): void;
|
|
32
32
|
}
|
|
33
|
-
declare function
|
|
33
|
+
declare function createActorStateUnit(options: ActorStateUnitOptions): ActorStateUnit;
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* HttpTransport — the HTTP/SSE implementation of ITransport.
|
|
@@ -48,9 +48,8 @@ type AuthResponse = components['schemas']['AuthResponse'];
|
|
|
48
48
|
type TokenRefreshResponse = components['schemas']['TokenRefreshResponse'];
|
|
49
49
|
type AdminUserStatsResponse = components['schemas']['AdminUserStatsResponse'];
|
|
50
50
|
type OAuthConfigResponse = components['schemas']['OAuthConfigResponse'];
|
|
51
|
-
type APIErrorCode = 'api.bad-request' | 'api.unauthorized' | 'api.forbidden' | 'api.not-found' | 'api.conflict' | 'api.server-error' | 'api.error';
|
|
52
51
|
declare class APIError extends SemiontError {
|
|
53
|
-
code:
|
|
52
|
+
code: TransportErrorCode;
|
|
54
53
|
readonly status: number;
|
|
55
54
|
readonly statusText: string;
|
|
56
55
|
constructor(message: string, status: number, statusText: string, body?: unknown);
|
|
@@ -66,11 +65,18 @@ interface HttpTransportConfig {
|
|
|
66
65
|
/** Optional 401-recovery hook. See {@link TokenRefresher}. */
|
|
67
66
|
tokenRefresher?: TokenRefresher;
|
|
68
67
|
}
|
|
69
|
-
declare class HttpTransport implements ITransport {
|
|
68
|
+
declare class HttpTransport implements ITransport, IBackendOperations {
|
|
70
69
|
readonly baseUrl: BaseUrl;
|
|
71
70
|
private readonly http;
|
|
72
71
|
private readonly token$;
|
|
73
72
|
private readonly logger?;
|
|
73
|
+
private readonly errorsSubject;
|
|
74
|
+
/**
|
|
75
|
+
* Stream of `APIError` instances surfaced from any HTTP request just
|
|
76
|
+
* before the transport throws to the caller. Satisfies the `ITransport`
|
|
77
|
+
* `errors$` contract — see `@semiont/core/transport.ts`.
|
|
78
|
+
*/
|
|
79
|
+
readonly errors$: Observable<SemiontError>;
|
|
74
80
|
private _actor;
|
|
75
81
|
private _actorStarted;
|
|
76
82
|
private disposed;
|
|
@@ -78,7 +84,7 @@ declare class HttpTransport implements ITransport {
|
|
|
78
84
|
/** Buses we've been asked to bridge wire events into. */
|
|
79
85
|
private readonly bridges;
|
|
80
86
|
constructor(config: HttpTransportConfig);
|
|
81
|
-
get actor():
|
|
87
|
+
get actor(): ActorStateUnit;
|
|
82
88
|
emit<K extends keyof EventMap>(channel: K, payload: EventMap[K], resourceScope?: ResourceId): Promise<void>;
|
|
83
89
|
on<K extends keyof EventMap>(channel: K, handler: (payload: EventMap[K]) => void): () => void;
|
|
84
90
|
stream<K extends keyof EventMap>(channel: K): Observable<EventMap[K]>;
|
|
@@ -93,6 +99,13 @@ declare class HttpTransport implements ITransport {
|
|
|
93
99
|
private makeUnsubscriber;
|
|
94
100
|
get state$(): Observable<ConnectionState>;
|
|
95
101
|
dispose(): void;
|
|
102
|
+
/**
|
|
103
|
+
* Route a transport-level error onto `errors$`. Used by sibling adapters
|
|
104
|
+
* (e.g. `HttpContentTransport`'s XHR upload path) that don't go through
|
|
105
|
+
* the `ky` `beforeError` hook and need to surface failures on the same
|
|
106
|
+
* stream the rest of the transport publishes to.
|
|
107
|
+
*/
|
|
108
|
+
pushError(error: SemiontError): void;
|
|
96
109
|
private authHeaders;
|
|
97
110
|
authenticatePassword(email: Email, password: string): Promise<AuthResponse>;
|
|
98
111
|
authenticateGoogle(credential: GoogleCredential): Promise<AuthResponse>;
|
|
@@ -110,13 +123,20 @@ declare class HttpTransport implements ITransport {
|
|
|
110
123
|
getUserStats(): Promise<AdminUserStatsResponse>;
|
|
111
124
|
updateUser(id: UserDID, data: UpdateUserRequest): Promise<UpdateUserResponse>;
|
|
112
125
|
getOAuthConfig(): Promise<OAuthConfigResponse>;
|
|
113
|
-
backupKnowledgeBase(): Promise<
|
|
114
|
-
restoreKnowledgeBase(file: File
|
|
126
|
+
backupKnowledgeBase(): Promise<BackendDownload>;
|
|
127
|
+
restoreKnowledgeBase(file: File): Observable<ProgressEvent>;
|
|
115
128
|
exportKnowledgeBase(params?: {
|
|
116
129
|
includeArchived?: boolean;
|
|
117
|
-
}): Promise<
|
|
118
|
-
importKnowledgeBase(file: File
|
|
119
|
-
|
|
130
|
+
}): Promise<BackendDownload>;
|
|
131
|
+
importKnowledgeBase(file: File): Observable<ProgressEvent>;
|
|
132
|
+
/**
|
|
133
|
+
* POST a file to a server-sent-events endpoint and surface each `data:`
|
|
134
|
+
* frame as an Observable emission. Completes when the stream closes;
|
|
135
|
+
* errors if the request itself fails or the SSE stream is aborted.
|
|
136
|
+
* The returned Observable is cold — the POST happens on subscribe and
|
|
137
|
+
* is aborted via `AbortController` on unsubscribe.
|
|
138
|
+
*/
|
|
139
|
+
private sseProgressStream;
|
|
120
140
|
healthCheck(): Promise<HealthCheckResponse>;
|
|
121
141
|
getStatus(): Promise<StatusResponse>;
|
|
122
142
|
/**
|
|
@@ -142,14 +162,45 @@ declare class HttpTransport implements ITransport {
|
|
|
142
162
|
* different backpressure and streaming characteristics than typed command
|
|
143
163
|
* payloads. Uses the HttpTransport's underlying ky instance + token, so
|
|
144
164
|
* retries, logging, and auth behave identically to the rest of the wire.
|
|
165
|
+
*
|
|
166
|
+
* Two `putBinary` paths live side by side, selected by runtime
|
|
167
|
+
* environment + caller intent:
|
|
168
|
+
* - **ky path (default + Node)** — the original `ky.post(...)` path.
|
|
169
|
+
* Keeps retry-with-refresh, beforeError → APIError, observability
|
|
170
|
+
* spans intact. Hits when no `onProgress`/`signal` is passed, OR
|
|
171
|
+
* when `XMLHttpRequest` isn't available in the runtime (Node
|
|
172
|
+
* workers, the CLI). On Node-side `signal`-aborts: the in-flight
|
|
173
|
+
* `fetch` continues in the background and the `cancelled` flag in
|
|
174
|
+
* `yield.resource` suppresses the resolve/reject callbacks.
|
|
175
|
+
* - **XHR path (browsers with `onProgress` or `signal`)** — hand-rolled
|
|
176
|
+
* because `ky` wraps `fetch` which can't observe upload byte-
|
|
177
|
+
* progress today (`Request({ duplex: 'half' })` is the long-term
|
|
178
|
+
* direction; not yet widely available across the webviews this
|
|
179
|
+
* codepath needs to run in). Threads auth + traceparent headers,
|
|
180
|
+
* emits `onProgress` from `xhr.upload.onprogress`, supports
|
|
181
|
+
* cancellation via the `signal` option (calling `xhr.abort()`),
|
|
182
|
+
* and routes failures onto the same `transport.errors$` stream
|
|
183
|
+
* the ky path uses.
|
|
184
|
+
*
|
|
185
|
+
* The runtime check on `XMLHttpRequest` is the load-bearing seam: a
|
|
186
|
+
* Node worker calling `client.yield.resource(...)` (which always passes
|
|
187
|
+
* a `signal` for unsubscribe-aborts) must NOT take the XHR path —
|
|
188
|
+
* `XMLHttpRequest` is undefined and the upload throws synchronously.
|
|
189
|
+
* Browsers always have it; Node does not.
|
|
190
|
+
*
|
|
191
|
+
* v1 limitation: the XHR path does NOT auto-refresh on 401. Mitigation:
|
|
192
|
+
* the session's proactive refresh fires before token expiry, so an
|
|
193
|
+
* upload that *starts* with a fresh token usually completes. An upload
|
|
194
|
+
* spanning the narrow window between expiry and proactive-refresh would
|
|
195
|
+
* fail; the existing `errors$` → modal path surfaces it as session-
|
|
196
|
+
* expired. If retry-with-refresh on the upload path becomes a real
|
|
197
|
+
* complaint, wire a manual retry loop here that reads `token$` afresh.
|
|
145
198
|
*/
|
|
146
199
|
|
|
147
200
|
declare class HttpContentTransport implements IContentTransport {
|
|
148
201
|
private readonly transport;
|
|
149
202
|
constructor(transport: HttpTransport);
|
|
150
|
-
putBinary(request: PutBinaryRequest, options?: {
|
|
151
|
-
auth?: AccessToken;
|
|
152
|
-
}): Promise<{
|
|
203
|
+
putBinary(request: PutBinaryRequest, options?: PutBinaryOptions): Promise<{
|
|
153
204
|
resourceId: ResourceId;
|
|
154
205
|
}>;
|
|
155
206
|
getBinary(resourceId: ResourceId, options?: {
|
|
@@ -171,4 +222,4 @@ declare class HttpContentTransport implements IContentTransport {
|
|
|
171
222
|
private requestHeaders;
|
|
172
223
|
}
|
|
173
224
|
|
|
174
|
-
export { APIError, type
|
|
225
|
+
export { APIError, type ActorStateUnit, type ActorStateUnitOptions, type BusEvent, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport, type HttpTransportConfig, type TokenRefresher, createActorStateUnit };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import ky, { HTTPError } from 'ky';
|
|
2
|
-
import { Subject, BehaviorSubject } from 'rxjs';
|
|
2
|
+
import { Subject, BehaviorSubject, Observable } from 'rxjs';
|
|
3
3
|
import { RESOURCE_BROADCAST_TYPES, PERSISTED_EVENT_TYPES, SemiontError, BRIDGED_CHANNELS, busLog } from '@semiont/core';
|
|
4
4
|
import { getActiveTraceparent, recordBusEmit, withSpan, SpanKind, extractTraceparent, withTraceparent } from '@semiont/observability';
|
|
5
5
|
import { share, filter, map } from 'rxjs/operators';
|
|
@@ -14,7 +14,7 @@ var ALLOWED_TRANSITIONS = {
|
|
|
14
14
|
degraded: ["connecting", "closed"],
|
|
15
15
|
closed: []
|
|
16
16
|
};
|
|
17
|
-
function
|
|
17
|
+
function createActorStateUnit(options) {
|
|
18
18
|
const { baseUrl, token: tokenOrGetter, channels: initialChannels, scope: initialScope, reconnectMs = 5e3 } = options;
|
|
19
19
|
const getToken = typeof tokenOrGetter === "function" ? tokenOrGetter : () => tokenOrGetter;
|
|
20
20
|
const globalChannels = new Set(initialChannels);
|
|
@@ -267,17 +267,27 @@ function createActorVM(options) {
|
|
|
267
267
|
};
|
|
268
268
|
}
|
|
269
269
|
var RESOURCE_SCOPED_CHANNELS = [
|
|
270
|
-
...PERSISTED_EVENT_TYPES.filter((t) => t !== "
|
|
270
|
+
...PERSISTED_EVENT_TYPES.filter((t) => t !== "frame:entity-type-added"),
|
|
271
271
|
...RESOURCE_BROADCAST_TYPES
|
|
272
272
|
];
|
|
273
|
+
function responseToDownload(response) {
|
|
274
|
+
const contentType = response.headers.get("Content-Type") ?? "application/octet-stream";
|
|
275
|
+
const contentDisposition = response.headers.get("Content-Disposition");
|
|
276
|
+
const filename = contentDisposition?.match(/filename="(.+?)"/)?.[1];
|
|
277
|
+
return {
|
|
278
|
+
stream: response.body,
|
|
279
|
+
contentType,
|
|
280
|
+
...filename ? { filename } : {}
|
|
281
|
+
};
|
|
282
|
+
}
|
|
273
283
|
function classifyApiCode(status) {
|
|
274
|
-
if (status === 400) return "
|
|
275
|
-
if (status === 401) return "
|
|
276
|
-
if (status === 403) return "
|
|
277
|
-
if (status === 404) return "
|
|
278
|
-
if (status === 409) return "
|
|
279
|
-
if (status >= 500) return "
|
|
280
|
-
return "
|
|
284
|
+
if (status === 400) return "bad-request";
|
|
285
|
+
if (status === 401) return "unauthorized";
|
|
286
|
+
if (status === 403) return "forbidden";
|
|
287
|
+
if (status === 404) return "not-found";
|
|
288
|
+
if (status === 409) return "conflict";
|
|
289
|
+
if (status >= 500) return "unavailable";
|
|
290
|
+
return "error";
|
|
281
291
|
}
|
|
282
292
|
var APIError = class extends SemiontError {
|
|
283
293
|
status;
|
|
@@ -294,6 +304,13 @@ var HttpTransport = class {
|
|
|
294
304
|
http;
|
|
295
305
|
token$;
|
|
296
306
|
logger;
|
|
307
|
+
errorsSubject = new Subject();
|
|
308
|
+
/**
|
|
309
|
+
* Stream of `APIError` instances surfaced from any HTTP request just
|
|
310
|
+
* before the transport throws to the caller. Satisfies the `ITransport`
|
|
311
|
+
* `errors$` contract — see `@semiont/core/transport.ts`.
|
|
312
|
+
*/
|
|
313
|
+
errors$ = this.errorsSubject.asObservable();
|
|
297
314
|
_actor = null;
|
|
298
315
|
_actorStarted = false;
|
|
299
316
|
disposed = false;
|
|
@@ -372,12 +389,14 @@ var HttpTransport = class {
|
|
|
372
389
|
error: body.message || `HTTP ${response.status}: ${response.statusText}`
|
|
373
390
|
});
|
|
374
391
|
}
|
|
375
|
-
|
|
392
|
+
const apiError = new APIError(
|
|
376
393
|
body.message || `HTTP ${response.status}: ${response.statusText}`,
|
|
377
394
|
response.status,
|
|
378
395
|
response.statusText,
|
|
379
396
|
body
|
|
380
397
|
);
|
|
398
|
+
this.errorsSubject.next(apiError);
|
|
399
|
+
throw apiError;
|
|
381
400
|
}
|
|
382
401
|
return error;
|
|
383
402
|
}
|
|
@@ -394,12 +413,12 @@ var HttpTransport = class {
|
|
|
394
413
|
// ── Lazy actor construction + per-channel fan-in to bridges ───────────
|
|
395
414
|
//
|
|
396
415
|
// `actor` is exposed so the legacy `SemiontClient` can keep `.actor`
|
|
397
|
-
// pointing at the same
|
|
416
|
+
// pointing at the same ActorStateUnit during the transport-abstraction
|
|
398
417
|
// migration. Once SemiontClient is removed, this should be made
|
|
399
418
|
// private again — external callers should use emit/on/stream/state$.
|
|
400
419
|
get actor() {
|
|
401
420
|
if (!this._actor) {
|
|
402
|
-
this._actor =
|
|
421
|
+
this._actor = createActorStateUnit({
|
|
403
422
|
baseUrl: this.baseUrl,
|
|
404
423
|
token: () => this.token$.getValue() ?? "",
|
|
405
424
|
channels: [...BRIDGED_CHANNELS]
|
|
@@ -510,6 +529,17 @@ var HttpTransport = class {
|
|
|
510
529
|
this._actor.dispose();
|
|
511
530
|
this._actor = null;
|
|
512
531
|
}
|
|
532
|
+
this.errorsSubject.complete();
|
|
533
|
+
}
|
|
534
|
+
/**
|
|
535
|
+
* Route a transport-level error onto `errors$`. Used by sibling adapters
|
|
536
|
+
* (e.g. `HttpContentTransport`'s XHR upload path) that don't go through
|
|
537
|
+
* the `ky` `beforeError` hook and need to surface failures on the same
|
|
538
|
+
* stream the rest of the transport publishes to.
|
|
539
|
+
*/
|
|
540
|
+
pushError(error) {
|
|
541
|
+
if (this.disposed) return;
|
|
542
|
+
this.errorsSubject.next(error);
|
|
513
543
|
}
|
|
514
544
|
// ── Auth ──────────────────────────────────────────────────────────────
|
|
515
545
|
authHeaders() {
|
|
@@ -584,57 +614,67 @@ var HttpTransport = class {
|
|
|
584
614
|
}
|
|
585
615
|
// ── Exchange (backup/restore/export/import) ───────────────────────────
|
|
586
616
|
async backupKnowledgeBase() {
|
|
587
|
-
|
|
617
|
+
const response = await this.http.post(`${this.baseUrl}/api/admin/exchange/backup`, {
|
|
588
618
|
headers: this.authHeaders()
|
|
589
619
|
});
|
|
620
|
+
return responseToDownload(response);
|
|
590
621
|
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
formData.append("file", file);
|
|
594
|
-
const response = await this.http.post(`${this.baseUrl}/api/admin/exchange/restore`, {
|
|
595
|
-
body: formData,
|
|
596
|
-
headers: this.authHeaders()
|
|
597
|
-
});
|
|
598
|
-
return this.parseSSEStream(response, onProgress);
|
|
622
|
+
restoreKnowledgeBase(file) {
|
|
623
|
+
return this.sseProgressStream(`${this.baseUrl}/api/admin/exchange/restore`, file);
|
|
599
624
|
}
|
|
600
625
|
async exportKnowledgeBase(params) {
|
|
601
626
|
const searchParams = params?.includeArchived ? new URLSearchParams({ includeArchived: "true" }) : void 0;
|
|
602
|
-
|
|
627
|
+
const response = await this.http.post(`${this.baseUrl}/api/moderate/exchange/export`, {
|
|
603
628
|
headers: this.authHeaders(),
|
|
604
629
|
...searchParams ? { searchParams } : {}
|
|
605
630
|
});
|
|
631
|
+
return responseToDownload(response);
|
|
606
632
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
const
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
+
importKnowledgeBase(file) {
|
|
634
|
+
return this.sseProgressStream(`${this.baseUrl}/api/moderate/exchange/import`, file);
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* POST a file to a server-sent-events endpoint and surface each `data:`
|
|
638
|
+
* frame as an Observable emission. Completes when the stream closes;
|
|
639
|
+
* errors if the request itself fails or the SSE stream is aborted.
|
|
640
|
+
* The returned Observable is cold — the POST happens on subscribe and
|
|
641
|
+
* is aborted via `AbortController` on unsubscribe.
|
|
642
|
+
*/
|
|
643
|
+
sseProgressStream(url, file) {
|
|
644
|
+
return new Observable((subscriber) => {
|
|
645
|
+
const ctrl = new AbortController();
|
|
646
|
+
const formData = new FormData();
|
|
647
|
+
formData.append("file", file);
|
|
648
|
+
(async () => {
|
|
649
|
+
try {
|
|
650
|
+
const response = await this.http.post(url, {
|
|
651
|
+
body: formData,
|
|
652
|
+
headers: this.authHeaders(),
|
|
653
|
+
signal: ctrl.signal
|
|
654
|
+
});
|
|
655
|
+
const reader = response.body.getReader();
|
|
656
|
+
const decoder = new TextDecoder();
|
|
657
|
+
let buffer = "";
|
|
658
|
+
while (!subscriber.closed) {
|
|
659
|
+
const { done, value } = await reader.read();
|
|
660
|
+
if (done) break;
|
|
661
|
+
buffer += decoder.decode(value, { stream: true });
|
|
662
|
+
const lines = buffer.split("\n");
|
|
663
|
+
buffer = lines.pop();
|
|
664
|
+
for (const line of lines) {
|
|
665
|
+
if (line.startsWith("data: ")) {
|
|
666
|
+
const event = JSON.parse(line.slice(6));
|
|
667
|
+
subscriber.next(event);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
633
670
|
}
|
|
671
|
+
subscriber.complete();
|
|
672
|
+
} catch (err) {
|
|
673
|
+
if (!subscriber.closed) subscriber.error(err);
|
|
634
674
|
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
|
|
675
|
+
})();
|
|
676
|
+
return () => ctrl.abort();
|
|
677
|
+
});
|
|
638
678
|
}
|
|
639
679
|
// ── System status ─────────────────────────────────────────────────────
|
|
640
680
|
async healthCheck() {
|
|
@@ -682,31 +722,22 @@ var HttpContentTransport = class {
|
|
|
682
722
|
return withSpan(
|
|
683
723
|
"content.put",
|
|
684
724
|
async () => {
|
|
685
|
-
const formData =
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
if (request.entityTypes && request.entityTypes.length > 0) {
|
|
698
|
-
formData.append("entityTypes", JSON.stringify(request.entityTypes));
|
|
725
|
+
const formData = buildFormData(request);
|
|
726
|
+
const headers = this.requestHeaders(options?.auth);
|
|
727
|
+
const xhrAvailable = typeof XMLHttpRequest !== "undefined";
|
|
728
|
+
if (xhrAvailable && (options?.onProgress || options?.signal)) {
|
|
729
|
+
return uploadViaXhr({
|
|
730
|
+
url: `${this.transport.baseUrl}/resources`,
|
|
731
|
+
formData,
|
|
732
|
+
headers,
|
|
733
|
+
onProgress: options.onProgress,
|
|
734
|
+
signal: options.signal,
|
|
735
|
+
onApiError: (err) => this.transport.pushError(err)
|
|
736
|
+
});
|
|
699
737
|
}
|
|
700
|
-
if (request.language) formData.append("language", request.language);
|
|
701
|
-
if (request.creationMethod) formData.append("creationMethod", String(request.creationMethod));
|
|
702
|
-
if (request.sourceAnnotationId) formData.append("sourceAnnotationId", String(request.sourceAnnotationId));
|
|
703
|
-
if (request.sourceResourceId) formData.append("sourceResourceId", String(request.sourceResourceId));
|
|
704
|
-
if (request.generationPrompt) formData.append("generationPrompt", request.generationPrompt);
|
|
705
|
-
if (request.generator) formData.append("generator", JSON.stringify(request.generator));
|
|
706
|
-
if (request.isDraft !== void 0) formData.append("isDraft", String(request.isDraft));
|
|
707
738
|
const result = await this.transport.rawHttp.post(`${this.transport.baseUrl}/resources`, {
|
|
708
739
|
body: formData,
|
|
709
|
-
headers
|
|
740
|
+
headers
|
|
710
741
|
}).json();
|
|
711
742
|
return { resourceId: result.resourceId };
|
|
712
743
|
},
|
|
@@ -774,7 +805,101 @@ var HttpContentTransport = class {
|
|
|
774
805
|
return headers;
|
|
775
806
|
}
|
|
776
807
|
};
|
|
808
|
+
function buildFormData(request) {
|
|
809
|
+
const formData = new FormData();
|
|
810
|
+
formData.append("name", request.name);
|
|
811
|
+
formData.append("format", request.format);
|
|
812
|
+
formData.append("storageUri", request.storageUri);
|
|
813
|
+
if (request.file instanceof File) {
|
|
814
|
+
formData.append("file", request.file);
|
|
815
|
+
} else if (typeof Buffer !== "undefined" && Buffer.isBuffer(request.file)) {
|
|
816
|
+
const blob = new Blob([new Uint8Array(request.file)], { type: request.format });
|
|
817
|
+
formData.append("file", blob, request.name);
|
|
818
|
+
} else {
|
|
819
|
+
throw new Error("file must be a File or Buffer");
|
|
820
|
+
}
|
|
821
|
+
if (request.entityTypes && request.entityTypes.length > 0) {
|
|
822
|
+
formData.append("entityTypes", JSON.stringify(request.entityTypes));
|
|
823
|
+
}
|
|
824
|
+
if (request.language) formData.append("language", request.language);
|
|
825
|
+
if (request.creationMethod) formData.append("creationMethod", String(request.creationMethod));
|
|
826
|
+
if (request.sourceAnnotationId) formData.append("sourceAnnotationId", String(request.sourceAnnotationId));
|
|
827
|
+
if (request.sourceResourceId) formData.append("sourceResourceId", String(request.sourceResourceId));
|
|
828
|
+
if (request.generationPrompt) formData.append("generationPrompt", request.generationPrompt);
|
|
829
|
+
if (request.generator) formData.append("generator", JSON.stringify(request.generator));
|
|
830
|
+
if (request.isDraft !== void 0) formData.append("isDraft", String(request.isDraft));
|
|
831
|
+
return formData;
|
|
832
|
+
}
|
|
833
|
+
function uploadViaXhr(opts) {
|
|
834
|
+
const { url, formData, headers, onProgress, signal, onApiError } = opts;
|
|
835
|
+
return new Promise((resolve, reject) => {
|
|
836
|
+
const xhr = new XMLHttpRequest();
|
|
837
|
+
if (signal?.aborted) {
|
|
838
|
+
const err = new APIError("Upload aborted", 0, "aborted");
|
|
839
|
+
onApiError(err);
|
|
840
|
+
reject(err);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
xhr.open("POST", url);
|
|
844
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
845
|
+
xhr.setRequestHeader(name, value);
|
|
846
|
+
}
|
|
847
|
+
if (onProgress) {
|
|
848
|
+
xhr.upload.onprogress = (e) => {
|
|
849
|
+
const totalBytes = e.lengthComputable ? e.total : 0;
|
|
850
|
+
onProgress({ bytesUploaded: e.loaded, totalBytes });
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
xhr.onload = () => {
|
|
854
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
855
|
+
try {
|
|
856
|
+
const body2 = JSON.parse(xhr.responseText);
|
|
857
|
+
resolve({ resourceId: body2.resourceId });
|
|
858
|
+
} catch (parseErr) {
|
|
859
|
+
const err2 = new APIError(
|
|
860
|
+
`Upload succeeded but response was not valid JSON: ${parseErr.message}`,
|
|
861
|
+
xhr.status,
|
|
862
|
+
xhr.statusText,
|
|
863
|
+
xhr.responseText
|
|
864
|
+
);
|
|
865
|
+
onApiError(err2);
|
|
866
|
+
reject(err2);
|
|
867
|
+
}
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
let body = xhr.responseText;
|
|
871
|
+
try {
|
|
872
|
+
body = JSON.parse(xhr.responseText);
|
|
873
|
+
} catch {
|
|
874
|
+
}
|
|
875
|
+
const message = body && typeof body === "object" && "message" in body && typeof body.message === "string" ? body.message : `HTTP ${xhr.status}: ${xhr.statusText}`;
|
|
876
|
+
const err = new APIError(message, xhr.status, xhr.statusText, body);
|
|
877
|
+
onApiError(err);
|
|
878
|
+
reject(err);
|
|
879
|
+
};
|
|
880
|
+
xhr.onerror = () => {
|
|
881
|
+
const err = new APIError("Network error during upload", 0, "network-error");
|
|
882
|
+
onApiError(err);
|
|
883
|
+
reject(err);
|
|
884
|
+
};
|
|
885
|
+
xhr.ontimeout = () => {
|
|
886
|
+
const err = new APIError("Upload timed out", 0, "timeout");
|
|
887
|
+
onApiError(err);
|
|
888
|
+
reject(err);
|
|
889
|
+
};
|
|
890
|
+
xhr.onabort = () => {
|
|
891
|
+
const err = new APIError("Upload aborted", 0, "aborted");
|
|
892
|
+
onApiError(err);
|
|
893
|
+
reject(err);
|
|
894
|
+
};
|
|
895
|
+
if (signal) {
|
|
896
|
+
const onAbort = () => xhr.abort();
|
|
897
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
898
|
+
}
|
|
899
|
+
xhr.send(formData);
|
|
900
|
+
});
|
|
901
|
+
}
|
|
777
902
|
|
|
778
|
-
export { APIError, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport,
|
|
903
|
+
export { APIError, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport, createActorStateUnit };
|
|
779
904
|
//# sourceMappingURL=index.js.map
|
|
780
905
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/transport/actor-vm.ts","../src/transport/http-transport.ts","../src/transport/http-content-transport.ts"],"names":["BehaviorSubject","busLog","withSpan","SpanKind","getActiveTraceparent"],"mappings":"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","file":"index.js","sourcesContent":["import { BehaviorSubject, Observable, Subject } from 'rxjs';\nimport { filter, map, share } from 'rxjs/operators';\nimport { busLog, type ConnectionState } from '@semiont/core';\nimport {\n SpanKind,\n extractTraceparent,\n getActiveTraceparent,\n withSpan,\n withTraceparent,\n} from '@semiont/observability';\n\n/** Minimal ViewModel surface — anything with a `dispose()` method. */\ninterface ViewModel {\n dispose(): void;\n}\n\nexport type { ConnectionState };\n\nexport interface BusEvent {\n channel: string;\n payload: Record<string, unknown>;\n scope?: string;\n}\n\nexport interface ActorVMOptions {\n baseUrl: string;\n token: string | (() => string);\n channels: string[];\n scope?: string;\n reconnectMs?: number;\n}\n\n/** Time in the `reconnecting` state before transitioning to `degraded`. */\nexport const DEGRADED_THRESHOLD_MS = 3_000;\n\nexport interface ActorVM extends ViewModel {\n on$<T = Record<string, unknown>>(channel: string): Observable<T>;\n emit(channel: string, payload: Record<string, unknown>, emitScope?: string): Promise<void>;\n state$: Observable<ConnectionState>;\n addChannels(channels: string[], scope?: string): void;\n removeChannels(channels: 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: ['connecting', 'closed'],\n closed: [],\n};\n\nexport function createActorVM(options: ActorVMOptions): ActorVM {\n const { baseUrl, token: tokenOrGetter, channels: initialChannels, scope: initialScope, reconnectMs = 5_000 } = options;\n const getToken = typeof tokenOrGetter === 'function' ? tokenOrGetter : () => tokenOrGetter;\n\n const globalChannels = new Set(initialChannels);\n const scopedChannels = new Set<string>();\n let activeScope = initialScope;\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`. Throws on invalid transitions.\n * The throw is deliberate — a bad transition means a bug in the\n * reconnect loop; silent correction would hide it. The reconnect\n * timer logic is responsible for ensuring we only transition\n * between valid states.\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 throw new Error(`Invalid connection state transition: ${currentState} → ${next}`);\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 * `Last-Event-ID` of the most recently delivered SSE event from the\n * server. Sent as a request header on each connect so the server can\n * replay persisted events missed during the disconnect (see\n * `apps/backend/src/routes/bus.ts` subscribe handler). Initialised\n * `null` — fresh connections send no header.\n *\n * We track both persisted (`p-*`) and ephemeral (`e-*`) ids. The server\n * treats ephemeral ids as \"no resumption context\" and responds live-\n * only; persisted ids drive replay.\n */\n let lastEventId: string | null = null;\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 };\n\n const connect = async () => {\n // Transition to `connecting` from whichever reconnect-ish state\n // we're currently in (`initial`, `reconnecting`, `degraded`).\n transition('connecting');\n\n // Abort every previous in-flight fetch before starting a new one.\n // This closes the orphan-stream leak described above.\n for (const c of inflightControllers) {\n try { c.abort(); } catch { /* noop */ }\n }\n inflightControllers.clear();\n\n const params = new URLSearchParams();\n for (const ch of globalChannels) {\n params.append('channel', ch);\n }\n if (activeScope && scopedChannels.size > 0) {\n params.append('scope', activeScope);\n for (const ch of scopedChannels) {\n params.append('scoped', ch);\n }\n }\n const url = `${baseUrl}/bus/subscribe?${params.toString()}`;\n\n const controller = new AbortController();\n inflightControllers.add(controller);\n\n try {\n const headers: Record<string, string> = { Authorization: `Bearer ${getToken()}` };\n if (lastEventId) headers['Last-Event-ID'] = lastEventId;\n const response = await fetch(url, { headers, signal: controller.signal });\n\n if (!response.ok || !response.body) {\n throw new Error(`SSE connect failed: ${response.status}`);\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 if (currentEvent === 'bus-event' && currentData) {\n if (currentId !== undefined) lastEventId = currentId;\n const parsed = JSON.parse(currentData) as BusEvent;\n busLog('RECV', parsed.channel, parsed.payload, parsed.scope);\n // Tier 2: lift trace context off the SSE payload (the\n // backend'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 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 }\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`.\n if (running) {\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 disconnect();\n connect();\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 let reconnectTimer2: ReturnType<typeof setTimeout> | null = null;\n const RECONNECT_DEBOUNCE_MS = 100;\n const scheduleReconnect = () => {\n if (reconnectTimer2) clearTimeout(reconnectTimer2);\n reconnectTimer2 = setTimeout(() => {\n reconnectTimer2 = null;\n reconnect();\n }, RECONNECT_DEBOUNCE_MS);\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<void> => {\n // EMIT logging + bus.emit span live at the transport contract layer\n // (`HttpTransport.emit`). ActorVM is plumbing. We do propagate the\n // active span's W3C traceparent on the outbound POST so the backend\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 await fetch(`${baseUrl}/bus/emit`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n },\n\n state$: state$.asObservable(),\n\n addChannels: (channels: string[], scope?: string) => {\n let changed = false;\n if (scope !== undefined) {\n for (const ch of channels) {\n if (!scopedChannels.has(ch)) { scopedChannels.add(ch); changed = true; }\n }\n if (scope !== activeScope) { activeScope = scope; changed = true; }\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[]) => {\n let changed = false;\n for (const ch of channels) {\n if (scopedChannels.delete(ch)) changed = true;\n if (globalChannels.delete(ch)) changed = true;\n }\n if (scopedChannels.size === 0) activeScope = undefined;\n if (changed) scheduleReconnect();\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 (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 (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, type Observable, type Subscription } 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 { createActorVM, type ActorVM } from './actor-vm';\nimport type {\n ConnectionState,\n ITransport,\n HealthCheckResponse,\n StatusResponse,\n UserResponse,\n UpdateUserRequest,\n UpdateUserResponse,\n ListUsersResponse,\n ProgressCallback,\n ProgressEvent,\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\nconst RESOURCE_SCOPED_CHANNELS = [\n ...PERSISTED_EVENT_TYPES.filter((t) => t !== 'mark:entity-type-added'),\n ...RESOURCE_BROADCAST_TYPES,\n];\n\nexport type APIErrorCode =\n | 'api.bad-request'\n | 'api.unauthorized'\n | 'api.forbidden'\n | 'api.not-found'\n | 'api.conflict'\n | 'api.server-error'\n | 'api.error';\n\nfunction classifyApiCode(status: number): APIErrorCode {\n if (status === 400) return 'api.bad-request';\n if (status === 401) return 'api.unauthorized';\n if (status === 403) return 'api.forbidden';\n if (status === 404) return 'api.not-found';\n if (status === 409) return 'api.conflict';\n if (status >= 500) return 'api.server-error';\n return 'api.error';\n}\n\nexport class APIError extends SemiontError {\n declare code: APIErrorCode;\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\nexport class HttpTransport implements ITransport {\n readonly baseUrl: BaseUrl;\n private readonly http: KyInstance;\n private readonly token$: BehaviorSubject<AccessToken | null>;\n private readonly logger?: Logger;\n\n private _actor: ActorVM | null = null;\n private _actorStarted = false;\n private disposed = false;\n\n private activeResource: {\n resourceId: ResourceId;\n refCount: number;\n bridgeSubs: Subscription[];\n } | null = null;\n\n /** Buses we've been asked to bridge wire events into. */\n private readonly bridges: EventBus[] = [];\n\n constructor(config: HttpTransportConfig) {\n const { baseUrl, timeout = 30000, retry = 2, logger, tokenRefresher } = 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 credentials: 'include',\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, _options, 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 (error) => {\n const { response, request } = error;\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 throw new APIError(\n body.message || `HTTP ${response.status}: ${response.statusText}`,\n response.status,\n response.statusText,\n body,\n );\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 ActorVM 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(): ActorVM {\n if (!this._actor) {\n this._actor = createActorVM({\n baseUrl: this.baseUrl,\n token: () => this.token$.getValue() ?? '',\n channels: [...BRIDGED_CHANNELS],\n });\n for (const channel of BRIDGED_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<void> {\n busLog('EMIT', channel as string, payload, resourceScope as string | undefined);\n recordBusEmit(channel as string, resourceScope as string | undefined);\n await withSpan(\n `bus.emit:${channel as string}`,\n async () => {\n if (resourceScope !== undefined) {\n await this.actor.emit(\n channel as string,\n payload as unknown as Record<string, unknown>,\n resourceScope as string,\n );\n } else {\n await this.actor.emit(\n channel as string,\n payload as unknown as Record<string, unknown>,\n );\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 if (this.activeResource) {\n if (this.activeResource.resourceId !== resourceId) {\n throw new Error(\n `HttpTransport already subscribed to resource ${this.activeResource.resourceId}; ` +\n `call the unsubscribe returned from the previous subscribeToResource before subscribing to ${resourceId}.`,\n );\n }\n this.activeResource.refCount++;\n return this.makeUnsubscriber();\n }\n\n this.actor.addChannels([...RESOURCE_SCOPED_CHANNELS], resourceId as string);\n\n const bridgeSubs: Subscription[] = [];\n for (const channel of RESOURCE_SCOPED_CHANNELS) {\n bridgeSubs.push(\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\n this.activeResource = { resourceId, refCount: 1, bridgeSubs };\n return this.makeUnsubscriber();\n }\n\n private makeUnsubscriber(): () => void {\n let called = false;\n return () => {\n if (called) return;\n called = true;\n if (!this.activeResource) return;\n this.activeResource.refCount--;\n if (this.activeResource.refCount > 0) return;\n for (const sub of this.activeResource.bridgeSubs) sub.unsubscribe();\n this.actor.removeChannels([...RESOURCE_SCOPED_CHANNELS]);\n this.activeResource = null;\n };\n }\n\n get state$(): Observable<ConnectionState> {\n return this.actor.state$;\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n if (this.activeResource) {\n for (const sub of this.activeResource.bridgeSubs) sub.unsubscribe();\n this.activeResource = null;\n }\n if (this._actor) {\n this._actor.dispose();\n this._actor = null;\n }\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 generateMcpToken(): Promise<{ token: string }> {\n return this.http.post(`${this.baseUrl}/api/tokens/mcp-generate`, {\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 // ── Exchange (backup/restore/export/import) ───────────────────────────\n\n async backupKnowledgeBase(): Promise<Response> {\n return this.http.post(`${this.baseUrl}/api/admin/exchange/backup`, {\n headers: this.authHeaders(),\n });\n }\n\n async restoreKnowledgeBase(\n file: File,\n onProgress?: ProgressCallback,\n ): Promise<ProgressEvent> {\n const formData = new FormData();\n formData.append('file', file);\n const response = await this.http.post(`${this.baseUrl}/api/admin/exchange/restore`, {\n body: formData,\n headers: this.authHeaders(),\n });\n return this.parseSSEStream(response, onProgress);\n }\n\n async exportKnowledgeBase(params?: { includeArchived?: boolean }): Promise<Response> {\n const searchParams = params?.includeArchived ? new URLSearchParams({ includeArchived: 'true' }) : undefined;\n return this.http.post(`${this.baseUrl}/api/moderate/exchange/export`, {\n headers: this.authHeaders(),\n ...(searchParams ? { searchParams } : {}),\n });\n }\n\n async importKnowledgeBase(\n file: File,\n onProgress?: ProgressCallback,\n ): Promise<ProgressEvent> {\n const formData = new FormData();\n formData.append('file', file);\n const response = await this.http.post(`${this.baseUrl}/api/moderate/exchange/import`, {\n body: formData,\n headers: this.authHeaders(),\n });\n return this.parseSSEStream(response, onProgress);\n }\n\n private async parseSSEStream(\n response: Response,\n onProgress?: ProgressCallback,\n ): Promise<ProgressEvent> {\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let finalResult: ProgressEvent = { phase: 'unknown' };\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop()!;\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const event = JSON.parse(line.slice(6));\n onProgress?.(event);\n if (event.phase === 'complete' || event.phase === 'error' || event.phase === 'failed') {\n finalResult = event;\n }\n }\n }\n }\n\n return finalResult;\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\nimport type { AccessToken, ContentFormat, ResourceId } from '@semiont/core';\nimport { busLog } from '@semiont/core';\nimport { SpanKind, getActiveTraceparent, withSpan } from '@semiont/observability';\nimport type { HttpTransport } from './http-transport';\nimport type { IContentTransport, PutBinaryRequest } from '@semiont/core';\n\nexport class HttpContentTransport implements IContentTransport {\n constructor(private readonly transport: HttpTransport) {}\n\n async putBinary(\n request: PutBinaryRequest,\n options?: { auth?: AccessToken },\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 = 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 (Buffer.isBuffer(request.file)) {\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.creationMethod) formData.append('creationMethod', String(request.creationMethod));\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.isDraft !== undefined) formData.append('isDraft', String(request.isDraft));\n\n const result = await this.transport.rawHttp\n .post(`${this.transport.baseUrl}/resources`, {\n body: formData,\n headers: this.requestHeaders(options?.auth),\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?: { accept?: ContentFormat | string; auth?: AccessToken },\n ): Promise<{ data: ArrayBuffer; contentType: string }> {\n busLog('GET', 'content', { resourceId, accept: options?.accept });\n return withSpan(\n 'content.get',\n async () => {\n const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/resources/${resourceId}`, {\n headers: {\n Accept: options?.accept ?? 'text/plain',\n ...this.requestHeaders(options?.auth),\n },\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?: { accept?: ContentFormat | string; auth?: AccessToken },\n ): Promise<{ stream: ReadableStream<Uint8Array>; contentType: string }> {\n busLog('GET', 'content', { resourceId, accept: options?.accept, stream: true });\n return withSpan(\n 'content.get',\n async () => {\n const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/resources/${resourceId}`, {\n headers: {\n Accept: options?.accept ?? 'text/plain',\n ...this.requestHeaders(options?.auth),\n },\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 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"]}
|
|
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","Observable","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, type ConnectionState } from '@semiont/core';\nimport {\n SpanKind,\n extractTraceparent,\n getActiveTraceparent,\n withSpan,\n withTraceparent,\n} from '@semiont/observability';\n\n/** Minimal StateUnit surface — anything with a `dispose()` method. */\ninterface StateUnit {\n dispose(): void;\n}\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 scope?: string;\n reconnectMs?: number;\n}\n\n/** Time in the `reconnecting` state before transitioning to `degraded`. */\nexport const DEGRADED_THRESHOLD_MS = 3_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<void>;\n state$: Observable<ConnectionState>;\n addChannels(channels: string[], scope?: string): void;\n removeChannels(channels: 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: ['connecting', 'closed'],\n closed: [],\n};\n\nexport function createActorStateUnit(options: ActorStateUnitOptions): ActorStateUnit {\n const { baseUrl, token: tokenOrGetter, channels: initialChannels, scope: initialScope, reconnectMs = 5_000 } = options;\n const getToken = typeof tokenOrGetter === 'function' ? tokenOrGetter : () => tokenOrGetter;\n\n const globalChannels = new Set(initialChannels);\n const scopedChannels = new Set<string>();\n let activeScope = initialScope;\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`. Throws on invalid transitions.\n * The throw is deliberate — a bad transition means a bug in the\n * reconnect loop; silent correction would hide it. The reconnect\n * timer logic is responsible for ensuring we only transition\n * between valid states.\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 throw new Error(`Invalid connection state transition: ${currentState} → ${next}`);\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 * `Last-Event-ID` of the most recently delivered SSE event from the\n * server. Sent as a request header on each connect so the server can\n * replay persisted events missed during the disconnect (see\n * `apps/backend/src/routes/bus.ts` subscribe handler). Initialised\n * `null` — fresh connections send no header.\n *\n * We track both persisted (`p-*`) and ephemeral (`e-*`) ids. The server\n * treats ephemeral ids as \"no resumption context\" and responds live-\n * only; persisted ids drive replay.\n */\n let lastEventId: string | null = null;\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 };\n\n const connect = async () => {\n // Transition to `connecting` from whichever reconnect-ish state\n // we're currently in (`initial`, `reconnecting`, `degraded`).\n transition('connecting');\n\n // Abort every previous in-flight fetch before starting a new one.\n // This closes the orphan-stream leak described above.\n for (const c of inflightControllers) {\n try { c.abort(); } catch { /* noop */ }\n }\n inflightControllers.clear();\n\n const params = new URLSearchParams();\n for (const ch of globalChannels) {\n params.append('channel', ch);\n }\n if (activeScope && scopedChannels.size > 0) {\n params.append('scope', activeScope);\n for (const ch of scopedChannels) {\n params.append('scoped', ch);\n }\n }\n const url = `${baseUrl}/bus/subscribe?${params.toString()}`;\n\n const controller = new AbortController();\n inflightControllers.add(controller);\n\n try {\n const headers: Record<string, string> = { Authorization: `Bearer ${getToken()}` };\n if (lastEventId) headers['Last-Event-ID'] = lastEventId;\n const response = await fetch(url, { headers, signal: controller.signal });\n\n if (!response.ok || !response.body) {\n throw new Error(`SSE connect failed: ${response.status}`);\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 if (currentEvent === 'bus-event' && currentData) {\n if (currentId !== undefined) lastEventId = currentId;\n const parsed = JSON.parse(currentData) as BusEvent;\n busLog('RECV', parsed.channel, parsed.payload, parsed.scope);\n // Tier 2: lift trace context off the SSE payload (the\n // backend'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 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 }\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`.\n if (running) {\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 disconnect();\n connect();\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 let reconnectTimer2: ReturnType<typeof setTimeout> | null = null;\n const RECONNECT_DEBOUNCE_MS = 100;\n const scheduleReconnect = () => {\n if (reconnectTimer2) clearTimeout(reconnectTimer2);\n reconnectTimer2 = setTimeout(() => {\n reconnectTimer2 = null;\n reconnect();\n }, RECONNECT_DEBOUNCE_MS);\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<void> => {\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 backend\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 await fetch(`${baseUrl}/bus/emit`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n },\n\n state$: state$.asObservable(),\n\n addChannels: (channels: string[], scope?: string) => {\n let changed = false;\n if (scope !== undefined) {\n for (const ch of channels) {\n if (!scopedChannels.has(ch)) { scopedChannels.add(ch); changed = true; }\n }\n if (scope !== activeScope) { activeScope = scope; changed = true; }\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[]) => {\n let changed = false;\n for (const ch of channels) {\n if (scopedChannels.delete(ch)) changed = true;\n if (globalChannels.delete(ch)) changed = true;\n }\n if (scopedChannels.size === 0) activeScope = undefined;\n if (changed) scheduleReconnect();\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 (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 (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, type Subscription } 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 BackendDownload,\n ConnectionState,\n IBackendOperations,\n ITransport,\n HealthCheckResponse,\n StatusResponse,\n UserResponse,\n UpdateUserRequest,\n UpdateUserResponse,\n ListUsersResponse,\n ProgressEvent,\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\nconst RESOURCE_SCOPED_CHANNELS = [\n ...PERSISTED_EVENT_TYPES.filter((t) => t !== 'frame:entity-type-added'),\n ...RESOURCE_BROADCAST_TYPES,\n];\n\n/**\n * Convert a fetch `Response` to the transport-neutral `BackendDownload`\n * shape. ky throws on non-OK by default, so callers can rely on the\n * response being healthy by the time it gets here. `response.body`\n * is non-null for successful responses with content.\n */\nfunction responseToDownload(response: Response): BackendDownload {\n const contentType = response.headers.get('Content-Type') ?? 'application/octet-stream';\n const contentDisposition = response.headers.get('Content-Disposition');\n const filename = contentDisposition?.match(/filename=\"(.+?)\"/)?.[1];\n return {\n stream: response.body!,\n contentType,\n ...(filename ? { filename } : {}),\n };\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\nexport class HttpTransport implements ITransport, IBackendOperations {\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 private activeResource: {\n resourceId: ResourceId;\n refCount: number;\n bridgeSubs: Subscription[];\n } | null = null;\n\n /** Buses we've been asked to bridge wire events into. */\n private readonly bridges: EventBus[] = [];\n\n constructor(config: HttpTransportConfig) {\n const { baseUrl, timeout = 30000, retry = 2, logger, tokenRefresher } = 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 credentials: 'include',\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, _options, 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 (error) => {\n const { response, request } = error;\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 });\n for (const channel of BRIDGED_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<void> {\n busLog('EMIT', channel as string, payload, resourceScope as string | undefined);\n recordBusEmit(channel as string, resourceScope as string | undefined);\n await withSpan(\n `bus.emit:${channel as string}`,\n async () => {\n if (resourceScope !== undefined) {\n await this.actor.emit(\n channel as string,\n payload as unknown as Record<string, unknown>,\n resourceScope as string,\n );\n } else {\n await this.actor.emit(\n channel as string,\n payload as unknown as Record<string, unknown>,\n );\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 if (this.activeResource) {\n if (this.activeResource.resourceId !== resourceId) {\n throw new Error(\n `HttpTransport already subscribed to resource ${this.activeResource.resourceId}; ` +\n `call the unsubscribe returned from the previous subscribeToResource before subscribing to ${resourceId}.`,\n );\n }\n this.activeResource.refCount++;\n return this.makeUnsubscriber();\n }\n\n this.actor.addChannels([...RESOURCE_SCOPED_CHANNELS], resourceId as string);\n\n const bridgeSubs: Subscription[] = [];\n for (const channel of RESOURCE_SCOPED_CHANNELS) {\n bridgeSubs.push(\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\n this.activeResource = { resourceId, refCount: 1, bridgeSubs };\n return this.makeUnsubscriber();\n }\n\n private makeUnsubscriber(): () => void {\n let called = false;\n return () => {\n if (called) return;\n called = true;\n if (!this.activeResource) return;\n this.activeResource.refCount--;\n if (this.activeResource.refCount > 0) return;\n for (const sub of this.activeResource.bridgeSubs) sub.unsubscribe();\n this.actor.removeChannels([...RESOURCE_SCOPED_CHANNELS]);\n this.activeResource = null;\n };\n }\n\n get state$(): Observable<ConnectionState> {\n return this.actor.state$;\n }\n\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n if (this.activeResource) {\n for (const sub of this.activeResource.bridgeSubs) sub.unsubscribe();\n this.activeResource = null;\n }\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 generateMcpToken(): Promise<{ token: string }> {\n return this.http.post(`${this.baseUrl}/api/tokens/mcp-generate`, {\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 // ── Exchange (backup/restore/export/import) ───────────────────────────\n\n async backupKnowledgeBase(): Promise<BackendDownload> {\n const response = await this.http.post(`${this.baseUrl}/api/admin/exchange/backup`, {\n headers: this.authHeaders(),\n });\n return responseToDownload(response);\n }\n\n restoreKnowledgeBase(file: File): Observable<ProgressEvent> {\n return this.sseProgressStream(`${this.baseUrl}/api/admin/exchange/restore`, file);\n }\n\n async exportKnowledgeBase(params?: { includeArchived?: boolean }): Promise<BackendDownload> {\n const searchParams = params?.includeArchived ? new URLSearchParams({ includeArchived: 'true' }) : undefined;\n const response = await this.http.post(`${this.baseUrl}/api/moderate/exchange/export`, {\n headers: this.authHeaders(),\n ...(searchParams ? { searchParams } : {}),\n });\n return responseToDownload(response);\n }\n\n importKnowledgeBase(file: File): Observable<ProgressEvent> {\n return this.sseProgressStream(`${this.baseUrl}/api/moderate/exchange/import`, file);\n }\n\n /**\n * POST a file to a server-sent-events endpoint and surface each `data:`\n * frame as an Observable emission. Completes when the stream closes;\n * errors if the request itself fails or the SSE stream is aborted.\n * The returned Observable is cold — the POST happens on subscribe and\n * is aborted via `AbortController` on unsubscribe.\n */\n private sseProgressStream(url: string, file: File): Observable<ProgressEvent> {\n return new Observable<ProgressEvent>((subscriber) => {\n const ctrl = new AbortController();\n const formData = new FormData();\n formData.append('file', file);\n\n (async () => {\n try {\n const response = await this.http.post(url, {\n body: formData,\n headers: this.authHeaders(),\n signal: ctrl.signal,\n });\n const reader = response.body!.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (!subscriber.closed) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop()!;\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const event = JSON.parse(line.slice(6)) as ProgressEvent;\n subscriber.next(event);\n }\n }\n }\n subscriber.complete();\n } catch (err) {\n if (!subscriber.closed) subscriber.error(err);\n }\n })();\n\n return () => ctrl.abort();\n });\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, ContentFormat, ResourceId, PutBinaryOptions } 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\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?: { accept?: ContentFormat | string; auth?: AccessToken },\n ): Promise<{ data: ArrayBuffer; contentType: string }> {\n busLog('GET', 'content', { resourceId, accept: options?.accept });\n return withSpan(\n 'content.get',\n async () => {\n const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/resources/${resourceId}`, {\n headers: {\n Accept: options?.accept ?? 'text/plain',\n ...this.requestHeaders(options?.auth),\n },\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?: { accept?: ContentFormat | string; auth?: AccessToken },\n ): Promise<{ stream: ReadableStream<Uint8Array>; contentType: string }> {\n busLog('GET', 'content', { resourceId, accept: options?.accept, stream: true });\n return withSpan(\n 'content.get',\n async () => {\n const response = await this.transport.rawHttp.get(`${this.transport.baseUrl}/resources/${resourceId}`, {\n headers: {\n Accept: options?.accept ?? 'text/plain',\n ...this.requestHeaders(options?.auth),\n },\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 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.creationMethod) formData.append('creationMethod', String(request.creationMethod));\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.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"]}
|