@procedures/peer 0.9.2 → 0.11.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.
@@ -1,277 +1,192 @@
1
1
  /**
2
2
  * Peer Module
3
3
  *
4
- * Transport-agnostic peer implementation for bidirectional RPC.
4
+ * A peer is a procedures host with a service of its own (CLI-7), for a process that has no service
5
+ * of its own: a test, or a host that a procedure starts (`server.create`). `createPeer()` builds it
6
+ * with `createProcedureHost()`: each HTTP or WebSocket transport of the options is a face on a
7
+ * listener of the host, and the listener is an `httpTransport` of `@mark1russell7/service/http`.
8
+ *
9
+ * - `start()` declares the parts in a service and starts `run()` of `@mark1russell7/service`, on
10
+ * a process port that installs no signal handler and never ends the process.
11
+ * - `stop()` asks the lifecycle for its shutdown, and waits for the end of its stages.
12
+ *
13
+ * A process that has a service of its own declares the parts of `createProcedureHost()` with
14
+ * `host.declare(svc)`, in place of a peer (decision D2). The peer has no listener, URL strategy,
15
+ * `/health` route or token check of its own.
5
16
  */
6
- import { ProcedureServer, HttpServerTransport, WebSocketServerTransport, PROCEDURE_REGISTRY, rpcServerUrlStrategy, defaultServerUrlStrategy, defaultUrlPattern, procedureServerOptions, hostProcedures, metricsInterceptor, Client, } from "@procedures/client";
7
- import { bearerToken, hostAllowed, originAllowed, tokenMatches } from "./security.js";
17
+ import { daemon, embeddedProcess, service } from "@mark1russell7/service";
18
+ import { hostPolicy, seconds, } from "@procedures/client";
19
+ import { defaultServerUrlStrategy, rpcServerUrlStrategy } from "@procedures/client/node";
20
+ import { createProcedureHost, httpFace, LOCAL_LISTENER_KNOBS, wsFace } from "../host/index.js";
21
+ import { originAllowed } from "./security.js";
22
+ // =============================================================================
23
+ // Implementation
24
+ // =============================================================================
8
25
  /**
9
- * URL strategy that matches the client's defaultUrlPattern
10
- * Maps: POST /api/server/status → { service: "server", operation: "status" }
26
+ * The policy of a peer, with its one token. A transport token and a policy token must be the same:
27
+ * the one server examines one token for each face.
11
28
  */
12
- function patternUrlStrategy(req) {
13
- const basePath = "/api";
14
- let path = req.path;
15
- // Remove base path prefix if present
16
- if (path.startsWith(basePath)) {
17
- path = path.slice(basePath.length);
29
+ function peerPolicy(options) {
30
+ const own = new Set(options.transports.flatMap((transport) => (transport.type !== "local" && transport.token !== undefined ? [transport.token] : [])));
31
+ const policyToken = options.policy?.token;
32
+ if (own.size > 1 || (own.size === 1 && policyToken !== undefined && !own.has(policyToken))) {
33
+ throw new Error("A peer has one token: give the same token to each transport, or give it only in the policy");
18
34
  }
19
- // Use the client's defaultUrlPattern parser
20
- // Cast method to expected type (parser doesn't actually use it for this pattern)
21
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
- return defaultUrlPattern.parse(path, req.method.toUpperCase());
35
+ const token = [...own][0] ?? policyToken;
36
+ if (options.policy)
37
+ return token === policyToken ? options.policy : { ...options.policy, token };
38
+ return hostPolicy(token !== undefined ? { token } : {});
23
39
  }
24
- /** The port that a listening server got, or undefined. With port 0 the system gives a free port. */
25
- function listeningPort(server) {
26
- const address = server.address();
27
- return typeof address === "object" && address !== null ? address.port : undefined;
40
+ /** The options of the HTTP face of a transport: the rules of the peer before CLI-7, on `procedureHandler`. */
41
+ function httpOptionsOf(config, browser) {
42
+ return {
43
+ basePath: config.basePath ?? "/api",
44
+ // Off by default: before, every peer sent Access-Control-Allow-Origin: * (deep dive CLI-2)
45
+ cors: config.cors ?? false,
46
+ ...(config.urlStrategy === "rpc" ? { urlStrategy: rpcServerUrlStrategy } : config.urlStrategy === "rest" ? { urlStrategy: defaultServerUrlStrategy } : {}),
47
+ // The browser checks follow the rule of the peer: loopback origins and the listed ones
48
+ allowedOrigins: browser.allowedOrigins ?? ((origin) => originAllowed(origin, config.corsOrigins)),
49
+ ...(browser.allowedHosts !== undefined ? { allowedHosts: browser.allowedHosts } : {}),
50
+ // The RESTful strategy calls by GET
51
+ allowGet: browser.allowGet ?? config.urlStrategy === "rest",
52
+ ...(config.bodyLimit !== undefined ? { bodyLimit: config.bodyLimit } : {}),
53
+ };
28
54
  }
29
- // =============================================================================
30
- // Implementation
31
- // =============================================================================
55
+ /** The options of the WebSocket face of a transport. */
56
+ function wsOptionsOf(config, browser) {
57
+ return {
58
+ path: config.path ?? "/ws",
59
+ allowedOrigins: browser.allowedOrigins ?? ((origin) => originAllowed(origin, config.origins)),
60
+ ...(browser.allowedHosts !== undefined ? { allowedHosts: browser.allowedHosts } : {}),
61
+ };
62
+ }
63
+ /**
64
+ * The faces and the listeners of the transports. Two transports on one address and port share one
65
+ * listener. Loopback is the default address, so the whole registry (`shell.run`, `fs.write`) is
66
+ * not served on each interface (BUGS-2026-07 H19). Give `host: "0.0.0.0"` on purpose for the LAN.
67
+ */
68
+ function facesOf(transports, browser) {
69
+ const faces = {};
70
+ const listeners = [];
71
+ const counts = { http: 0, websocket: 0 };
72
+ for (const transport of transports) {
73
+ if (transport.type === "local")
74
+ continue;
75
+ const kind = transport.type === "http" ? "http" : "ws";
76
+ const index = counts[transport.type]++;
77
+ const faceName = index === 0 ? kind : `${kind}${index + 1}`;
78
+ faces[faceName] =
79
+ transport.type === "http" ? httpFace(httpOptionsOf(transport, browser)) : wsFace(wsOptionsOf(transport, browser));
80
+ const port = transport.port ?? (transport.type === "http" ? 3000 : 3001);
81
+ const host = transport.host ?? "127.0.0.1";
82
+ const shared = port === 0 ? undefined : listeners.find((listener) => listener.port === port && listener.host === host);
83
+ if (shared)
84
+ shared.faces.push(faceName);
85
+ else
86
+ listeners.push({ name: faceName, port, host, faces: [faceName] });
87
+ }
88
+ return { faces, listen: listeners.map((listener) => ({ ...listener, knobs: LOCAL_LISTENER_KNOBS })) };
89
+ }
90
+ /**
91
+ * This function gives the options of a procedures host for the options of a peer: each HTTP or
92
+ * WebSocket transport is a face on a listener with `LOCAL_LISTENER_KNOBS`, and the peer has one
93
+ * token. `createPeer()` uses it, and so does `runProcedureHost()` of `@procedures/server` (CLI-8).
94
+ */
95
+ export function peerHostOptions(options, name = "peer") {
96
+ const policy = peerPolicy(options);
97
+ const { faces, listen } = facesOf(options.transports, policy.browser);
98
+ const local = options.transports.some((transport) => transport.type === "local");
99
+ return {
100
+ name,
101
+ id: options.id,
102
+ policy,
103
+ faces,
104
+ listen,
105
+ autoRegister: options.autoRegister ?? true,
106
+ ...(options.registry ? { registry: options.registry } : {}),
107
+ ...(options.expose ? { expose: options.expose } : {}),
108
+ ...(options.metrics !== undefined ? { metrics: options.metrics } : {}),
109
+ ...(local ? { endpoints: [{ type: "local", address: "local://in-process" }] } : {}),
110
+ };
111
+ }
112
+ /** The cap of the stage `drain` of the profile `daemon`. */
113
+ const DAEMON_DRAIN = daemon.phases?.find((phase) => phase.name === "drain")?.cap ?? seconds(4);
114
+ /**
115
+ * The profile of the service of a peer, as data: the caps of `daemon`, and the cooperative abort at
116
+ * the start of the drain. Thus `stop()` aborts the calls in flight at once, as `stop()` without a
117
+ * grace did before (CLI-7). A repeat signal has no effect, and the port of a peer has no signal.
118
+ */
119
+ export const PEER_PROFILE = Object.freeze({ ...daemon, cooperativeWindow: DAEMON_DRAIN, cooperativeFloor: "none" });
32
120
  class PeerImpl {
33
121
  id;
34
- server;
35
- transports;
36
- endpoints = [];
37
- started = false;
38
- /** The WebSocket transports of the peer: the calls to its clients go over them (R24) */
39
- wsTransports = [];
40
- /** The HTTP servers that the peer made for its WebSocket transports: `stop()` closes them */
41
- ownedServers = [];
42
- registry;
43
- startTime = Date.now();
44
- /** The browser checks of the policy (an origin rule here replaces the peer's own rule). */
45
- browser;
122
+ host;
123
+ profile;
124
+ life;
125
+ state = "new";
46
126
  constructor(options) {
47
127
  this.id = options.id;
48
- const policy = options.policy;
49
- // The policy's token goes to each network transport that has none of its own
50
- this.transports = options.transports.map((transport) => transport.type === "local" || policy?.token === undefined || transport.token !== undefined
51
- ? transport
52
- : { ...transport, token: policy.token });
53
- this.registry = options.registry ?? PROCEDURE_REGISTRY;
54
- this.browser = policy?.browser ?? {};
55
- // Create procedure server. The metrics interceptor counts each call for host.metrics (R19).
56
- const metrics = options.metrics === false ? undefined : metricsInterceptor();
57
- const serverOptions = policy ? procedureServerOptions(policy, this.registry) : {};
58
- const interceptors = [...(serverOptions.interceptors ?? []), ...(metrics ? [metrics.interceptor] : [])];
59
- this.server = new ProcedureServer({
60
- ...serverOptions,
61
- ...(interceptors.length > 0 ? { interceptors } : {}),
62
- autoRegister: options.autoRegister ?? true,
63
- registry: this.registry,
64
- ...(options.expose ? { expose: options.expose } : {}),
65
- });
66
- // host.health, host.info and host.metrics: pinned on the server, not in the registry (R19)
67
- this.server.registerProcedures(hostProcedures({
68
- id: this.id,
69
- startedAt: this.startTime,
70
- server: this.server,
71
- endpoints: () => this.getEndpoints(),
72
- ...(metrics ? { metrics } : {}),
73
- }));
128
+ this.host = createProcedureHost(peerHostOptions(options));
129
+ this.profile = options.profile ?? PEER_PROFILE;
74
130
  }
75
131
  async start() {
76
- if (this.started)
132
+ if (this.state === "started")
77
133
  return;
78
- try {
79
- for (const transport of this.transports) {
80
- await this.startTransport(transport);
81
- }
82
- }
83
- catch (error) {
84
- // Stop the transports that started, so a failed start leaves no port open
85
- await this.server.stop().catch(() => { });
86
- this.endpoints = [];
87
- throw error;
134
+ if (this.state === "stopped")
135
+ throw new Error("This peer stopped. A peer starts one time: create a new peer.");
136
+ this.state = "started";
137
+ const svc = this.host.declare(service(this.id).profile(this.profile));
138
+ // No check gates the policies of a peer: it is ready from the end of its startup
139
+ if (!this.profile.readiness)
140
+ svc.readiness({ type: "debounce", interval: seconds(5), failAfter: 3, recoverAfter: 1 });
141
+ if (!this.profile.liveness)
142
+ svc.liveness({ type: "window", failAfter: seconds(45) });
143
+ if (!this.profile.startup)
144
+ svc.startup(seconds(30));
145
+ if (!this.profile.onRepeatSignal)
146
+ svc.onRepeatSignal("ignore");
147
+ // The port of a lifecycle in a process that another part owns (SYS-27): no signal handler, no
148
+ // hold of the process, and an exit that only records the code
149
+ const life = await svc.run({ process: embeddedProcess() });
150
+ if (life.state !== "running") {
151
+ // The startup failed: the lifecycle stops the parts that started, so no port stays open
152
+ this.state = "stopped";
153
+ await life.done;
154
+ throw new Error(life.errors.join("; ") || "The peer did not start");
88
155
  }
89
- this.started = true;
156
+ this.life = life;
90
157
  }
91
- async startTransport(config) {
92
- switch (config.type) {
93
- case "http":
94
- await this.startHttpTransport(config);
95
- break;
96
- case "websocket":
97
- await this.startWebSocketTransport(config);
98
- break;
99
- case "local":
100
- // Local transport doesn't need network setup
101
- this.endpoints.push({
102
- type: "local",
103
- address: "local://in-process",
104
- });
105
- break;
106
- }
107
- }
108
- async startHttpTransport(config) {
109
- // Dynamic import to avoid bundling express if not used
110
- const express = (await import("express")).default;
111
- const app = express();
112
- // No body parser here: the transport reads the body itself, with bodyLimit, and accepts each
113
- // JSON value (roadmap R5). Before, the strict parser of express refused a bare value (a
114
- // number, a string, null) with 400, and its limit of 100 kB replaced bodyLimit.
115
- const port = config.port ?? 3000;
116
- // Loopback by default so the full procedure registry (incl. shell.run / fs.write) is not
117
- // served unauthenticated on all interfaces. Pass host: "0.0.0.0" to expose on the LAN
118
- // deliberately. See documentation/BUGS-2026-07.md (H19).
119
- const host = config.host ?? "127.0.0.1";
120
- const basePath = config.basePath ?? "/api";
121
- // Host, origin and token checks (deep dive TRN-1, CLI-1, CLI-2). The health endpoints need no
122
- // token: a client uses them to check that a lockfile's server is alive.
123
- app.use((req, res, next) => {
124
- if (!hostAllowed(host, req.headers.host) || !originAllowed(req.headers.origin, config.corsOrigins)) {
125
- res.status(403).json({ error: "Forbidden: the host or the origin is not allowed", code: "FORBIDDEN" });
126
- return;
127
- }
128
- const isHealth = req.path === "/health" || req.path === `${basePath}/health`;
129
- if (!isHealth && req.method !== "OPTIONS" && !tokenMatches(config.token, bearerToken(req.headers.authorization))) {
130
- res.status(401).json({ error: "Unauthorized: this server requires its token", code: "UNAUTHORIZED" });
131
- return;
132
- }
133
- next();
134
- });
135
- // Health endpoint for container orchestration
136
- app.get(`${basePath}/health`, (_req, res) => {
137
- const procedureCount = this.registry.getAll().filter((p) => p.handler).length;
138
- res.json({
139
- status: "ok",
140
- uptime: Date.now() - this.startTime,
141
- procedures: procedureCount,
142
- peerId: this.id,
143
- });
144
- });
145
- // Also add at root /health for convenience
146
- app.get("/health", (_req, res) => {
147
- const procedureCount = this.registry.getAll().filter((p) => p.handler).length;
148
- res.json({
149
- status: "ok",
150
- uptime: Date.now() - this.startTime,
151
- procedures: procedureCount,
152
- peerId: this.id,
153
- });
154
- });
155
- // Select URL strategy based on config
156
- // Default to patternUrlStrategy which matches the client's defaultUrlPattern
157
- const urlStrategy = config.urlStrategy === "rpc"
158
- ? rpcServerUrlStrategy
159
- : config.urlStrategy === "rest"
160
- ? defaultServerUrlStrategy
161
- : patternUrlStrategy;
162
- const httpTransport = new HttpServerTransport(this.server, {
163
- app,
164
- port,
165
- host,
166
- basePath,
167
- // Off by default: before, every peer sent Access-Control-Allow-Origin: * (deep dive CLI-2)
168
- cors: config.cors ?? false,
169
- urlStrategy,
170
- // The transport's browser checks follow the peer's rule: loopback origins and the listed
171
- // ones (the token is required in any case). The RESTful strategy calls by GET.
172
- allowedOrigins: this.browser.allowedOrigins ?? ((origin) => originAllowed(origin, config.corsOrigins)),
173
- ...(this.browser.allowedHosts ? { allowedHosts: this.browser.allowedHosts } : {}),
174
- allowGet: this.browser.allowGet ?? config.urlStrategy === "rest",
175
- ...(config.bodyLimit !== undefined ? { bodyLimit: config.bodyLimit } : {}),
176
- });
177
- this.server.addTransport(httpTransport);
178
- await httpTransport.start();
179
- this.endpoints.push({
180
- type: "http",
181
- // The real port: with port 0 the system gives a free port
182
- address: `http://${host === "0.0.0.0" ? "localhost" : host}:${httpTransport.port() ?? port}${basePath}`,
183
- });
184
- }
185
- async startWebSocketTransport(config) {
186
- // WebSocket transport requires an HTTP server to attach to
187
- // For standalone WebSocket, we create an HTTP server first
188
- const { createServer } = await import("http");
189
- const port = config.port ?? 3001;
190
- // Loopback by default so the full procedure registry (incl. shell.run / fs.write) is not
191
- // served unauthenticated on all interfaces. Pass host: "0.0.0.0" to expose on the LAN
192
- // deliberately. See documentation/BUGS-2026-07.md (H19).
193
- const host = config.host ?? "127.0.0.1";
194
- const path = config.path ?? "/ws";
195
- const httpServer = createServer();
196
- // Listen first, then attach the WebSocket server. A listen error (EADDRINUSE, EACCES) rejects:
197
- // before, the error had no listener, so the process ended with an unhandled 'error' event
198
- // (deep dive CLI-16).
199
- await new Promise((resolve, reject) => {
200
- const onError = (error) => {
201
- reject(new Error(`The WebSocket server cannot listen on ${host}:${port}: ${error.message}`));
202
- };
203
- httpServer.once("error", onError);
204
- httpServer.listen(port, host, () => {
205
- httpServer.off("error", onError);
206
- resolve();
207
- });
208
- });
209
- const wsTransport = new WebSocketServerTransport(this.server, {
210
- server: httpServer,
211
- path,
212
- allowedOrigins: this.browser.allowedOrigins ?? ((origin) => originAllowed(origin, config.origins)),
213
- ...(this.browser.allowedHosts ? { allowedHosts: this.browser.allowedHosts } : {}),
214
- // Host, origin and token checks: before, any web page could open a connection and call
215
- // procedures (cross-site WebSocket hijacking, deep dive TRN-1)
216
- authenticate: (req) => {
217
- const token = new URL(req.url ?? "/", "http://localhost").searchParams.get("token") ?? bearerToken(req.headers.authorization);
218
- return (hostAllowed(host, req.headers.host) &&
219
- originAllowed(req.headers.origin, config.origins) &&
220
- tokenMatches(config.token, token));
221
- },
222
- });
223
- this.server.addTransport(wsTransport);
224
- try {
225
- await wsTransport.start();
226
- }
227
- catch (error) {
228
- httpServer.close();
229
- throw error;
230
- }
231
- this.ownedServers.push(httpServer);
232
- this.wsTransports.push(wsTransport);
233
- this.endpoints.push({
234
- type: "websocket",
235
- address: `ws://${host === "0.0.0.0" ? "localhost" : host}:${listeningPort(httpServer) ?? port}${path}`,
236
- });
237
- }
238
- async stop(options = {}) {
239
- if (!this.started)
158
+ async stop(_options = {}) {
159
+ const life = this.life;
160
+ if (this.state !== "started" || life === undefined)
240
161
  return;
241
- await this.server.stop(options);
242
- // The HTTP servers of the WebSocket transports: before, they stayed open, so the port stayed
243
- // bound after stop() (roadmap R5)
244
- const owned = this.ownedServers;
245
- this.ownedServers = [];
246
- await Promise.all(owned.map((httpServer) => new Promise((resolve) => {
247
- httpServer.close(() => resolve());
248
- httpServer.closeAllConnections();
249
- })));
250
- this.started = false;
251
- this.endpoints = [];
252
- this.wsTransports = [];
162
+ this.state = "stopped";
163
+ await life.shutdown("the peer stops");
164
+ await life.done;
253
165
  }
254
166
  getEndpoints() {
255
- return [...this.endpoints];
167
+ if (this.state !== "started" || this.life === undefined)
168
+ return [];
169
+ return this.host.info.endpoints().map((endpoint) => ({ type: endpoint.type, address: endpoint.address }));
256
170
  }
257
171
  getServer() {
258
- return this.server;
172
+ return this.host.server;
173
+ }
174
+ getHost() {
175
+ return this.host;
259
176
  }
260
177
  connections() {
261
- return this.wsTransports.flatMap((transport) => transport.getTrackedConnections().map((connection) => connection.id));
178
+ return this.host.connections();
262
179
  }
263
180
  clientFor(connectionId) {
264
- const transport = this.wsTransports.find((ws) => ws.getTrackedConnection(connectionId) !== undefined);
265
- if (!transport)
266
- throw new Error(`Connection not found: ${connectionId}`);
267
- return new Client({ transport: transport.transportFor(connectionId), throwOnError: true });
181
+ return this.host.clientFor(connectionId);
268
182
  }
269
183
  }
270
184
  // =============================================================================
271
185
  // Factory
272
186
  // =============================================================================
273
187
  /**
274
- * Create a new peer instance
188
+ * Create a new peer instance: a procedures host with a service of its own, for a process that has
189
+ * no service of its own. A process with a service uses `createProcedureHost()` and `host.declare(svc)`.
275
190
  */
276
191
  export async function createPeer(options) {
277
192
  return new PeerImpl(options);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/peer/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,eAAe,EACf,mBAAmB,EACnB,wBAAwB,EAExB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,iBAAiB,EACjB,sBAAsB,EACtB,cAAc,EACd,kBAAkB,EAClB,MAAM,GAIP,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEtF;;;GAGG;AACH,SAAS,kBAAkB,CAAC,GAAY;IACtC,MAAM,QAAQ,GAAG,MAAM,CAAC;IACxB,IAAI,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IAEpB,qCAAqC;IACrC,IAAI,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC9B,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,4CAA4C;IAC5C,iFAAiF;IACjF,8DAA8D;IAC9D,OAAO,iBAAiB,CAAC,KAAK,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,WAAW,EAAS,CAAC,CAAC;AACxE,CAAC;AA8BD,oGAAoG;AACpG,SAAS,aAAa,CAAC,MAAkC;IACvD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;IACjC,OAAO,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACpF,CAAC;AA4BD,gFAAgF;AAChF,iBAAiB;AACjB,gFAAgF;AAEhF,MAAM,QAAQ;IACH,EAAE,CAAS;IACZ,MAAM,CAAkB;IACxB,UAAU,CAAoB;IAC9B,SAAS,GAAmB,EAAE,CAAC;IAC/B,OAAO,GAAG,KAAK,CAAC;IACxB,wFAAwF;IAChF,YAAY,GAA+B,EAAE,CAAC;IACtD,6FAA6F;IACrF,YAAY,GAAsC,EAAE,CAAC;IACrD,QAAQ,CAAoB;IAC5B,SAAS,GAAW,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvC,2FAA2F;IACnF,OAAO,CAAsB;IAErC,YAAY,OAAoB;QAC9B,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;QACrB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC9B,6EAA6E;QAC7E,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CACrD,SAAS,CAAC,IAAI,KAAK,OAAO,IAAI,MAAM,EAAE,KAAK,KAAK,SAAS,IAAI,SAAS,CAAC,KAAK,KAAK,SAAS;YACxF,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAC1C,CAAC;QACF,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,kBAAkB,CAAC;QACvD,IAAI,CAAC,OAAO,GAAG,MAAM,EAAE,OAAO,IAAI,EAAE,CAAC;QAErC,4FAA4F;QAC5F,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,EAAE,CAAC;QAC7E,MAAM,aAAa,GAAG,MAAM,CAAC,CAAC,CAAC,sBAAsB,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAClF,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,YAAY,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACxG,IAAI,CAAC,MAAM,GAAG,IAAI,eAAe,CAAC;YAChC,GAAG,aAAa;YAChB,GAAG,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,IAAI;YAC1C,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtD,CAAC,CAAC;QACH,2FAA2F;QAC3F,IAAI,CAAC,MAAM,CAAC,kBAAkB,CAC5B,cAAc,CAAC;YACb,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE;YACpC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChC,CAAC,CACH,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QAEzB,IAAI,CAAC;YACH,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACxC,MAAM,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,0EAA0E;YAC1E,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACzC,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;YACpB,MAAM,KAAK,CAAC;QACd,CAAC;QAED,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IACtB,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,MAAuB;QAClD,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,MAAM;gBACT,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC;gBACtC,MAAM;YACR,KAAK,WAAW;gBACd,MAAM,IAAI,CAAC,uBAAuB,CAAC,MAAM,CAAC,CAAC;gBAC3C,MAAM;YACR,KAAK,OAAO;gBACV,6CAA6C;gBAC7C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;oBAClB,IAAI,EAAE,OAAO;oBACb,OAAO,EAAE,oBAAoB;iBAC9B,CAAC,CAAC;gBACH,MAAM;QACV,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAAC,MAA2B;QAC1D,uDAAuD;QACvD,MAAM,OAAO,GAAG,CAAC,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC;QAElD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;QACtB,6FAA6F;QAC7F,wFAAwF;QACxF,gFAAgF;QAEhF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC;QACjC,yFAAyF;QACzF,sFAAsF;QACtF,yDAAyD;QACzD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,WAAW,CAAC;QACxC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC;QAE3C,8FAA8F;QAC9F,wEAAwE;QACxE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;YACzB,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;gBACnG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kDAAkD,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC;gBACvG,OAAO;YACT,CAAC;YACD,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,GAAG,QAAQ,SAAS,CAAC;YAC7E,IAAI,CAAC,QAAQ,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC;gBACjH,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,8CAA8C,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;gBACtG,OAAO;YACT,CAAC;YACD,IAAI,EAAE,CAAC;QACT,CAAC,CAAC,CAAC;QAEH,8CAA8C;QAC9C,GAAG,CAAC,GAAG,CAAC,GAAG,QAAQ,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC1C,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;YAC9E,GAAG,CAAC,IAAI,CAAC;gBACP,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS;gBACnC,UAAU,EAAE,cAAc;gBAC1B,MAAM,EAAE,IAAI,CAAC,EAAE;aAChB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,2CAA2C;QAC3C,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YAC/B,MAAM,cAAc,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;YAC9E,GAAG,CAAC,IAAI,CAAC;gBACP,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS;gBACnC,UAAU,EAAE,cAAc;gBAC1B,MAAM,EAAE,IAAI,CAAC,EAAE;aAChB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,sCAAsC;QACtC,6EAA6E;QAC7E,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,KAAK,KAAK;YAC9C,CAAC,CAAC,oBAAoB;YACtB,CAAC,CAAC,MAAM,CAAC,WAAW,KAAK,MAAM;gBAC7B,CAAC,CAAC,wBAAwB;gBAC1B,CAAC,CAAC,kBAAkB,CAAC;QAEzB,MAAM,aAAa,GAAG,IAAI,mBAAmB,CAAC,IAAI,CAAC,MAAM,EAAE;YACzD,GAAG;YACH,IAAI;YACJ,IAAI;YACJ,QAAQ;YACR,2FAA2F;YAC3F,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,KAAK;YAC1B,WAAW;YACX,yFAAyF;YACzF,+EAA+E;YAC/E,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,cAAc,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC;YACtG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjF,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,MAAM,CAAC,WAAW,KAAK,MAAM;YAChE,GAAG,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3E,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;QACxC,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC;QAE5B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAClB,IAAI,EAAE,MAAM;YACZ,0DAA0D;YAC1D,OAAO,EAAE,UAAU,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,IAAI,aAAa,CAAC,IAAI,EAAE,IAAI,IAAI,GAAG,QAAQ,EAAE;SACxG,CAAC,CAAC;IACL,CAAC;IAEO,KAAK,CAAC,uBAAuB,CAAC,MAAgC;QACpE,2DAA2D;QAC3D,2DAA2D;QAC3D,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QAE9C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC;QACjC,yFAAyF;QACzF,sFAAsF;QACtF,yDAAyD;QACzD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,WAAW,CAAC;QACxC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,KAAK,CAAC;QAElC,MAAM,UAAU,GAAG,YAAY,EAAE,CAAC;QAElC,+FAA+F;QAC/F,0FAA0F;QAC1F,sBAAsB;QACtB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YAC1C,MAAM,OAAO,GAAG,CAAC,KAAY,EAAQ,EAAE;gBACrC,MAAM,CAAC,IAAI,KAAK,CAAC,yCAAyC,IAAI,IAAI,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YAC/F,CAAC,CAAC;YACF,UAAU,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAClC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE;gBACjC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBACjC,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,MAAM,WAAW,GAAG,IAAI,wBAAwB,CAAC,IAAI,CAAC,MAAM,EAAE;YAC5D,MAAM,EAAE,UAAU;YAClB,IAAI;YACJ,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,cAAc,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YAClG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjF,uFAAuF;YACvF,+DAA+D;YAC/D,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE;gBACpB,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;gBAC9H,OAAO,CACL,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC;oBACnC,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC;oBACjD,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,CAClC,CAAC;YACJ,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QACtC,IAAI,CAAC;YACH,MAAM,WAAW,CAAC,KAAK,EAAE,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,UAAU,CAAC,KAAK,EAAE,CAAC;YACnB,MAAM,KAAK,CAAC;QACd,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACnC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAEpC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;YAClB,IAAI,EAAE,WAAW;YACjB,OAAO,EAAE,QAAQ,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,IAAI,aAAa,CAAC,UAAU,CAAC,IAAI,IAAI,GAAG,IAAI,EAAE;SACvG,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,OAAO,GAAgB,EAAE;QAClC,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAChC,6FAA6F;QAC7F,kCAAkC;QAClC,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC;QAChC,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,CAAC,GAAG,CACf,KAAK,CAAC,GAAG,CACP,CAAC,UAAU,EAAE,EAAE,CACb,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YAC5B,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YAClC,UAAU,CAAC,mBAAmB,EAAE,CAAC;QACnC,CAAC,CAAC,CACL,CACF,CAAC;QACF,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QACpB,IAAI,CAAC,YAAY,GAAG,EAAE,CAAC;IACzB,CAAC;IAED,YAAY;QACV,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;IAC7B,CAAC;IAED,SAAS;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,WAAW;QACT,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,qBAAqB,EAAE,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;IACxH,CAAC;IAED,SAAS,CAAC,YAAoB;QAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,oBAAoB,CAAC,YAAY,CAAC,KAAK,SAAS,CAAC,CAAC;QACtG,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,YAAY,EAAE,CAAC,CAAC;QACzE,OAAO,IAAI,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7F,CAAC;CACF;AAED,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAoB;IACnD,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/peer/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@procedures/peer",
4
- "version": "0.9.2",
5
- "description": "Serve procedures: HTTP and WebSocket peers with token authentication, browser checks, an expose list, server.create, server.call and server.connections, and manifests.",
4
+ "version": "0.11.0",
5
+ "description": "Serve procedures: createProcedureHost() gives the parts of a service, with HTTP, WebSocket and MCP faces on one listener. Also peers, server.create, server.call and server.connections, and manifests.",
6
6
  "keywords": [
7
7
  "server",
8
8
  "rpc",
@@ -67,23 +67,20 @@
67
67
  "provenance": true
68
68
  },
69
69
  "devDependencies": {
70
- "@procedures/fs": "0.9.2",
70
+ "@procedures/fs": "0.11.0",
71
71
  "typescript": "^7.0.2",
72
72
  "@types/node": "^22.0.0",
73
73
  "@mark1russell7/cue": "^1.0.0",
74
- "vitest": "^5.0.3",
75
- "@types/express": "^5.0.6"
74
+ "vitest": "^5.0.3"
76
75
  },
77
76
  "client": {
78
77
  "procedures": "./dist/register.js"
79
78
  },
80
79
  "dependencies": {
81
- "@procedures/client": "0.9.2",
80
+ "@mark1russell7/service": "^0.5.2",
81
+ "@procedures/client": "0.11.0",
82
82
  "zod": "^4.6.5"
83
83
  },
84
- "optionalDependencies": {
85
- "express": "^5.0.0"
86
- },
87
84
  "scripts": {
88
85
  "build": "tsc -b",
89
86
  "typecheck": "tsc --noEmit",
@@ -0,0 +1,130 @@
1
+ /**
2
+ * The faces of the procedures host (CLI-7): HTTP, WebSocket, MCP and each face that a package adds.
3
+ *
4
+ * A face is a factory. The host gives it the handler of its calls, the server, the registry and
5
+ * the policy. The face gives what it mounts on a listener and its stop parts:
6
+ *
7
+ * - `handler`: a request handler for the mount list of a listener. The first match wins.
8
+ * - `upgrade`: an upgrade handler and the path that it takes.
9
+ * - `parts`: a transport of the service, for a face that holds work outside its requests (the
10
+ * connections of WebSocket).
11
+ * - `endpoint`: the scheme and the path of the face, for `host.info`.
12
+ *
13
+ * `httpFace()` and `wsFace()` are the faces of this package, on `procedureHandler` and
14
+ * `upgradeHandler` of `@procedures/client/node`. `mcpFace()` of `@procedures/mcp-server` is the
15
+ * MCP face. A face of another package has the same shape, so the host needs no change for it.
16
+ */
17
+
18
+ import type { IncomingMessage, ServerResponse as NodeResponse } from "node:http";
19
+ import type { Duplex } from "node:stream";
20
+ import type { TransportImpl } from "@mark1russell7/service";
21
+ import { Client, requestCredentials, type BrowserGuardOptions, type Clock, type HostPolicy, type ProcedureRegistry, type ProcedureServer } from "@procedures/client";
22
+ import { procedureHandler, upgradeHandler, type ProcedureHandlerOptions, type UpgradeHandlerOptions } from "@procedures/client/node";
23
+ import type { FaceHandler } from "./tracking.js";
24
+
25
+ /** What the host gives a face. */
26
+ export interface HostFaceContext {
27
+ /** The name of the face in the host: the first part of the route of each call. */
28
+ readonly name: string;
29
+ /** The handler of the calls of the face: each call goes through the request tracking of the service. */
30
+ readonly handler: FaceHandler;
31
+ readonly server: ProcedureServer;
32
+ readonly registry: ProcedureRegistry;
33
+ readonly policy: HostPolicy;
34
+ /** The clock of the host: the clock of the lifecycle. */
35
+ readonly clock: Clock;
36
+ }
37
+
38
+ /** The upgrade handler of a face, and the path or the function that tells which upgrades it takes. */
39
+ export interface HostFaceUpgrade {
40
+ readonly match: string | ((req: IncomingMessage) => boolean);
41
+ readonly handler: (req: IncomingMessage, socket: Duplex, head: Buffer) => unknown;
42
+ }
43
+
44
+ /** The scheme and the path of a face on a listener, for `host.info`. */
45
+ export interface HostFaceEndpoint {
46
+ /** The type of the endpoint in `host.info`: `http`, `websocket`, `mcp` or another name. */
47
+ readonly type: string;
48
+ readonly scheme: "http" | "ws";
49
+ readonly path: string;
50
+ }
51
+
52
+ /** What a face gives the host. */
53
+ export interface HostFace {
54
+ /** The request handler of the face. It gives true when it takes a request, and false for another request. */
55
+ readonly handler?: ((req: IncomingMessage, res: NodeResponse) => boolean) | undefined;
56
+ readonly upgrade?: HostFaceUpgrade | undefined;
57
+ /** The stop parts of the face: a transport of the service, with the name `<host>.<face>`. */
58
+ readonly parts?: TransportImpl | undefined;
59
+ readonly endpoint?: HostFaceEndpoint | undefined;
60
+ /** The ids of the connections of the face, for a face of connections (WebSocket). */
61
+ readonly connections?: (() => string[]) | undefined;
62
+ /** A client of one connection: its calls go to the procedures that the connected client serves. */
63
+ readonly clientFor?: ((connectionId: string) => Client | undefined) | undefined;
64
+ }
65
+
66
+ /** A face factory: the host calls it one time, with the context of the face. */
67
+ export type HostFaceFactory = (context: HostFaceContext) => HostFace;
68
+
69
+ /** The browser checks of the policy, as options of a face. The options of the face win over them. */
70
+ function browserOptions(browser: BrowserGuardOptions, http: boolean): BrowserGuardOptions {
71
+ return {
72
+ ...(browser.allowedOrigins !== undefined ? { allowedOrigins: browser.allowedOrigins } : {}),
73
+ ...(browser.allowedHosts !== undefined ? { allowedHosts: browser.allowedHosts } : {}),
74
+ ...(http && browser.allowGet !== undefined ? { allowGet: browser.allowGet } : {}),
75
+ };
76
+ }
77
+
78
+ /**
79
+ * This function gives the HTTP face: `procedureHandler` of `@procedures/client/node`, under
80
+ * `options.basePath` (default `/api`). The browser checks of the policy apply, and the options win over them.
81
+ */
82
+ export function httpFace(options: ProcedureHandlerOptions = {}): HostFaceFactory {
83
+ return (context) => {
84
+ const handler = procedureHandler(context.handler, { clock: context.clock, ...browserOptions(context.policy.browser, true), ...options });
85
+ return {
86
+ handler: (req, res) => handler(req, res),
87
+ endpoint: { type: "http", scheme: "http", path: handler.basePath },
88
+ };
89
+ };
90
+ }
91
+
92
+ /**
93
+ * This function gives the WebSocket face: `upgradeHandler` of `@procedures/client/node`, at
94
+ * `options.path` (default `/ws`).
95
+ *
96
+ * - The upgrade: a failed browser check gets 403. A connection that the server does not admit
97
+ * gets 401: the token check and the request hook of each call run on the credentials of the
98
+ * upgrade (`ProcedureServer.admits`). Thus no idle connection without credentials stays open.
99
+ * - The stop parts refuse new calls at the stop of the intake. In the drain, each connection
100
+ * closes when its calls end, so the drain of the listener does not wait for an idle connection.
101
+ */
102
+ export function wsFace(options: UpgradeHandlerOptions = {}): HostFaceFactory {
103
+ return (context) => {
104
+ const { server } = context;
105
+ const admits = (req: IncomingMessage): Promise<boolean> =>
106
+ server.admits(requestCredentials({ headers: req.headers as Record<string, unknown>, url: req.url }, { tokenQuery: "token" }));
107
+ const upgrade = upgradeHandler(context.handler, { ...browserOptions(context.policy.browser, false), authenticate: admits, ...options });
108
+ const transport = upgrade.transport;
109
+ return {
110
+ upgrade: { match: upgrade.path, handler: upgrade },
111
+ parts: {
112
+ start: () => upgrade.start(),
113
+ stopIntake: () => upgrade.stopIntake(),
114
+ drain: async (deadline, signal) => {
115
+ await upgrade.drain(deadline, signal);
116
+ // The calls ended, or the cooperative abort came: the connections end in their protocol
117
+ await upgrade.close();
118
+ },
119
+ inFlight: () => upgrade.inFlight(),
120
+ close: () => upgrade.close(),
121
+ },
122
+ endpoint: { type: "websocket", scheme: "ws", path: upgrade.path },
123
+ connections: () => transport.getTrackedConnections().map((connection) => connection.id),
124
+ clientFor: (connectionId) =>
125
+ transport.getTrackedConnection(connectionId) === undefined
126
+ ? undefined
127
+ : new Client({ transport: transport.transportFor(connectionId), throwOnError: true }),
128
+ };
129
+ };
130
+ }