@zhin.js/host-http 1.1.17 → 1.2.1
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 +60 -21
- package/lib/access-control.d.ts +127 -0
- package/lib/access-control.js +148 -0
- package/lib/addresses.d.ts +9 -0
- package/lib/addresses.js +9 -0
- package/lib/bridge/bridged-host.d.ts +43 -0
- package/lib/bridge/bridged-host.js +497 -0
- package/lib/bridge/control-channel.d.ts +59 -0
- package/lib/bridge/control-channel.js +131 -0
- package/lib/bridge/flow.d.ts +58 -0
- package/lib/bridge/flow.js +188 -0
- package/lib/bridge/index.d.ts +6 -0
- package/lib/bridge/index.js +6 -0
- package/lib/bridge/instance-transport.d.ts +112 -0
- package/lib/bridge/instance-transport.js +532 -0
- package/lib/bridge/protocol.d.ts +242 -0
- package/lib/bridge/protocol.js +369 -0
- package/lib/console-events.d.ts +5 -3
- package/lib/console-events.js +53 -27
- package/lib/console-plugin-rpc.d.ts +12 -1
- package/lib/console-plugin-rpc.js +16 -66
- package/lib/console-rest-pages-helpers.d.ts +4 -5
- package/lib/console-rest-pages-helpers.js +62 -79
- package/lib/console-rest-pages.js +80 -65
- package/lib/console-rpc-extended/dispatcher.js +1 -6
- package/lib/console-rpc-handlers.d.ts +0 -2
- package/lib/console-rpc-handlers.js +2 -40
- package/lib/console-rpc.d.ts +6 -18
- package/lib/console-rpc.js +6 -6
- package/lib/host-installer.d.ts +6 -3
- package/lib/host-installer.js +10 -40
- package/lib/host-options.d.ts +47 -0
- package/lib/host-options.js +1 -0
- package/lib/http-host.d.ts +124 -48
- package/lib/http-host.js +319 -332
- package/lib/index.d.ts +11 -6
- package/lib/index.js +10 -6
- package/lib/json-body.d.ts +1 -2
- package/lib/json-body.js +1 -2
- package/lib/marketplace-registry.d.ts +17 -0
- package/lib/marketplace-registry.js +52 -0
- package/lib/node/index.d.ts +10 -0
- package/lib/node/index.js +10 -0
- package/lib/{listener-group.d.ts → node/listener-group.d.ts} +2 -3
- package/lib/{listener-group.js → node/listener-group.js} +6 -10
- package/lib/node/listener.d.ts +26 -0
- package/lib/node/listener.js +231 -0
- package/lib/node/node-http.d.ts +38 -0
- package/lib/node/node-http.js +272 -0
- package/lib/node/server.d.ts +13 -0
- package/lib/node/server.js +64 -0
- package/lib/protocol-auth.d.ts +24 -0
- package/lib/protocol-auth.js +48 -0
- package/lib/public-api.d.ts +1 -1
- package/lib/token-registry.d.ts +54 -36
- package/lib/token-registry.js +133 -126
- package/lib/trust-proxy.d.ts +22 -0
- package/lib/trust-proxy.js +168 -0
- package/package.json +14 -6
- package/src/access-control.ts +255 -0
- package/src/addresses.ts +16 -0
- package/src/bridge/bridged-host.ts +548 -0
- package/src/bridge/control-channel.ts +173 -0
- package/src/bridge/flow.ts +208 -0
- package/src/bridge/index.ts +31 -0
- package/src/bridge/instance-transport.ts +627 -0
- package/src/bridge/protocol.ts +574 -0
- package/src/console-events.ts +59 -28
- package/src/console-plugin-rpc.ts +17 -77
- package/src/console-rest-pages-helpers.ts +60 -82
- package/src/console-rest-pages.ts +87 -67
- package/src/console-rpc-extended/dispatcher.ts +1 -6
- package/src/console-rpc-handlers.ts +2 -31
- package/src/console-rpc.ts +10 -24
- package/src/host-installer.ts +12 -44
- package/src/host-options.ts +47 -0
- package/src/http-host.ts +467 -421
- package/src/index.ts +56 -14
- package/src/json-body.ts +7 -2
- package/src/marketplace-registry.ts +62 -0
- package/src/node/index.ts +21 -0
- package/src/{listener-group.ts → node/listener-group.ts} +13 -24
- package/src/node/listener.ts +265 -0
- package/src/node/node-http.ts +263 -0
- package/src/node/server.ts +82 -0
- package/src/protocol-auth.ts +52 -0
- package/src/public-api.ts +2 -0
- package/src/token-registry.ts +143 -146
- package/src/trust-proxy.ts +178 -0
package/lib/http-host.d.ts
CHANGED
|
@@ -1,14 +1,40 @@
|
|
|
1
|
-
import { type IncomingMessage, type ServerResponse } from 'node:http';
|
|
2
|
-
import { type ServerOptions as HttpsServerOptions } from 'node:https';
|
|
3
|
-
import { WebSocket } from 'ws';
|
|
4
1
|
import { type GenerationAdmissionBindable } from '@zhin.js/plugin-runtime';
|
|
5
|
-
import {
|
|
2
|
+
import { type HttpRouteContext } from '@zhin.js/host-http-contract';
|
|
3
|
+
import { TokenRegistry } from './token-registry.js';
|
|
6
4
|
import { type ListedRoute, type RouteMeta } from './openapi.js';
|
|
5
|
+
import { type ConsolePrincipal } from './access-control.js';
|
|
6
|
+
import type { HttpHostAddress } from './host-options.js';
|
|
7
|
+
export type { HttpHostAddress } from './host-options.js';
|
|
7
8
|
export declare const REMOTE_CONSOLE_ORIGIN = "https://console.zhin.dev";
|
|
9
|
+
/** Per-request data resolved by the Host before a route runs. */
|
|
10
|
+
export interface RouteContext extends HttpRouteContext {
|
|
11
|
+
readonly url: URL;
|
|
12
|
+
readonly params: Readonly<Record<string, string>>;
|
|
13
|
+
/**
|
|
14
|
+
* Console principal the Host authenticated. Set for `{apiBase}` routes and for public
|
|
15
|
+
* routes that presented a valid credential; absent otherwise.
|
|
16
|
+
*/
|
|
17
|
+
readonly principal?: ConsolePrincipal;
|
|
18
|
+
readonly remoteAddress?: string;
|
|
19
|
+
}
|
|
20
|
+
export type HttpHandler = (request: Request, context: RouteContext) => Response | Promise<Response>;
|
|
21
|
+
export interface HttpRouteRegistration {
|
|
22
|
+
(): void;
|
|
23
|
+
}
|
|
24
|
+
/** An accepted WebSocket, independent of the transport that carries it. */
|
|
8
25
|
export interface WsConnection {
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
11
|
-
|
|
26
|
+
readonly url: URL;
|
|
27
|
+
readonly headers: Headers;
|
|
28
|
+
/** Authenticated principal (absent for `auth: 'protocol'` routes, which authenticate themselves). */
|
|
29
|
+
readonly principal?: ConsolePrincipal;
|
|
30
|
+
readonly remoteAddress?: string;
|
|
31
|
+
/** Text frames as strings, binary frames as bytes. Dropped once the socket is closing. */
|
|
32
|
+
send(data: string | Uint8Array): void;
|
|
33
|
+
close(code?: number, reason?: string): void;
|
|
34
|
+
onMessage(listener: (data: string | Uint8Array) => void): () => void;
|
|
35
|
+
onClose(listener: (code: number, reason: string) => void): () => void;
|
|
36
|
+
/** Bytes queued but not yet sent (backpressure). */
|
|
37
|
+
readonly bufferedAmount: number;
|
|
12
38
|
}
|
|
13
39
|
export interface WsHandle {
|
|
14
40
|
onConnection(listener: (connection: WsConnection) => void): () => void;
|
|
@@ -21,28 +47,20 @@ export interface WsRouteOptions {
|
|
|
21
47
|
*/
|
|
22
48
|
readonly auth?: 'host' | 'protocol';
|
|
23
49
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
readonly
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
readonly key: NonNullable<HttpsServerOptions['key']>;
|
|
33
|
-
readonly cert: NonNullable<HttpsServerOptions['cert']>;
|
|
34
|
-
readonly ca?: HttpsServerOptions['ca'];
|
|
35
|
-
readonly passphrase?: string;
|
|
36
|
-
readonly minVersion?: HttpsServerOptions['minVersion'];
|
|
37
|
-
readonly ciphers?: string;
|
|
50
|
+
/** @public Stable HTTP and WebSocket Host contract resolved through `httpHostToken`. */
|
|
51
|
+
export interface HttpHost {
|
|
52
|
+
ws(path: string, options?: WsRouteOptions): WsHandle;
|
|
53
|
+
route(method: string, path: string, handler: HttpHandler, meta?: RouteMeta): HttpRouteRegistration;
|
|
54
|
+
listRoutes(): readonly ListedRoute[];
|
|
55
|
+
get address(): HttpHostAddress | undefined;
|
|
56
|
+
/** @internal Console authentication registry owned by the process Host. */
|
|
57
|
+
get tokenRegistry(): TokenRegistry;
|
|
38
58
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
/**
|
|
59
|
+
/** @public Stable Plugin Runtime HTTP Host token. */
|
|
60
|
+
export declare const httpHostToken: import("@zhin.js/plugin-runtime").Token<HttpHost>;
|
|
61
|
+
export interface HttpRouterOptions {
|
|
62
|
+
/** Bootstrap Bearer token (`http.token`), accepted only until the first owner exists. */
|
|
43
63
|
readonly token?: string;
|
|
44
|
-
/** Additional scoped tokens (`http.tokens`). */
|
|
45
|
-
readonly tokens?: readonly ScopedTokenConfig[];
|
|
46
64
|
/** CORS allowlist; always merged with Remote Console origin. */
|
|
47
65
|
readonly corsOrigins?: readonly string[];
|
|
48
66
|
/**
|
|
@@ -52,28 +70,86 @@ export interface HttpHostOptions {
|
|
|
52
70
|
readonly apiBase?: string;
|
|
53
71
|
/** Extra HTTP path prefixes that skip auth even under `apiBase`. */
|
|
54
72
|
readonly authExemptPaths?: readonly string[];
|
|
55
|
-
/**
|
|
56
|
-
readonly
|
|
73
|
+
/** Shared registry for listener groups; `token` is ignored when set. */
|
|
74
|
+
readonly tokenRegistry?: TokenRegistry;
|
|
75
|
+
/** Whether the carrying transport is TLS (OpenAPI server URL default). */
|
|
76
|
+
readonly secure?: boolean;
|
|
77
|
+
/** Bound listener address as reported by the transport. */
|
|
78
|
+
readonly address?: () => HttpHostAddress | undefined;
|
|
79
|
+
/**
|
|
80
|
+
* Who authenticates requests (ADR 0054 §3):
|
|
81
|
+
* - `credentials` (default): the router resolves the Bearer / `?token=` credential through
|
|
82
|
+
* {@link TokenRegistry}. A `principal` passed by the transport is ignored.
|
|
83
|
+
* - `forwarded`: the carrying transport already authenticated the request (the gateway, over
|
|
84
|
+
* the bridge) and passes the principal in {@link HttpRequestInfo.principal} /
|
|
85
|
+
* {@link WsUpgradeRequest.principal}. The router never parses credentials itself but still
|
|
86
|
+
* authorizes every `{apiBase}` route and host-authenticated socket against the Console
|
|
87
|
+
* permission map, exactly as in `credentials` mode. A missing principal is anonymous.
|
|
88
|
+
*/
|
|
89
|
+
readonly authentication?: 'credentials' | 'forwarded';
|
|
90
|
+
/**
|
|
91
|
+
* Register the built-in `/pub/health` and `/pub/openapi.json` routes (default true). The
|
|
92
|
+
* gateway turns them off: it owns its own health probe and forwards the instance's catalog.
|
|
93
|
+
*/
|
|
94
|
+
readonly builtinRoutes?: boolean;
|
|
57
95
|
}
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
96
|
+
export interface HttpRequestInfo {
|
|
97
|
+
readonly remoteAddress?: string;
|
|
98
|
+
/**
|
|
99
|
+
* An in-process caller with no network peer (tests, embedding). Only such an explicit caller is
|
|
100
|
+
* admitted to an open Host without a loopback `remoteAddress`.
|
|
101
|
+
*/
|
|
102
|
+
readonly inProcess?: boolean;
|
|
103
|
+
/** Pre-authenticated principal; honoured only by a `forwarded` router. */
|
|
104
|
+
readonly principal?: ConsolePrincipal;
|
|
61
105
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
/** @internal Console authentication registry owned by the process Host. */
|
|
69
|
-
get tokenRegistry(): TokenRegistry;
|
|
106
|
+
export interface WsUpgradeRequest {
|
|
107
|
+
readonly url: URL;
|
|
108
|
+
readonly headers: Headers;
|
|
109
|
+
readonly remoteAddress?: string;
|
|
110
|
+
/** Pre-authenticated principal; honoured only by a `forwarded` router. */
|
|
111
|
+
readonly principal?: ConsolePrincipal;
|
|
70
112
|
}
|
|
71
|
-
/**
|
|
72
|
-
export interface
|
|
73
|
-
|
|
74
|
-
|
|
113
|
+
/** A registered WebSocket path and how it authenticates (admission-active only). */
|
|
114
|
+
export interface ListedWsRoute {
|
|
115
|
+
readonly path: string;
|
|
116
|
+
readonly auth: 'host' | 'protocol';
|
|
75
117
|
}
|
|
76
|
-
/**
|
|
77
|
-
export
|
|
78
|
-
|
|
79
|
-
|
|
118
|
+
/** The transport half of a {@link WsConnection}. */
|
|
119
|
+
export interface WsTransport {
|
|
120
|
+
send(data: string | Uint8Array): void;
|
|
121
|
+
close(code?: number, reason?: string): void;
|
|
122
|
+
onMessage(listener: (data: string | Uint8Array) => void): () => void;
|
|
123
|
+
onClose(listener: (code: number, reason: string) => void): () => void;
|
|
124
|
+
readonly bufferedAmount: number;
|
|
125
|
+
}
|
|
126
|
+
export type WsUpgradeDecision = Readonly<{
|
|
127
|
+
accepted: false;
|
|
128
|
+
/** 404 drops the socket silently; other statuses are written as a bare HTTP response. */
|
|
129
|
+
status: 401 | 404 | 503;
|
|
130
|
+
/** Extra response headers for the refusal (e.g. `Retry-After`). */
|
|
131
|
+
headers?: Readonly<Record<string, string>>;
|
|
132
|
+
}> | Readonly<{
|
|
133
|
+
accepted: true;
|
|
134
|
+
/** Hand the established socket to the route listeners. */
|
|
135
|
+
accept(transport: WsTransport): void;
|
|
136
|
+
/** The transport failed to complete the handshake. */
|
|
137
|
+
reject(): void;
|
|
138
|
+
}>;
|
|
139
|
+
/** @internal Transport-facing router; `createHttpHost` (node) and the gateway bridge drive it. */
|
|
140
|
+
export interface HttpRouter extends HttpHost, GenerationAdmissionBindable<HttpHost> {
|
|
141
|
+
handle(request: Request, info?: HttpRequestInfo): Promise<Response>;
|
|
142
|
+
upgrade(request: WsUpgradeRequest): Promise<WsUpgradeDecision>;
|
|
143
|
+
readonly routeCount: number;
|
|
144
|
+
readonly wsPaths: readonly string[];
|
|
145
|
+
/** Admission-active WebSocket routes (`protocol` only when every listener uses it). */
|
|
146
|
+
listWsRoutes(): readonly ListedWsRoute[];
|
|
147
|
+
/** Called after any route or socket registration changes (not on admission changes). */
|
|
148
|
+
onRoutesChanged(listener: () => void): () => void;
|
|
149
|
+
readonly apiBase: string;
|
|
150
|
+
readonly corsOrigins: readonly string[];
|
|
151
|
+
/** Terminal: routes are dropped and every later request gets 503. */
|
|
152
|
+
close(): void;
|
|
153
|
+
}
|
|
154
|
+
export declare function createHttpRouter(options?: HttpRouterOptions): HttpRouter;
|
|
155
|
+
export declare function normalizePath(pathname: string): string;
|