@deepseek-ai/dsh-client-connection 0.1.1-rc.2 → 0.1.2-alpha.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.i18n.yaml +2 -2
- package/README.md +54 -6
- package/README.zh.md +54 -6
- package/lib/client.js +1936 -7453
- package/lib/index.js +387 -254
- package/lib/invariant.js +6 -5
- package/lib/types/api-path.d.ts +1 -6
- package/lib/types/api-request-trust.d.ts +2 -7
- package/lib/types/browser-auth.d.ts +46 -0
- package/lib/types/client/api.d.ts +7 -13
- package/lib/types/client/connection.d.ts +50 -28
- package/lib/types/client/fixture.d.ts +6 -43
- package/lib/types/client/index.d.ts +72 -33
- package/lib/types/client/rpc.d.ts +4 -1
- package/lib/types/http-bridge.d.ts +1 -1
- package/lib/types/index.d.ts +12 -10
- package/lib/types/rpc-host.d.ts +19 -8
- package/lib/types/rpc-schema.d.ts +30 -0
- package/lib/types/rpc.d.ts +145 -15
- package/package.json +18 -23
- package/lib/types/client/web-api-client.d.ts +0 -11
- package/lib/types/websocket-downlink.d.ts +0 -43
package/lib/invariant.js
CHANGED
|
@@ -9,11 +9,12 @@ const name = "client-connection-invariant";
|
|
|
9
9
|
/** Service required before the companion can reserve package ownership. */
|
|
10
10
|
const inject = ["invariants"];
|
|
11
11
|
/**
|
|
12
|
-
* No runtime invariant:
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* register/dispose symmetry is
|
|
12
|
+
* No runtime invariant: browser-session verification reads the credential
|
|
13
|
+
* record asynchronously at the request that authorizes work, while the
|
|
14
|
+
* credentials companion owns record commit-event lifetime. Stream/reconnect
|
|
15
|
+
* sequencing and rpcId round-trip discipline are exercised directly by
|
|
16
|
+
* behavior specs, and route register/dispose symmetry is
|
|
17
|
+
* audited by the webserver companion.
|
|
17
18
|
*/
|
|
18
19
|
const install = () => {};
|
|
19
20
|
/**
|
package/lib/types/api-path.d.ts
CHANGED
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The /api URL prefix — single source for both halves of the web transport.
|
|
3
|
-
* The node half registers this prefix on the web server
|
|
4
|
-
* event paths below for the browser WebSocket downlinks.
|
|
3
|
+
* The node half registers this prefix on the web server.
|
|
5
4
|
*/
|
|
6
5
|
/** Route prefix owning every api request (`/api` and `/api/<anything>`). */
|
|
7
6
|
export declare const API_PATH = "/api";
|
|
8
|
-
/** Browser mux-frame WebSocket pathname. */
|
|
9
|
-
export declare const MUX_EVENTS_PATH = "/api/events.mux";
|
|
10
|
-
/** Browser host-frame WebSocket pathname. */
|
|
11
|
-
export declare const HOST_EVENTS_PATH = "/api/events.host";
|
|
12
7
|
//# sourceMappingURL=api-path.d.ts.map
|
|
@@ -12,11 +12,7 @@
|
|
|
12
12
|
* Network reachability and authentication stay out of scope: binding policy
|
|
13
13
|
* belongs to the webserver config, and this fence is not an auth layer.
|
|
14
14
|
*/
|
|
15
|
-
import type {
|
|
16
|
-
/** The request facts the fence reads from either HTTP representation. */
|
|
17
|
-
interface ApiTrustRequest {
|
|
18
|
-
headers: IncomingHttpHeaders | Headers;
|
|
19
|
-
}
|
|
15
|
+
import type { ConnectionTrustRequest } from './rpc.ts';
|
|
20
16
|
/**
|
|
21
17
|
* Assert one configured `trustedHosts` entry is a bare authority (`host` or
|
|
22
18
|
* `host:port`) in canonical form: it must survive WHATWG parsing unchanged
|
|
@@ -38,6 +34,5 @@ export declare function assertTrustedAuthority(entry: string): void;
|
|
|
38
34
|
* @param trustedHosts - non-loopback authorities this deployment serves: exact `host:port`, or port-less `host` matching any port.
|
|
39
35
|
* @returns true when the Host is ours (loopback or trusted) and any attached browser markers are same-origin.
|
|
40
36
|
*/
|
|
41
|
-
export declare function isTrustedApiRequest(request:
|
|
42
|
-
export {};
|
|
37
|
+
export declare function isTrustedApiRequest(request: ConnectionTrustRequest, trustedHosts: readonly string[]): boolean;
|
|
43
38
|
//# sourceMappingURL=api-request-trust.d.ts.map
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Browser-session authentication for the Host Connection carrier. */
|
|
2
|
+
import type { CredentialProvider } from '@deepseek-ai/dsh-credentials';
|
|
3
|
+
import type { ConnectionIndexRequest, ConnectionIndexResponse, ConnectionTrustRequest } from './rpc.ts';
|
|
4
|
+
/**
|
|
5
|
+
* Process launch-token exchange and persistent signed-cookie verification.
|
|
6
|
+
* Connection loads the credential provider's signing secret during activation
|
|
7
|
+
* and retains it for synchronous request authentication.
|
|
8
|
+
*/
|
|
9
|
+
export declare class BrowserAuth {
|
|
10
|
+
private readonly secret;
|
|
11
|
+
private readonly launchToken;
|
|
12
|
+
private readonly maxAgeMilliseconds;
|
|
13
|
+
private constructor();
|
|
14
|
+
/**
|
|
15
|
+
* Initialize browser authentication and create its durable signing secret
|
|
16
|
+
* when this Harness home has none.
|
|
17
|
+
* @param processOwner - root application context retaining one token across Connection reloads.
|
|
18
|
+
* @param credentials - persistent credential provider for the Web profile.
|
|
19
|
+
* @param maxAgeDays - positive absolute browser-cookie lifetime in days.
|
|
20
|
+
* @returns initialized authentication owner with the process owner's launch token.
|
|
21
|
+
*/
|
|
22
|
+
static create(processOwner: object, credentials: CredentialProvider, maxAgeDays: number): Promise<BrowserAuth>;
|
|
23
|
+
/**
|
|
24
|
+
* Add this process's launch token to the ordinary application root URL.
|
|
25
|
+
* @param baseUrl - canonical browser origin without credentials.
|
|
26
|
+
* @returns root URL carrying the process token as its sole authentication input.
|
|
27
|
+
*/
|
|
28
|
+
authenticatedUrl(baseUrl: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Authenticate an index request. A valid root query token mints the cookie
|
|
31
|
+
* and redirects to clean `/`; a valid cookie lets the caller serve the
|
|
32
|
+
* index; every other request receives the same minimal 401 response.
|
|
33
|
+
* @param req - incoming root or configured-index request.
|
|
34
|
+
* @param res - response owned when this method returns false.
|
|
35
|
+
* @returns true only when the caller may serve index.html.
|
|
36
|
+
*/
|
|
37
|
+
authorizeIndex(req: ConnectionIndexRequest, res: ConnectionIndexResponse): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Verify the authority-bound browser cookie on a Host request.
|
|
40
|
+
* @param request - request headers carrying Host and Cookie.
|
|
41
|
+
* @returns true only for an unexpired cookie signed by this activation's loaded secret.
|
|
42
|
+
*/
|
|
43
|
+
isAuthenticated(request: ConnectionTrustRequest): boolean;
|
|
44
|
+
private writeUnauthorized;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=browser-auth.d.ts.map
|
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
export type {
|
|
3
|
-
export
|
|
4
|
-
export { RpcId, SESSION_SEARCH_RESULT_LIMIT, transportError, } from '@deepseek-ai/dsh-host-apiproxy/api';
|
|
5
|
-
export { AbstractApiClient } from '@deepseek-ai/dsh-host-apiproxy/client';
|
|
6
|
-
export type { IApiClient } from '@deepseek-ai/dsh-host-apiproxy/client';
|
|
1
|
+
/** Browser-safe Connection protocol and shared application value exports. */
|
|
2
|
+
export type { ClientRequest, RpcMessage, RpcRequest, RpcResponse, RpcResult, ServerResponse, } from '../rpc.ts';
|
|
3
|
+
export { RpcId, transportError } from '../rpc.ts';
|
|
7
4
|
export type { SessionId, SessionEvent } from '@deepseek-ai/dsh-session/types';
|
|
8
5
|
export type { MessageId } from '@deepseek-ai/dsh-llm/brand';
|
|
9
6
|
export type { ContentBlock, StreamChunk } from '@deepseek-ai/dsh-llm/types';
|
|
10
|
-
|
|
11
|
-
export type HostDescription = import('@deepseek-ai/dsh-host-apiproxy/api').ResponseValue<'host.describe'>;
|
|
12
|
-
import type { RpcResponse, RpcResult } from '@deepseek-ai/dsh-host-apiproxy/api';
|
|
7
|
+
import type { RpcResponse, RpcResult } from '../rpc.ts';
|
|
13
8
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* @
|
|
17
|
-
* @returns its result slot.
|
|
9
|
+
* Return the business result carried by a narrow fixture response.
|
|
10
|
+
* @param response - fixture response to unwrap.
|
|
11
|
+
* @returns the response's business result.
|
|
18
12
|
*/
|
|
19
13
|
export declare function resultOf<T>(response: RpcResponse<T>): RpcResult<T>;
|
|
20
14
|
//# sourceMappingURL=api.d.ts.map
|
|
@@ -1,55 +1,78 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/** Stable Host facts delivered by one established Remote event generation. */
|
|
2
|
+
export interface ConnectionHostInfo {
|
|
3
|
+
/** Host account home used only to abbreviate displayed filesystem paths. */
|
|
4
|
+
readonly home: string;
|
|
5
|
+
}
|
|
6
|
+
/** One successfully established Host generation. */
|
|
7
|
+
export interface ConnectionGeneration {
|
|
8
|
+
/** Monotone generation number within this Client runtime. */
|
|
9
|
+
readonly id: number;
|
|
10
|
+
/** Host facts carried by this generation's opening frame. */
|
|
11
|
+
readonly host: ConnectionHostInfo;
|
|
12
|
+
}
|
|
13
|
+
/** Reconnect/backoff tunables. All fields are optional; defaults are below. */
|
|
4
14
|
export interface ConnectionConfig {
|
|
5
15
|
/** First-retry backoff cap in ms (jittered: actual delay is cap/2..cap). */
|
|
6
16
|
backoffBaseMs?: number;
|
|
7
|
-
/** Exponential growth factor per
|
|
17
|
+
/** Exponential growth factor per failed attempt; values at or below 1 make the base tier final. */
|
|
8
18
|
backoffFactor?: number;
|
|
9
19
|
/** Upper bound for the backoff cap in ms. */
|
|
10
20
|
backoffMaxMs?: number;
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
* fires onOpen (misbehaving proxy) must not wedge the connection forever — on timeout the
|
|
14
|
-
* generation proceeds as connected and the live-gap repair path covers stragglers. */
|
|
15
|
-
streamOpenTimeoutMs?: number;
|
|
21
|
+
/** Maximum wait for the registered generation source's ready signal. */
|
|
22
|
+
generationReadyTimeoutMs?: number;
|
|
16
23
|
}
|
|
17
|
-
/**
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/** Frame sink callbacks: the Controller owns the physical streams; business dispatch belongs to
|
|
21
|
-
* SessionManager. */
|
|
24
|
+
/** Connection lifecycle state published after the first attempt has an outcome. */
|
|
25
|
+
export type ConnectionState = 'connected' | 'disconnected' | 'connecting';
|
|
26
|
+
/** Connection-generation callbacks owned by API Gateway. */
|
|
22
27
|
export interface ConnectionSinks {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
onConnected?: (description: HostDescription) => void;
|
|
27
|
-
/** Coarse state transitions (deduplicated: fires only on change). The initial pre-connect
|
|
28
|
-
* span reports nothing — the UI treats "no state yet" as connecting, not as an outage. */
|
|
28
|
+
/** After the generation source reports ready, first connect included. */
|
|
29
|
+
onConnected?: (host: ConnectionHostInfo) => void;
|
|
30
|
+
/** State transitions after the initial attempt has an outcome. Equivalent states are deduplicated. */
|
|
29
31
|
onStateChange?: (state: ConnectionState) => void;
|
|
32
|
+
/** Start one fresh physical-carrier attempt before each logical retry. */
|
|
33
|
+
onReconnectRequested?: () => void;
|
|
30
34
|
}
|
|
31
35
|
/**
|
|
32
|
-
*
|
|
33
|
-
*
|
|
36
|
+
* One long-lived source defining a Connection generation. The source must
|
|
37
|
+
* attach its incremental listeners before calling `ready`, then remain pending
|
|
38
|
+
* until the generation is lost or `signal` aborts.
|
|
39
|
+
* @param signal - cancellation for the current generation.
|
|
40
|
+
* @param ready - one-shot report that incremental delivery is attached.
|
|
41
|
+
* @returns a promise settling only when this generation ends or fails.
|
|
42
|
+
*/
|
|
43
|
+
export type ConnectionGenerationSource = (signal: AbortSignal, ready: (host: ConnectionHostInfo) => void) => Promise<void>;
|
|
44
|
+
/**
|
|
45
|
+
* Opens the registered generation source, reconnecting with exponential backoff on loss.
|
|
34
46
|
* State (generation/attempt) is instance-private, never in the store.
|
|
35
|
-
*
|
|
36
|
-
* not kill the pump — a broken business layer must not drag down the connection layer).
|
|
47
|
+
* Sink exceptions do not kill the generation loop.
|
|
37
48
|
*/
|
|
38
49
|
export declare class ConnectionController {
|
|
39
|
-
private readonly
|
|
50
|
+
private readonly source;
|
|
40
51
|
private readonly sinks;
|
|
41
52
|
private generation;
|
|
42
53
|
private attempt;
|
|
43
54
|
private current;
|
|
55
|
+
private retryDelay;
|
|
44
56
|
private running;
|
|
57
|
+
private immediateRetry;
|
|
58
|
+
private networkAvailable;
|
|
45
59
|
private lastState;
|
|
46
60
|
private readonly config;
|
|
47
|
-
constructor(
|
|
61
|
+
constructor(source: ConnectionGenerationSource, sinks?: ConnectionSinks, config?: ConnectionConfig);
|
|
48
62
|
/** Idempotent: begin the connect/pump/reconnect loop. */
|
|
49
63
|
start(): void;
|
|
50
|
-
/** Stop the loop and abort the current generation
|
|
64
|
+
/** Stop the loop and abort the current generation source. */
|
|
51
65
|
stop(): void;
|
|
66
|
+
/** Reset the retry sequence and replace the current generation or retry delay immediately. */
|
|
67
|
+
reconnect(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Suspend automatic retries while offline and restart backoff when the network returns.
|
|
70
|
+
* @param available - whether the browser reports network access.
|
|
71
|
+
*/
|
|
72
|
+
setNetworkAvailable(available: boolean): void;
|
|
73
|
+
private backoffCap;
|
|
52
74
|
private backoffDelay;
|
|
75
|
+
private isFinalBackoffTier;
|
|
53
76
|
/** Read through a method: stop() flips the flag across awaits, so narrowing from the loop condition must not stick. */
|
|
54
77
|
private isRunning;
|
|
55
78
|
/** Re-read both mutable liveness guards after a potentially reentrant sink. */
|
|
@@ -57,7 +80,6 @@ export declare class ConnectionController {
|
|
|
57
80
|
private loop;
|
|
58
81
|
/** Deduplicated state emission (sink isolation applies). */
|
|
59
82
|
private emitState;
|
|
60
|
-
private pumpStream;
|
|
61
83
|
/** Sink exception isolation: a business-layer throw is logged only, never affecting pump or reconnect semantics. */
|
|
62
84
|
private callSink;
|
|
63
85
|
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
2
|
-
import type { ApiProxy, ClientResponse, HostFrame, MuxFrame, RpcReceipt, RpcRequest, RpcResponse } from './api.ts';
|
|
3
|
-
import type { RequestPayload, ResponseValue, RpcMethodMap } from '@deepseek-ai/dsh-host-apiproxy/api';
|
|
4
|
-
import { AbstractApiClient } from './api.ts';
|
|
5
1
|
import type { ClientConnectionRpc } from '../rpc.ts';
|
|
6
2
|
/** Deterministic fixture branches used by keyless Web assembly tests. */
|
|
7
3
|
export interface FixtureOptions {
|
|
@@ -16,53 +12,20 @@ export interface FixtureOptions {
|
|
|
16
12
|
/** Order of the two successful create frames. */
|
|
17
13
|
createFrameOrder?: 'session-first' | 'workspace-first';
|
|
18
14
|
}
|
|
19
|
-
/**
|
|
20
|
-
* In-memory fake host: fx-alpha carries history and replay scripts; fx-beta is fx-alpha's child session (lineage indent material).
|
|
21
|
-
* @param options - fixture branches for empty state and failure timing.
|
|
22
|
-
* @returns an ApiProxy backed entirely by in-memory state — no host process, no network.
|
|
23
|
-
*/
|
|
24
|
-
export declare function createFixtureApi(options?: FixtureOptions): ApiProxy;
|
|
25
|
-
/** Both fixture faces over one state graph. */
|
|
15
|
+
/** Fixture RPC face over one in-memory state graph. */
|
|
26
16
|
export interface FixtureWorld {
|
|
27
|
-
/** Legacy unary/stream API the fixture still answers. */
|
|
28
|
-
readonly api: ApiProxy;
|
|
29
17
|
/** Generic Remote caller for the endpoints business services own. */
|
|
30
18
|
readonly rpc: ClientConnectionRpc;
|
|
31
19
|
}
|
|
32
20
|
/**
|
|
33
|
-
* Build
|
|
34
|
-
* legacy API against one in-memory state graph.
|
|
21
|
+
* Build the fixture RPC face over one in-memory state graph.
|
|
35
22
|
* @param options - fixture branches for empty state and failure timing.
|
|
36
|
-
* @returns the
|
|
23
|
+
* @returns the Remote RPC face.
|
|
37
24
|
*/
|
|
38
25
|
export declare function createFixtureFaces(options?: FixtureOptions): FixtureWorld;
|
|
39
26
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* straight into the in-memory ApiProxy — while still minting rpcIds, fabricating the four
|
|
43
|
-
* named full forms, and feeding the same tap as a real carrier. TODO: delete when the fixture
|
|
44
|
-
* moves to the isomorphic pipeline (InProcessApiClient over toFetchHandler(fixtureImpl)).
|
|
27
|
+
* Build the browser fixture transport from the current page's query switches.
|
|
28
|
+
* @returns an in-memory Connection RPC transport.
|
|
45
29
|
*/
|
|
46
|
-
export declare
|
|
47
|
-
private readonly api;
|
|
48
|
-
/** Generic Remote caller backed by the same in-memory state as the legacy fixture API. */
|
|
49
|
-
readonly rpc: ClientConnectionRpc;
|
|
50
|
-
constructor();
|
|
51
|
-
protected doFetch(): Promise<Response>;
|
|
52
|
-
protected callUnary<K extends keyof RpcMethodMap>(method: K, payload: RequestPayload<K>, signal?: AbortSignal): Promise<RpcResponse<ResponseValue<K>>>;
|
|
53
|
-
/** Method-key dispatch into the in-memory contract impl (a real carrier routes by URL path instead). */
|
|
54
|
-
private dispatch;
|
|
55
|
-
protected openMux(payload: {
|
|
56
|
-
since?: Record<SessionId, number>;
|
|
57
|
-
}, signal: AbortSignal, onOpen?: () => void): AsyncIterable<RpcRequest<MuxFrame>>;
|
|
58
|
-
protected openHost(payload: Record<never, never>, signal: AbortSignal, onOpen?: () => void): AsyncIterable<RpcRequest<HostFrame>>;
|
|
59
|
-
private tapStream;
|
|
60
|
-
/**
|
|
61
|
-
* Deliver a client response to the in-memory contract impl (no HTTP POST),
|
|
62
|
-
* echoing the envelope to the observation tap like every other path.
|
|
63
|
-
* @param message - the client-response envelope answering a server request.
|
|
64
|
-
* @returns the carrier receipt from the fixture impl.
|
|
65
|
-
*/
|
|
66
|
-
respond(message: ClientResponse): Promise<RpcReceipt>;
|
|
67
|
-
}
|
|
30
|
+
export declare function createFixtureConnectionRpc(): ClientConnectionRpc;
|
|
68
31
|
//# sourceMappingURL=fixture.d.ts.map
|
|
@@ -1,23 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Browser wire client. The plugin selects fixture or HTTP transport, provides
|
|
3
|
-
* the shared API client, and lets
|
|
4
|
-
* controller with its sinks.
|
|
3
|
+
* the shared API client, and lets API Gateway own the connection loop.
|
|
5
4
|
*/
|
|
6
5
|
import type { Context } from '@deepseek-ai/cordis';
|
|
7
|
-
import type
|
|
8
|
-
import { type
|
|
9
|
-
import { type RpcFetch } from './rpc.ts';
|
|
6
|
+
import { type ConnectionConfig, type ConnectionGeneration, type ConnectionGenerationSource, type ConnectionSinks, type ConnectionState } from './connection.ts';
|
|
7
|
+
import { type RpcFetch, type RpcStreamOpen } from './rpc.ts';
|
|
10
8
|
import type { ClientConnectionRpc } from '../rpc.ts';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
declare module '@deepseek-ai/cordis' {
|
|
10
|
+
interface Events {
|
|
11
|
+
/**
|
|
12
|
+
* A connection generation was established. Wire-derived caches must
|
|
13
|
+
* repull; long-lived streams own their own resume and baseline lifecycle.
|
|
14
|
+
* @mode emit
|
|
15
|
+
*/
|
|
16
|
+
'connection/reset'(): void;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export type { MessageId, RpcRequest, RpcResponse, RpcResult, ClientRequest, ServerResponse, RpcMessage, SessionId, SessionEvent, ContentBlock, StreamChunk, } from './api.ts';
|
|
20
|
+
export { RpcId, transportError, } from './api.ts';
|
|
21
|
+
export type { ConnectionConfig, ConnectionGeneration, ConnectionGenerationSource, ConnectionHostInfo, ConnectionSinks, ConnectionState, } from './connection.ts';
|
|
22
|
+
export type { ClientConnectionRpc, ConnectionRpcFailure, ConnectionRpcResult, } from '../rpc.ts';
|
|
15
23
|
export type { RpcFetch } from './rpc.ts';
|
|
16
|
-
/** Observable Host
|
|
17
|
-
export interface
|
|
18
|
-
/**
|
|
19
|
-
getSnapshot():
|
|
20
|
-
/** Subscribe to
|
|
24
|
+
/** Observable identity and Host facts for the active connection generation. */
|
|
25
|
+
export interface ConnectionGenerationState {
|
|
26
|
+
/** Active generation, or undefined before readiness and while reconnecting. */
|
|
27
|
+
getSnapshot(): ConnectionGeneration | undefined;
|
|
28
|
+
/** Subscribe to generation establishment, replacement, and loss. */
|
|
29
|
+
subscribe(listener: () => void): () => void;
|
|
30
|
+
}
|
|
31
|
+
/** Observable recovery lifecycle of the owned Connection loop. */
|
|
32
|
+
export interface ConnectionStateSource {
|
|
33
|
+
/** Current state, or undefined before the first connection outcome. */
|
|
34
|
+
getSnapshot(): ConnectionState | undefined;
|
|
35
|
+
/** Subscribe to state changes. */
|
|
21
36
|
subscribe(listener: () => void): () => void;
|
|
22
37
|
}
|
|
23
38
|
/** Required services (none — this is the wire root). */
|
|
@@ -29,42 +44,66 @@ export declare const inject: string[];
|
|
|
29
44
|
* provides both halves here instead of forking this plugin.
|
|
30
45
|
*/
|
|
31
46
|
export interface ClientTransportHooks {
|
|
32
|
-
/** Build the API carrier: unary calls plus the two downstream event streams. */
|
|
33
|
-
createApiClient(): IApiClient;
|
|
34
47
|
/** Transport for generic unary RPC channels (the Typert gateway). */
|
|
35
48
|
fetch: RpcFetch;
|
|
49
|
+
/** Worker-local Gateway stream carrier; absent when the page uses the Gateway WebSocket. */
|
|
50
|
+
openStream?: RpcStreamOpen;
|
|
36
51
|
/**
|
|
37
52
|
* Bundle transport for the module system, present when the carrier also owns
|
|
38
53
|
* bundle bytes (the worker tunnel). Absent in the served web app, whose
|
|
39
54
|
* bundles load over HTTP.
|
|
40
55
|
*/
|
|
41
56
|
loadBundle?(url: string): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* The transport owner declares the page owns the Host outright: the Host
|
|
59
|
+
* runs inside a worker this page spawned, so no other party can reach it and
|
|
60
|
+
* the loopback stand-in for "the operator's own machine" is vacuous.
|
|
61
|
+
* `ctx.connection.isLoopback` then reports the privileged surface reachable
|
|
62
|
+
* regardless of the page authority. Only a shell that assembles its own
|
|
63
|
+
* transport can set this; served pages never carry the global at all.
|
|
64
|
+
*/
|
|
65
|
+
ownsHost?: boolean;
|
|
42
66
|
}
|
|
43
67
|
/**
|
|
44
|
-
* The ctx.connection service API: the API client plus a one-shot
|
|
45
|
-
*
|
|
46
|
-
*
|
|
68
|
+
* The ctx.connection service API: the API client plus a one-shot controller
|
|
69
|
+
* starter. API Gateway supplies generation readiness and reset callbacks;
|
|
70
|
+
* Connection stays independent of downstream domain state.
|
|
47
71
|
*/
|
|
48
72
|
export interface ConnectionHandle {
|
|
49
|
-
/**
|
|
50
|
-
|
|
51
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Whether the privileged surface is reachable: the page authority is
|
|
75
|
+
* loopback, the transport declares the page owns the Host
|
|
76
|
+
* ({@link ClientTransportHooks.ownsHost}), or the context is not a browser.
|
|
77
|
+
*/
|
|
52
78
|
readonly isLoopback: boolean;
|
|
53
|
-
/**
|
|
54
|
-
readonly
|
|
79
|
+
/** Current Remote event generation and the Host facts carried by its opening frame. */
|
|
80
|
+
readonly generation: ConnectionGenerationState;
|
|
81
|
+
/** Current recovery lifecycle for connection-specific consumers. */
|
|
82
|
+
readonly state: ConnectionStateSource;
|
|
55
83
|
/** Generic logical RPC channels over the same Connection transport. */
|
|
56
84
|
readonly rpc: ClientConnectionRpc;
|
|
85
|
+
/** Reset retry progression and replace the current attempt immediately. */
|
|
86
|
+
reconnect(): void;
|
|
57
87
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* @
|
|
62
|
-
* @param config - reconnect/backoff tunables.
|
|
63
|
-
* @returns stop handle for the loop.
|
|
88
|
+
* Register the sole source defining Host generations. The source reports
|
|
89
|
+
* ready only after its incremental listeners are attached.
|
|
90
|
+
* @param source - long-lived generation source owned by the push carrier.
|
|
91
|
+
* @returns disposer withdrawing the source and stopping an active loop.
|
|
64
92
|
*/
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
93
|
+
registerGenerationSource(source: ConnectionGenerationSource): () => void;
|
|
94
|
+
/**
|
|
95
|
+
* Start the connect/reconnect loop with the consumer's state callbacks.
|
|
96
|
+
* API Gateway owns the loop; a second call throws.
|
|
97
|
+
* @param sinks - connection-state callbacks.
|
|
98
|
+
* @param config - reconnect timing tunables.
|
|
99
|
+
* @returns lifecycle controls for the loop.
|
|
100
|
+
*/
|
|
101
|
+
start(sinks: ConnectionSinks, config?: ConnectionConfig): ConnectionLoop;
|
|
102
|
+
}
|
|
103
|
+
/** Controls retained by the sole owner of a running connection loop. */
|
|
104
|
+
export interface ConnectionLoop {
|
|
105
|
+
/** Stop the loop and withdraw its active generation. */
|
|
106
|
+
stop(): void;
|
|
68
107
|
}
|
|
69
108
|
/**
|
|
70
109
|
* Client plugin body: pick the api by page mode and provide ctx.connection.
|
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
import type { ClientConnectionRpc } from '../rpc.ts';
|
|
3
3
|
/** Transport this caller posts through; same signature as the global `fetch`. */
|
|
4
4
|
export type RpcFetch = (input: URL, init: RequestInit) => Promise<Response>;
|
|
5
|
+
/** Worker-local opener for decoded Gateway Remote streams. */
|
|
6
|
+
export type RpcStreamOpen = (endpoint: string, payload: unknown, signal: AbortSignal) => AsyncIterable<unknown>;
|
|
5
7
|
/**
|
|
6
8
|
* Create the browser-backed generic RPC caller.
|
|
7
9
|
* @param doFetch - transport override; defaults to the page's global fetch.
|
|
10
|
+
* @param openStream - optional worker-local Gateway stream carrier.
|
|
8
11
|
* @returns caller that owns request correlation and response-envelope validation.
|
|
9
12
|
*/
|
|
10
|
-
export declare function createWebConnectionRpc(doFetch?: RpcFetch): ClientConnectionRpc;
|
|
13
|
+
export declare function createWebConnectionRpc(doFetch?: RpcFetch, openStream?: RpcStreamOpen): ClientConnectionRpc;
|
|
11
14
|
//# sourceMappingURL=rpc.d.ts.map
|
|
@@ -19,7 +19,7 @@ export interface FetchHandler {
|
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
21
|
* Bridge one node:http request to the fetch-shaped handler (client close
|
|
22
|
-
* aborts;
|
|
22
|
+
* aborts; response bodies stream out chunk by chunk).
|
|
23
23
|
* @param req - incoming node:http request (fully read before dispatch).
|
|
24
24
|
* @param res - node:http response the bridge writes and owns to completion.
|
|
25
25
|
* @param apiHandler - fetch-shaped API carrier the request is dispatched to.
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/** Host HTTP bridge for browser-client RPC. */
|
|
2
2
|
import type { Context } from '@deepseek-ai/cordis';
|
|
3
3
|
import z from '@deepseek-ai/schemastery';
|
|
4
|
-
export type {
|
|
4
|
+
export type { ConnectionFetchMethod, ConnectionFetchHandler, ConnectionFetchRoute, ConnectionIndexRequest, ConnectionIndexResponse, ConnectionRpcEndpointMatcher, ConnectionRpcFailure, ConnectionRpcHandler, ConnectionRequestRejection, ConnectionRpcResult, ConnectionTrustRequest, ClientRequest, HostConnectionHandle, HostConnectionFetch, HostConnectionRpc, RpcMessage, ServerResponse, } from './rpc.ts';
|
|
5
|
+
export { RpcId, transportError } from './rpc.ts';
|
|
6
|
+
export { clientRequestSchema, rpcErrorSchema, rpcIdSchema, rpcMessageSchema, rpcResultSchema, serverResponseSchema, } from './rpc-schema.ts';
|
|
5
7
|
export { HostConnectionService } from './rpc-host.ts';
|
|
6
|
-
export { API_PATH
|
|
8
|
+
export { API_PATH } from './api-path.ts';
|
|
7
9
|
/** Stable Cordis plugin name. */
|
|
8
10
|
export declare const name = "client-connection";
|
|
9
|
-
/** Services required before providing Connection
|
|
11
|
+
/** Services required before providing Connection. */
|
|
10
12
|
export declare const inject: string[];
|
|
11
13
|
/** Plugin config: the deployment's non-loopback serving authorities. */
|
|
12
14
|
export interface ConnectionConfig {
|
|
@@ -15,22 +17,22 @@ export interface ConnectionConfig {
|
|
|
15
17
|
* port-less `host` matching any port. The /api trust fence refuses any
|
|
16
18
|
* request whose Host is neither loopback nor listed here, so a
|
|
17
19
|
* non-loopback (`0.0.0.0`) deployment must declare the names it is reached
|
|
18
|
-
* by
|
|
19
|
-
* that is not a bare, canonical authority fails
|
|
20
|
+
* by; the Web runtime derives LAN IP literals from an active all-interface
|
|
21
|
+
* bind. An entry that is not a bare, canonical authority fails plugin load.
|
|
20
22
|
*/
|
|
21
23
|
trustedHosts?: string[];
|
|
24
|
+
/** Absolute browser-session lifetime in days. Default: 30. */
|
|
25
|
+
cookieMaxAgeDays?: number;
|
|
22
26
|
/** Maximum buffered JSON body for every `/api` request. Default: 300 MiB. */
|
|
23
27
|
maxRequestBodyBytes?: number;
|
|
24
28
|
}
|
|
25
29
|
export declare const Config: z<ConnectionConfig>;
|
|
26
30
|
/**
|
|
27
31
|
* Mounts the API gateway under the browser transport prefix. Every request on
|
|
28
|
-
* the prefix passes the browser-trust fence
|
|
29
|
-
*
|
|
30
|
-
* privileged methods additionally pass it with an empty trust list, which
|
|
31
|
-
* pins them to loopback.
|
|
32
|
+
* the prefix passes the Host/Origin browser-trust fence and persistent browser
|
|
33
|
+
* authentication before dispatch.
|
|
32
34
|
* @param ctx - Host plugin context.
|
|
33
35
|
* @param config - resolved plugin config (schema defaults applied).
|
|
34
36
|
*/
|
|
35
|
-
export declare function apply(ctx: Context, config?: ConnectionConfig): void
|
|
37
|
+
export declare function apply(ctx: Context, config?: ConnectionConfig): Promise<void>;
|
|
36
38
|
//# sourceMappingURL=index.d.ts.map
|
package/lib/types/rpc-host.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** Host registry and HTTP adapter for generic Connection RPC channels. */
|
|
2
2
|
import { Context, Service } from '@deepseek-ai/cordis';
|
|
3
|
-
import {
|
|
4
|
-
import type { HostConnectionHandle, HostConnectionRpc } from './rpc.ts';
|
|
3
|
+
import type { BrowserAuth } from './browser-auth.ts';
|
|
4
|
+
import type { ConnectionIndexRequest, ConnectionIndexResponse, ConnectionFetchHandler, HostConnectionFetch, ConnectionRequestRejection, ConnectionTrustRequest, HostConnectionHandle, HostConnectionRpc } from './rpc.ts';
|
|
5
5
|
declare module '@deepseek-ai/cordis' {
|
|
6
6
|
interface Context {
|
|
7
7
|
/** Host Connection transport and RPC registrations. */
|
|
@@ -11,22 +11,33 @@ declare module '@deepseek-ai/cordis' {
|
|
|
11
11
|
/** Host Connection service whose channel registrations belong to the caller fiber. */
|
|
12
12
|
export declare class HostConnectionService extends Service implements HostConnectionHandle {
|
|
13
13
|
private readonly trustedHosts;
|
|
14
|
+
private readonly browserAuth;
|
|
14
15
|
private readonly interceptors;
|
|
16
|
+
private readonly fetchRoutes;
|
|
15
17
|
/**
|
|
16
18
|
* Provide the Host half over the active HTTP server.
|
|
17
19
|
* @param ctx - owning Connection plugin context.
|
|
18
|
-
* @param trustedHosts - deployment authorities accepted by
|
|
20
|
+
* @param trustedHosts - deployment authorities accepted by the Host/Origin fence.
|
|
21
|
+
* @param browserAuth - process token and persistent browser-session owner.
|
|
19
22
|
*/
|
|
20
|
-
constructor(ctx: Context, trustedHosts: readonly string[]);
|
|
23
|
+
constructor(ctx: Context, trustedHosts: readonly string[], browserAuth: BrowserAuth);
|
|
21
24
|
/** Generic channel registry scoped to the Context reading this service. */
|
|
22
25
|
get rpc(): HostConnectionRpc;
|
|
26
|
+
/** Exact Fetch-route registry scoped to the Context reading this service. */
|
|
27
|
+
get fetch(): HostConnectionFetch;
|
|
28
|
+
/** Apply the configured Host/Origin fence, then browser authentication. */
|
|
29
|
+
requestRejection(request: ConnectionTrustRequest): ConnectionRequestRejection;
|
|
30
|
+
/** Authenticate an index request through the process-token exchange or cookie. */
|
|
31
|
+
authorizeIndex(request: ConnectionIndexRequest, response: ConnectionIndexResponse): boolean;
|
|
32
|
+
/** Add this process's launch token to the clean application URL. */
|
|
33
|
+
authenticatedUrl(baseUrl: string): string;
|
|
23
34
|
/**
|
|
24
|
-
* Compose one shared-channel Fetch handler from
|
|
35
|
+
* Compose one shared-channel Fetch handler from exact routes and its interceptor.
|
|
25
36
|
* @param channel - shared channel mounted by Connection.
|
|
26
|
-
* @
|
|
27
|
-
* @returns Fetch handler that selects exactly one target for each request.
|
|
37
|
+
* @returns Fetch handler that selects one owner or returns 404.
|
|
28
38
|
*/
|
|
29
|
-
createSharedFetchHandler(channel: '/api'
|
|
39
|
+
createSharedFetchHandler(channel: '/api'): ConnectionFetchHandler;
|
|
40
|
+
private registerFetchRoute;
|
|
30
41
|
private register;
|
|
31
42
|
private registerInterceptor;
|
|
32
43
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Runtime validation for Connection RPC envelopes. */
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { ClientRequest, RpcId, RpcMessage, ServerResponse } from './rpc.ts';
|
|
4
|
+
/** Correlation id after wire validation. */
|
|
5
|
+
export declare const rpcIdSchema: z.ZodType<RpcId>;
|
|
6
|
+
/** Generic endpoint failure carried in a response envelope. */
|
|
7
|
+
export declare const rpcErrorSchema: z.ZodObject<{
|
|
8
|
+
code: z.ZodString;
|
|
9
|
+
message: z.ZodString;
|
|
10
|
+
details: z.ZodRecord<z.ZodString, z.ZodUnknown>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
/**
|
|
13
|
+
* Build the result parser for one endpoint value parser.
|
|
14
|
+
* @param value - endpoint-owned success-value parser.
|
|
15
|
+
* @returns parser for either a success value or generic failure.
|
|
16
|
+
*/
|
|
17
|
+
export declare function rpcResultSchema<T>(value: z.ZodType<T>): z.ZodType<{
|
|
18
|
+
readonly ok: true;
|
|
19
|
+
readonly value: T;
|
|
20
|
+
} | {
|
|
21
|
+
readonly ok: false;
|
|
22
|
+
readonly error: z.infer<typeof rpcErrorSchema>;
|
|
23
|
+
}>;
|
|
24
|
+
/** Client request envelope; endpoint payload validation belongs to its owner. */
|
|
25
|
+
export declare const clientRequestSchema: z.ZodType<ClientRequest>;
|
|
26
|
+
/** Server response envelope; endpoint value validation belongs to its caller. */
|
|
27
|
+
export declare const serverResponseSchema: z.ZodType<ServerResponse>;
|
|
28
|
+
/** Either Connection RPC envelope direction. */
|
|
29
|
+
export declare const rpcMessageSchema: z.ZodType<RpcMessage>;
|
|
30
|
+
//# sourceMappingURL=rpc-schema.d.ts.map
|