@semiont/http-transport 0.5.6
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 +74 -0
- package/dist/index.d.ts +233 -0
- package/dist/index.js +948 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @semiont/http-transport
|
|
2
|
+
|
|
3
|
+
[](https://github.com/The-AI-Alliance/semiont/actions/workflows/package-tests.yml?query=branch%3Amain+is%3Asuccess+job%3A%22Test+http-transport%22)
|
|
4
|
+
[](https://codecov.io/gh/The-AI-Alliance/semiont?flag=http-transport)
|
|
5
|
+
[](https://www.npmjs.com/package/@semiont/http-transport)
|
|
6
|
+
[](https://www.npmjs.com/package/@semiont/http-transport)
|
|
7
|
+
[](https://github.com/The-AI-Alliance/semiont/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
HTTP-specific transport adapters for the Semiont SDK. This is the wire-side
|
|
10
|
+
implementation of the `ITransport` and `IContentTransport` contracts defined
|
|
11
|
+
in [`@semiont/core`](../core/), consumed by [`@semiont/sdk`](../sdk/).
|
|
12
|
+
|
|
13
|
+
Most application code does **not** import this package directly. The sdk
|
|
14
|
+
re-exports the HTTP adapters for convenience, so a typical consumer writes:
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { SemiontClient, HttpTransport, HttpContentTransport } from '@semiont/sdk';
|
|
18
|
+
import { baseUrl } from '@semiont/core';
|
|
19
|
+
|
|
20
|
+
const transport = new HttpTransport({ baseUrl: baseUrl('https://kb.example/') });
|
|
21
|
+
// HttpTransport implements both ITransport and IBackendOperations; passing it
|
|
22
|
+
// third enables the `auth` / `admin` namespaces.
|
|
23
|
+
const client = new SemiontClient(transport, new HttpContentTransport(transport), transport);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Direct imports from `@semiont/http-transport` are appropriate when constructing
|
|
27
|
+
the transport stack by hand — e.g. CLI factories, MCP entrypoints, or worker
|
|
28
|
+
pools that wire bespoke `tokenRefresher` / `BehaviorSubject` token sources.
|
|
29
|
+
|
|
30
|
+
## Public surface
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import {
|
|
34
|
+
HttpTransport,
|
|
35
|
+
HttpContentTransport,
|
|
36
|
+
type HttpTransportConfig,
|
|
37
|
+
type TokenRefresher,
|
|
38
|
+
APIError,
|
|
39
|
+
// SSE-actor machinery used by SDK adapters; not application code:
|
|
40
|
+
createActorStateUnit,
|
|
41
|
+
type ActorStateUnit,
|
|
42
|
+
type BusEvent,
|
|
43
|
+
type ActorStateUnitOptions,
|
|
44
|
+
DEGRADED_THRESHOLD_MS,
|
|
45
|
+
} from '@semiont/http-transport';
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
That's the entire surface. Everything else moved out:
|
|
49
|
+
|
|
50
|
+
- **`ITransport`, `IContentTransport`, `BRIDGED_CHANNELS`, `ConnectionState`,
|
|
51
|
+
response/progress types** live in [`@semiont/core`](../core/).
|
|
52
|
+
- **`SemiontClient`, namespaces, `SemiontSession`, `SemiontBrowser`,
|
|
53
|
+
state units, `bus-request`, `cache`** live in [`@semiont/sdk`](../sdk/).
|
|
54
|
+
|
|
55
|
+
## Behavioral contract
|
|
56
|
+
|
|
57
|
+
The guarantees every `ITransport` implementation must honor — including
|
|
58
|
+
`HttpTransport` — are documented in
|
|
59
|
+
[`docs/protocol/TRANSPORT-CONTRACT.md`](../../docs/protocol/TRANSPORT-CONTRACT.md).
|
|
60
|
+
HTTP-specific guarantees (the `/bus/emit` gateway, SSE reconnect, `Last-Event-ID`
|
|
61
|
+
replay, etc.) live in
|
|
62
|
+
[`docs/protocol/TRANSPORT-HTTP.md`](../../docs/protocol/TRANSPORT-HTTP.md).
|
|
63
|
+
|
|
64
|
+
## Writing a new transport
|
|
65
|
+
|
|
66
|
+
If you need to add a non-HTTP transport (gRPC, WebSocket, IPC, …), implement
|
|
67
|
+
`ITransport` + `IContentTransport` from `@semiont/core` and consume the
|
|
68
|
+
contract from there. There's no inheritance from `HttpTransport`. For an
|
|
69
|
+
in-process example, see `LocalTransport` in
|
|
70
|
+
[`@semiont/make-meaning`](../make-meaning/).
|
|
71
|
+
|
|
72
|
+
## License
|
|
73
|
+
|
|
74
|
+
Apache-2.0 — see [LICENSE](../../LICENSE).
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { KyInstance } from 'ky';
|
|
2
|
+
import { Observable, BehaviorSubject } from 'rxjs';
|
|
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 } from '@semiont/core';
|
|
4
|
+
|
|
5
|
+
/** Minimal StateUnit surface — anything with a `dispose()` method. */
|
|
6
|
+
interface StateUnit {
|
|
7
|
+
dispose(): void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface BusEvent {
|
|
11
|
+
channel: string;
|
|
12
|
+
payload: Record<string, unknown>;
|
|
13
|
+
scope?: string;
|
|
14
|
+
}
|
|
15
|
+
interface ActorStateUnitOptions {
|
|
16
|
+
baseUrl: string;
|
|
17
|
+
token: string | (() => string);
|
|
18
|
+
channels: string[];
|
|
19
|
+
scope?: string;
|
|
20
|
+
reconnectMs?: number;
|
|
21
|
+
}
|
|
22
|
+
/** Time in the `reconnecting` state before transitioning to `degraded`. */
|
|
23
|
+
declare const DEGRADED_THRESHOLD_MS = 3000;
|
|
24
|
+
interface ActorStateUnit extends StateUnit {
|
|
25
|
+
on$<T = Record<string, unknown>>(channel: string): Observable<T>;
|
|
26
|
+
emit(channel: string, payload: Record<string, unknown>, emitScope?: string): Promise<void>;
|
|
27
|
+
state$: Observable<ConnectionState>;
|
|
28
|
+
addChannels(channels: string[], scope?: string): void;
|
|
29
|
+
removeChannels(channels: string[]): void;
|
|
30
|
+
start(): void;
|
|
31
|
+
stop(): void;
|
|
32
|
+
}
|
|
33
|
+
declare function createActorStateUnit(options: ActorStateUnitOptions): ActorStateUnit;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* HttpTransport — the HTTP/SSE implementation of ITransport.
|
|
37
|
+
*
|
|
38
|
+
* Phase 1 of TRANSPORT-ABSTRACTION. Owns everything that crosses the wire
|
|
39
|
+
* in remote mode: the bus actor (SSE + POST /bus/emit), auth/admin/exchange/
|
|
40
|
+
* system HTTP endpoints, and connection-state plumbing.
|
|
41
|
+
*
|
|
42
|
+
* Does NOT own the local coordination bus — that lives on `SemiontClient`.
|
|
43
|
+
* `bridgeInto(bus)` wires SSE-received events into the caller-supplied bus
|
|
44
|
+
* once at construction.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
type AuthResponse = components['schemas']['AuthResponse'];
|
|
48
|
+
type TokenRefreshResponse = components['schemas']['TokenRefreshResponse'];
|
|
49
|
+
type AdminUserStatsResponse = components['schemas']['AdminUserStatsResponse'];
|
|
50
|
+
type OAuthConfigResponse = components['schemas']['OAuthConfigResponse'];
|
|
51
|
+
declare class APIError extends SemiontError {
|
|
52
|
+
code: TransportErrorCode;
|
|
53
|
+
readonly status: number;
|
|
54
|
+
readonly statusText: string;
|
|
55
|
+
constructor(message: string, status: number, statusText: string, body?: unknown);
|
|
56
|
+
}
|
|
57
|
+
type TokenRefresher = () => Promise<string | null>;
|
|
58
|
+
interface HttpTransportConfig {
|
|
59
|
+
baseUrl: BaseUrl;
|
|
60
|
+
/** Observable token source; headers read the current value. */
|
|
61
|
+
token$?: BehaviorSubject<AccessToken | null>;
|
|
62
|
+
timeout?: number;
|
|
63
|
+
retry?: number;
|
|
64
|
+
logger?: Logger;
|
|
65
|
+
/** Optional 401-recovery hook. See {@link TokenRefresher}. */
|
|
66
|
+
tokenRefresher?: TokenRefresher;
|
|
67
|
+
}
|
|
68
|
+
declare class HttpTransport implements ITransport, IBackendOperations {
|
|
69
|
+
readonly baseUrl: BaseUrl;
|
|
70
|
+
private readonly http;
|
|
71
|
+
private readonly token$;
|
|
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>;
|
|
80
|
+
private _actor;
|
|
81
|
+
private _actorStarted;
|
|
82
|
+
private disposed;
|
|
83
|
+
private activeResource;
|
|
84
|
+
/** Buses we've been asked to bridge wire events into. */
|
|
85
|
+
private readonly bridges;
|
|
86
|
+
constructor(config: HttpTransportConfig);
|
|
87
|
+
get actor(): ActorStateUnit;
|
|
88
|
+
emit<K extends keyof EventMap>(channel: K, payload: EventMap[K], resourceScope?: ResourceId): Promise<void>;
|
|
89
|
+
on<K extends keyof EventMap>(channel: K, handler: (payload: EventMap[K]) => void): () => void;
|
|
90
|
+
stream<K extends keyof EventMap>(channel: K): Observable<EventMap[K]>;
|
|
91
|
+
/**
|
|
92
|
+
* Wire this transport's SSE fan-in into the given bus. Every channel
|
|
93
|
+
* in `BRIDGED_CHANNELS` (and subsequently per-resource scoped channels
|
|
94
|
+
* opened by `subscribeToResource`) is published on the bus. Safe to
|
|
95
|
+
* call multiple times — each bus is added to the fan-out list.
|
|
96
|
+
*/
|
|
97
|
+
bridgeInto(bus: EventBus): void;
|
|
98
|
+
subscribeToResource(resourceId: ResourceId): () => void;
|
|
99
|
+
private makeUnsubscriber;
|
|
100
|
+
get state$(): Observable<ConnectionState>;
|
|
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;
|
|
109
|
+
private authHeaders;
|
|
110
|
+
authenticatePassword(email: Email, password: string): Promise<AuthResponse>;
|
|
111
|
+
authenticateGoogle(credential: GoogleCredential): Promise<AuthResponse>;
|
|
112
|
+
refreshAccessToken(token: RefreshToken): Promise<TokenRefreshResponse>;
|
|
113
|
+
logout(): Promise<void>;
|
|
114
|
+
acceptTerms(): Promise<void>;
|
|
115
|
+
getCurrentUser(): Promise<UserResponse>;
|
|
116
|
+
generateMcpToken(): Promise<{
|
|
117
|
+
token: string;
|
|
118
|
+
}>;
|
|
119
|
+
getMediaToken(resourceId: ResourceId): Promise<{
|
|
120
|
+
token: string;
|
|
121
|
+
}>;
|
|
122
|
+
listUsers(): Promise<ListUsersResponse>;
|
|
123
|
+
getUserStats(): Promise<AdminUserStatsResponse>;
|
|
124
|
+
updateUser(id: UserDID, data: UpdateUserRequest): Promise<UpdateUserResponse>;
|
|
125
|
+
getOAuthConfig(): Promise<OAuthConfigResponse>;
|
|
126
|
+
backupKnowledgeBase(): Promise<BackendDownload>;
|
|
127
|
+
restoreKnowledgeBase(file: File): Observable<ProgressEvent>;
|
|
128
|
+
exportKnowledgeBase(params?: {
|
|
129
|
+
includeArchived?: boolean;
|
|
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;
|
|
140
|
+
healthCheck(): Promise<HealthCheckResponse>;
|
|
141
|
+
getStatus(): Promise<StatusResponse>;
|
|
142
|
+
/**
|
|
143
|
+
* Temporary escape hatch for the ongoing transport migration: namespaces
|
|
144
|
+
* that still need to issue ad-hoc HTTP calls (e.g. legacy browse/mark
|
|
145
|
+
* HTTP fallbacks) can borrow the configured `ky` instance here. Will be
|
|
146
|
+
* deleted once all namespaces route through bus channels or through
|
|
147
|
+
* typed methods on this transport.
|
|
148
|
+
*/
|
|
149
|
+
get rawHttp(): KyInstance;
|
|
150
|
+
/**
|
|
151
|
+
* Current access token (synchronously read from the BehaviorSubject).
|
|
152
|
+
* Used by content-transport and legacy namespace HTTP fallbacks that
|
|
153
|
+
* need to pass `auth: token` through some code paths.
|
|
154
|
+
*/
|
|
155
|
+
getToken(): AccessToken | undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* HttpContentTransport — binary I/O over HTTP.
|
|
160
|
+
*
|
|
161
|
+
* Phase 1 of TRANSPORT-ABSTRACTION. Narrow by design because binary has
|
|
162
|
+
* different backpressure and streaming characteristics than typed command
|
|
163
|
+
* payloads. Uses the HttpTransport's underlying ky instance + token, so
|
|
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.
|
|
198
|
+
*/
|
|
199
|
+
|
|
200
|
+
type GetResourceResponse = components['schemas']['GetResourceResponse'];
|
|
201
|
+
declare class HttpContentTransport implements IContentTransport {
|
|
202
|
+
private readonly transport;
|
|
203
|
+
constructor(transport: HttpTransport);
|
|
204
|
+
putBinary(request: PutBinaryRequest, options?: PutBinaryOptions): Promise<{
|
|
205
|
+
resourceId: ResourceId;
|
|
206
|
+
}>;
|
|
207
|
+
getBinary(resourceId: ResourceId, options?: {
|
|
208
|
+
auth?: AccessToken;
|
|
209
|
+
}): Promise<{
|
|
210
|
+
data: ArrayBuffer;
|
|
211
|
+
contentType: string;
|
|
212
|
+
}>;
|
|
213
|
+
getBinaryStream(resourceId: ResourceId, options?: {
|
|
214
|
+
auth?: AccessToken;
|
|
215
|
+
}): Promise<{
|
|
216
|
+
stream: ReadableStream<Uint8Array>;
|
|
217
|
+
contentType: string;
|
|
218
|
+
}>;
|
|
219
|
+
/**
|
|
220
|
+
* Dereference the resource's JSON-LD graph over HTTP — the LD face an
|
|
221
|
+
* external linked-data client sees. Deliberately HTTP, not the bus
|
|
222
|
+
* (SIMPLER-JSON-LD.md §5).
|
|
223
|
+
*/
|
|
224
|
+
getResourceGraph(resourceId: ResourceId, options?: {
|
|
225
|
+
auth?: AccessToken;
|
|
226
|
+
}): Promise<GetResourceResponse>;
|
|
227
|
+
dispose(): void;
|
|
228
|
+
/** Auth header + W3C trace propagation for the active span. */
|
|
229
|
+
private requestHeaders;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export { APIError, DEGRADED_THRESHOLD_MS, HttpContentTransport, HttpTransport, createActorStateUnit };
|
|
233
|
+
export type { ActorStateUnit, ActorStateUnitOptions, BusEvent, HttpTransportConfig, TokenRefresher };
|