@procedures/peer 0.10.0 → 0.12.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 +46 -7
- package/dist/.tsbuildinfo +1 -1
- package/dist/host/faces.d.ts +78 -0
- package/dist/host/faces.d.ts.map +1 -0
- package/dist/host/faces.js +77 -0
- package/dist/host/faces.js.map +1 -0
- package/dist/host/index.d.ts +186 -0
- package/dist/host/index.d.ts.map +1 -0
- package/dist/host/index.js +257 -0
- package/dist/host/index.js.map +1 -0
- package/dist/host/tracking.d.ts +60 -0
- package/dist/host/tracking.d.ts.map +1 -0
- package/dist/host/tracking.js +162 -0
- package/dist/host/tracking.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/lockfile.d.ts +21 -4
- package/dist/lockfile.d.ts.map +1 -1
- package/dist/lockfile.js +31 -7
- package/dist/lockfile.js.map +1 -1
- package/dist/peer/index.d.ts +43 -9
- package/dist/peer/index.d.ts.map +1 -1
- package/dist/peer/index.js +156 -241
- package/dist/peer/index.js.map +1 -1
- package/package.json +6 -9
- package/src/host/faces.ts +130 -0
- package/src/host/index.ts +434 -0
- package/src/host/tracking.ts +217 -0
- package/src/index.ts +38 -2
- package/src/lockfile.ts +39 -7
- package/src/peer/index.ts +172 -292
|
@@ -0,0 +1,78 @@
|
|
|
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
|
+
import type { IncomingMessage, ServerResponse as NodeResponse } from "node:http";
|
|
18
|
+
import type { Duplex } from "node:stream";
|
|
19
|
+
import type { TransportImpl } from "@mark1russell7/service";
|
|
20
|
+
import { Client, type Clock, type HostPolicy, type ProcedureRegistry, type ProcedureServer } from "@procedures/client";
|
|
21
|
+
import { type ProcedureHandlerOptions, type UpgradeHandlerOptions } from "@procedures/client/node";
|
|
22
|
+
import type { FaceHandler } from "./tracking.js";
|
|
23
|
+
/** What the host gives a face. */
|
|
24
|
+
export interface HostFaceContext {
|
|
25
|
+
/** The name of the face in the host: the first part of the route of each call. */
|
|
26
|
+
readonly name: string;
|
|
27
|
+
/** The handler of the calls of the face: each call goes through the request tracking of the service. */
|
|
28
|
+
readonly handler: FaceHandler;
|
|
29
|
+
readonly server: ProcedureServer;
|
|
30
|
+
readonly registry: ProcedureRegistry;
|
|
31
|
+
readonly policy: HostPolicy;
|
|
32
|
+
/** The clock of the host: the clock of the lifecycle. */
|
|
33
|
+
readonly clock: Clock;
|
|
34
|
+
}
|
|
35
|
+
/** The upgrade handler of a face, and the path or the function that tells which upgrades it takes. */
|
|
36
|
+
export interface HostFaceUpgrade {
|
|
37
|
+
readonly match: string | ((req: IncomingMessage) => boolean);
|
|
38
|
+
readonly handler: (req: IncomingMessage, socket: Duplex, head: Buffer) => unknown;
|
|
39
|
+
}
|
|
40
|
+
/** The scheme and the path of a face on a listener, for `host.info`. */
|
|
41
|
+
export interface HostFaceEndpoint {
|
|
42
|
+
/** The type of the endpoint in `host.info`: `http`, `websocket`, `mcp` or another name. */
|
|
43
|
+
readonly type: string;
|
|
44
|
+
readonly scheme: "http" | "ws";
|
|
45
|
+
readonly path: string;
|
|
46
|
+
}
|
|
47
|
+
/** What a face gives the host. */
|
|
48
|
+
export interface HostFace {
|
|
49
|
+
/** The request handler of the face. It gives true when it takes a request, and false for another request. */
|
|
50
|
+
readonly handler?: ((req: IncomingMessage, res: NodeResponse) => boolean) | undefined;
|
|
51
|
+
readonly upgrade?: HostFaceUpgrade | undefined;
|
|
52
|
+
/** The stop parts of the face: a transport of the service, with the name `<host>.<face>`. */
|
|
53
|
+
readonly parts?: TransportImpl | undefined;
|
|
54
|
+
readonly endpoint?: HostFaceEndpoint | undefined;
|
|
55
|
+
/** The ids of the connections of the face, for a face of connections (WebSocket). */
|
|
56
|
+
readonly connections?: (() => string[]) | undefined;
|
|
57
|
+
/** A client of one connection: its calls go to the procedures that the connected client serves. */
|
|
58
|
+
readonly clientFor?: ((connectionId: string) => Client | undefined) | undefined;
|
|
59
|
+
}
|
|
60
|
+
/** A face factory: the host calls it one time, with the context of the face. */
|
|
61
|
+
export type HostFaceFactory = (context: HostFaceContext) => HostFace;
|
|
62
|
+
/**
|
|
63
|
+
* This function gives the HTTP face: `procedureHandler` of `@procedures/client/node`, under
|
|
64
|
+
* `options.basePath` (default `/api`). The browser checks of the policy apply, and the options win over them.
|
|
65
|
+
*/
|
|
66
|
+
export declare function httpFace(options?: ProcedureHandlerOptions): HostFaceFactory;
|
|
67
|
+
/**
|
|
68
|
+
* This function gives the WebSocket face: `upgradeHandler` of `@procedures/client/node`, at
|
|
69
|
+
* `options.path` (default `/ws`).
|
|
70
|
+
*
|
|
71
|
+
* - The upgrade: a failed browser check gets 403. A connection that the server does not admit
|
|
72
|
+
* gets 401: the token check and the request hook of each call run on the credentials of the
|
|
73
|
+
* upgrade (`ProcedureServer.admits`). Thus no idle connection without credentials stays open.
|
|
74
|
+
* - The stop parts refuse new calls at the stop of the intake. In the drain, each connection
|
|
75
|
+
* closes when its calls end, so the drain of the listener does not wait for an idle connection.
|
|
76
|
+
*/
|
|
77
|
+
export declare function wsFace(options?: UpgradeHandlerOptions): HostFaceFactory;
|
|
78
|
+
//# sourceMappingURL=faces.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faces.d.ts","sourceRoot":"","sources":["../../src/host/faces.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,IAAI,YAAY,EAAE,MAAM,WAAW,CAAC;AACjF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAgD,KAAK,KAAK,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrK,OAAO,EAAoC,KAAK,uBAAuB,EAAE,KAAK,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACrI,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,kCAAkC;AAClC,MAAM,WAAW,eAAe;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wGAAwG;IACxG,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,yDAAyD;IACzD,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED,sGAAsG;AACtG,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,eAAe,KAAK,OAAO,CAAC,CAAC;IAC7D,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACnF;AAED,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,kCAAkC;AAClC,MAAM,WAAW,QAAQ;IACvB,6GAA6G;IAC7G,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC,GAAG,SAAS,CAAC;IACtF,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;IAC/C,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC3C,QAAQ,CAAC,QAAQ,CAAC,EAAE,gBAAgB,GAAG,SAAS,CAAC;IACjD,qFAAqF;IACrF,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,MAAM,MAAM,EAAE,CAAC,GAAG,SAAS,CAAC;IACpD,mGAAmG;IACnG,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;CACjF;AAED,gFAAgF;AAChF,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,eAAe,KAAK,QAAQ,CAAC;AAWrE;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,OAAO,GAAE,uBAA4B,GAAG,eAAe,CAQ/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,OAAO,GAAE,qBAA0B,GAAG,eAAe,CA4B3E"}
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
import { Client, requestCredentials } from "@procedures/client";
|
|
18
|
+
import { procedureHandler, upgradeHandler } from "@procedures/client/node";
|
|
19
|
+
/** The browser checks of the policy, as options of a face. The options of the face win over them. */
|
|
20
|
+
function browserOptions(browser, http) {
|
|
21
|
+
return {
|
|
22
|
+
...(browser.allowedOrigins !== undefined ? { allowedOrigins: browser.allowedOrigins } : {}),
|
|
23
|
+
...(browser.allowedHosts !== undefined ? { allowedHosts: browser.allowedHosts } : {}),
|
|
24
|
+
...(http && browser.allowGet !== undefined ? { allowGet: browser.allowGet } : {}),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* This function gives the HTTP face: `procedureHandler` of `@procedures/client/node`, under
|
|
29
|
+
* `options.basePath` (default `/api`). The browser checks of the policy apply, and the options win over them.
|
|
30
|
+
*/
|
|
31
|
+
export function httpFace(options = {}) {
|
|
32
|
+
return (context) => {
|
|
33
|
+
const handler = procedureHandler(context.handler, { clock: context.clock, ...browserOptions(context.policy.browser, true), ...options });
|
|
34
|
+
return {
|
|
35
|
+
handler: (req, res) => handler(req, res),
|
|
36
|
+
endpoint: { type: "http", scheme: "http", path: handler.basePath },
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* This function gives the WebSocket face: `upgradeHandler` of `@procedures/client/node`, at
|
|
42
|
+
* `options.path` (default `/ws`).
|
|
43
|
+
*
|
|
44
|
+
* - The upgrade: a failed browser check gets 403. A connection that the server does not admit
|
|
45
|
+
* gets 401: the token check and the request hook of each call run on the credentials of the
|
|
46
|
+
* upgrade (`ProcedureServer.admits`). Thus no idle connection without credentials stays open.
|
|
47
|
+
* - The stop parts refuse new calls at the stop of the intake. In the drain, each connection
|
|
48
|
+
* closes when its calls end, so the drain of the listener does not wait for an idle connection.
|
|
49
|
+
*/
|
|
50
|
+
export function wsFace(options = {}) {
|
|
51
|
+
return (context) => {
|
|
52
|
+
const { server } = context;
|
|
53
|
+
const admits = (req) => server.admits(requestCredentials({ headers: req.headers, url: req.url }, { tokenQuery: "token" }));
|
|
54
|
+
const upgrade = upgradeHandler(context.handler, { ...browserOptions(context.policy.browser, false), authenticate: admits, ...options });
|
|
55
|
+
const transport = upgrade.transport;
|
|
56
|
+
return {
|
|
57
|
+
upgrade: { match: upgrade.path, handler: upgrade },
|
|
58
|
+
parts: {
|
|
59
|
+
start: () => upgrade.start(),
|
|
60
|
+
stopIntake: () => upgrade.stopIntake(),
|
|
61
|
+
drain: async (deadline, signal) => {
|
|
62
|
+
await upgrade.drain(deadline, signal);
|
|
63
|
+
// The calls ended, or the cooperative abort came: the connections end in their protocol
|
|
64
|
+
await upgrade.close();
|
|
65
|
+
},
|
|
66
|
+
inFlight: () => upgrade.inFlight(),
|
|
67
|
+
close: () => upgrade.close(),
|
|
68
|
+
},
|
|
69
|
+
endpoint: { type: "websocket", scheme: "ws", path: upgrade.path },
|
|
70
|
+
connections: () => transport.getTrackedConnections().map((connection) => connection.id),
|
|
71
|
+
clientFor: (connectionId) => transport.getTrackedConnection(connectionId) === undefined
|
|
72
|
+
? undefined
|
|
73
|
+
: new Client({ transport: transport.transportFor(connectionId), throwOnError: true }),
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=faces.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"faces.js","sourceRoot":"","sources":["../../src/host/faces.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The procedures host: one factory for each procedures server in Node (CLI-7, decision D2).
|
|
3
|
+
*
|
|
4
|
+
* `createProcedureHost()` builds the server, its faces and its stop parts from one set of
|
|
5
|
+
* options. It starts nothing and installs no signal handler. The service of the process declares
|
|
6
|
+
* the parts (`host.declare(svc)`), and only the entry of the process starts `run()`.
|
|
7
|
+
*
|
|
8
|
+
* - The faces: HTTP (`procedureHandler`), WebSocket (`upgradeHandler`), MCP (`mcpFace()` of
|
|
9
|
+
* `@procedures/mcp-server`) and each face factory of another package.
|
|
10
|
+
* - A listener that the host owns is an `httpTransport` of `@mark1russell7/service/http`. Its
|
|
11
|
+
* faces mount on its mount list, so they share one port. Without a listener, the app mounts
|
|
12
|
+
* `host.handler` and `host.upgrade` on its own listener.
|
|
13
|
+
* - The request tracking: one route pattern for each face, and a function of the idempotency of
|
|
14
|
+
* each call (decision D3). Each call is a stream (decision D4).
|
|
15
|
+
* - The collections: the option `collections` gives the storages, and the host serves
|
|
16
|
+
* `collections.<name>.*`. Each storage is a dependency of the service.
|
|
17
|
+
*/
|
|
18
|
+
import type { IncomingMessage, ServerResponse as NodeResponse } from "node:http";
|
|
19
|
+
import type { Duplex } from "node:stream";
|
|
20
|
+
import type { Check, CheckFn, Dependency, DependencyHandle, Operation, OperationOf, ServiceBuilder, TransportImpl } from "@mark1russell7/service";
|
|
21
|
+
import { type HttpKnobs } from "@mark1russell7/service/http";
|
|
22
|
+
import { ProcedureServer, type Client, type Clock, type CollectionProceduresOptions, type CollectionStorage, type HostPolicy, type ProcedurePath, type ProcedureRegistry, type ProcedureServerOptions } from "@procedures/client";
|
|
23
|
+
import type { ProcedureHandlerOptions, UpgradeHandlerOptions } from "@procedures/client/node";
|
|
24
|
+
import { type HostFaceFactory } from "./faces.js";
|
|
25
|
+
/** The knobs of a transport of the service, by the names of the ordering law. */
|
|
26
|
+
export type HostKnobs = Parameters<ServiceBuilder["server"]>[1];
|
|
27
|
+
/**
|
|
28
|
+
* The knobs of a listener on the computer of a developer, as data. The transport has no defaults,
|
|
29
|
+
* so the caller gives these values or its own: an idle close of 5 s (the default of Node), a sweep
|
|
30
|
+
* of the idle connections each second, and a `Retry-After` of 1 s on each 503.
|
|
31
|
+
*/
|
|
32
|
+
export declare const LOCAL_LISTENER_KNOBS: HttpKnobs;
|
|
33
|
+
/** A collection of the host: its storage, and the procedures `collections.<name>.*`. */
|
|
34
|
+
export interface HostCollection {
|
|
35
|
+
readonly storage: CollectionStorage<unknown>;
|
|
36
|
+
/**
|
|
37
|
+
* The procedures `collections.<name>.*`: true (the default), false for none, or the options of
|
|
38
|
+
* `createCollectionProcedures()` (the query policy, the change events).
|
|
39
|
+
*/
|
|
40
|
+
readonly procedures?: boolean | CollectionProceduresOptions | undefined;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The faces of the host. `http` and `ws` take `true`, their options or a face factory. Each other
|
|
44
|
+
* name takes a face factory, for example `mcp: mcpFace()` of `@procedures/mcp-server`.
|
|
45
|
+
*/
|
|
46
|
+
export interface ProcedureHostFaces {
|
|
47
|
+
readonly http?: boolean | ProcedureHandlerOptions | HostFaceFactory | undefined;
|
|
48
|
+
readonly ws?: boolean | UpgradeHandlerOptions | HostFaceFactory | undefined;
|
|
49
|
+
readonly mcp?: HostFaceFactory | undefined;
|
|
50
|
+
readonly [face: string]: boolean | ProcedureHandlerOptions | UpgradeHandlerOptions | HostFaceFactory | undefined;
|
|
51
|
+
}
|
|
52
|
+
/** A listener that the host owns: an `httpTransport` of the service on one port. */
|
|
53
|
+
export interface HostListener {
|
|
54
|
+
/** The name of the listener: its part is `<host>.listen.<name>`. The default is `http`, or the port when the host has more listeners. */
|
|
55
|
+
readonly name?: string | undefined;
|
|
56
|
+
/** The port. 0 gives a free port, and `host.info` gives the real one. */
|
|
57
|
+
readonly port: number;
|
|
58
|
+
/** The address. The default is `127.0.0.1`: only this computer can reach the procedures. */
|
|
59
|
+
readonly host?: string | undefined;
|
|
60
|
+
/** The faces that mount on this listener. The default is each face of the host. */
|
|
61
|
+
readonly faces?: readonly string[] | undefined;
|
|
62
|
+
/** The knobs of the transport. They have no defaults: give `LOCAL_LISTENER_KNOBS` or your own. */
|
|
63
|
+
readonly knobs: HttpKnobs;
|
|
64
|
+
}
|
|
65
|
+
/** The options of `ProcedureServer` that the host and its policy do not give. */
|
|
66
|
+
export type HostServerOptions = Omit<ProcedureServerOptions, "registry" | "collections" | "clock" | "transports" | "expose" | "token" | "principalOf" | "events" | "interceptors">;
|
|
67
|
+
/** The options of `createProcedureHost()`. */
|
|
68
|
+
export interface ProcedureHostOptions {
|
|
69
|
+
/** The name of the host: the prefix of the names of its parts, and the name of its controller. The default is `procedures`. */
|
|
70
|
+
readonly name?: string | undefined;
|
|
71
|
+
/** The id of the host: `host.info` and `host.health` give it as `peerId`. The default is a new id. */
|
|
72
|
+
readonly id?: string | undefined;
|
|
73
|
+
/** The registry of the procedures. The default is the global registry. */
|
|
74
|
+
readonly registry?: ProcedureRegistry | undefined;
|
|
75
|
+
/** The host policy (`hostPolicy()`): the expose rule, the token, the request hook, the browser checks and the capability. */
|
|
76
|
+
readonly policy?: HostPolicy | undefined;
|
|
77
|
+
/** The procedures that the host serves. It wins over the expose rule of the policy. */
|
|
78
|
+
readonly expose?: ((path: ProcedurePath) => boolean) | undefined;
|
|
79
|
+
/** True (the default): the host serves each procedure of the registry, also a procedure that the registry gets later. */
|
|
80
|
+
readonly autoRegister?: boolean | undefined;
|
|
81
|
+
/** True (the default): the host counts the calls of each procedure for `host.metrics`. */
|
|
82
|
+
readonly metrics?: boolean | undefined;
|
|
83
|
+
/** The faces. The default is `{ http: true }`. */
|
|
84
|
+
readonly faces?: ProcedureHostFaces | undefined;
|
|
85
|
+
/** The listeners that the host owns. Without them, the app mounts `host.handler` and `host.upgrade`. */
|
|
86
|
+
readonly listen?: readonly HostListener[] | undefined;
|
|
87
|
+
/** The collections by name: a storage, or a storage and its procedures. */
|
|
88
|
+
readonly collections?: Readonly<Record<string, CollectionStorage<unknown> | HostCollection>> | undefined;
|
|
89
|
+
/** The clock of the host: give the clock of the lifecycle. The default is the clock of the platform. */
|
|
90
|
+
readonly clock?: Clock | undefined;
|
|
91
|
+
/** More options of the server: `idempotency`, `authenticate`, `ambientCredentials`, the loggers. */
|
|
92
|
+
readonly serverOptions?: HostServerOptions | undefined;
|
|
93
|
+
/** More endpoints for `host.info`, after the endpoints of the listeners, for example `local://in-process`. */
|
|
94
|
+
readonly endpoints?: readonly HostEndpoint[] | undefined;
|
|
95
|
+
}
|
|
96
|
+
/** A transport of the host for the service: its name, its knobs and its implementation. */
|
|
97
|
+
export interface HostPart {
|
|
98
|
+
readonly name: string;
|
|
99
|
+
readonly knobs: HostKnobs;
|
|
100
|
+
readonly impl: TransportImpl;
|
|
101
|
+
}
|
|
102
|
+
/** A dependency of the host for the service: a storage, as a dependency handle. */
|
|
103
|
+
export interface HostDependency {
|
|
104
|
+
readonly name: string;
|
|
105
|
+
readonly spec: Omit<Dependency, "name">;
|
|
106
|
+
readonly handle: DependencyHandle;
|
|
107
|
+
}
|
|
108
|
+
/** A check of the host for the service, with its function. */
|
|
109
|
+
export interface HostCheck {
|
|
110
|
+
readonly check: Check;
|
|
111
|
+
readonly run: CheckFn;
|
|
112
|
+
}
|
|
113
|
+
/** A route pattern of the controller of the host: one for each face (decision D3). */
|
|
114
|
+
export interface HostPattern {
|
|
115
|
+
readonly pattern: string;
|
|
116
|
+
readonly operations: Readonly<Record<string, Operation>>;
|
|
117
|
+
readonly operationOf: OperationOf;
|
|
118
|
+
}
|
|
119
|
+
/** The controller of the calls of the host, for the declaration of the service. */
|
|
120
|
+
export interface HostController {
|
|
121
|
+
readonly name: string;
|
|
122
|
+
readonly requires: readonly string[];
|
|
123
|
+
readonly patterns: readonly HostPattern[];
|
|
124
|
+
}
|
|
125
|
+
/** An endpoint of the host: the type of its face and its address. */
|
|
126
|
+
export interface HostEndpoint {
|
|
127
|
+
readonly type: string;
|
|
128
|
+
readonly address: string;
|
|
129
|
+
}
|
|
130
|
+
/** The identity of the host, as `host.info` gives it. */
|
|
131
|
+
export interface HostInfo {
|
|
132
|
+
readonly id: string;
|
|
133
|
+
/** The time at which the host was made, from `Date.now()`. */
|
|
134
|
+
readonly startedAt: number;
|
|
135
|
+
/** The endpoints of the faces on the listeners of the host. A listener has its address only after its start. */
|
|
136
|
+
endpoints(): HostEndpoint[];
|
|
137
|
+
/** The number of the procedures that the host serves now. */
|
|
138
|
+
procedureCount(): number;
|
|
139
|
+
}
|
|
140
|
+
/** A procedures host: the parts of a service, and the handlers of the faces that the app mounts. */
|
|
141
|
+
export interface ProcedureHost {
|
|
142
|
+
readonly name: string;
|
|
143
|
+
readonly id: string;
|
|
144
|
+
readonly server: ProcedureServer;
|
|
145
|
+
readonly registry: ProcedureRegistry;
|
|
146
|
+
readonly policy: HostPolicy;
|
|
147
|
+
/** The request handler of the faces that are on no listener of the host, for the mount list of the app. */
|
|
148
|
+
readonly handler: (req: IncomingMessage, res: NodeResponse) => boolean;
|
|
149
|
+
/** The upgrade handler of the faces that are on no listener of the host. */
|
|
150
|
+
readonly upgrade: (req: IncomingMessage, socket: Duplex, head: Buffer) => boolean;
|
|
151
|
+
/** The transports: the procedure server first, then the stop parts of the faces, then each listener of the host. */
|
|
152
|
+
readonly parts: readonly HostPart[];
|
|
153
|
+
/** The dependencies: one for each storage of the collections. */
|
|
154
|
+
readonly dependencies: readonly HostDependency[];
|
|
155
|
+
readonly checks: readonly HostCheck[];
|
|
156
|
+
/** The controller of the calls, with one route pattern for each face. */
|
|
157
|
+
readonly controllers: readonly HostController[];
|
|
158
|
+
readonly info: HostInfo;
|
|
159
|
+
/** This method declares the dependencies, the transports, the checks and the controller on the builder of a service. */
|
|
160
|
+
declare(svc: ServiceBuilder): ServiceBuilder;
|
|
161
|
+
/** The ids of the connections of the faces of connections (WebSocket). */
|
|
162
|
+
connections(): string[];
|
|
163
|
+
/** A client of one connection of a face: its calls go to the procedures that the connected client serves. */
|
|
164
|
+
clientFor(connectionId: string): Client;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* This function builds a procedures host. It starts nothing: the service of the process starts
|
|
168
|
+
* its parts (`host.declare(svc)` and `run()`). `createPeer()` declares them in a service of its own.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const host = createProcedureHost({
|
|
173
|
+
* registry,
|
|
174
|
+
* policy: hostPolicy({ token }),
|
|
175
|
+
* faces: { http: true, ws: true, mcp: mcpFace() },
|
|
176
|
+
* listen: [{ port: 3000, knobs: LOCAL_LISTENER_KNOBS }],
|
|
177
|
+
* });
|
|
178
|
+
* const svc = host.declare(service("mark").profile(localDev));
|
|
179
|
+
* ```
|
|
180
|
+
*/
|
|
181
|
+
export declare function createProcedureHost(options?: ProcedureHostOptions): ProcedureHost;
|
|
182
|
+
export { httpFace, wsFace } from "./faces.js";
|
|
183
|
+
export type { HostFace, HostFaceContext, HostFaceEndpoint, HostFaceFactory, HostFaceUpgrade } from "./faces.js";
|
|
184
|
+
export { callOperations, faceHandler, operationOfCall } from "./tracking.js";
|
|
185
|
+
export type { CallDetail, CallOperation, FaceHandler, FaceHandlerOptions } from "./tracking.js";
|
|
186
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The procedures host: one factory for each procedures server in Node (CLI-7, decision D2).
|
|
3
|
+
*
|
|
4
|
+
* `createProcedureHost()` builds the server, its faces and its stop parts from one set of
|
|
5
|
+
* options. It starts nothing and installs no signal handler. The service of the process declares
|
|
6
|
+
* the parts (`host.declare(svc)`), and only the entry of the process starts `run()`.
|
|
7
|
+
*
|
|
8
|
+
* - The faces: HTTP (`procedureHandler`), WebSocket (`upgradeHandler`), MCP (`mcpFace()` of
|
|
9
|
+
* `@procedures/mcp-server`) and each face factory of another package.
|
|
10
|
+
* - A listener that the host owns is an `httpTransport` of `@mark1russell7/service/http`. Its
|
|
11
|
+
* faces mount on its mount list, so they share one port. Without a listener, the app mounts
|
|
12
|
+
* `host.handler` and `host.upgrade` on its own listener.
|
|
13
|
+
* - The request tracking: one route pattern for each face, and a function of the idempotency of
|
|
14
|
+
* each call (decision D3). Each call is a stream (decision D4).
|
|
15
|
+
* - The collections: the option `collections` gives the storages, and the host serves
|
|
16
|
+
* `collections.<name>.*`. Each storage is a dependency of the service.
|
|
17
|
+
*/
|
|
18
|
+
import { createServer } from "node:http";
|
|
19
|
+
import { httpTransport, pathOf } from "@mark1russell7/service/http";
|
|
20
|
+
import { PROCEDURE_REGISTRY, ProcedureServer, TIMER_DEFAULTS, hostPolicy, hostProcedures, metricsInterceptor, optionalMs, procedureServerOptions, realClock, seconds, } from "@procedures/client";
|
|
21
|
+
import { httpFace, wsFace } from "./faces.js";
|
|
22
|
+
import { callOperations, faceHandler, operationOfCall } from "./tracking.js";
|
|
23
|
+
/**
|
|
24
|
+
* The knobs of a listener on the computer of a developer, as data. The transport has no defaults,
|
|
25
|
+
* so the caller gives these values or its own: an idle close of 5 s (the default of Node), a sweep
|
|
26
|
+
* of the idle connections each second, and a `Retry-After` of 1 s on each 503.
|
|
27
|
+
*/
|
|
28
|
+
export const LOCAL_LISTENER_KNOBS = Object.freeze({
|
|
29
|
+
server_idle_close: seconds(5),
|
|
30
|
+
server_idle_sweep_interval: seconds(1),
|
|
31
|
+
server_retry_after: seconds(1),
|
|
32
|
+
});
|
|
33
|
+
/** The face factory of a name and its value in the options. */
|
|
34
|
+
function factoryOf(name, value) {
|
|
35
|
+
if (value === undefined || value === false)
|
|
36
|
+
return undefined;
|
|
37
|
+
if (typeof value === "function")
|
|
38
|
+
return value;
|
|
39
|
+
const options = value === true ? {} : value;
|
|
40
|
+
if (name === "http")
|
|
41
|
+
return httpFace(options);
|
|
42
|
+
if (name === "ws")
|
|
43
|
+
return wsFace(options);
|
|
44
|
+
throw new Error(`The face "${name}" of the host needs a face factory, for example mcpFace() of @procedures/mcp-server`);
|
|
45
|
+
}
|
|
46
|
+
/** The storage and the procedures of a collection of the options. */
|
|
47
|
+
function collectionOf(value) {
|
|
48
|
+
return typeof value.get === "function" ? { storage: value } : value;
|
|
49
|
+
}
|
|
50
|
+
/** The text of an error. */
|
|
51
|
+
function messageOf(error) {
|
|
52
|
+
return error instanceof Error ? error.message : String(error);
|
|
53
|
+
}
|
|
54
|
+
/** The operations of a storage as a dependency: a read can go again, a write cannot. */
|
|
55
|
+
const STORAGE_OPERATIONS = Object.freeze({ read: { idempotent: true }, write: { idempotent: false } });
|
|
56
|
+
/**
|
|
57
|
+
* This function builds a procedures host. It starts nothing: the service of the process starts
|
|
58
|
+
* its parts (`host.declare(svc)` and `run()`). `createPeer()` declares them in a service of its own.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const host = createProcedureHost({
|
|
63
|
+
* registry,
|
|
64
|
+
* policy: hostPolicy({ token }),
|
|
65
|
+
* faces: { http: true, ws: true, mcp: mcpFace() },
|
|
66
|
+
* listen: [{ port: 3000, knobs: LOCAL_LISTENER_KNOBS }],
|
|
67
|
+
* });
|
|
68
|
+
* const svc = host.declare(service("mark").profile(localDev));
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export function createProcedureHost(options = {}) {
|
|
72
|
+
const name = options.name ?? "procedures";
|
|
73
|
+
const id = options.id ?? `peer-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
74
|
+
const registry = options.registry ?? PROCEDURE_REGISTRY;
|
|
75
|
+
const policy = options.policy ?? hostPolicy();
|
|
76
|
+
const clock = options.clock ?? realClock;
|
|
77
|
+
const startedAt = Date.now();
|
|
78
|
+
// The server: the policy, the metrics of host.metrics (R19) and the options of the caller
|
|
79
|
+
const metrics = options.metrics === false ? undefined : metricsInterceptor();
|
|
80
|
+
const base = procedureServerOptions(policy, registry);
|
|
81
|
+
const interceptors = [...(base.interceptors ?? []), ...(metrics ? [metrics.interceptor] : [])];
|
|
82
|
+
const server = new ProcedureServer({
|
|
83
|
+
...base,
|
|
84
|
+
...options.serverOptions,
|
|
85
|
+
...(interceptors.length > 0 ? { interceptors } : {}),
|
|
86
|
+
autoRegister: options.autoRegister ?? true,
|
|
87
|
+
registry,
|
|
88
|
+
clock,
|
|
89
|
+
...(options.expose ? { expose: options.expose } : {}),
|
|
90
|
+
});
|
|
91
|
+
// The collections, from the option: the storage, and the procedures collections.<name>.*
|
|
92
|
+
for (const [collection, value] of Object.entries(options.collections ?? {})) {
|
|
93
|
+
const { storage, procedures } = collectionOf(value);
|
|
94
|
+
server.useStorage(collection, storage);
|
|
95
|
+
if (procedures !== false)
|
|
96
|
+
server.registerCollectionProcedures(collection, procedures === true || procedures === undefined ? undefined : procedures);
|
|
97
|
+
}
|
|
98
|
+
// The lifecycle of the service, from the start of the part of the server. Before it, no tracking.
|
|
99
|
+
let life = null;
|
|
100
|
+
const idempotency = options.serverOptions?.idempotency;
|
|
101
|
+
const keyedWindowMs = idempotency === false ? undefined : (optionalMs(idempotency?.ttl, "idempotency.ttl") ?? TIMER_DEFAULTS.idempotency.ttlMs);
|
|
102
|
+
// The faces
|
|
103
|
+
const faceSpecs = options.faces ?? { http: true };
|
|
104
|
+
const faces = new Map();
|
|
105
|
+
for (const [faceName, value] of Object.entries(faceSpecs)) {
|
|
106
|
+
const factory = factoryOf(faceName, value);
|
|
107
|
+
if (!factory)
|
|
108
|
+
continue;
|
|
109
|
+
const handler = faceHandler({ server, controller: name, face: faceName, life: () => life, keyed: keyedWindowMs !== undefined });
|
|
110
|
+
faces.set(faceName, factory({ name: faceName, handler, server, registry, policy, clock }));
|
|
111
|
+
}
|
|
112
|
+
// The listeners that the host owns, each one with the faces of its mount list
|
|
113
|
+
const listeners = options.listen ?? [];
|
|
114
|
+
const onListener = new Set();
|
|
115
|
+
const endpoints = [];
|
|
116
|
+
const listenerParts = listeners.map((listener, index) => {
|
|
117
|
+
const host = listener.host ?? "127.0.0.1";
|
|
118
|
+
const listenerName = listener.name ?? (listeners.length === 1 ? "http" : String(listener.port || index));
|
|
119
|
+
const transport = httpTransport(createServer(), listener.knobs, { listen: { port: listener.port, host }, clock });
|
|
120
|
+
const mounted = (listener.faces ?? [...faces.keys()]).map((faceName) => {
|
|
121
|
+
const face = faces.get(faceName);
|
|
122
|
+
if (!face)
|
|
123
|
+
throw new Error(`The listener "${listenerName}" names the face "${faceName}", which the host does not have`);
|
|
124
|
+
onListener.add(faceName);
|
|
125
|
+
if (face.handler) {
|
|
126
|
+
const handle = face.handler;
|
|
127
|
+
transport.mount((req, res) => handle(req, res));
|
|
128
|
+
}
|
|
129
|
+
if (face.upgrade) {
|
|
130
|
+
const upgrade = face.upgrade;
|
|
131
|
+
transport.upgrade(upgrade.match, (req, socket, head) => {
|
|
132
|
+
upgrade.handler(req, socket, head);
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
return face;
|
|
136
|
+
});
|
|
137
|
+
const shown = host === "0.0.0.0" || host === "::" ? "localhost" : host;
|
|
138
|
+
endpoints.push(() => {
|
|
139
|
+
const address = transport.server.address();
|
|
140
|
+
if (typeof address !== "object" || address === null)
|
|
141
|
+
return [];
|
|
142
|
+
return mounted.flatMap((face) => (face.endpoint ? [{ type: face.endpoint.type, address: `${face.endpoint.scheme}://${shown}:${address.port}${face.endpoint.path}` }] : []));
|
|
143
|
+
});
|
|
144
|
+
// The faces of a listener of WebSocket only: the error of its listen names WebSocket
|
|
145
|
+
const what = mounted.every((face) => face.upgrade && !face.handler) ? "WebSocket" : "HTTP";
|
|
146
|
+
const impl = {
|
|
147
|
+
start: async (signal, lifecycle) => {
|
|
148
|
+
try {
|
|
149
|
+
await transport.start(signal, lifecycle);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
throw new Error(`The ${what} server cannot listen on ${host}:${listener.port}: ${messageOf(error)}`, { cause: error });
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
stopIntake: () => transport.stopIntake(),
|
|
156
|
+
drain: (deadline, signal) => transport.drain(deadline, signal),
|
|
157
|
+
inFlight: () => transport.inFlight(),
|
|
158
|
+
close: () => transport.close(),
|
|
159
|
+
};
|
|
160
|
+
return { name: `${name}.listen.${listenerName}`, knobs: transport.knobs, impl };
|
|
161
|
+
});
|
|
162
|
+
// The faces on no listener: the app mounts them
|
|
163
|
+
const appFaces = [...faces.entries()].filter(([faceName]) => !onListener.has(faceName)).map(([, face]) => face);
|
|
164
|
+
const handler = (req, res) => {
|
|
165
|
+
for (const face of appFaces)
|
|
166
|
+
if (face.handler?.(req, res))
|
|
167
|
+
return true;
|
|
168
|
+
return false;
|
|
169
|
+
};
|
|
170
|
+
const upgrade = (req, socket, head) => {
|
|
171
|
+
for (const face of appFaces) {
|
|
172
|
+
const match = face.upgrade?.match;
|
|
173
|
+
if (match === undefined)
|
|
174
|
+
continue;
|
|
175
|
+
if (typeof match === "string" ? pathOf(req.url) === match : match(req)) {
|
|
176
|
+
face.upgrade.handler(req, socket, head);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
};
|
|
182
|
+
// The transports: the server first, so its part has the lifecycle before a listener takes a call
|
|
183
|
+
const serverPart = {
|
|
184
|
+
name,
|
|
185
|
+
knobs: {},
|
|
186
|
+
impl: {
|
|
187
|
+
start: (_signal, lifecycle) => {
|
|
188
|
+
life = lifecycle;
|
|
189
|
+
return server.start();
|
|
190
|
+
},
|
|
191
|
+
stopIntake: () => server.stopIntake(),
|
|
192
|
+
drain: (deadline, signal) => server.drain(deadline, signal),
|
|
193
|
+
inFlight: () => server.inFlight(),
|
|
194
|
+
close: () => server.close(),
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
const faceParts = [...faces.entries()].flatMap(([faceName, face]) => (face.parts ? [{ name: `${name}.${faceName}`, knobs: {}, impl: face.parts }] : []));
|
|
198
|
+
const parts = [serverPart, ...faceParts, ...listenerParts];
|
|
199
|
+
const dependencies = server.storageHandles().map((handle) => ({
|
|
200
|
+
name: handle.name,
|
|
201
|
+
spec: { system: null, knobs: {}, operations: STORAGE_OPERATIONS },
|
|
202
|
+
handle,
|
|
203
|
+
}));
|
|
204
|
+
const checks = [];
|
|
205
|
+
const operations = callOperations(keyedWindowMs);
|
|
206
|
+
const controllers = [
|
|
207
|
+
{ name, requires: [], patterns: [...faces.keys()].map((faceName) => ({ pattern: `${faceName} *`, operations, operationOf: operationOfCall })) },
|
|
208
|
+
];
|
|
209
|
+
const info = {
|
|
210
|
+
id,
|
|
211
|
+
startedAt,
|
|
212
|
+
endpoints: () => [...endpoints.flatMap((each) => each()), ...(options.endpoints ?? [])],
|
|
213
|
+
procedureCount: () => server.procedureCount,
|
|
214
|
+
};
|
|
215
|
+
// host.health, host.info and host.metrics: pinned on the server, not in the registry (R19)
|
|
216
|
+
server.registerProcedures(hostProcedures({ id, startedAt, server, endpoints: () => info.endpoints(), ...(metrics ? { metrics } : {}) }));
|
|
217
|
+
return {
|
|
218
|
+
name,
|
|
219
|
+
id,
|
|
220
|
+
server,
|
|
221
|
+
registry,
|
|
222
|
+
policy,
|
|
223
|
+
handler,
|
|
224
|
+
upgrade,
|
|
225
|
+
parts,
|
|
226
|
+
dependencies,
|
|
227
|
+
checks,
|
|
228
|
+
controllers,
|
|
229
|
+
info,
|
|
230
|
+
declare(svc) {
|
|
231
|
+
for (const dependency of dependencies)
|
|
232
|
+
svc.dependency(dependency.name, dependency.spec, dependency.handle);
|
|
233
|
+
for (const part of parts)
|
|
234
|
+
svc.server(part.name, part.knobs, part.impl);
|
|
235
|
+
for (const { check, run } of checks)
|
|
236
|
+
svc.check(check, run);
|
|
237
|
+
for (const controller of controllers) {
|
|
238
|
+
const builder = svc.controller(controller.name).requires(...controller.requires);
|
|
239
|
+
for (const pattern of controller.patterns)
|
|
240
|
+
builder.pattern(pattern.pattern, pattern.operations, pattern.operationOf);
|
|
241
|
+
}
|
|
242
|
+
return svc;
|
|
243
|
+
},
|
|
244
|
+
connections: () => [...faces.values()].flatMap((face) => face.connections?.() ?? []),
|
|
245
|
+
clientFor(connectionId) {
|
|
246
|
+
for (const face of faces.values()) {
|
|
247
|
+
const client = face.clientFor?.(connectionId);
|
|
248
|
+
if (client)
|
|
249
|
+
return client;
|
|
250
|
+
}
|
|
251
|
+
throw new Error(`Connection not found: ${connectionId}`);
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
export { httpFace, wsFace } from "./faces.js";
|
|
256
|
+
export { callOperations, faceHandler, operationOfCall } from "./tracking.js";
|
|
257
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/host/index.ts"],"names":[],"mappings":"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"}
|