wire-mesh 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/server.cjs +131 -29
- package/dist/server.d.cts +0 -7
- package/dist/server.d.mts +0 -7
- package/dist/server.mjs +132 -28
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -28,6 +28,19 @@ pnpm add wire-mesh
|
|
|
28
28
|
wire-mesh --bind 0.0.0.0:8787
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Every flag the CLI accepts, exactly as `wire-mesh --help` prints it. An unknown flag, a flag missing its value, or a malformed `--bind` address exits non-zero with a message on stderr rather than starting a node; the `--tls-cert`/`--tls-key` pairing is enforced the same way. The parser, this text, and the help output all come from one flag table in `src/cli-options.ts`, and a test fails if this block stops matching it.
|
|
32
|
+
|
|
33
|
+
```text
|
|
34
|
+
Usage: wire-mesh [options]
|
|
35
|
+
|
|
36
|
+
Options:
|
|
37
|
+
--bind <host:port> Address to listen on. Port 0 asks the OS for a free port. Use 127.0.0.1:8787 to accept local connections only. (default: 0.0.0.0:8787)
|
|
38
|
+
--tls-cert <path> PEM certificate file, to serve wss:// and https://. Requires --tls-key.
|
|
39
|
+
--tls-key <path> PEM private key file for --tls-cert. Requires --tls-cert.
|
|
40
|
+
-h, --help Print this help and exit.
|
|
41
|
+
-v, --version Print the version and exit.
|
|
42
|
+
```
|
|
43
|
+
|
|
31
44
|
**Default bind address is `0.0.0.0:8787`, not loopback.** Unlike a typical dev-server tool, whose loopback-only default assumes only the machine itself needs to reach it, this package's whole purpose is LAN reachability — a phone on the same network, a laptop in the next room. `--bind` overrides the address if you want to restrict it (loopback-only, a specific interface, a different port).
|
|
32
45
|
|
|
33
46
|
Point a browser at the bound address to reach the console, or check its health directly:
|
package/dist/server.cjs
CHANGED
|
@@ -5,6 +5,98 @@ let node_fs = require("node:fs");
|
|
|
5
5
|
let node_path = require("node:path");
|
|
6
6
|
let node_url = require("node:url");
|
|
7
7
|
let wire_mesh_core_domain_relay_hub = require("wire-mesh-core/domain/relay-hub");
|
|
8
|
+
let node_util = require("node:util");
|
|
9
|
+
//#region src/cli-options.ts
|
|
10
|
+
/** The address the server binds when --bind is not given. */
|
|
11
|
+
const DEFAULT_BIND_ADDRESS = "0.0.0.0:8787";
|
|
12
|
+
const MAX_PORT = 65535;
|
|
13
|
+
/** Every flag the CLI accepts, keyed by its long name (without the leading dashes). */
|
|
14
|
+
const FLAGS = {
|
|
15
|
+
bind: {
|
|
16
|
+
type: "string",
|
|
17
|
+
valueName: "host:port",
|
|
18
|
+
default: DEFAULT_BIND_ADDRESS,
|
|
19
|
+
description: "Address to listen on. Port 0 asks the OS for a free port. Use 127.0.0.1:8787 to accept local connections only."
|
|
20
|
+
},
|
|
21
|
+
"tls-cert": {
|
|
22
|
+
type: "string",
|
|
23
|
+
valueName: "path",
|
|
24
|
+
description: "PEM certificate file, to serve wss:// and https://. Requires --tls-key."
|
|
25
|
+
},
|
|
26
|
+
"tls-key": {
|
|
27
|
+
type: "string",
|
|
28
|
+
valueName: "path",
|
|
29
|
+
description: "PEM private key file for --tls-cert. Requires --tls-cert."
|
|
30
|
+
},
|
|
31
|
+
help: {
|
|
32
|
+
type: "boolean",
|
|
33
|
+
short: "h",
|
|
34
|
+
description: "Print this help and exit."
|
|
35
|
+
},
|
|
36
|
+
version: {
|
|
37
|
+
type: "boolean",
|
|
38
|
+
short: "v",
|
|
39
|
+
description: "Print the version and exit."
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/** Thrown for arguments the CLI cannot act on; the message names the offending flag and is fit to print as-is. */
|
|
43
|
+
var CliUsageError = class extends Error {
|
|
44
|
+
name = "CliUsageError";
|
|
45
|
+
};
|
|
46
|
+
function usageLabelOf(name, definition) {
|
|
47
|
+
const long = `--${name}${definition.valueName === void 0 ? "" : ` <${definition.valueName}>`}`;
|
|
48
|
+
return definition.short === void 0 ? ` ${long}` : `-${definition.short}, ${long}`;
|
|
49
|
+
}
|
|
50
|
+
/** The usage text printed for --help, generated from FLAGS. */
|
|
51
|
+
function helpText() {
|
|
52
|
+
const rows = Object.entries(FLAGS).map(([name, definition]) => ({
|
|
53
|
+
label: usageLabelOf(name, definition),
|
|
54
|
+
description: definition.default === void 0 ? definition.description : `${definition.description} (default: ${definition.default})`
|
|
55
|
+
}));
|
|
56
|
+
const labelWidth = Math.max(...rows.map((row) => row.label.length));
|
|
57
|
+
return [
|
|
58
|
+
"Usage: wire-mesh [options]",
|
|
59
|
+
"",
|
|
60
|
+
"Options:",
|
|
61
|
+
...rows.map((row) => ` ${row.label.padEnd(labelWidth)} ${row.description}`)
|
|
62
|
+
].join("\n");
|
|
63
|
+
}
|
|
64
|
+
function validatedBindAddress(address) {
|
|
65
|
+
const separator = address.lastIndexOf(":");
|
|
66
|
+
const host = address.slice(0, separator);
|
|
67
|
+
const port = address.slice(separator + 1);
|
|
68
|
+
if (separator === -1 || host === "" || !/^\d+$/.test(port) || Number(port) > MAX_PORT) throw new CliUsageError(`--bind expects host:port with a port from 0 to ${String(MAX_PORT)}, got "${address}"`);
|
|
69
|
+
return address;
|
|
70
|
+
}
|
|
71
|
+
/** Parses the process arguments (without the node and script entries) into the command they ask for. Throws CliUsageError for an unknown flag, a stray positional argument, a flag missing or given an unwanted value, a malformed --bind address, or --tls-cert without --tls-key (or the reverse). */
|
|
72
|
+
function parseCliArguments(argv) {
|
|
73
|
+
let values;
|
|
74
|
+
try {
|
|
75
|
+
({values} = (0, node_util.parseArgs)({
|
|
76
|
+
args: [...argv],
|
|
77
|
+
options: FLAGS,
|
|
78
|
+
strict: true,
|
|
79
|
+
allowPositionals: false
|
|
80
|
+
}));
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (error instanceof Error) throw new CliUsageError(error.message, { cause: error });
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
if (values.help === true) return { kind: "help" };
|
|
86
|
+
if (values.version === true) return { kind: "version" };
|
|
87
|
+
const certPath = values["tls-cert"];
|
|
88
|
+
const keyPath = values["tls-key"];
|
|
89
|
+
if (certPath === void 0 !== (keyPath === void 0)) throw new CliUsageError("--tls-cert and --tls-key must be given together");
|
|
90
|
+
return {
|
|
91
|
+
kind: "serve",
|
|
92
|
+
bindAddress: validatedBindAddress(values.bind),
|
|
93
|
+
tls: certPath === void 0 || keyPath === void 0 ? void 0 : {
|
|
94
|
+
certPath,
|
|
95
|
+
keyPath
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
8
100
|
//#region src/static-console.ts
|
|
9
101
|
const CONTENT_TYPES = {
|
|
10
102
|
".css": "text/css; charset=utf-8",
|
|
@@ -54,28 +146,18 @@ function healthResponse() {
|
|
|
54
146
|
roles: ["relay"]
|
|
55
147
|
};
|
|
56
148
|
}
|
|
57
|
-
const DEFAULT_BIND_ADDRESS = "0.0.0.0:8787";
|
|
58
149
|
const HTTP_OK = 200;
|
|
59
150
|
const HTTP_NOT_FOUND = 404;
|
|
60
151
|
const HEALTH_PATH = "/health";
|
|
152
|
+
const EXIT_FAILURE = 1;
|
|
153
|
+
const EXIT_USAGE = 2;
|
|
61
154
|
const __dirname$1 = (0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
62
155
|
const CONSOLE_DIR = (0, node_path.join)(__dirname$1, "web-console");
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function tlsFromArgs(argv) {
|
|
69
|
-
const certIndex = argv.indexOf("--tls-cert");
|
|
70
|
-
const keyIndex = argv.indexOf("--tls-key");
|
|
71
|
-
const certPath = certIndex === -1 ? void 0 : argv[certIndex + 1];
|
|
72
|
-
const keyPath = keyIndex === -1 ? void 0 : argv[keyIndex + 1];
|
|
73
|
-
if (certPath === void 0 && keyPath === void 0) return;
|
|
74
|
-
if (certPath === void 0 || keyPath === void 0) throw new Error("--tls-cert and --tls-key must be given together");
|
|
75
|
-
return {
|
|
76
|
-
certPath,
|
|
77
|
-
keyPath
|
|
78
|
-
};
|
|
156
|
+
const PACKAGE_MANIFEST_URL = new URL("../package.json", require("url").pathToFileURL(__filename).href);
|
|
157
|
+
function readPackageVersion() {
|
|
158
|
+
const manifest = JSON.parse((0, node_fs.readFileSync)(PACKAGE_MANIFEST_URL, "utf-8"));
|
|
159
|
+
if (typeof manifest !== "object" || manifest === null || !("version" in manifest) || typeof manifest.version !== "string") throw new Error(`no string "version" in ${(0, node_url.fileURLToPath)(PACKAGE_MANIFEST_URL)}`);
|
|
160
|
+
return manifest.version;
|
|
79
161
|
}
|
|
80
162
|
/** Builds the listener's onHttpRequest handler: /health answers the same JSON shape healthResponse() returns, everything else resolves against consoleDir via resolveConsoleFile. Takes consoleDir as a parameter, not a module-level constant, so a test can point it at a temp fixture instead of the real build-time CONSOLE_DIR. */
|
|
81
163
|
function createHttpRequestHandler(consoleDir) {
|
|
@@ -98,29 +180,49 @@ function createHttpRequestHandler(consoleDir) {
|
|
|
98
180
|
response.writeHead(HTTP_NOT_FOUND).end();
|
|
99
181
|
};
|
|
100
182
|
}
|
|
101
|
-
function
|
|
183
|
+
function logOutput(message) {
|
|
102
184
|
console.log(message);
|
|
103
185
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
186
|
+
function logError(message) {
|
|
187
|
+
console.error(message);
|
|
188
|
+
}
|
|
189
|
+
async function main(argv) {
|
|
190
|
+
const command = parseCliArguments(argv);
|
|
191
|
+
if (command.kind === "help") {
|
|
192
|
+
logOutput(helpText());
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (command.kind === "version") {
|
|
196
|
+
logOutput(readPackageVersion());
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const tls = command.tls ? {
|
|
200
|
+
certificatePem: (0, node_fs.readFileSync)(command.tls.certPath, "utf-8"),
|
|
201
|
+
privateKeyPem: (0, node_fs.readFileSync)(command.tls.keyPath, "utf-8")
|
|
110
202
|
} : void 0;
|
|
111
203
|
const hub = (0, wire_mesh_core_domain_relay_hub.createRelayHub)();
|
|
112
204
|
const listener = await require_adapters_node_websocket_transport.createNodeWebSocketTransport({
|
|
113
205
|
onHttpRequest: createHttpRequestHandler(CONSOLE_DIR),
|
|
114
206
|
...tls ? { tls } : {}
|
|
115
|
-
}).listen(
|
|
207
|
+
}).listen(command.bindAddress, (connection) => {
|
|
116
208
|
hub.handleConnection(connection);
|
|
117
209
|
});
|
|
118
|
-
|
|
210
|
+
logOutput(`wire-mesh listening on ${tls ? "wss" : "ws"}://${listener.address}`);
|
|
211
|
+
}
|
|
212
|
+
/** Runs the CLI and turns a failure into a one-line message on stderr and a non-zero exit code, instead of an unhandled rejection's stack trace. */
|
|
213
|
+
async function run(argv) {
|
|
214
|
+
try {
|
|
215
|
+
await main(argv);
|
|
216
|
+
} catch (error) {
|
|
217
|
+
logError(`wire-mesh: ${error instanceof Error ? error.message : String(error)}`);
|
|
218
|
+
if (error instanceof CliUsageError) {
|
|
219
|
+
logError("Run wire-mesh --help for usage.");
|
|
220
|
+
process.exitCode = EXIT_USAGE;
|
|
221
|
+
} else process.exitCode = EXIT_FAILURE;
|
|
222
|
+
}
|
|
119
223
|
}
|
|
120
224
|
const invokedPath = process.argv[1];
|
|
121
|
-
if (invokedPath !== void 0 && (0, node_fs.realpathSync)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href)) === (0, node_fs.realpathSync)(invokedPath))
|
|
225
|
+
if (invokedPath !== void 0 && (0, node_fs.realpathSync)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href)) === (0, node_fs.realpathSync)(invokedPath)) run(process.argv.slice(2));
|
|
122
226
|
//#endregion
|
|
123
|
-
exports.bindAddressFromArgs = bindAddressFromArgs;
|
|
124
227
|
exports.createHttpRequestHandler = createHttpRequestHandler;
|
|
125
228
|
exports.healthResponse = healthResponse;
|
|
126
|
-
exports.tlsFromArgs = tlsFromArgs;
|
package/dist/server.d.cts
CHANGED
|
@@ -5,13 +5,6 @@ export declare function healthResponse(): {
|
|
|
5
5
|
node: string;
|
|
6
6
|
roles: string[];
|
|
7
7
|
};
|
|
8
|
-
export declare function bindAddressFromArgs(argv: readonly string[]): string;
|
|
9
|
-
export interface TlsFilePaths {
|
|
10
|
-
certPath: string;
|
|
11
|
-
keyPath: string;
|
|
12
|
-
}
|
|
13
|
-
/** Reads --tls-cert/--tls-key from argv, required together — a self-hosted node answering wss:// (wire-mesh#184, so an https://-served PWA can reach it without mixed-content blocking) needs an operator-supplied certificate; this package generates none of its own, matching the README's security note. Returns undefined when neither flag is given, the existing plain-ws:// default. */
|
|
14
|
-
export declare function tlsFromArgs(argv: readonly string[]): TlsFilePaths | undefined;
|
|
15
8
|
/** Builds the listener's onHttpRequest handler: /health answers the same JSON shape healthResponse() returns, everything else resolves against consoleDir via resolveConsoleFile. Takes consoleDir as a parameter, not a module-level constant, so a test can point it at a temp fixture instead of the real build-time CONSOLE_DIR. */
|
|
16
9
|
export declare function createHttpRequestHandler(consoleDir: string): (request: IncomingMessage, response: ServerResponse) => void;
|
|
17
10
|
//#endregion
|
package/dist/server.d.mts
CHANGED
|
@@ -5,13 +5,6 @@ export declare function healthResponse(): {
|
|
|
5
5
|
node: string;
|
|
6
6
|
roles: string[];
|
|
7
7
|
};
|
|
8
|
-
export declare function bindAddressFromArgs(argv: readonly string[]): string;
|
|
9
|
-
export interface TlsFilePaths {
|
|
10
|
-
certPath: string;
|
|
11
|
-
keyPath: string;
|
|
12
|
-
}
|
|
13
|
-
/** Reads --tls-cert/--tls-key from argv, required together — a self-hosted node answering wss:// (wire-mesh#184, so an https://-served PWA can reach it without mixed-content blocking) needs an operator-supplied certificate; this package generates none of its own, matching the README's security note. Returns undefined when neither flag is given, the existing plain-ws:// default. */
|
|
14
|
-
export declare function tlsFromArgs(argv: readonly string[]): TlsFilePaths | undefined;
|
|
15
8
|
/** Builds the listener's onHttpRequest handler: /health answers the same JSON shape healthResponse() returns, everything else resolves against consoleDir via resolveConsoleFile. Takes consoleDir as a parameter, not a module-level constant, so a test can point it at a temp fixture instead of the real build-time CONSOLE_DIR. */
|
|
16
9
|
export declare function createHttpRequestHandler(consoleDir: string): (request: IncomingMessage, response: ServerResponse) => void;
|
|
17
10
|
//#endregion
|
package/dist/server.mjs
CHANGED
|
@@ -4,6 +4,98 @@ import { existsSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
|
4
4
|
import { dirname, extname, join, normalize, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { createRelayHub } from "wire-mesh-core/domain/relay-hub";
|
|
7
|
+
import { parseArgs } from "node:util";
|
|
8
|
+
//#region src/cli-options.ts
|
|
9
|
+
/** The address the server binds when --bind is not given. */
|
|
10
|
+
const DEFAULT_BIND_ADDRESS = "0.0.0.0:8787";
|
|
11
|
+
const MAX_PORT = 65535;
|
|
12
|
+
/** Every flag the CLI accepts, keyed by its long name (without the leading dashes). */
|
|
13
|
+
const FLAGS = {
|
|
14
|
+
bind: {
|
|
15
|
+
type: "string",
|
|
16
|
+
valueName: "host:port",
|
|
17
|
+
default: DEFAULT_BIND_ADDRESS,
|
|
18
|
+
description: "Address to listen on. Port 0 asks the OS for a free port. Use 127.0.0.1:8787 to accept local connections only."
|
|
19
|
+
},
|
|
20
|
+
"tls-cert": {
|
|
21
|
+
type: "string",
|
|
22
|
+
valueName: "path",
|
|
23
|
+
description: "PEM certificate file, to serve wss:// and https://. Requires --tls-key."
|
|
24
|
+
},
|
|
25
|
+
"tls-key": {
|
|
26
|
+
type: "string",
|
|
27
|
+
valueName: "path",
|
|
28
|
+
description: "PEM private key file for --tls-cert. Requires --tls-cert."
|
|
29
|
+
},
|
|
30
|
+
help: {
|
|
31
|
+
type: "boolean",
|
|
32
|
+
short: "h",
|
|
33
|
+
description: "Print this help and exit."
|
|
34
|
+
},
|
|
35
|
+
version: {
|
|
36
|
+
type: "boolean",
|
|
37
|
+
short: "v",
|
|
38
|
+
description: "Print the version and exit."
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
/** Thrown for arguments the CLI cannot act on; the message names the offending flag and is fit to print as-is. */
|
|
42
|
+
var CliUsageError = class extends Error {
|
|
43
|
+
name = "CliUsageError";
|
|
44
|
+
};
|
|
45
|
+
function usageLabelOf(name, definition) {
|
|
46
|
+
const long = `--${name}${definition.valueName === void 0 ? "" : ` <${definition.valueName}>`}`;
|
|
47
|
+
return definition.short === void 0 ? ` ${long}` : `-${definition.short}, ${long}`;
|
|
48
|
+
}
|
|
49
|
+
/** The usage text printed for --help, generated from FLAGS. */
|
|
50
|
+
function helpText() {
|
|
51
|
+
const rows = Object.entries(FLAGS).map(([name, definition]) => ({
|
|
52
|
+
label: usageLabelOf(name, definition),
|
|
53
|
+
description: definition.default === void 0 ? definition.description : `${definition.description} (default: ${definition.default})`
|
|
54
|
+
}));
|
|
55
|
+
const labelWidth = Math.max(...rows.map((row) => row.label.length));
|
|
56
|
+
return [
|
|
57
|
+
"Usage: wire-mesh [options]",
|
|
58
|
+
"",
|
|
59
|
+
"Options:",
|
|
60
|
+
...rows.map((row) => ` ${row.label.padEnd(labelWidth)} ${row.description}`)
|
|
61
|
+
].join("\n");
|
|
62
|
+
}
|
|
63
|
+
function validatedBindAddress(address) {
|
|
64
|
+
const separator = address.lastIndexOf(":");
|
|
65
|
+
const host = address.slice(0, separator);
|
|
66
|
+
const port = address.slice(separator + 1);
|
|
67
|
+
if (separator === -1 || host === "" || !/^\d+$/.test(port) || Number(port) > MAX_PORT) throw new CliUsageError(`--bind expects host:port with a port from 0 to ${String(MAX_PORT)}, got "${address}"`);
|
|
68
|
+
return address;
|
|
69
|
+
}
|
|
70
|
+
/** Parses the process arguments (without the node and script entries) into the command they ask for. Throws CliUsageError for an unknown flag, a stray positional argument, a flag missing or given an unwanted value, a malformed --bind address, or --tls-cert without --tls-key (or the reverse). */
|
|
71
|
+
function parseCliArguments(argv) {
|
|
72
|
+
let values;
|
|
73
|
+
try {
|
|
74
|
+
({values} = parseArgs({
|
|
75
|
+
args: [...argv],
|
|
76
|
+
options: FLAGS,
|
|
77
|
+
strict: true,
|
|
78
|
+
allowPositionals: false
|
|
79
|
+
}));
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (error instanceof Error) throw new CliUsageError(error.message, { cause: error });
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
if (values.help === true) return { kind: "help" };
|
|
85
|
+
if (values.version === true) return { kind: "version" };
|
|
86
|
+
const certPath = values["tls-cert"];
|
|
87
|
+
const keyPath = values["tls-key"];
|
|
88
|
+
if (certPath === void 0 !== (keyPath === void 0)) throw new CliUsageError("--tls-cert and --tls-key must be given together");
|
|
89
|
+
return {
|
|
90
|
+
kind: "serve",
|
|
91
|
+
bindAddress: validatedBindAddress(values.bind),
|
|
92
|
+
tls: certPath === void 0 || keyPath === void 0 ? void 0 : {
|
|
93
|
+
certPath,
|
|
94
|
+
keyPath
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//#endregion
|
|
7
99
|
//#region src/static-console.ts
|
|
8
100
|
const CONTENT_TYPES = {
|
|
9
101
|
".css": "text/css; charset=utf-8",
|
|
@@ -53,28 +145,18 @@ function healthResponse() {
|
|
|
53
145
|
roles: ["relay"]
|
|
54
146
|
};
|
|
55
147
|
}
|
|
56
|
-
const DEFAULT_BIND_ADDRESS = "0.0.0.0:8787";
|
|
57
148
|
const HTTP_OK = 200;
|
|
58
149
|
const HTTP_NOT_FOUND = 404;
|
|
59
150
|
const HEALTH_PATH = "/health";
|
|
151
|
+
const EXIT_FAILURE = 1;
|
|
152
|
+
const EXIT_USAGE = 2;
|
|
60
153
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
61
154
|
const CONSOLE_DIR = join(__dirname, "web-console");
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function tlsFromArgs(argv) {
|
|
68
|
-
const certIndex = argv.indexOf("--tls-cert");
|
|
69
|
-
const keyIndex = argv.indexOf("--tls-key");
|
|
70
|
-
const certPath = certIndex === -1 ? void 0 : argv[certIndex + 1];
|
|
71
|
-
const keyPath = keyIndex === -1 ? void 0 : argv[keyIndex + 1];
|
|
72
|
-
if (certPath === void 0 && keyPath === void 0) return;
|
|
73
|
-
if (certPath === void 0 || keyPath === void 0) throw new Error("--tls-cert and --tls-key must be given together");
|
|
74
|
-
return {
|
|
75
|
-
certPath,
|
|
76
|
-
keyPath
|
|
77
|
-
};
|
|
155
|
+
const PACKAGE_MANIFEST_URL = new URL("../package.json", import.meta.url);
|
|
156
|
+
function readPackageVersion() {
|
|
157
|
+
const manifest = JSON.parse(readFileSync(PACKAGE_MANIFEST_URL, "utf-8"));
|
|
158
|
+
if (typeof manifest !== "object" || manifest === null || !("version" in manifest) || typeof manifest.version !== "string") throw new Error(`no string "version" in ${fileURLToPath(PACKAGE_MANIFEST_URL)}`);
|
|
159
|
+
return manifest.version;
|
|
78
160
|
}
|
|
79
161
|
/** Builds the listener's onHttpRequest handler: /health answers the same JSON shape healthResponse() returns, everything else resolves against consoleDir via resolveConsoleFile. Takes consoleDir as a parameter, not a module-level constant, so a test can point it at a temp fixture instead of the real build-time CONSOLE_DIR. */
|
|
80
162
|
function createHttpRequestHandler(consoleDir) {
|
|
@@ -97,26 +179,48 @@ function createHttpRequestHandler(consoleDir) {
|
|
|
97
179
|
response.writeHead(HTTP_NOT_FOUND).end();
|
|
98
180
|
};
|
|
99
181
|
}
|
|
100
|
-
function
|
|
182
|
+
function logOutput(message) {
|
|
101
183
|
console.log(message);
|
|
102
184
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
185
|
+
function logError(message) {
|
|
186
|
+
console.error(message);
|
|
187
|
+
}
|
|
188
|
+
async function main(argv) {
|
|
189
|
+
const command = parseCliArguments(argv);
|
|
190
|
+
if (command.kind === "help") {
|
|
191
|
+
logOutput(helpText());
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
if (command.kind === "version") {
|
|
195
|
+
logOutput(readPackageVersion());
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const tls = command.tls ? {
|
|
199
|
+
certificatePem: readFileSync(command.tls.certPath, "utf-8"),
|
|
200
|
+
privateKeyPem: readFileSync(command.tls.keyPath, "utf-8")
|
|
109
201
|
} : void 0;
|
|
110
202
|
const hub = createRelayHub();
|
|
111
203
|
const listener = await createNodeWebSocketTransport({
|
|
112
204
|
onHttpRequest: createHttpRequestHandler(CONSOLE_DIR),
|
|
113
205
|
...tls ? { tls } : {}
|
|
114
|
-
}).listen(
|
|
206
|
+
}).listen(command.bindAddress, (connection) => {
|
|
115
207
|
hub.handleConnection(connection);
|
|
116
208
|
});
|
|
117
|
-
|
|
209
|
+
logOutput(`wire-mesh listening on ${tls ? "wss" : "ws"}://${listener.address}`);
|
|
210
|
+
}
|
|
211
|
+
/** Runs the CLI and turns a failure into a one-line message on stderr and a non-zero exit code, instead of an unhandled rejection's stack trace. */
|
|
212
|
+
async function run(argv) {
|
|
213
|
+
try {
|
|
214
|
+
await main(argv);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
logError(`wire-mesh: ${error instanceof Error ? error.message : String(error)}`);
|
|
217
|
+
if (error instanceof CliUsageError) {
|
|
218
|
+
logError("Run wire-mesh --help for usage.");
|
|
219
|
+
process.exitCode = EXIT_USAGE;
|
|
220
|
+
} else process.exitCode = EXIT_FAILURE;
|
|
221
|
+
}
|
|
118
222
|
}
|
|
119
223
|
const invokedPath = process.argv[1];
|
|
120
|
-
if (invokedPath !== void 0 && realpathSync(fileURLToPath(import.meta.url)) === realpathSync(invokedPath))
|
|
224
|
+
if (invokedPath !== void 0 && realpathSync(fileURLToPath(import.meta.url)) === realpathSync(invokedPath)) run(process.argv.slice(2));
|
|
121
225
|
//#endregion
|
|
122
|
-
export {
|
|
226
|
+
export { createHttpRequestHandler, healthResponse };
|