wire-mesh 0.0.0 → 1.0.1
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 +54 -0
- package/dist/adapters/node-websocket-transport.cjs +183 -0
- package/dist/adapters/node-websocket-transport.d.cts +18 -0
- package/dist/adapters/node-websocket-transport.d.mts +18 -0
- package/dist/adapters/node-websocket-transport.mjs +180 -0
- package/dist/server.cjs +126 -0
- package/dist/server.d.cts +17 -0
- package/dist/server.d.mts +17 -0
- package/dist/server.mjs +122 -0
- package/dist/web-console/assets/index-BMlnc-0m.css +1 -0
- package/dist/web-console/assets/index-CFw8TlSC.js +72 -0
- package/dist/web-console/assets/workbox-window.prod.es5-Bd17z0YL.js +2 -0
- package/dist/web-console/favicon.svg +13 -0
- package/dist/web-console/icons/icon-192x192.svg +13 -0
- package/dist/web-console/icons/icon-512x512.svg +13 -0
- package/dist/web-console/index.html +19 -0
- package/dist/web-console/manifest.webmanifest +1 -0
- package/dist/web-console/sw.js +1 -0
- package/dist/web-console/workbox-2fbc6a65.js +1 -0
- package/package.json +68 -3
package/dist/server.mjs
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createNodeWebSocketTransport } from "./adapters/node-websocket-transport.mjs";
|
|
3
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
4
|
+
import { dirname, extname, join, normalize, sep } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { createRelayHub } from "wire-mesh-core/domain/relay-hub";
|
|
7
|
+
//#region src/static-console.ts
|
|
8
|
+
const CONTENT_TYPES = {
|
|
9
|
+
".css": "text/css; charset=utf-8",
|
|
10
|
+
".html": "text/html; charset=utf-8",
|
|
11
|
+
".ico": "image/x-icon",
|
|
12
|
+
".js": "application/javascript; charset=utf-8",
|
|
13
|
+
".json": "application/json; charset=utf-8",
|
|
14
|
+
".mjs": "application/javascript; charset=utf-8",
|
|
15
|
+
".png": "image/png",
|
|
16
|
+
".svg": "image/svg+xml",
|
|
17
|
+
".webmanifest": "application/manifest+json; charset=utf-8",
|
|
18
|
+
".woff2": "font/woff2"
|
|
19
|
+
};
|
|
20
|
+
const DEFAULT_CONTENT_TYPE = "application/octet-stream";
|
|
21
|
+
const INDEX_FILE = "index.html";
|
|
22
|
+
const SERVICE_WORKER_ALLOWED_HEADERS = { "Service-Worker-Allowed": "/" };
|
|
23
|
+
/** Resolves `pathname` (a request URL's raw, still percent-encoded path) to a file under `consoleDir`. Returns undefined for a path that decodes to something outside `consoleDir` (a traversal attempt), a malformed percent-encoding, or a real 404 — an extension-bearing path with no matching file. */
|
|
24
|
+
function resolveConsoleFile(consoleDir, pathname) {
|
|
25
|
+
let decoded;
|
|
26
|
+
try {
|
|
27
|
+
decoded = decodeURIComponent(pathname);
|
|
28
|
+
} catch {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const root = normalize(consoleDir);
|
|
32
|
+
const relative = decoded === "/" ? INDEX_FILE : decoded.replace(/^\/+/, "");
|
|
33
|
+
let filePath = normalize(join(root, relative));
|
|
34
|
+
if (!filePath.startsWith(root + sep) && filePath !== root) return;
|
|
35
|
+
if (!existsSync(filePath) || !statSync(filePath).isFile()) {
|
|
36
|
+
if (extname(decoded) !== "") return;
|
|
37
|
+
filePath = join(root, INDEX_FILE);
|
|
38
|
+
if (!existsSync(filePath) || !statSync(filePath).isFile()) return;
|
|
39
|
+
}
|
|
40
|
+
const contentType = CONTENT_TYPES[extname(filePath)] ?? DEFAULT_CONTENT_TYPE;
|
|
41
|
+
return {
|
|
42
|
+
body: readFileSync(filePath),
|
|
43
|
+
contentType,
|
|
44
|
+
...filePath.endsWith("sw.js") ? { headers: SERVICE_WORKER_ALLOWED_HEADERS } : {}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/server.ts
|
|
49
|
+
function healthResponse() {
|
|
50
|
+
return {
|
|
51
|
+
ok: true,
|
|
52
|
+
node: "wire-mesh",
|
|
53
|
+
roles: ["relay"]
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const DEFAULT_BIND_ADDRESS = "0.0.0.0:8787";
|
|
57
|
+
const HTTP_OK = 200;
|
|
58
|
+
const HTTP_NOT_FOUND = 404;
|
|
59
|
+
const HEALTH_PATH = "/health";
|
|
60
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
61
|
+
const CONSOLE_DIR = join(__dirname, "web-console");
|
|
62
|
+
function bindAddressFromArgs(argv) {
|
|
63
|
+
const flagIndex = argv.indexOf("--bind");
|
|
64
|
+
return (flagIndex === -1 ? void 0 : argv[flagIndex + 1]) ?? DEFAULT_BIND_ADDRESS;
|
|
65
|
+
}
|
|
66
|
+
/** 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. */
|
|
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
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** 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
|
+
function createHttpRequestHandler(consoleDir) {
|
|
81
|
+
return (request, response) => {
|
|
82
|
+
const url = new URL(request.url ?? "/", "http://localhost");
|
|
83
|
+
if (url.pathname === HEALTH_PATH) {
|
|
84
|
+
response.writeHead(HTTP_OK, { "content-type": "application/json" });
|
|
85
|
+
response.end(JSON.stringify(healthResponse()));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const file = resolveConsoleFile(consoleDir, url.pathname);
|
|
89
|
+
if (file) {
|
|
90
|
+
response.writeHead(HTTP_OK, {
|
|
91
|
+
"content-type": file.contentType,
|
|
92
|
+
...file.headers
|
|
93
|
+
});
|
|
94
|
+
response.end(file.body);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
response.writeHead(HTTP_NOT_FOUND).end();
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function logStartup(message) {
|
|
101
|
+
console.log(message);
|
|
102
|
+
}
|
|
103
|
+
async function main() {
|
|
104
|
+
const argv = process.argv.slice(2);
|
|
105
|
+
const tlsFiles = tlsFromArgs(argv);
|
|
106
|
+
const tls = tlsFiles ? {
|
|
107
|
+
certificatePem: readFileSync(tlsFiles.certPath, "utf-8"),
|
|
108
|
+
privateKeyPem: readFileSync(tlsFiles.keyPath, "utf-8")
|
|
109
|
+
} : void 0;
|
|
110
|
+
const hub = createRelayHub();
|
|
111
|
+
const listener = await createNodeWebSocketTransport({
|
|
112
|
+
onHttpRequest: createHttpRequestHandler(CONSOLE_DIR),
|
|
113
|
+
...tls ? { tls } : {}
|
|
114
|
+
}).listen(bindAddressFromArgs(argv), (connection) => {
|
|
115
|
+
hub.handleConnection(connection);
|
|
116
|
+
});
|
|
117
|
+
logStartup(`wire-mesh listening on ${tls ? "wss" : "ws"}://${listener.address}`);
|
|
118
|
+
}
|
|
119
|
+
const invokedPath = process.argv[1];
|
|
120
|
+
if (invokedPath !== void 0 && import.meta.url === `file://${invokedPath}`) main();
|
|
121
|
+
//#endregion
|
|
122
|
+
export { bindAddressFromArgs, createHttpRequestHandler, healthResponse, tlsFromArgs };
|