@procedures/client 0.8.0 → 0.9.0
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/CHANGELOG.md +78 -0
- package/README.md +95 -8
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapters/http/client/transport.d.ts +4 -0
- package/dist/adapters/http/client/transport.d.ts.map +1 -1
- package/dist/adapters/http/client/transport.js +11 -1
- package/dist/adapters/http/client/transport.js.map +1 -1
- package/dist/adapters/http/client/types.d.ts +19 -0
- package/dist/adapters/http/client/types.d.ts.map +1 -1
- package/dist/adapters/http/server/cors.d.ts +64 -0
- package/dist/adapters/http/server/cors.d.ts.map +1 -0
- package/dist/adapters/http/server/cors.js +113 -0
- package/dist/adapters/http/server/cors.js.map +1 -0
- package/dist/adapters/http/server/handler.d.ts +136 -0
- package/dist/adapters/http/server/handler.d.ts.map +1 -0
- package/dist/adapters/http/server/handler.js +495 -0
- package/dist/adapters/http/server/handler.js.map +1 -0
- package/dist/adapters/http/server/strategies.d.ts +14 -5
- package/dist/adapters/http/server/strategies.d.ts.map +1 -1
- package/dist/adapters/http/server/strategies.js.map +1 -1
- package/dist/adapters/http/server/transport.d.ts +30 -46
- package/dist/adapters/http/server/transport.d.ts.map +1 -1
- package/dist/adapters/http/server/transport.js +48 -432
- package/dist/adapters/http/server/transport.js.map +1 -1
- package/dist/adapters/http/server/types.d.ts +13 -0
- package/dist/adapters/http/server/types.d.ts.map +1 -1
- package/dist/adapters/websocket/client/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/client/transport.js +4 -12
- package/dist/adapters/websocket/client/transport.js.map +1 -1
- package/dist/adapters/websocket/server/transport.d.ts +46 -1
- package/dist/adapters/websocket/server/transport.d.ts.map +1 -1
- package/dist/adapters/websocket/server/transport.js +152 -15
- package/dist/adapters/websocket/server/transport.js.map +1 -1
- package/dist/adapters/websocket/server/types.d.ts +10 -3
- package/dist/adapters/websocket/server/types.d.ts.map +1 -1
- package/dist/adapters/websocket/server/upgrade-router.d.ts +28 -0
- package/dist/adapters/websocket/server/upgrade-router.d.ts.map +1 -0
- package/dist/adapters/websocket/server/upgrade-router.js +62 -0
- package/dist/adapters/websocket/server/upgrade-router.js.map +1 -0
- package/dist/adapters/websocket/server/upgrade.d.ts +68 -0
- package/dist/adapters/websocket/server/upgrade.d.ts.map +1 -0
- package/dist/adapters/websocket/server/upgrade.js +49 -0
- package/dist/adapters/websocket/server/upgrade.js.map +1 -0
- package/dist/adapters/websocket/shared/rpc.d.ts +2 -0
- package/dist/adapters/websocket/shared/rpc.d.ts.map +1 -1
- package/dist/adapters/websocket/shared/rpc.js +8 -2
- package/dist/adapters/websocket/shared/rpc.js.map +1 -1
- package/dist/browser.d.ts +12 -4
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +13 -2
- package/dist/browser.js.map +1 -1
- package/dist/client/client.d.ts +43 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +69 -1
- package/dist/client/client.js.map +1 -1
- package/dist/events/publisher.d.ts +25 -1
- package/dist/events/publisher.d.ts.map +1 -1
- package/dist/events/publisher.js +66 -9
- package/dist/events/publisher.js.map +1 -1
- package/dist/events/rows.d.ts +31 -0
- package/dist/events/rows.d.ts.map +1 -0
- package/dist/events/rows.js +39 -0
- package/dist/events/rows.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/node.d.ts +14 -0
- package/dist/node.d.ts.map +1 -1
- package/dist/node.js +9 -0
- package/dist/node.js.map +1 -1
- package/dist/procedures/capability.d.ts +6 -0
- package/dist/procedures/capability.d.ts.map +1 -1
- package/dist/procedures/capability.js +25 -0
- package/dist/procedures/capability.js.map +1 -1
- package/dist/procedures/collection/changes.d.ts +94 -0
- package/dist/procedures/collection/changes.d.ts.map +1 -0
- package/dist/procedures/collection/changes.js +182 -0
- package/dist/procedures/collection/changes.js.map +1 -0
- package/dist/procedures/collection/index.d.ts +10 -3
- package/dist/procedures/collection/index.d.ts.map +1 -1
- package/dist/procedures/collection/index.js +8 -1
- package/dist/procedures/collection/index.js.map +1 -1
- package/dist/procedures/collection/partition.d.ts +67 -0
- package/dist/procedures/collection/partition.d.ts.map +1 -0
- package/dist/procedures/collection/partition.js +82 -0
- package/dist/procedures/collection/partition.js.map +1 -0
- package/dist/procedures/collection/procedures.d.ts +39 -2
- package/dist/procedures/collection/procedures.d.ts.map +1 -1
- package/dist/procedures/collection/procedures.js +147 -44
- package/dist/procedures/collection/procedures.js.map +1 -1
- package/dist/procedures/collection/query.d.ts +246 -0
- package/dist/procedures/collection/query.d.ts.map +1 -0
- package/dist/procedures/collection/query.js +854 -0
- package/dist/procedures/collection/query.js.map +1 -0
- package/dist/procedures/collection/schemas.d.ts +50 -0
- package/dist/procedures/collection/schemas.d.ts.map +1 -1
- package/dist/procedures/collection/schemas.js +144 -6
- package/dist/procedures/collection/schemas.js.map +1 -1
- package/dist/procedures/collection/storage.d.ts +58 -6
- package/dist/procedures/collection/storage.d.ts.map +1 -1
- package/dist/procedures/collection/storage.js +23 -0
- package/dist/procedures/collection/storage.js.map +1 -1
- package/dist/procedures/elicit.d.ts +190 -0
- package/dist/procedures/elicit.d.ts.map +1 -0
- package/dist/procedures/elicit.js +127 -0
- package/dist/procedures/elicit.js.map +1 -0
- package/dist/procedures/hints.d.ts +43 -0
- package/dist/procedures/hints.d.ts.map +1 -0
- package/dist/procedures/hints.js +74 -0
- package/dist/procedures/hints.js.map +1 -0
- package/dist/procedures/index.d.ts +9 -2
- package/dist/procedures/index.d.ts.map +1 -1
- package/dist/procedures/index.js +5 -1
- package/dist/procedures/index.js.map +1 -1
- package/dist/procedures/invoke.d.ts +24 -2
- package/dist/procedures/invoke.d.ts.map +1 -1
- package/dist/procedures/invoke.js +14 -1
- package/dist/procedures/invoke.js.map +1 -1
- package/dist/procedures/progress.d.ts +24 -0
- package/dist/procedures/progress.d.ts.map +1 -0
- package/dist/procedures/progress.js +11 -0
- package/dist/procedures/progress.js.map +1 -0
- package/dist/procedures/registry.d.ts.map +1 -1
- package/dist/procedures/registry.js +5 -0
- package/dist/procedures/registry.js.map +1 -1
- package/dist/procedures/types.d.ts +55 -0
- package/dist/procedures/types.d.ts.map +1 -1
- package/dist/procedures/types.js.map +1 -1
- package/dist/procedures/untrusted.d.ts +28 -0
- package/dist/procedures/untrusted.d.ts.map +1 -0
- package/dist/procedures/untrusted.js +32 -0
- package/dist/procedures/untrusted.js.map +1 -0
- package/dist/server/browser-guard.d.ts +12 -2
- package/dist/server/browser-guard.d.ts.map +1 -1
- package/dist/server/browser-guard.js +43 -6
- package/dist/server/browser-guard.js.map +1 -1
- package/dist/server/host-policy.d.ts +10 -1
- package/dist/server/host-policy.d.ts.map +1 -1
- package/dist/server/host-policy.js +5 -0
- package/dist/server/host-policy.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/procedure-server.d.ts +68 -7
- package/dist/server/procedure-server.d.ts.map +1 -1
- package/dist/server/procedure-server.js +124 -24
- package/dist/server/procedure-server.js.map +1 -1
- package/dist/server/request-auth.d.ts +51 -4
- package/dist/server/request-auth.d.ts.map +1 -1
- package/dist/server/request-auth.js +116 -5
- package/dist/server/request-auth.js.map +1 -1
- package/dist/server/types.d.ts +73 -5
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/package.json +7 -3
- package/src/adapters/http/client/transport.ts +11 -1
- package/src/adapters/http/client/types.ts +22 -0
- package/src/adapters/http/server/cors.ts +144 -0
- package/src/adapters/http/server/handler.ts +624 -0
- package/src/adapters/http/server/strategies.ts +16 -6
- package/src/adapters/http/server/transport.ts +55 -478
- package/src/adapters/http/server/types.ts +15 -0
- package/src/adapters/websocket/client/transport.ts +4 -12
- package/src/adapters/websocket/server/transport.ts +166 -16
- package/src/adapters/websocket/server/types.ts +11 -3
- package/src/adapters/websocket/server/upgrade-router.ts +77 -0
- package/src/adapters/websocket/server/upgrade.ts +88 -0
- package/src/adapters/websocket/shared/rpc.ts +9 -2
- package/src/browser.ts +85 -2
- package/src/client/client.ts +83 -1
- package/src/events/publisher.ts +75 -11
- package/src/events/rows.ts +48 -0
- package/src/index.ts +1 -1
- package/src/node.ts +21 -0
- package/src/procedures/capability.ts +21 -0
- package/src/procedures/collection/changes.ts +245 -0
- package/src/procedures/collection/index.ts +57 -1
- package/src/procedures/collection/partition.ts +123 -0
- package/src/procedures/collection/procedures.ts +206 -51
- package/src/procedures/collection/query.ts +1009 -0
- package/src/procedures/collection/schemas.ts +196 -7
- package/src/procedures/collection/storage.ts +78 -6
- package/src/procedures/elicit.ts +320 -0
- package/src/procedures/hints.ts +91 -0
- package/src/procedures/index.ts +83 -2
- package/src/procedures/invoke.ts +33 -2
- package/src/procedures/progress.ts +25 -0
- package/src/procedures/registry.ts +4 -0
- package/src/procedures/types.ts +65 -0
- package/src/procedures/untrusted.ts +50 -0
- package/src/server/browser-guard.ts +48 -6
- package/src/server/host-policy.ts +13 -1
- package/src/server/index.ts +3 -0
- package/src/server/procedure-server.ts +147 -26
- package/src/server/request-auth.ts +128 -6
- package/src/server/types.ts +82 -5
|
@@ -3,9 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Adapts WebSocket connections to unified RPC format.
|
|
5
5
|
* Supports bidirectional streaming and persistent connections.
|
|
6
|
+
*
|
|
7
|
+
* The transport runs the `WebSocketServer` of `ws` in its "no server" mode (CLI-3). `upgrade()`
|
|
8
|
+
* takes the upgrade requests of its path. With the option `server`, the transport attaches
|
|
9
|
+
* `upgrade()` to that server through one router for each server, so two endpoints of different
|
|
10
|
+
* paths can share a server. Without it, the owner of the listener gives the upgrades to
|
|
11
|
+
* `upgrade()`, or mounts `upgradeHandler()` of `@procedures/client/node`.
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
14
|
import type { IncomingMessage } from "http";
|
|
15
|
+
import type { Duplex } from "stream";
|
|
9
16
|
import { WebSocketServer, WebSocket } from "ws";
|
|
10
17
|
import type { ServerTransport, ServerRequest } from "../../../server/types.js";
|
|
11
18
|
import type { Message, ResponseItem, Transport } from "../../../client/types.js";
|
|
@@ -19,9 +26,11 @@ import type {
|
|
|
19
26
|
ConnectionEventHandler,
|
|
20
27
|
} from "./types.js";
|
|
21
28
|
import { withoutInternalKeys } from "../../metadata.js";
|
|
22
|
-
import { checkBrowserRequest, type BrowserGuardOptions } from "../../../server/browser-guard.js";
|
|
29
|
+
import { assertNamedOrigins, checkBrowserRequest, needsNamedOrigin, type BrowserGuardOptions } from "../../../server/browser-guard.js";
|
|
23
30
|
import { optionalMs } from "../../../duration.js";
|
|
24
|
-
import {
|
|
31
|
+
import { headerNames, requestCredentials, urlParts, withoutSecretHeaders } from "../../../server/request-auth.js";
|
|
32
|
+
import { attachUpgradeTarget, refuseUpgrade } from "./upgrade-router.js";
|
|
33
|
+
import { realClock, type Cancel } from "../../../clock.js";
|
|
25
34
|
import { PROTOCOL_VERSION, protocolVersionOf } from "../../../protocol.js";
|
|
26
35
|
import { TIMER_DEFAULTS } from "../../../timers.js";
|
|
27
36
|
import {
|
|
@@ -64,11 +73,25 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
64
73
|
|
|
65
74
|
private wss: WebSocketServer | null = null;
|
|
66
75
|
private options: Required<
|
|
67
|
-
Omit<
|
|
76
|
+
Omit<
|
|
77
|
+
WebSocketServerTransportOptions,
|
|
78
|
+
"server" | "authenticate" | "onConnection" | "allowedOrigins" | "allowedHosts" | "heartbeat" | "reverseRequestTimeout" | "secretHeaders"
|
|
79
|
+
>
|
|
68
80
|
> & {
|
|
81
|
+
server: WebSocketServerTransportOptions["server"];
|
|
69
82
|
authenticate?: WebSocketServerTransportOptions["authenticate"];
|
|
70
83
|
onConnection?: WebSocketServerTransportOptions["onConnection"];
|
|
71
84
|
};
|
|
85
|
+
/** The more headers that carry a secret (CLI-2a) */
|
|
86
|
+
private readonly secretHeaders: ReadonlySet<string>;
|
|
87
|
+
/** The function that detaches `upgrade()` from the `server` of the options */
|
|
88
|
+
private detach: (() => void) | undefined;
|
|
89
|
+
/** True after `stopIntake()`: a new upgrade gets 503, and a new call gets `UNAVAILABLE` */
|
|
90
|
+
private intakeStopped = false;
|
|
91
|
+
/** The functions that `drain()` waits on: each one runs when no call is in flight */
|
|
92
|
+
private idleWaiters: Array<() => void> = [];
|
|
93
|
+
/** True: a cookie of an upgrade is trusted only from a named origin, with or without an auth path */
|
|
94
|
+
private cookiesNamed = false;
|
|
72
95
|
private server: RequestHandler;
|
|
73
96
|
private connections: Set<WebSocket> = new Set();
|
|
74
97
|
|
|
@@ -103,7 +126,7 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
103
126
|
private connectHandlers: ConnectionEventHandler[] = [];
|
|
104
127
|
private disconnectHandlers: ConnectionEventHandler[] = [];
|
|
105
128
|
|
|
106
|
-
constructor(server: RequestHandler, options: WebSocketServerTransportOptions) {
|
|
129
|
+
constructor(server: RequestHandler, options: WebSocketServerTransportOptions = {}) {
|
|
107
130
|
this.server = server;
|
|
108
131
|
this.options = {
|
|
109
132
|
server: options.server,
|
|
@@ -116,6 +139,9 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
116
139
|
highWaterMark: options.highWaterMark ?? 1024 * 1024,
|
|
117
140
|
};
|
|
118
141
|
this.guard = { allowedOrigins: options.allowedOrigins, allowedHosts: options.allowedHosts };
|
|
142
|
+
// CSRF: a server that reads the cookies of each request needs named origins (CLI-2a)
|
|
143
|
+
assertNamedOrigins("WebSocketServerTransport", server.ambientCredentials, options.allowedOrigins, "allowedOrigins");
|
|
144
|
+
this.secretHeaders = headerNames(options.secretHeaders);
|
|
119
145
|
this.reverseRequestTimeoutMs = optionalMs(options.reverseRequestTimeout, "reverseRequestTimeout") ?? TIMER_DEFAULTS.webSocketServer.reverseRequestTimeoutMs;
|
|
120
146
|
this.heartbeat =
|
|
121
147
|
options.heartbeat === false
|
|
@@ -149,9 +175,21 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
149
175
|
}
|
|
150
176
|
|
|
151
177
|
async start(): Promise<void> {
|
|
152
|
-
|
|
178
|
+
this.open();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* This method creates the WebSocket server at once, and attaches it to the `server` of the
|
|
183
|
+
* options. `start()` calls it, and `upgradeHandler()` too. A second call does nothing.
|
|
184
|
+
*
|
|
185
|
+
* @internal
|
|
186
|
+
*/
|
|
187
|
+
open(): void {
|
|
188
|
+
this.intakeStopped = false;
|
|
189
|
+
if (this.wss) return;
|
|
190
|
+
// The "no server" mode: upgrade() takes the upgrades of the path, and leaves the others alone
|
|
153
191
|
this.wss = new WebSocketServer({
|
|
154
|
-
|
|
192
|
+
noServer: true,
|
|
155
193
|
path: this.options.path,
|
|
156
194
|
perMessageDeflate: this.options.perMessageDeflate,
|
|
157
195
|
maxPayload: this.options.maxPayload,
|
|
@@ -163,7 +201,11 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
163
201
|
{ req }: { req: IncomingMessage },
|
|
164
202
|
callback: (result: boolean, code?: number, message?: string) => void
|
|
165
203
|
) => {
|
|
166
|
-
|
|
204
|
+
// The cookies of an upgrade are trusted only from a named origin, when the server or the
|
|
205
|
+
// authenticate handler can read them (cross-site WebSocket hijacking with cookies)
|
|
206
|
+
const readsCookies = this.cookiesNamed || this.options.authenticate !== undefined;
|
|
207
|
+
const ambient = needsNamedOrigin(this.server.ambientCredentials, req.headers, readsCookies);
|
|
208
|
+
const guard = checkBrowserRequest(req, this.guard, false, ambient);
|
|
167
209
|
if (!guard.ok) {
|
|
168
210
|
callback(false, guard.status, guard.message);
|
|
169
211
|
return;
|
|
@@ -185,6 +227,7 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
185
227
|
this.wss.on("connection", (ws: WebSocket, req: IncomingMessage) => {
|
|
186
228
|
this.handleConnection(ws, req);
|
|
187
229
|
});
|
|
230
|
+
if (this.options.server) this.detach = attachUpgradeTarget(this.options.server, this);
|
|
188
231
|
|
|
189
232
|
if (this.heartbeat) {
|
|
190
233
|
this.heartbeatTimer = setInterval(() => this.beat(), this.heartbeat.interval);
|
|
@@ -200,6 +243,8 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
200
243
|
clearInterval(this.heartbeatTimer);
|
|
201
244
|
this.heartbeatTimer = undefined;
|
|
202
245
|
}
|
|
246
|
+
this.detach?.();
|
|
247
|
+
this.detach = undefined;
|
|
203
248
|
if (this.wss) {
|
|
204
249
|
// Stop the requests in progress, so their handlers end (deep dive TRN-15), and fail the
|
|
205
250
|
// calls that the server sent to its clients
|
|
@@ -208,6 +253,7 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
208
253
|
channel.outgoing.failAll("Server stopped");
|
|
209
254
|
}
|
|
210
255
|
this.channels.clear();
|
|
256
|
+
this.callSettled();
|
|
211
257
|
|
|
212
258
|
// Close all connections
|
|
213
259
|
for (const ws of this.connections) {
|
|
@@ -234,6 +280,93 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
234
280
|
return this.wss !== null;
|
|
235
281
|
}
|
|
236
282
|
|
|
283
|
+
/**
|
|
284
|
+
* This method makes the transport trust a cookie of an upgrade only from a named origin, also
|
|
285
|
+
* without an auth path that reads it. `upgradeHandler()` calls it.
|
|
286
|
+
*
|
|
287
|
+
* @internal
|
|
288
|
+
*/
|
|
289
|
+
nameOriginsForCookies(): void {
|
|
290
|
+
this.cookiesNamed = true;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** The path of the WebSocket endpoint. */
|
|
294
|
+
get path(): string {
|
|
295
|
+
return this.options.path;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/** This method gives true when an upgrade request is for the path of this endpoint. */
|
|
299
|
+
matches(req: IncomingMessage): boolean {
|
|
300
|
+
return urlParts(req.url).path === this.options.path;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* This method takes an upgrade request of its path and gives true. The browser checks, the
|
|
305
|
+
* `authenticate` handler and the handshake of `ws` follow. It gives false for another path, or
|
|
306
|
+
* before `start()`, and does not touch the socket. After `stopIntake()`, it refuses the upgrade
|
|
307
|
+
* with 503.
|
|
308
|
+
*/
|
|
309
|
+
upgrade(req: IncomingMessage, socket: Duplex, head: Buffer): boolean {
|
|
310
|
+
const wss = this.wss;
|
|
311
|
+
if (!wss || !this.matches(req)) return false;
|
|
312
|
+
if (this.intakeStopped) {
|
|
313
|
+
refuseUpgrade(socket, 503, "The server is stopping");
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
wss.handleUpgrade(req, socket, head, (ws) => wss.emit("connection", ws, req));
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* This method stops the intake: a new upgrade gets 503, and a new call on an open connection gets
|
|
322
|
+
* the retryable error `UNAVAILABLE`. The calls in flight go on. `start()` accepts again.
|
|
323
|
+
*/
|
|
324
|
+
stopIntake(): void {
|
|
325
|
+
this.intakeStopped = true;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** The number of the calls in flight on all connections, open streams too. */
|
|
329
|
+
inFlight(): number {
|
|
330
|
+
let count = 0;
|
|
331
|
+
for (const channel of this.channels.values()) count += channel.incoming.size;
|
|
332
|
+
return count;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* This method waits until no call is in flight, until the `deadline` (milliseconds on the
|
|
337
|
+
* monotonic clock of `performance.now()`), or until the signal aborts.
|
|
338
|
+
*/
|
|
339
|
+
drain(deadline: number, signal?: AbortSignal): Promise<void> {
|
|
340
|
+
if (this.inFlight() === 0 || signal?.aborted) return Promise.resolve();
|
|
341
|
+
return new Promise<void>((resolve) => {
|
|
342
|
+
let timer: Cancel | undefined;
|
|
343
|
+
const done = (): void => {
|
|
344
|
+
timer?.();
|
|
345
|
+
signal?.removeEventListener("abort", done);
|
|
346
|
+
const index = this.idleWaiters.indexOf(done);
|
|
347
|
+
if (index >= 0) this.idleWaiters.splice(index, 1);
|
|
348
|
+
resolve();
|
|
349
|
+
};
|
|
350
|
+
this.idleWaiters.push(done);
|
|
351
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
352
|
+
timer = realClock.after(Math.max(0, deadline - realClock.now()), done);
|
|
353
|
+
timer.unref?.();
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/** This method stops the transport: the same as `stop()`. */
|
|
358
|
+
close(): Promise<void> {
|
|
359
|
+
return this.stop();
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** A call ended: a drain can end when no call is in flight. */
|
|
363
|
+
private callSettled(): void {
|
|
364
|
+
if (this.idleWaiters.length === 0 || this.inFlight() > 0) return;
|
|
365
|
+
const waiters = this.idleWaiters;
|
|
366
|
+
this.idleWaiters = [];
|
|
367
|
+
for (const waiter of waiters) waiter();
|
|
368
|
+
}
|
|
369
|
+
|
|
237
370
|
/**
|
|
238
371
|
* Handle new WebSocket connection.
|
|
239
372
|
*/
|
|
@@ -249,10 +382,13 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
249
382
|
connectedAt: new Date(),
|
|
250
383
|
metadata: {
|
|
251
384
|
remoteAddress: req.socket.remoteAddress,
|
|
252
|
-
headers: withoutSecretHeaders(req.headers as Record<string, unknown
|
|
385
|
+
headers: withoutSecretHeaders(req.headers as Record<string, unknown>, this.secretHeaders),
|
|
253
386
|
},
|
|
254
387
|
};
|
|
255
388
|
this.trackedConnections.set(connectionId, trackedConnection);
|
|
389
|
+
// The credentials of the upgrade, for each call of the connection: the bearer token of its
|
|
390
|
+
// Authorization header or of its token query, its cookies, its headers and its query (CLI-2a)
|
|
391
|
+
const credentials = requestCredentials({ headers: req.headers as Record<string, unknown>, url: req.url }, { tokenQuery: "token" });
|
|
256
392
|
|
|
257
393
|
// The calls of this connection in the two directions (roadmap R24)
|
|
258
394
|
const isOpen = (): boolean => ws.readyState === WebSocket.OPEN;
|
|
@@ -263,7 +399,8 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
263
399
|
send,
|
|
264
400
|
sendWithBackpressure: (frame, signal) => this.sendWithBackpressure(ws, frame, signal),
|
|
265
401
|
isOpen,
|
|
266
|
-
requestOf: (frame, signal) => this.requestOf(frame, req, connectionId, signal),
|
|
402
|
+
requestOf: (frame, signal) => this.requestOf(frame, req, connectionId, signal, credentials),
|
|
403
|
+
onSettled: () => this.callSettled(),
|
|
267
404
|
}),
|
|
268
405
|
outgoing: new OutgoingCalls({
|
|
269
406
|
send,
|
|
@@ -309,6 +446,7 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
309
446
|
channel?.incoming.abortAll();
|
|
310
447
|
channel?.outgoing.failAll("WebSocket connection closed");
|
|
311
448
|
this.channels.delete(connectionId);
|
|
449
|
+
this.callSettled();
|
|
312
450
|
this.eventTransports.delete(connectionId);
|
|
313
451
|
const conn = this.trackedConnections.get(connectionId);
|
|
314
452
|
if (conn) {
|
|
@@ -336,7 +474,13 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
336
474
|
* The server request of a `request` frame of a connection: the metadata of the frame and of the
|
|
337
475
|
* connection, the signal of the call, and the credentials of the upgrade (roadmap R18).
|
|
338
476
|
*/
|
|
339
|
-
private requestOf(
|
|
477
|
+
private requestOf(
|
|
478
|
+
frame: WebSocketMessage,
|
|
479
|
+
req: IncomingMessage,
|
|
480
|
+
connectionId: string,
|
|
481
|
+
signal: AbortSignal,
|
|
482
|
+
credentials: ServerRequest["credentials"]
|
|
483
|
+
): ServerRequest {
|
|
340
484
|
return {
|
|
341
485
|
id: frame.id,
|
|
342
486
|
method: frame.method!,
|
|
@@ -348,11 +492,12 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
348
492
|
connectionId,
|
|
349
493
|
remoteAddress: req.socket.remoteAddress,
|
|
350
494
|
// The upgrade headers without their secrets (roadmap R18)
|
|
351
|
-
headers: withoutSecretHeaders(req.headers as Record<string, unknown
|
|
495
|
+
headers: withoutSecretHeaders(req.headers as Record<string, unknown>, this.secretHeaders),
|
|
352
496
|
},
|
|
353
497
|
signal,
|
|
354
|
-
// The credentials of the connection: the bearer token of its upgrade (R18)
|
|
355
|
-
|
|
498
|
+
// The credentials of the connection: the bearer token of its upgrade (R18), its cookies,
|
|
499
|
+
// its headers and its query (CLI-2a)
|
|
500
|
+
credentials,
|
|
356
501
|
};
|
|
357
502
|
}
|
|
358
503
|
|
|
@@ -401,6 +546,11 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
401
546
|
if (!channel) return;
|
|
402
547
|
// A frame of a call that the server sent to this client (roadmap R24)
|
|
403
548
|
if (channel.outgoing.receive(message)) return;
|
|
549
|
+
// After stopIntake(), a new call goes to another server or comes again later
|
|
550
|
+
if (this.intakeStopped && message.type === "request") {
|
|
551
|
+
this.sendError(ws, message.id || "unknown", "The server is stopping", "UNAVAILABLE", true);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
404
554
|
// A frame of a call of the client: a request, a cancel or a credit
|
|
405
555
|
if (channel.incoming.receive(message)) return;
|
|
406
556
|
// A server-response frame of an older client, for a call that no longer exists
|
|
@@ -436,7 +586,7 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
436
586
|
/**
|
|
437
587
|
* Send error to client.
|
|
438
588
|
*/
|
|
439
|
-
private sendError(ws: WebSocket, id: string, message: string, code = "INTERNAL_ERROR"): void {
|
|
589
|
+
private sendError(ws: WebSocket, id: string, message: string, code = "INTERNAL_ERROR", retryable = false): void {
|
|
440
590
|
const errorMessage: WebSocketMessage = {
|
|
441
591
|
id,
|
|
442
592
|
type: "error",
|
|
@@ -444,12 +594,12 @@ export class WebSocketServerTransport implements ServerTransport {
|
|
|
444
594
|
type: "error",
|
|
445
595
|
code,
|
|
446
596
|
message,
|
|
447
|
-
retryable
|
|
597
|
+
retryable,
|
|
448
598
|
},
|
|
449
599
|
error: {
|
|
450
600
|
code,
|
|
451
601
|
message,
|
|
452
|
-
retryable
|
|
602
|
+
retryable,
|
|
453
603
|
},
|
|
454
604
|
};
|
|
455
605
|
|
|
@@ -29,10 +29,11 @@ export type WebSocketConnectionHandler = (ws: WebSocket, req: IncomingMessage) =
|
|
|
29
29
|
*/
|
|
30
30
|
export interface WebSocketServerTransportOptions extends Omit<BrowserGuardOptions, "allowGet"> {
|
|
31
31
|
/**
|
|
32
|
-
* HTTP server
|
|
33
|
-
*
|
|
32
|
+
* The HTTP server whose upgrade requests the transport takes. The transport takes only the
|
|
33
|
+
* upgrades of its path, so several endpoints can share one server. Without it, the owner of the
|
|
34
|
+
* listener gives each upgrade to `upgrade()`, or mounts `upgradeHandler()`.
|
|
34
35
|
*/
|
|
35
|
-
server
|
|
36
|
+
server?: HttpServer | undefined;
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
39
|
* Path for WebSocket connections.
|
|
@@ -91,6 +92,13 @@ export interface WebSocketServerTransportOptions extends Omit<BrowserGuardOption
|
|
|
91
92
|
* @default seconds(30)
|
|
92
93
|
*/
|
|
93
94
|
reverseRequestTimeout?: Duration;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* More headers that carry a secret, for example the token header of an application. They go
|
|
98
|
+
* into the credentials of each call, not into its metadata. `Authorization`,
|
|
99
|
+
* `Proxy-Authorization`, `Cookie` and `X-Api-Key` are always secret.
|
|
100
|
+
*/
|
|
101
|
+
secretHeaders?: readonly string[] | undefined;
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
/**
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The router of the upgrade requests of one `node:http` server (CLI-3).
|
|
3
|
+
*
|
|
4
|
+
* Before, each `WebSocketServerTransport` attached its own `WebSocketServer` of `ws` to the server.
|
|
5
|
+
* Such a server refuses each upgrade of another path with 400, so a second endpoint on the same
|
|
6
|
+
* server did not get its upgrades. With this router, each transport of the server is a target of
|
|
7
|
+
* one router. The router gives an upgrade to the first target that takes it. When no target takes it and no other
|
|
8
|
+
* listener of the server can take it, the router refuses it with 400, as before.
|
|
9
|
+
*
|
|
10
|
+
* This module imports only types of Node.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { IncomingMessage, Server as HttpServer } from "http";
|
|
14
|
+
import type { Duplex } from "stream";
|
|
15
|
+
|
|
16
|
+
/** A target of the router: it takes an upgrade (true), or leaves it alone (false). */
|
|
17
|
+
export interface UpgradeTarget {
|
|
18
|
+
upgrade(req: IncomingMessage, socket: Duplex, head: Buffer): boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** The reason texts of the statuses of a refused upgrade. */
|
|
22
|
+
const STATUS_TEXT: Record<number, string> = {
|
|
23
|
+
400: "Bad Request",
|
|
24
|
+
401: "Unauthorized",
|
|
25
|
+
403: "Forbidden",
|
|
26
|
+
503: "Service Unavailable",
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* This function refuses an upgrade: it writes an HTTP response with the status and the message,
|
|
31
|
+
* then it destroys the socket.
|
|
32
|
+
*/
|
|
33
|
+
export function refuseUpgrade(socket: Duplex, status: number, message?: string): void {
|
|
34
|
+
if (socket.writable) {
|
|
35
|
+
const text = STATUS_TEXT[status] ?? "Error";
|
|
36
|
+
const body = message ?? text;
|
|
37
|
+
socket.write(
|
|
38
|
+
`HTTP/1.1 ${status} ${text}\r\nConnection: close\r\nContent-Type: text/plain; charset=utf-8\r\nContent-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
socket.destroy();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface Router {
|
|
45
|
+
readonly targets: Set<UpgradeTarget>;
|
|
46
|
+
readonly listener: (req: IncomingMessage, socket: Duplex, head: Buffer) => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const routers = new WeakMap<HttpServer, Router>();
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* This function attaches a target to the upgrades of a server. It gives the function that
|
|
53
|
+
* detaches it. The router leaves the server when its last target goes.
|
|
54
|
+
*/
|
|
55
|
+
export function attachUpgradeTarget(server: HttpServer, target: UpgradeTarget): () => void {
|
|
56
|
+
let router = routers.get(server);
|
|
57
|
+
if (!router) {
|
|
58
|
+
const targets = new Set<UpgradeTarget>();
|
|
59
|
+
const listener = (req: IncomingMessage, socket: Duplex, head: Buffer): void => {
|
|
60
|
+
for (const each of targets) if (each.upgrade(req, socket, head)) return;
|
|
61
|
+
// Another listener of the server (the upgrade handler of the application) can take it
|
|
62
|
+
if (server.listenerCount("upgrade") <= 1) refuseUpgrade(socket, 400);
|
|
63
|
+
};
|
|
64
|
+
router = { targets, listener };
|
|
65
|
+
routers.set(server, router);
|
|
66
|
+
server.on("upgrade", listener);
|
|
67
|
+
}
|
|
68
|
+
const current = router;
|
|
69
|
+
current.targets.add(target);
|
|
70
|
+
return () => {
|
|
71
|
+
current.targets.delete(target);
|
|
72
|
+
if (current.targets.size === 0 && routers.get(server) === current) {
|
|
73
|
+
server.off("upgrade", current.listener);
|
|
74
|
+
routers.delete(server);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The procedure upgrade handler of WebSocket: an upgrade handler of `node:http` for the "no
|
|
3
|
+
* server" mode of `ws` (CLI-3).
|
|
4
|
+
*
|
|
5
|
+
* `upgradeHandler(server, options)` gives a function `(req, socket, head) => boolean`. The function
|
|
6
|
+
* takes each upgrade request of its path and gives `true`. It gives `false` for each other path and
|
|
7
|
+
* does not touch the socket. Thus two endpoints can share one listener, and so can an endpoint and
|
|
8
|
+
* the WebSocket server of the application. The connections are the connections of
|
|
9
|
+
* `WebSocketServerTransport`: calls in the two directions, streams with credit and `seq`, events,
|
|
10
|
+
* the heartbeat and the browser checks. `handler.transport` gives that API.
|
|
11
|
+
*
|
|
12
|
+
* The handler also has the stop parts of a service transport: `start()`, `stopIntake()`,
|
|
13
|
+
* `drain(deadline, signal)`, `inFlight()` and `close()`. It does not make a server and does not
|
|
14
|
+
* listen: the owner of the listener does that.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { IncomingMessage } from "http";
|
|
18
|
+
import type { Duplex } from "stream";
|
|
19
|
+
import type { RequestHandler } from "../../../server/types.js";
|
|
20
|
+
import type { WebSocketServerTransportOptions } from "./types.js";
|
|
21
|
+
import { WebSocketServerTransport } from "./transport.js";
|
|
22
|
+
|
|
23
|
+
/** The options of `upgradeHandler`: the options of `WebSocketServerTransport` without `server`. */
|
|
24
|
+
export type UpgradeHandlerOptions = Omit<WebSocketServerTransportOptions, "server">;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* The upgrade handler of `upgradeHandler`. A call gives `true` when the handler takes the upgrade
|
|
28
|
+
* (its path is the path of the endpoint), and `false` for another path. The handler then does not
|
|
29
|
+
* touch the socket.
|
|
30
|
+
*/
|
|
31
|
+
export interface NodeUpgradeHandler {
|
|
32
|
+
(req: IncomingMessage, socket: Duplex, head: Buffer): boolean;
|
|
33
|
+
/** The path of the endpoint. */
|
|
34
|
+
readonly path: string;
|
|
35
|
+
/**
|
|
36
|
+
* The transport of the connections: `broadcast`, `callConnection`, `transportFor`, `subscribe`,
|
|
37
|
+
* `events`, `onConnect` and the other methods of `WebSocketServerTransport`.
|
|
38
|
+
*/
|
|
39
|
+
readonly transport: WebSocketServerTransport;
|
|
40
|
+
/** This method gives true when an upgrade request is for the path of the endpoint. */
|
|
41
|
+
matches(req: IncomingMessage): boolean;
|
|
42
|
+
/** This method accepts new upgrades and calls again, after `stopIntake()` or `close()`. */
|
|
43
|
+
start(): void;
|
|
44
|
+
/** This method refuses each new upgrade with 503, and each new call with `UNAVAILABLE`. */
|
|
45
|
+
stopIntake(): void;
|
|
46
|
+
/**
|
|
47
|
+
* This method waits until no call is in flight, until the `deadline` (milliseconds on the
|
|
48
|
+
* monotonic clock of `performance.now()`), or until the signal aborts.
|
|
49
|
+
*/
|
|
50
|
+
drain(deadline: number, signal?: AbortSignal): Promise<void>;
|
|
51
|
+
/** The number of the calls in flight on all connections. */
|
|
52
|
+
inFlight(): number;
|
|
53
|
+
/** This method closes each connection and the WebSocket server. */
|
|
54
|
+
close(): Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* This function makes a procedure upgrade handler: an upgrade handler of `node:http` that serves
|
|
59
|
+
* the procedures of `server` over WebSocket at `options.path`.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const events = upgradeHandler(server, { path: "/ws" });
|
|
64
|
+
* const live = upgradeHandler(server, { path: "/live" });
|
|
65
|
+
* http.on("upgrade", (req, socket, head) => {
|
|
66
|
+
* if (events(req, socket, head) || live(req, socket, head)) return;
|
|
67
|
+
* socket.destroy();
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export function upgradeHandler(server: RequestHandler, options: UpgradeHandlerOptions = {}): NodeUpgradeHandler {
|
|
72
|
+
const { server: _ignored, ...rest } = options as WebSocketServerTransportOptions;
|
|
73
|
+
const transport = new WebSocketServerTransport(server, rest);
|
|
74
|
+
// A new face: a cookie is trusted only from a named origin (CSRF)
|
|
75
|
+
transport.nameOriginsForCookies();
|
|
76
|
+
transport.open();
|
|
77
|
+
const handler = ((req: IncomingMessage, socket: Duplex, head: Buffer): boolean => transport.upgrade(req, socket, head)) as NodeUpgradeHandler;
|
|
78
|
+
return Object.assign(handler, {
|
|
79
|
+
path: transport.path,
|
|
80
|
+
transport,
|
|
81
|
+
matches: (req: IncomingMessage): boolean => transport.matches(req),
|
|
82
|
+
start: (): void => transport.open(),
|
|
83
|
+
stopIntake: (): void => transport.stopIntake(),
|
|
84
|
+
drain: (deadline: number, signal?: AbortSignal): Promise<void> => transport.drain(deadline, signal),
|
|
85
|
+
inFlight: (): number => transport.inFlight(),
|
|
86
|
+
close: (): Promise<void> => transport.close(),
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -317,6 +317,8 @@ export interface IncomingCallsOptions {
|
|
|
317
317
|
isOpen: () => boolean;
|
|
318
318
|
/** This function makes the server request of a `request` frame: the metadata, the credentials. */
|
|
319
319
|
requestOf: (frame: WebSocketMessage, signal: AbortSignal) => ServerRequest;
|
|
320
|
+
/** This function runs when the number of calls that run becomes smaller. A drain waits on it. */
|
|
321
|
+
onSettled?: (() => void) | undefined;
|
|
320
322
|
}
|
|
321
323
|
|
|
322
324
|
/** The calls that the other side sends on one socket, served by a handler. */
|
|
@@ -340,7 +342,7 @@ export class IncomingCalls {
|
|
|
340
342
|
receive(frame: WebSocketMessage): boolean {
|
|
341
343
|
if (frame.type === "cancel") {
|
|
342
344
|
this.inflight.get(frame.id)?.abort();
|
|
343
|
-
this.inflight.delete(frame.id);
|
|
345
|
+
if (this.inflight.delete(frame.id)) this.options.onSettled?.();
|
|
344
346
|
return true;
|
|
345
347
|
}
|
|
346
348
|
if (frame.type === "credit") {
|
|
@@ -365,8 +367,10 @@ export class IncomingCalls {
|
|
|
365
367
|
/** This function aborts each call that runs: the socket closed, or the side stops. */
|
|
366
368
|
abortAll(): void {
|
|
367
369
|
for (const controller of this.inflight.values()) controller.abort();
|
|
370
|
+
const had = this.inflight.size > 0;
|
|
368
371
|
this.inflight.clear();
|
|
369
372
|
this.credits.clear();
|
|
373
|
+
if (had) this.options.onSettled?.();
|
|
370
374
|
}
|
|
371
375
|
|
|
372
376
|
private async serve(frame: WebSocketMessage): Promise<void> {
|
|
@@ -395,7 +399,10 @@ export class IncomingCalls {
|
|
|
395
399
|
this.sendError(frame.id, error instanceof Error ? error.message : "Internal server error", "INTERNAL_ERROR");
|
|
396
400
|
} finally {
|
|
397
401
|
// Delete only this request's entries (deep dive TRN-9)
|
|
398
|
-
if (this.inflight.get(frame.id) === controller)
|
|
402
|
+
if (this.inflight.get(frame.id) === controller) {
|
|
403
|
+
this.inflight.delete(frame.id);
|
|
404
|
+
this.options.onSettled?.();
|
|
405
|
+
}
|
|
399
406
|
this.credits.delete(frame.id);
|
|
400
407
|
}
|
|
401
408
|
}
|
package/src/browser.ts
CHANGED
|
@@ -76,8 +76,9 @@ export {
|
|
|
76
76
|
export { Server } from "./server/server.js";
|
|
77
77
|
export type { ServerOptions } from "./server/server.js";
|
|
78
78
|
export { ProcedureServer, createProcedureServer, STREAM_METADATA_KEY, isUnboundedStream, tokenMatches } from "./server/procedure-server.js";
|
|
79
|
-
// The credentials of a network request (roadmap R18)
|
|
80
|
-
export { credentialsOf, bearerOf, withoutSecretHeaders } from "./server/request-auth.js";
|
|
79
|
+
// The credentials of a network request (roadmap R18), and the parts that a request hook reads (CLI-2a)
|
|
80
|
+
export { credentialsOf, bearerOf, withoutSecretHeaders, cookiesOf, requestCredentials } from "./server/request-auth.js";
|
|
81
|
+
export type { RequestCredentialsOptions } from "./server/request-auth.js";
|
|
81
82
|
// The procedures of a host: host.health, host.info, host.metrics (roadmap R19)
|
|
82
83
|
export { hostProcedures } from "./server/host-procedures.js";
|
|
83
84
|
export type { HostSource } from "./server/host-procedures.js";
|
|
@@ -93,6 +94,9 @@ export type {
|
|
|
93
94
|
RequestHandler,
|
|
94
95
|
Credentials,
|
|
95
96
|
Principal,
|
|
97
|
+
PrincipalOf,
|
|
98
|
+
RequestIdentity,
|
|
99
|
+
AmbientCredentials,
|
|
96
100
|
ServerHandler,
|
|
97
101
|
ServerMiddleware,
|
|
98
102
|
ServerContext,
|
|
@@ -117,6 +121,27 @@ export {
|
|
|
117
121
|
pathToKey,
|
|
118
122
|
keyToPath,
|
|
119
123
|
createCollectionProcedures,
|
|
124
|
+
// The change events of the collection writes (roadmap CLI-15)
|
|
125
|
+
collectionChangeTopic,
|
|
126
|
+
collectionTopicReads,
|
|
127
|
+
// The partitions of a collection (roadmap CLI-17)
|
|
128
|
+
PARTITION_PATTERN,
|
|
129
|
+
collectionTopicPartition,
|
|
130
|
+
// The query language of the collection storages (roadmap CLI-14, CLI-34)
|
|
131
|
+
DEFAULT_FIND_LIMIT,
|
|
132
|
+
DEFAULT_QUERY_OPERATORS,
|
|
133
|
+
DEFAULT_QUERY_POLICY,
|
|
134
|
+
QUERY_OPERATORS,
|
|
135
|
+
applyPatch,
|
|
136
|
+
compareStorageValues,
|
|
137
|
+
compileSelector,
|
|
138
|
+
isPatchOperations,
|
|
139
|
+
matchesSelector,
|
|
140
|
+
mergePatch,
|
|
141
|
+
patchProblems,
|
|
142
|
+
queryEntries,
|
|
143
|
+
queryOptionsProblems,
|
|
144
|
+
selectorProblems,
|
|
120
145
|
// Manual registration helpers
|
|
121
146
|
registerModule,
|
|
122
147
|
registerProcedures,
|
|
@@ -235,6 +260,28 @@ export type {
|
|
|
235
260
|
CollectionStorage,
|
|
236
261
|
StorageChanges,
|
|
237
262
|
StorageMetadata,
|
|
263
|
+
StorageWriteOptions,
|
|
264
|
+
StorageSetChange,
|
|
265
|
+
StorageDeleteChange,
|
|
266
|
+
StorageClearChange,
|
|
267
|
+
StorageChangeEvent,
|
|
268
|
+
StorageChangeBatch,
|
|
269
|
+
StorageObserver,
|
|
270
|
+
StorageFieldOperators,
|
|
271
|
+
StoragePatch,
|
|
272
|
+
StorageQueryOptions,
|
|
273
|
+
StorageSelector,
|
|
274
|
+
StorageSort,
|
|
275
|
+
StorageSortDirection,
|
|
276
|
+
StoragePatchOperations,
|
|
277
|
+
StorageQueryOperator,
|
|
278
|
+
StorageQueryPolicy,
|
|
279
|
+
CollectionProceduresOptions,
|
|
280
|
+
CollectionChangeEventsOptions,
|
|
281
|
+
CollectionPartitions,
|
|
282
|
+
PartitionInput,
|
|
283
|
+
PartitionResolver,
|
|
284
|
+
CollectionChangeEvent,
|
|
238
285
|
ProcedureModule,
|
|
239
286
|
ModuleInput,
|
|
240
287
|
Caller,
|
|
@@ -335,6 +382,42 @@ export { TIMEOUT_METADATA_KEY, DEADLINE_METADATA_KEY } from "./client/deadline.j
|
|
|
335
382
|
// Typed durations: each public option that is a time takes a Duration (roadmap R11)
|
|
336
383
|
export { ms, seconds, minutes, hours, toMs, optionalMs, isDuration } from "./duration.js";
|
|
337
384
|
export type { Duration, TimeUnit } from "./duration.js";
|
|
385
|
+
// What the face of a call offers the procedure: the questions to the person at the caller, the
|
|
386
|
+
// progress, and the text from outside the host (CLI-10, CLI-12)
|
|
387
|
+
export {
|
|
388
|
+
createElicit,
|
|
389
|
+
elicitWithPolicy,
|
|
390
|
+
operatorAnswer,
|
|
391
|
+
elicitInput,
|
|
392
|
+
elicitConfirmation,
|
|
393
|
+
NO_ELICITATION_REASON,
|
|
394
|
+
} from "./procedures/elicit.js";
|
|
395
|
+
export type {
|
|
396
|
+
Elicit,
|
|
397
|
+
ElicitRequest,
|
|
398
|
+
ElicitFormRequest,
|
|
399
|
+
ElicitUrlRequest,
|
|
400
|
+
ElicitMode,
|
|
401
|
+
ElicitAction,
|
|
402
|
+
ElicitResult,
|
|
403
|
+
ElicitOptions,
|
|
404
|
+
ElicitField,
|
|
405
|
+
ElicitFieldBase,
|
|
406
|
+
ElicitTextField,
|
|
407
|
+
ElicitNumberField,
|
|
408
|
+
ElicitBooleanField,
|
|
409
|
+
ElicitChoiceField,
|
|
410
|
+
ElicitChoicesField,
|
|
411
|
+
ElicitValue,
|
|
412
|
+
ElicitationPolicy,
|
|
413
|
+
ConfirmationOptions,
|
|
414
|
+
ConfirmationResult,
|
|
415
|
+
} from "./procedures/elicit.js";
|
|
416
|
+
export type { ProgressUpdate, ReportProgress } from "./procedures/progress.js";
|
|
417
|
+
export { toolHintsOf, toolHintOverrideOf, toolHintOverrideRefusal } from "./procedures/hints.js";
|
|
418
|
+
export type { ToolHintOverride } from "./procedures/hints.js";
|
|
419
|
+
export { untrustedText, untrustedFieldsOf, untrustedTextsOf } from "./procedures/untrusted.js";
|
|
420
|
+
export type { UntrustedText, UntrustedSource, UntrustedValue } from "./procedures/untrusted.js";
|
|
338
421
|
// One manifest type (roadmap R17)
|
|
339
422
|
export { MANIFEST_FORMAT, describeProcedure as describeProcedureEntry, describeRegistry, exposedProcedures, hashOf } from "./procedures/manifest.js";
|
|
340
423
|
export type { ProcedureEntry, ProcedureManifest, CliDescription, DescribeOptions, DescribeRegistryOptions } from "./procedures/manifest.js";
|