@typerighter/rpc-server 0.0.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/.env +1 -0
- package/.env.publish +1 -0
- package/index.d.ts +30 -0
- package/index.js +86 -0
- package/install.js +60 -0
- package/package.json +30 -0
- package/platform.js +61 -0
package/.env
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DEV=true
|
package/.env.publish
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DEV=false
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
|
|
3
|
+
export interface RpcServerOptions {
|
|
4
|
+
/** Project root (default: cwd) */
|
|
5
|
+
root?: string;
|
|
6
|
+
/** Bind address (default: "127.0.0.1") */
|
|
7
|
+
addr?: string;
|
|
8
|
+
/** Bind port (default: 4747) */
|
|
9
|
+
port?: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class RpcServer extends EventEmitter {
|
|
13
|
+
constructor(options?: RpcServerOptions);
|
|
14
|
+
|
|
15
|
+
/** The ws:// address the server is listening on, or null if not started */
|
|
16
|
+
get address(): string | null;
|
|
17
|
+
|
|
18
|
+
/** Whether the server is currently listening */
|
|
19
|
+
get listening(): boolean;
|
|
20
|
+
|
|
21
|
+
/** Start the server. Emits "listening" when ready */
|
|
22
|
+
listen(callback?: () => void): this;
|
|
23
|
+
|
|
24
|
+
/** Stop the server. Emits "close" when the process exits */
|
|
25
|
+
close(): this;
|
|
26
|
+
|
|
27
|
+
on(event: 'listening', listener: () => void): this;
|
|
28
|
+
on(event: 'close', listener: (code: number | null, signal: string | null) => void): this;
|
|
29
|
+
on(event: 'error', listener: (error: Error) => void): this;
|
|
30
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { EventEmitter } from "node:events";
|
|
4
|
+
import { binPath } from "./platform.js";
|
|
5
|
+
|
|
6
|
+
const bin = binPath();
|
|
7
|
+
|
|
8
|
+
if (!existsSync(bin)) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
`tdr-rpc binary not found at ${bin}. Run "pnpm install" to download it, ` +
|
|
11
|
+
`or build manually with "cargo build --release -p tdr-server".`,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const DEFAULT_PORT = 4747;
|
|
16
|
+
|
|
17
|
+
export class RpcServer extends EventEmitter {
|
|
18
|
+
constructor({ root, addr, port } = {}) {
|
|
19
|
+
super();
|
|
20
|
+
this._root = root ?? process.cwd();
|
|
21
|
+
this._addr = addr ?? "127.0.0.1";
|
|
22
|
+
this._port = port ?? DEFAULT_PORT;
|
|
23
|
+
this._process = null;
|
|
24
|
+
this._listening = false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get address() {
|
|
28
|
+
if (!this._listening) return null;
|
|
29
|
+
return `ws://${this._addr}:${this._port}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get listening() {
|
|
33
|
+
return this._listening;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
listen(callback) {
|
|
37
|
+
if (this._process) {
|
|
38
|
+
throw new Error("Server is already running");
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const child = spawn(bin, [], {
|
|
42
|
+
cwd: this._root,
|
|
43
|
+
env: {
|
|
44
|
+
...process.env,
|
|
45
|
+
TDR_RPC_ROOT: this._root,
|
|
46
|
+
TDR_RPC_ADDR: this._addr,
|
|
47
|
+
TDR_RPC_PORT: String(this._port),
|
|
48
|
+
},
|
|
49
|
+
stdio: ["ignore", "pipe", "inherit"],
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
this._process = child;
|
|
53
|
+
|
|
54
|
+
const stdout = child.stdout;
|
|
55
|
+
if (!stdout) {
|
|
56
|
+
this.emit("error", new Error("Failed to capture tdr-rpc stdout"));
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// The server prints the ws:// address when ready
|
|
61
|
+
stdout.once("data", () => {
|
|
62
|
+
this._listening = true;
|
|
63
|
+
this.emit("listening");
|
|
64
|
+
if (callback) callback();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
child.on("error", (error) => {
|
|
68
|
+
this.emit("error", error);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
child.on("exit", (code, signal) => {
|
|
72
|
+
this._listening = false;
|
|
73
|
+
this._process = null;
|
|
74
|
+
this.emit("close", code, signal);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return this;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
close() {
|
|
81
|
+
if (this._process) {
|
|
82
|
+
this._process.kill();
|
|
83
|
+
}
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
}
|
package/install.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync, chmodSync } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
dev,
|
|
6
|
+
osArch,
|
|
7
|
+
releaseTag,
|
|
8
|
+
artifactName,
|
|
9
|
+
artifactUrl,
|
|
10
|
+
binPath,
|
|
11
|
+
repoRoot,
|
|
12
|
+
} from "./platform.js";
|
|
13
|
+
|
|
14
|
+
const bin = binPath();
|
|
15
|
+
|
|
16
|
+
if (existsSync(bin)) {
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// In dev mode, we compile and `bin` will just automatically point to the artifact
|
|
21
|
+
if (dev()) {
|
|
22
|
+
console.log("[rpc-server] Development mode: building tdr-rpc with cargo");
|
|
23
|
+
try {
|
|
24
|
+
execFileSync("cargo", ["build", "-p", "tdr-server"], {
|
|
25
|
+
cwd: repoRoot(),
|
|
26
|
+
stdio: "inherit",
|
|
27
|
+
});
|
|
28
|
+
} catch {
|
|
29
|
+
console.error("[rpc-server] cargo build failed");
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// In non dev mode, fetch the artifacts
|
|
36
|
+
const arch = osArch();
|
|
37
|
+
const tag = releaseTag();
|
|
38
|
+
const artifact = artifactName(arch);
|
|
39
|
+
const url = artifactUrl(tag, artifact);
|
|
40
|
+
|
|
41
|
+
const binDir = path.dirname(bin);
|
|
42
|
+
mkdirSync(binDir, { recursive: true });
|
|
43
|
+
|
|
44
|
+
console.log(`[rpc-server] Downloading tdr-rpc from ${url}`);
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
execFileSync("curl", ["-fsSL", "-o", bin, url], { stdio: "inherit" });
|
|
48
|
+
} catch {
|
|
49
|
+
console.error(`[rpc-server] Failed to download tdr-rpc from ${url}`);
|
|
50
|
+
console.error(
|
|
51
|
+
"[rpc-server] You can build it manually: cargo build --release -p tdr-server",
|
|
52
|
+
);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (process.platform !== "win32") {
|
|
57
|
+
chmodSync(bin, 0o755);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
console.log("[rpc-server] tdr-rpc installed successfully");
|
package/package.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/package",
|
|
3
|
+
"name": "@typerighter/rpc-server",
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"description": "Typedown RPC server binary.",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/Huy-DNA/typedown.git",
|
|
9
|
+
"directory": "packages/rpc-server"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "./index.js",
|
|
13
|
+
"types": "./index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./index.d.ts",
|
|
17
|
+
"import": "./index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"postinstall": "node install.js",
|
|
22
|
+
"prepublishOnly": "cargo build --release -p tdr-server && cp .env .env.bak && cp .env.publish .env",
|
|
23
|
+
"postpublish": "mv .env.bak .env"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"dotenv": "^16.0.0"
|
|
27
|
+
},
|
|
28
|
+
"author": "Huy-DNA <huydo862003@gmail.com>",
|
|
29
|
+
"license": "AGPL-3.0-only"
|
|
30
|
+
}
|
package/platform.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import dotenv from "dotenv";
|
|
5
|
+
|
|
6
|
+
const require = createRequire(import.meta.url);
|
|
7
|
+
const pkg = require("./package.json");
|
|
8
|
+
|
|
9
|
+
const env = dotenv.config({ path: new URL(".env", import.meta.url) });
|
|
10
|
+
const isDev = env.parsed?.DEV === "true";
|
|
11
|
+
|
|
12
|
+
export function dev() {
|
|
13
|
+
return isDev;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const PLATFORM_MAP = {
|
|
17
|
+
"linux-x64": "linux-x86_64",
|
|
18
|
+
"darwin-x64": "darwin-x86_64",
|
|
19
|
+
"darwin-arm64": "darwin-aarch64",
|
|
20
|
+
"win32-x64": "windows-x86_64",
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function osArch() {
|
|
24
|
+
const key = `${process.platform}-${process.arch}`;
|
|
25
|
+
const mapped = PLATFORM_MAP[key];
|
|
26
|
+
if (!mapped) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`Unsupported platform: ${process.platform} ${process.arch}`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return mapped;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function releaseTag() {
|
|
35
|
+
const version = pkg.version;
|
|
36
|
+
if (version.includes("-")) {
|
|
37
|
+
return `staging/v${version}`;
|
|
38
|
+
}
|
|
39
|
+
return `v${version}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function artifactName(osArchStr) {
|
|
43
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
44
|
+
return `tdr-rpc-${pkg.version}-${osArchStr}${ext}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function artifactUrl(tag, artifact) {
|
|
48
|
+
return `https://github.com/Huy-DNA/typedown/releases/download/${tag}/${artifact}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function repoRoot() {
|
|
52
|
+
return path.resolve(path.dirname(import.meta.filename), "..", "..");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function binPath() {
|
|
56
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
57
|
+
if (isDev) {
|
|
58
|
+
return path.join(repoRoot(), "target", "debug", `tdr-rpc${ext}`);
|
|
59
|
+
}
|
|
60
|
+
return path.join(path.dirname(import.meta.filename), "bin", `tdr-rpc${ext}`);
|
|
61
|
+
}
|