@procedures/server 0.0.0-stage → 0.2.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 +15 -0
- package/LICENSE +21 -0
- package/README.md +308 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +184 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/run.d.ts +42 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +176 -0
- package/dist/run.js.map +1 -0
- package/dist/types.d.ts +33 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -3
- package/src/cli.ts +19 -0
- package/src/config.ts +193 -0
- package/src/index.ts +11 -0
- package/src/run.ts +208 -0
- package/src/types.ts +32 -0
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The procedure server of the `server` CLI and of `mark serve` (roadmap 2.3, 3.1).
|
|
3
|
+
*
|
|
4
|
+
* The server builds a registry of its own from the modules of its `--procedures` packages (the
|
|
5
|
+
* side-effect-free `<package>/module` entry), with the `server.*` procedures, and serves it under
|
|
6
|
+
* the host policy of the core (`hostPolicy()`): the token, the `--expose` list and the browser
|
|
7
|
+
* checks. A package without a module entry still works: the server imports it and takes the
|
|
8
|
+
* procedures that the import added to the global registry.
|
|
9
|
+
*/
|
|
10
|
+
import { type HostPolicy, type ProcedureModule, type ProcedureRegistry } from "@procedures/client";
|
|
11
|
+
import { type Peer, type PeerEndpoint } from "@procedures/peer";
|
|
12
|
+
import type { ServerConfig } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* The module of a procedure package: its `module` entry, or else the procedures that importing
|
|
15
|
+
* the package adds to the global registry. A package resolves from the folders of `resolveFrom`
|
|
16
|
+
* (the current folder by default), then from this package. "pkg/register" (the form of the old
|
|
17
|
+
* examples) gives the module of "pkg". A file URL or a path is imported as it is.
|
|
18
|
+
*/
|
|
19
|
+
export declare function loadProcedureModule(spec: string, resolveFrom?: readonly string[]): Promise<ProcedureModule>;
|
|
20
|
+
/** The server that `runServer()` started. */
|
|
21
|
+
export interface RunningServer {
|
|
22
|
+
peer: Peer;
|
|
23
|
+
registry: ProcedureRegistry;
|
|
24
|
+
policy: HostPolicy;
|
|
25
|
+
endpoints: PeerEndpoint[];
|
|
26
|
+
token: string | undefined;
|
|
27
|
+
/** True when the token is random, for this run */
|
|
28
|
+
generatedToken: boolean;
|
|
29
|
+
}
|
|
30
|
+
/** The options of `startServer()` and `runServer()`. */
|
|
31
|
+
export interface ServerRunOptions {
|
|
32
|
+
/** The folders that a `--procedures` package resolves from, first to last (default: the current folder). */
|
|
33
|
+
resolveFrom?: readonly string[] | undefined;
|
|
34
|
+
}
|
|
35
|
+
/** This function starts the server of a configuration. It returns when the transports listen. */
|
|
36
|
+
export declare function startServer(config: ServerConfig, log?: (line: string) => void, options?: ServerRunOptions): Promise<RunningServer>;
|
|
37
|
+
/**
|
|
38
|
+
* The `server` CLI: parse the options, start the server, print where it listens, and keep the
|
|
39
|
+
* process alive until a signal. `mark serve` calls it with its own arguments.
|
|
40
|
+
*/
|
|
41
|
+
export declare function runServer(argv: string[], options?: ServerRunOptions): Promise<void>;
|
|
42
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,EAML,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAc,KAAK,IAAI,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAG5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA8C/C;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,GAAE,SAAS,MAAM,EAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,CAclI;AAUD,6CAA6C;AAC7C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,MAAM,EAAE,UAAU,CAAC;IACnB,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,kDAAkD;IAClD,cAAc,EAAE,OAAO,CAAC;CACzB;AAWD,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,4GAA4G;IAC5G,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;CAC7C;AAED,iGAAiG;AACjG,wBAAsB,WAAW,CAC/B,MAAM,EAAE,YAAY,EACpB,GAAG,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAkB,EACzC,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,aAAa,CAAC,CA4BxB;AAED;;;GAGG;AACH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuC7F"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The procedure server of the `server` CLI and of `mark serve` (roadmap 2.3, 3.1).
|
|
3
|
+
*
|
|
4
|
+
* The server builds a registry of its own from the modules of its `--procedures` packages (the
|
|
5
|
+
* side-effect-free `<package>/module` entry), with the `server.*` procedures, and serves it under
|
|
6
|
+
* the host policy of the core (`hostPolicy()`): the token, the `--expose` list and the browser
|
|
7
|
+
* checks. A package without a module entry still works: the server imports it and takes the
|
|
8
|
+
* procedures that the import added to the global registry.
|
|
9
|
+
*/
|
|
10
|
+
import { randomBytes } from "node:crypto";
|
|
11
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
12
|
+
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
13
|
+
import { pathToFileURL } from "node:url";
|
|
14
|
+
import { PROCEDURE_REGISTRY, createRegistry, hostPolicy, pathToKey, } from "@procedures/client";
|
|
15
|
+
import { createPeer } from "@procedures/peer";
|
|
16
|
+
import serverModule from "@procedures/peer/module";
|
|
17
|
+
import { parseConfig } from "./config.js";
|
|
18
|
+
/** A package name and the subpath after it: "@a/b/register" gives ["@a/b", "./register"]. */
|
|
19
|
+
function splitSpec(spec) {
|
|
20
|
+
const parts = spec.split("/");
|
|
21
|
+
const count = spec.startsWith("@") ? 2 : 1;
|
|
22
|
+
const name = parts.slice(0, count).join("/");
|
|
23
|
+
const rest = parts.slice(count).join("/");
|
|
24
|
+
return { name, subpath: rest ? `./${rest}` : "." };
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The file of an export of a package (`subpath`: "." or "./module"), from the `node_modules`
|
|
28
|
+
* folders above `from`. Undefined when no folder has the package or the export.
|
|
29
|
+
*/
|
|
30
|
+
function resolveExport(name, subpath, from) {
|
|
31
|
+
let dir = resolve(from);
|
|
32
|
+
for (;;) {
|
|
33
|
+
const folder = join(dir, "node_modules", ...name.split("/"));
|
|
34
|
+
const manifestPath = join(folder, "package.json");
|
|
35
|
+
if (existsSync(manifestPath)) {
|
|
36
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
37
|
+
const entry = manifest.exports?.[subpath] ?? manifest.exports?.[`${subpath}.js`];
|
|
38
|
+
const target = typeof entry === "string"
|
|
39
|
+
? entry
|
|
40
|
+
: (entry?.import ?? entry?.default);
|
|
41
|
+
if (target)
|
|
42
|
+
return join(folder, target);
|
|
43
|
+
if (subpath === "." && manifest.main)
|
|
44
|
+
return join(folder, manifest.main);
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
const parent = dirname(dir);
|
|
48
|
+
if (parent === dir)
|
|
49
|
+
return undefined;
|
|
50
|
+
dir = parent;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** The URL to import for a package export, from the first folder that has it; else the bare specifier. */
|
|
54
|
+
function importTarget(name, subpath, folders) {
|
|
55
|
+
for (const from of folders) {
|
|
56
|
+
const file = resolveExport(name, subpath, from);
|
|
57
|
+
if (file && existsSync(file))
|
|
58
|
+
return pathToFileURL(file).href;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The module of a procedure package: its `module` entry, or else the procedures that importing
|
|
64
|
+
* the package adds to the global registry. A package resolves from the folders of `resolveFrom`
|
|
65
|
+
* (the current folder by default), then from this package. "pkg/register" (the form of the old
|
|
66
|
+
* examples) gives the module of "pkg". A file URL or a path is imported as it is.
|
|
67
|
+
*/
|
|
68
|
+
export async function loadProcedureModule(spec, resolveFrom = [process.cwd()]) {
|
|
69
|
+
const isFile = spec.startsWith("file:") || spec.startsWith(".") || isAbsolute(spec);
|
|
70
|
+
if (!isFile) {
|
|
71
|
+
const { name, subpath } = splitSpec(spec);
|
|
72
|
+
const moduleUrl = importTarget(name, "./module", resolveFrom);
|
|
73
|
+
try {
|
|
74
|
+
const exports = (await import(moduleUrl ?? `${name}/module`));
|
|
75
|
+
if (exports.default && Array.isArray(exports.default.procedures))
|
|
76
|
+
return exports.default;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// No module entry: the package registers on import
|
|
80
|
+
}
|
|
81
|
+
return importedModule(spec, importTarget(name, subpath, resolveFrom) ?? spec);
|
|
82
|
+
}
|
|
83
|
+
return importedModule(spec, spec.startsWith("file:") ? spec : pathToFileURL(resolve(spec)).href);
|
|
84
|
+
}
|
|
85
|
+
/** The procedures that importing `target` adds to the global registry, as a module. */
|
|
86
|
+
async function importedModule(name, target) {
|
|
87
|
+
const before = new Set(PROCEDURE_REGISTRY.getAll().map((procedure) => pathToKey(procedure.path)));
|
|
88
|
+
await import(target);
|
|
89
|
+
const added = PROCEDURE_REGISTRY.getAll().filter((procedure) => !before.has(pathToKey(procedure.path)));
|
|
90
|
+
return { name, procedures: added };
|
|
91
|
+
}
|
|
92
|
+
/** The token of a configuration: `--token`, else `SERVER_TOKEN`, else a random one. `--no-token`: none. */
|
|
93
|
+
function tokenOf(config) {
|
|
94
|
+
const envToken = process.env["SERVER_TOKEN"] || undefined;
|
|
95
|
+
if (config.token === null)
|
|
96
|
+
return { token: undefined, generated: false };
|
|
97
|
+
if (config.token !== undefined)
|
|
98
|
+
return { token: config.token, generated: false };
|
|
99
|
+
if (envToken !== undefined)
|
|
100
|
+
return { token: envToken, generated: false };
|
|
101
|
+
return { token: randomBytes(24).toString("hex"), generated: true };
|
|
102
|
+
}
|
|
103
|
+
/** This function starts the server of a configuration. It returns when the transports listen. */
|
|
104
|
+
export async function startServer(config, log = console.log, options = {}) {
|
|
105
|
+
const modules = [serverModule];
|
|
106
|
+
for (const spec of config.procedures) {
|
|
107
|
+
if (config.verbose)
|
|
108
|
+
log(` Loading: ${spec}`);
|
|
109
|
+
try {
|
|
110
|
+
modules.push(await loadProcedureModule(spec, options.resolveFrom ?? [process.cwd()]));
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
throw new Error(`Failed to load procedure package ${spec}: ${error instanceof Error ? error.message : String(error)}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const registry = createRegistry(...modules);
|
|
117
|
+
if (config.verbose)
|
|
118
|
+
log(` Registered procedures: ${registry.getAll().length}`);
|
|
119
|
+
// A token by default: the server serves every procedure it loads, and any local program
|
|
120
|
+
// could call it. `--no-token` turns this off on purpose.
|
|
121
|
+
const { token, generated } = tokenOf(config);
|
|
122
|
+
const policy = hostPolicy({
|
|
123
|
+
...(config.expose.length > 0 ? { expose: config.expose } : {}),
|
|
124
|
+
...(token !== undefined ? { token } : {}),
|
|
125
|
+
});
|
|
126
|
+
const peer = await createPeer({
|
|
127
|
+
id: `peer-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
128
|
+
transports: config.transports,
|
|
129
|
+
registry,
|
|
130
|
+
policy,
|
|
131
|
+
});
|
|
132
|
+
await peer.start();
|
|
133
|
+
return { peer, registry, policy, endpoints: peer.getEndpoints(), token, generatedToken: generated };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* The `server` CLI: parse the options, start the server, print where it listens, and keep the
|
|
137
|
+
* process alive until a signal. `mark serve` calls it with its own arguments.
|
|
138
|
+
*/
|
|
139
|
+
export async function runServer(argv, options = {}) {
|
|
140
|
+
const config = parseConfig(argv);
|
|
141
|
+
console.log("Starting procedure server...");
|
|
142
|
+
if (config.verbose) {
|
|
143
|
+
console.log(" Procedures:", config.procedures.join(", ") || "(none specified)");
|
|
144
|
+
console.log(" Transports:", config.transports.map((t) => t.type).join(", "));
|
|
145
|
+
}
|
|
146
|
+
const running = await startServer(config, console.log, options);
|
|
147
|
+
console.log("\nServer started!");
|
|
148
|
+
console.log(` Server ID: ${running.peer.id}`);
|
|
149
|
+
console.log(` Procedures: ${running.peer.getServer().procedureCount}`);
|
|
150
|
+
console.log("\nEndpoints:");
|
|
151
|
+
for (const endpoint of running.endpoints) {
|
|
152
|
+
console.log(` [${endpoint.type}] ${endpoint.address}`);
|
|
153
|
+
}
|
|
154
|
+
if (running.token === undefined) {
|
|
155
|
+
console.log("\nNo token: any program on this computer can call the server.");
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.log(`\nToken${running.generatedToken ? " (random, for this run)" : ""}: ${running.token}`);
|
|
159
|
+
console.log(" Send it as 'Authorization: Bearer <token>', or as ?token=<token> for WebSocket.");
|
|
160
|
+
}
|
|
161
|
+
if (config.expose.length > 0) {
|
|
162
|
+
console.log(`Serves only: ${config.expose.join(", ")}`);
|
|
163
|
+
}
|
|
164
|
+
console.log("\nPress Ctrl+C to stop the server.");
|
|
165
|
+
const stop = () => {
|
|
166
|
+
console.log("\nShutting down...");
|
|
167
|
+
running.peer.stop().finally(() => process.exit(0));
|
|
168
|
+
};
|
|
169
|
+
process.on("SIGINT", stop);
|
|
170
|
+
process.on("SIGTERM", stop);
|
|
171
|
+
// Keep the process alive
|
|
172
|
+
await new Promise(() => {
|
|
173
|
+
// Never resolves - keeps process running
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server Configuration Types
|
|
3
|
+
*/
|
|
4
|
+
export interface TransportConfig {
|
|
5
|
+
type: "http" | "websocket" | "local";
|
|
6
|
+
port?: number;
|
|
7
|
+
host?: string;
|
|
8
|
+
basePath?: string;
|
|
9
|
+
cors?: boolean;
|
|
10
|
+
corsOrigins?: string[];
|
|
11
|
+
path?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ServerConfig {
|
|
14
|
+
procedures: string[];
|
|
15
|
+
transports: TransportConfig[];
|
|
16
|
+
verbose: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* The token that every request must send. `undefined`: the server makes a random token and
|
|
19
|
+
* prints it. `null` (`--no-token`): no token.
|
|
20
|
+
*/
|
|
21
|
+
token: string | null | undefined;
|
|
22
|
+
/** The procedures to serve (`--expose`): exact keys or `prefix.*`. Empty: every procedure. */
|
|
23
|
+
expose: string[];
|
|
24
|
+
}
|
|
25
|
+
export interface ServerCreateResult {
|
|
26
|
+
serverId: string;
|
|
27
|
+
endpoints: Array<{
|
|
28
|
+
type: string;
|
|
29
|
+
address: string;
|
|
30
|
+
}>;
|
|
31
|
+
procedureCount: number;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACjC,8FAA8F;IAC9F,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,cAAc,EAAE,MAAM,CAAC;CACxB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,70 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package",
|
|
2
3
|
"name": "@procedures/server",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"description": "A standalone procedure server: load procedure packages and serve them over HTTP and WebSocket with a token and an expose list.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"server",
|
|
8
|
+
"rpc",
|
|
9
|
+
"http",
|
|
10
|
+
"websocket",
|
|
11
|
+
"procedures"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://mark1russell7.github.io/client/",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/mark1russell7/client/issues"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/mark1russell7/client.git",
|
|
20
|
+
"directory": "packages/server"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Mark Russell (https://github.com/mark1russell7)",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"bin": {
|
|
34
|
+
"procedure-server": "./dist/cli.js"
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"src",
|
|
39
|
+
"!src/**/*.test.ts",
|
|
40
|
+
"!src/**/*.test.tsx",
|
|
41
|
+
"!src/**/__tests__",
|
|
42
|
+
"CHANGELOG.md"
|
|
43
|
+
],
|
|
44
|
+
"sideEffects": false,
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=22"
|
|
47
|
+
},
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public",
|
|
50
|
+
"provenance": true
|
|
51
|
+
},
|
|
52
|
+
"dependencies": {
|
|
53
|
+
"@procedures/client": "0.2.0",
|
|
54
|
+
"@procedures/peer": "0.2.0"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"typescript": "^7.0.2",
|
|
58
|
+
"@types/node": "^22.0.0",
|
|
59
|
+
"@mark1russell7/cue": "^1.0.0",
|
|
60
|
+
"vitest": "^5.0.3"
|
|
61
|
+
},
|
|
62
|
+
"scripts": {
|
|
63
|
+
"build": "tsc -b",
|
|
64
|
+
"typecheck": "tsc --noEmit",
|
|
65
|
+
"clean": "rm -rf dist .tsbuildinfo",
|
|
66
|
+
"test": "vitest run",
|
|
67
|
+
"test:watch": "vitest",
|
|
68
|
+
"start": "node dist/cli.js"
|
|
69
|
+
}
|
|
6
70
|
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* General Procedure Server CLI
|
|
4
|
+
*
|
|
5
|
+
* Starts a server that exposes the procedures of some packages via HTTP/WebSocket. The server
|
|
6
|
+
* has a registry of its own, built from the module of each package (roadmap 3.1), and the host
|
|
7
|
+
* policy of the core (roadmap 2.3). `mark serve` starts the same server.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* procedure-server --procedures @procedures/mongo --port 3000
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { runServer } from "./run.js";
|
|
14
|
+
|
|
15
|
+
runServer(process.argv.slice(2)).catch((error) => {
|
|
16
|
+
console.error("Server failed to start:");
|
|
17
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration Parser
|
|
3
|
+
*
|
|
4
|
+
* Parses command line arguments for the server.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ServerConfig, TransportConfig } from "./types.js";
|
|
8
|
+
|
|
9
|
+
const TRANSPORT_TYPES = ["http", "websocket", "local"] as const;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The port number of a `--port` value. An invalid port is an error: before, it was ignored and
|
|
13
|
+
* the server used port 3000 (deep dive CLI-16).
|
|
14
|
+
*/
|
|
15
|
+
function parsePort(value: string): number {
|
|
16
|
+
const port = /^\d+$/.test(value) ? Number(value) : NaN;
|
|
17
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
18
|
+
throw new Error(`Invalid port: "${value}". Give a number from 1 to 65535.`);
|
|
19
|
+
}
|
|
20
|
+
return port;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Parse command line arguments into server configuration.
|
|
25
|
+
*
|
|
26
|
+
* Usage:
|
|
27
|
+
* procedure-server --procedures @procedures/mongo/register --port 3000
|
|
28
|
+
* procedure-server --procedures pkg1,pkg2 --transport http,websocket
|
|
29
|
+
*
|
|
30
|
+
* An unknown option, an option with no value, an invalid port and an unknown transport are
|
|
31
|
+
* errors. The order of the options does not matter: `--cors` before `--transport` also applies.
|
|
32
|
+
*/
|
|
33
|
+
export function parseConfig(argv: string[]): ServerConfig {
|
|
34
|
+
const procedures: string[] = [];
|
|
35
|
+
const types: TransportConfig["type"][] = [];
|
|
36
|
+
let port: number | undefined;
|
|
37
|
+
let host: string | undefined;
|
|
38
|
+
let basePath: string | undefined;
|
|
39
|
+
let cors: boolean | undefined;
|
|
40
|
+
let verbose = false;
|
|
41
|
+
let serverToken: string | null | undefined;
|
|
42
|
+
const expose: string[] = [];
|
|
43
|
+
|
|
44
|
+
for (let i = 0; i < argv.length; i++) {
|
|
45
|
+
const token = argv[i]!;
|
|
46
|
+
const eq = token.startsWith("--") ? token.indexOf("=") : -1;
|
|
47
|
+
const arg = eq === -1 ? token : token.slice(0, eq);
|
|
48
|
+
const inline = eq === -1 ? undefined : token.slice(eq + 1);
|
|
49
|
+
const value = (): string => {
|
|
50
|
+
if (inline !== undefined) return inline;
|
|
51
|
+
const next = argv[i + 1];
|
|
52
|
+
if (next === undefined || next.startsWith("-")) {
|
|
53
|
+
throw new Error(`${arg} needs a value`);
|
|
54
|
+
}
|
|
55
|
+
i++;
|
|
56
|
+
return next;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
switch (arg) {
|
|
60
|
+
case "--procedures":
|
|
61
|
+
case "-p":
|
|
62
|
+
procedures.push(...value().split(",").map((p) => p.trim()).filter((p) => p.length > 0));
|
|
63
|
+
break;
|
|
64
|
+
case "--port":
|
|
65
|
+
port = parsePort(value());
|
|
66
|
+
break;
|
|
67
|
+
case "--host":
|
|
68
|
+
host = value();
|
|
69
|
+
break;
|
|
70
|
+
case "--transport":
|
|
71
|
+
case "-t":
|
|
72
|
+
for (const type of value().split(",").map((t) => t.trim())) {
|
|
73
|
+
const known = TRANSPORT_TYPES.find((name) => name === type);
|
|
74
|
+
if (!known) {
|
|
75
|
+
throw new Error(`Unknown transport: "${type}". Give ${TRANSPORT_TYPES.join(", ")}.`);
|
|
76
|
+
}
|
|
77
|
+
if (!types.includes(known)) {
|
|
78
|
+
types.push(known);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
case "--cors":
|
|
83
|
+
cors = true;
|
|
84
|
+
break;
|
|
85
|
+
case "--no-cors":
|
|
86
|
+
cors = false;
|
|
87
|
+
break;
|
|
88
|
+
case "--base-path":
|
|
89
|
+
basePath = value();
|
|
90
|
+
break;
|
|
91
|
+
case "--token":
|
|
92
|
+
serverToken = value();
|
|
93
|
+
break;
|
|
94
|
+
case "--no-token":
|
|
95
|
+
serverToken = null;
|
|
96
|
+
break;
|
|
97
|
+
case "--expose":
|
|
98
|
+
case "-e":
|
|
99
|
+
expose.push(...value().split(",").map((p) => p.trim()).filter((p) => p.length > 0));
|
|
100
|
+
break;
|
|
101
|
+
case "--verbose":
|
|
102
|
+
case "-v":
|
|
103
|
+
verbose = true;
|
|
104
|
+
break;
|
|
105
|
+
case "--help":
|
|
106
|
+
case "-h":
|
|
107
|
+
printHelp();
|
|
108
|
+
return process.exit(0);
|
|
109
|
+
default:
|
|
110
|
+
throw new Error(`Unknown option: ${token}. Run 'procedure-server --help' for the options.`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Default to HTTP transport if none specified. Loopback and no CORS by default: the server
|
|
115
|
+
// serves every procedure it loads. Before, it listened on 0.0.0.0 with
|
|
116
|
+
// Access-Control-Allow-Origin: * (deep dive CLI-1, DATA-20).
|
|
117
|
+
if (types.length === 0) {
|
|
118
|
+
types.push("http");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// --port is the port of the first transport. --host applies to each network transport.
|
|
122
|
+
const transports = types.map((type, index): TransportConfig => {
|
|
123
|
+
const first = index === 0;
|
|
124
|
+
if (type === "http") {
|
|
125
|
+
return {
|
|
126
|
+
type,
|
|
127
|
+
port: (first ? port : undefined) ?? 3000,
|
|
128
|
+
host: host ?? "127.0.0.1",
|
|
129
|
+
basePath: basePath ?? "/api",
|
|
130
|
+
...(cors !== undefined ? { cors } : {}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
if (type === "websocket") {
|
|
134
|
+
return { type, port: (first ? port : undefined) ?? 3001, host: host ?? "127.0.0.1", path: "/ws" };
|
|
135
|
+
}
|
|
136
|
+
return { type };
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return { procedures, transports, verbose, token: serverToken, expose };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function printHelp(): void {
|
|
143
|
+
console.log(`
|
|
144
|
+
procedure-server - General procedure server
|
|
145
|
+
|
|
146
|
+
USAGE:
|
|
147
|
+
procedure-server [OPTIONS]
|
|
148
|
+
|
|
149
|
+
OPTIONS:
|
|
150
|
+
--procedures, -p <pkgs> Comma-separated procedure packages to load
|
|
151
|
+
Example: @procedures/mongo/register
|
|
152
|
+
|
|
153
|
+
--transport, -t <types> Comma-separated transports: http, websocket, local
|
|
154
|
+
Default: http
|
|
155
|
+
|
|
156
|
+
--port <number> Port for the first transport
|
|
157
|
+
Default: 3000 (HTTP), 3001 (WebSocket)
|
|
158
|
+
|
|
159
|
+
--host <address> Host to bind to
|
|
160
|
+
Default: 127.0.0.1 (0.0.0.0 exposes every
|
|
161
|
+
procedure to the network)
|
|
162
|
+
|
|
163
|
+
--base-path <path> Base path for HTTP transport
|
|
164
|
+
Default: /api
|
|
165
|
+
|
|
166
|
+
--cors Enable CORS
|
|
167
|
+
--no-cors Disable CORS (default)
|
|
168
|
+
|
|
169
|
+
--token <secret> The token that every request must send
|
|
170
|
+
(Authorization: Bearer <token>, or ?token= for
|
|
171
|
+
WebSocket). Default: SERVER_TOKEN, or a random
|
|
172
|
+
token that the server prints at start
|
|
173
|
+
--no-token Serve without a token (only for a trusted host)
|
|
174
|
+
|
|
175
|
+
--expose, -e <patterns> Comma-separated procedures to serve: exact keys
|
|
176
|
+
(git.status) or prefixes (git.*)
|
|
177
|
+
Default: every loaded procedure
|
|
178
|
+
|
|
179
|
+
--verbose, -v Verbose output
|
|
180
|
+
|
|
181
|
+
--help, -h Show this help
|
|
182
|
+
|
|
183
|
+
EXAMPLES:
|
|
184
|
+
# Start MongoDB procedure server
|
|
185
|
+
procedure-server --procedures @procedures/mongo/register --port 3000
|
|
186
|
+
|
|
187
|
+
# Start with multiple procedure packages
|
|
188
|
+
procedure-server -p @procedures/fs/register,@procedures/git/register
|
|
189
|
+
|
|
190
|
+
# Start with both HTTP and WebSocket
|
|
191
|
+
procedure-server -p @procedures/mongo/register -t http,websocket
|
|
192
|
+
`);
|
|
193
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @procedures/server
|
|
3
|
+
*
|
|
4
|
+
* General procedure server - run any procedure packages via HTTP/WebSocket. Importing this entry
|
|
5
|
+
* starts nothing: `runServer(argv)` is the CLI (`server` and `mark serve`), and `startServer()`
|
|
6
|
+
* starts a server from a parsed configuration.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { parseConfig } from "./config.js";
|
|
10
|
+
export { runServer, startServer, loadProcedureModule, type RunningServer, type ServerRunOptions } from "./run.js";
|
|
11
|
+
export type { ServerConfig, TransportConfig, ServerCreateResult } from "./types.js";
|