@push.rocks/smartrust 1.1.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/.gitea/workflows/default_nottags.yaml +66 -0
- package/.gitea/workflows/default_tags.yaml +124 -0
- package/.vscode/launch.json +11 -0
- package/.vscode/settings.json +26 -0
- package/changelog.md +22 -0
- package/dist_ts/00_commitinfo_data.d.ts +8 -0
- package/dist_ts/00_commitinfo_data.js +9 -0
- package/dist_ts/classes.rustbinarylocator.d.ts +28 -0
- package/dist_ts/classes.rustbinarylocator.js +126 -0
- package/dist_ts/classes.rustbridge.d.ts +39 -0
- package/dist_ts/classes.rustbridge.js +231 -0
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -0
- package/dist_ts/interfaces/config.d.ts +40 -0
- package/dist_ts/interfaces/config.js +2 -0
- package/dist_ts/interfaces/index.d.ts +2 -0
- package/dist_ts/interfaces/index.js +3 -0
- package/dist_ts/interfaces/ipc.d.ts +36 -0
- package/dist_ts/interfaces/ipc.js +2 -0
- package/dist_ts/paths.d.ts +1 -0
- package/dist_ts/paths.js +3 -0
- package/dist_ts/plugins.d.ts +8 -0
- package/dist_ts/plugins.js +11 -0
- package/npmextra.json +24 -0
- package/package.json +25 -0
- package/readme.md +5 -0
- package/test/helpers/mock-rust-binary.mjs +62 -0
- package/test/test.rustbinarylocator.node.ts +98 -0
- package/test/test.rustbridge.node.ts +191 -0
- package/test/test.ts +12 -0
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/classes.rustbinarylocator.ts +140 -0
- package/ts/classes.rustbridge.ts +256 -0
- package/ts/index.ts +3 -0
- package/ts/interfaces/config.ts +42 -0
- package/ts/interfaces/index.ts +2 -0
- package/ts/interfaces/ipc.ts +40 -0
- package/ts/paths.ts +5 -0
- package/ts/plugins.ts +13 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { RustBinaryLocator } from './classes.rustbinarylocator.js';
|
|
3
|
+
const defaultLogger = {
|
|
4
|
+
log() { },
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Generic bridge between TypeScript and a Rust binary.
|
|
8
|
+
* Communicates via JSON-over-stdin/stdout IPC protocol.
|
|
9
|
+
*
|
|
10
|
+
* @typeParam TCommands - Map of command names to their param/result types
|
|
11
|
+
*/
|
|
12
|
+
export class RustBridge extends plugins.events.EventEmitter {
|
|
13
|
+
locator;
|
|
14
|
+
options;
|
|
15
|
+
logger;
|
|
16
|
+
childProcess = null;
|
|
17
|
+
readlineInterface = null;
|
|
18
|
+
pendingRequests = new Map();
|
|
19
|
+
requestCounter = 0;
|
|
20
|
+
isRunning = false;
|
|
21
|
+
binaryPath = null;
|
|
22
|
+
constructor(options) {
|
|
23
|
+
super();
|
|
24
|
+
this.logger = options.logger || defaultLogger;
|
|
25
|
+
this.options = {
|
|
26
|
+
cliArgs: ['--management'],
|
|
27
|
+
requestTimeoutMs: 30000,
|
|
28
|
+
readyTimeoutMs: 10000,
|
|
29
|
+
readyEventName: 'ready',
|
|
30
|
+
...options,
|
|
31
|
+
};
|
|
32
|
+
this.locator = new RustBinaryLocator(options, this.logger);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Spawn the Rust binary and wait for it to signal readiness.
|
|
36
|
+
* Returns true if the binary was found and spawned successfully.
|
|
37
|
+
*/
|
|
38
|
+
async spawn() {
|
|
39
|
+
this.binaryPath = await this.locator.findBinary();
|
|
40
|
+
if (!this.binaryPath) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
return new Promise((resolve) => {
|
|
44
|
+
try {
|
|
45
|
+
const env = this.options.env
|
|
46
|
+
? { ...process.env, ...this.options.env }
|
|
47
|
+
: { ...process.env };
|
|
48
|
+
this.childProcess = plugins.childProcess.spawn(this.binaryPath, this.options.cliArgs, {
|
|
49
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
50
|
+
env,
|
|
51
|
+
});
|
|
52
|
+
// Handle stderr
|
|
53
|
+
this.childProcess.stderr?.on('data', (data) => {
|
|
54
|
+
const lines = data.toString().split('\n').filter((l) => l.trim());
|
|
55
|
+
for (const line of lines) {
|
|
56
|
+
this.logger.log('debug', `[${this.options.binaryName}] ${line}`);
|
|
57
|
+
this.emit('stderr', line);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
// Handle stdout via readline for line-delimited JSON
|
|
61
|
+
this.readlineInterface = plugins.readline.createInterface({ input: this.childProcess.stdout });
|
|
62
|
+
this.readlineInterface.on('line', (line) => {
|
|
63
|
+
this.handleLine(line.trim());
|
|
64
|
+
});
|
|
65
|
+
// Handle process exit
|
|
66
|
+
this.childProcess.on('exit', (code, signal) => {
|
|
67
|
+
this.logger.log('info', `Process exited (code=${code}, signal=${signal})`);
|
|
68
|
+
this.cleanup();
|
|
69
|
+
this.emit('exit', code, signal);
|
|
70
|
+
});
|
|
71
|
+
this.childProcess.on('error', (err) => {
|
|
72
|
+
this.logger.log('error', `Process error: ${err.message}`);
|
|
73
|
+
this.cleanup();
|
|
74
|
+
resolve(false);
|
|
75
|
+
});
|
|
76
|
+
// Wait for the ready event
|
|
77
|
+
const readyTimeout = setTimeout(() => {
|
|
78
|
+
this.logger.log('error', `Process did not send ready event within ${this.options.readyTimeoutMs}ms`);
|
|
79
|
+
this.kill();
|
|
80
|
+
resolve(false);
|
|
81
|
+
}, this.options.readyTimeoutMs);
|
|
82
|
+
this.once(`management:${this.options.readyEventName}`, () => {
|
|
83
|
+
clearTimeout(readyTimeout);
|
|
84
|
+
this.isRunning = true;
|
|
85
|
+
this.logger.log('info', `Bridge connected to ${this.options.binaryName}`);
|
|
86
|
+
this.emit('ready');
|
|
87
|
+
resolve(true);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
this.logger.log('error', `Failed to spawn: ${err.message}`);
|
|
92
|
+
resolve(false);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Send a typed command to the Rust process and wait for the response.
|
|
98
|
+
*/
|
|
99
|
+
async sendCommand(method, params) {
|
|
100
|
+
if (!this.childProcess || !this.isRunning) {
|
|
101
|
+
throw new Error(`${this.options.binaryName} bridge is not running`);
|
|
102
|
+
}
|
|
103
|
+
const id = `req_${++this.requestCounter}`;
|
|
104
|
+
const request = { id, method, params };
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
const timer = setTimeout(() => {
|
|
107
|
+
this.pendingRequests.delete(id);
|
|
108
|
+
reject(new Error(`Command '${method}' timed out after ${this.options.requestTimeoutMs}ms`));
|
|
109
|
+
}, this.options.requestTimeoutMs);
|
|
110
|
+
this.pendingRequests.set(id, { resolve, reject, timer });
|
|
111
|
+
const json = JSON.stringify(request) + '\n';
|
|
112
|
+
this.childProcess.stdin.write(json, (err) => {
|
|
113
|
+
if (err) {
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
this.pendingRequests.delete(id);
|
|
116
|
+
reject(new Error(`Failed to write to stdin: ${err.message}`));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Kill the Rust process and clean up all resources.
|
|
123
|
+
*/
|
|
124
|
+
kill() {
|
|
125
|
+
if (this.childProcess) {
|
|
126
|
+
const proc = this.childProcess;
|
|
127
|
+
this.childProcess = null;
|
|
128
|
+
this.isRunning = false;
|
|
129
|
+
// Close readline
|
|
130
|
+
if (this.readlineInterface) {
|
|
131
|
+
this.readlineInterface.close();
|
|
132
|
+
this.readlineInterface = null;
|
|
133
|
+
}
|
|
134
|
+
// Reject pending requests
|
|
135
|
+
for (const [, pending] of this.pendingRequests) {
|
|
136
|
+
clearTimeout(pending.timer);
|
|
137
|
+
pending.reject(new Error(`${this.options.binaryName} process killed`));
|
|
138
|
+
}
|
|
139
|
+
this.pendingRequests.clear();
|
|
140
|
+
// Remove all listeners
|
|
141
|
+
proc.removeAllListeners();
|
|
142
|
+
proc.stdout?.removeAllListeners();
|
|
143
|
+
proc.stderr?.removeAllListeners();
|
|
144
|
+
proc.stdin?.removeAllListeners();
|
|
145
|
+
// Kill the process
|
|
146
|
+
try {
|
|
147
|
+
proc.kill('SIGTERM');
|
|
148
|
+
}
|
|
149
|
+
catch { /* already dead */ }
|
|
150
|
+
// Destroy stdio pipes
|
|
151
|
+
try {
|
|
152
|
+
proc.stdin?.destroy();
|
|
153
|
+
}
|
|
154
|
+
catch { /* ignore */ }
|
|
155
|
+
try {
|
|
156
|
+
proc.stdout?.destroy();
|
|
157
|
+
}
|
|
158
|
+
catch { /* ignore */ }
|
|
159
|
+
try {
|
|
160
|
+
proc.stderr?.destroy();
|
|
161
|
+
}
|
|
162
|
+
catch { /* ignore */ }
|
|
163
|
+
// Unref so Node doesn't wait
|
|
164
|
+
try {
|
|
165
|
+
proc.unref();
|
|
166
|
+
}
|
|
167
|
+
catch { /* ignore */ }
|
|
168
|
+
// Force kill after 5 seconds
|
|
169
|
+
setTimeout(() => {
|
|
170
|
+
try {
|
|
171
|
+
proc.kill('SIGKILL');
|
|
172
|
+
}
|
|
173
|
+
catch { /* already dead */ }
|
|
174
|
+
}, 5000).unref();
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Whether the bridge is currently running.
|
|
179
|
+
*/
|
|
180
|
+
get running() {
|
|
181
|
+
return this.isRunning;
|
|
182
|
+
}
|
|
183
|
+
handleLine(line) {
|
|
184
|
+
if (!line)
|
|
185
|
+
return;
|
|
186
|
+
let parsed;
|
|
187
|
+
try {
|
|
188
|
+
parsed = JSON.parse(line);
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
this.logger.log('warn', `Non-JSON output: ${line}`);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
// Check if it's an event (has 'event' field, no 'id')
|
|
195
|
+
if ('event' in parsed && !('id' in parsed)) {
|
|
196
|
+
const event = parsed;
|
|
197
|
+
this.emit(`management:${event.event}`, event.data);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
// Otherwise it's a response (has 'id' field)
|
|
201
|
+
if ('id' in parsed) {
|
|
202
|
+
const response = parsed;
|
|
203
|
+
const pending = this.pendingRequests.get(response.id);
|
|
204
|
+
if (pending) {
|
|
205
|
+
clearTimeout(pending.timer);
|
|
206
|
+
this.pendingRequests.delete(response.id);
|
|
207
|
+
if (response.success) {
|
|
208
|
+
pending.resolve(response.result);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
pending.reject(new Error(response.error || 'Unknown error from Rust process'));
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
cleanup() {
|
|
217
|
+
this.isRunning = false;
|
|
218
|
+
this.childProcess = null;
|
|
219
|
+
if (this.readlineInterface) {
|
|
220
|
+
this.readlineInterface.close();
|
|
221
|
+
this.readlineInterface = null;
|
|
222
|
+
}
|
|
223
|
+
// Reject all pending requests
|
|
224
|
+
for (const [, pending] of this.pendingRequests) {
|
|
225
|
+
clearTimeout(pending.timer);
|
|
226
|
+
pending.reject(new Error(`${this.options.binaryName} process exited`));
|
|
227
|
+
}
|
|
228
|
+
this.pendingRequests.clear();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ydXN0YnJpZGdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5ydXN0YnJpZGdlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBVW5FLE1BQU0sYUFBYSxHQUFzQjtJQUN2QyxHQUFHLEtBQUksQ0FBQztDQUNULENBQUM7QUFFRjs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxVQUF3RCxTQUFRLE9BQU8sQ0FBQyxNQUFNLENBQUMsWUFBWTtJQUM5RixPQUFPLENBQW9CO0lBQzNCLE9BQU8sQ0FBZ0k7SUFDdkksTUFBTSxDQUFvQjtJQUMxQixZQUFZLEdBQTZDLElBQUksQ0FBQztJQUM5RCxpQkFBaUIsR0FBc0MsSUFBSSxDQUFDO0lBQzVELGVBQWUsR0FBRyxJQUFJLEdBQUcsRUFJN0IsQ0FBQztJQUNHLGNBQWMsR0FBRyxDQUFDLENBQUM7SUFDbkIsU0FBUyxHQUFHLEtBQUssQ0FBQztJQUNsQixVQUFVLEdBQWtCLElBQUksQ0FBQztJQUV6QyxZQUFZLE9BQTJCO1FBQ3JDLEtBQUssRUFBRSxDQUFDO1FBQ1IsSUFBSSxDQUFDLE1BQU0sR0FBRyxPQUFPLENBQUMsTUFBTSxJQUFJLGFBQWEsQ0FBQztRQUM5QyxJQUFJLENBQUMsT0FBTyxHQUFHO1lBQ2IsT0FBTyxFQUFFLENBQUMsY0FBYyxDQUFDO1lBQ3pCLGdCQUFnQixFQUFFLEtBQUs7WUFDdkIsY0FBYyxFQUFFLEtBQUs7WUFDckIsY0FBYyxFQUFFLE9BQU87WUFDdkIsR0FBRyxPQUFPO1NBQ1gsQ0FBQztRQUNGLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxpQkFBaUIsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFFRDs7O09BR0c7SUFDSSxLQUFLLENBQUMs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
|
package/dist_ts/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { RustBridge } from './classes.rustbridge.js';
|
|
2
|
+
export { RustBinaryLocator } from './classes.rustbinarylocator.js';
|
|
3
|
+
export * from './interfaces/index.js';
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDckQsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDbkUsY0FBYyx1QkFBdUIsQ0FBQyJ9
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal logger interface for the bridge.
|
|
3
|
+
*/
|
|
4
|
+
export interface IRustBridgeLogger {
|
|
5
|
+
log(level: string, message: string, data?: Record<string, any>): void;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Options for locating a Rust binary.
|
|
9
|
+
*/
|
|
10
|
+
export interface IBinaryLocatorOptions {
|
|
11
|
+
/** Name of the binary (e.g., 'rustproxy') */
|
|
12
|
+
binaryName: string;
|
|
13
|
+
/** Environment variable to check for explicit binary path (e.g., 'SMARTPROXY_RUST_BINARY') */
|
|
14
|
+
envVarName?: string;
|
|
15
|
+
/** Prefix for platform-specific npm packages (e.g., '@push.rocks/smartproxy') */
|
|
16
|
+
platformPackagePrefix?: string;
|
|
17
|
+
/** Additional local paths to search (defaults to ./rust/target/release/<binaryName> and ./rust/target/debug/<binaryName>) */
|
|
18
|
+
localPaths?: string[];
|
|
19
|
+
/** Whether to search the system PATH (default: true) */
|
|
20
|
+
searchSystemPath?: boolean;
|
|
21
|
+
/** Explicit binary path override - skips all other search */
|
|
22
|
+
binaryPath?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Options for the RustBridge.
|
|
26
|
+
*/
|
|
27
|
+
export interface IRustBridgeOptions extends IBinaryLocatorOptions {
|
|
28
|
+
/** CLI arguments passed to the binary (default: ['--management']) */
|
|
29
|
+
cliArgs?: string[];
|
|
30
|
+
/** Timeout for individual requests in ms (default: 30000) */
|
|
31
|
+
requestTimeoutMs?: number;
|
|
32
|
+
/** Timeout for the ready event during spawn in ms (default: 10000) */
|
|
33
|
+
readyTimeoutMs?: number;
|
|
34
|
+
/** Additional environment variables for the child process */
|
|
35
|
+
env?: Record<string, string>;
|
|
36
|
+
/** Name of the ready event emitted by the Rust binary (default: 'ready') */
|
|
37
|
+
readyEventName?: string;
|
|
38
|
+
/** Optional logger instance */
|
|
39
|
+
logger?: IRustBridgeLogger;
|
|
40
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export * from './ipc.js';
|
|
2
|
+
export * from './config.js';
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9pbnRlcmZhY2VzL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLGNBQWMsVUFBVSxDQUFDO0FBQ3pCLGNBQWMsYUFBYSxDQUFDIn0=
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Management request sent to the Rust binary via stdin.
|
|
3
|
+
*/
|
|
4
|
+
export interface IManagementRequest {
|
|
5
|
+
id: string;
|
|
6
|
+
method: string;
|
|
7
|
+
params: Record<string, any>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Management response received from the Rust binary via stdout.
|
|
11
|
+
*/
|
|
12
|
+
export interface IManagementResponse {
|
|
13
|
+
id: string;
|
|
14
|
+
success: boolean;
|
|
15
|
+
result?: any;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Management event received from the Rust binary (unsolicited, no id field).
|
|
20
|
+
*/
|
|
21
|
+
export interface IManagementEvent {
|
|
22
|
+
event: string;
|
|
23
|
+
data: any;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Definition of a single command supported by a Rust binary.
|
|
27
|
+
*/
|
|
28
|
+
export interface ICommandDefinition<TParams = any, TResult = any> {
|
|
29
|
+
params: TParams;
|
|
30
|
+
result: TResult;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Map of command names to their definitions.
|
|
34
|
+
* Used to type-safe the bridge's sendCommand method.
|
|
35
|
+
*/
|
|
36
|
+
export type TCommandMap = Record<string, ICommandDefinition>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const packageDir: string;
|
package/dist_ts/paths.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
export const packageDir = plugins.path.join(plugins.smartpath.get.dirnameFromImportMetaUrl(import.meta.url), '../');
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGF0aHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9wYXRocy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQ3pDLE9BQU8sQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLHdCQUF3QixDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQy9ELEtBQUssQ0FDTixDQUFDIn0=
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as childProcess from 'child_process';
|
|
4
|
+
import * as readline from 'readline';
|
|
5
|
+
import * as events from 'events';
|
|
6
|
+
export { path, fs, childProcess, readline, events };
|
|
7
|
+
import * as smartpath from '@push.rocks/smartpath';
|
|
8
|
+
export { smartpath };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// native scope
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as childProcess from 'child_process';
|
|
5
|
+
import * as readline from 'readline';
|
|
6
|
+
import * as events from 'events';
|
|
7
|
+
export { path, fs, childProcess, readline, events };
|
|
8
|
+
// @push.rocks scope
|
|
9
|
+
import * as smartpath from '@push.rocks/smartpath';
|
|
10
|
+
export { smartpath };
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsZUFBZTtBQUNmLE9BQU8sS0FBSyxJQUFJLE1BQU0sTUFBTSxDQUFDO0FBQzdCLE9BQU8sS0FBSyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBQ3pCLE9BQU8sS0FBSyxZQUFZLE1BQU0sZUFBZSxDQUFDO0FBQzlDLE9BQU8sS0FBSyxRQUFRLE1BQU0sVUFBVSxDQUFDO0FBQ3JDLE9BQU8sS0FBSyxNQUFNLE1BQU0sUUFBUSxDQUFDO0FBRWpDLE9BQU8sRUFBRSxJQUFJLEVBQUUsRUFBRSxFQUFFLFlBQVksRUFBRSxRQUFRLEVBQUUsTUFBTSxFQUFFLENBQUM7QUFFcEQsb0JBQW9CO0FBQ3BCLE9BQU8sS0FBSyxTQUFTLE1BQU0sdUJBQXVCLENBQUM7QUFFbkQsT0FBTyxFQUFFLFNBQVMsRUFBRSxDQUFDIn0=
|
package/npmextra.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"@git.zone/cli": {
|
|
3
|
+
"projectType": "npm",
|
|
4
|
+
"module": {
|
|
5
|
+
"githost": "code.foss.global",
|
|
6
|
+
"gitscope": "push.rocks",
|
|
7
|
+
"gitrepo": "smartrust",
|
|
8
|
+
"description": "a bridge between JS engines and rust",
|
|
9
|
+
"npmPackagename": "@push.rocks/smartrust",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"projectDomain": "push.rocks"
|
|
12
|
+
},
|
|
13
|
+
"release": {
|
|
14
|
+
"accessLevel": "public",
|
|
15
|
+
"registries": [
|
|
16
|
+
"https://verdaccio.lossless.digital",
|
|
17
|
+
"https://registry.npmjs.org"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"@ship.zone/szci": {
|
|
22
|
+
"npmGlobalTools": []
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@push.rocks/smartrust",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "a bridge between JS engines and rust",
|
|
6
|
+
"main": "dist_ts/index.js",
|
|
7
|
+
"typings": "dist_ts/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"author": "Task Venture Capital GmbH",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "(tstest test/ --verbose --logfile --timeout 60)",
|
|
13
|
+
"build": "(tsbuild tsfolders --allowimplicitany)",
|
|
14
|
+
"buildDocs": "(tsdoc)"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@git.zone/tsbuild": "^4.1.2",
|
|
18
|
+
"@git.zone/tsrun": "^2.0.1",
|
|
19
|
+
"@git.zone/tstest": "^3.1.8",
|
|
20
|
+
"@types/node": "^25.2.0"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@push.rocks/smartpath": "^6.0.0"
|
|
24
|
+
}
|
|
25
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Mock "Rust binary" for testing the RustBridge IPC protocol.
|
|
5
|
+
* Reads JSON lines from stdin, writes JSON lines to stdout.
|
|
6
|
+
* Emits a ready event on startup.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createInterface } from 'readline';
|
|
10
|
+
|
|
11
|
+
// Emit ready event
|
|
12
|
+
const readyEvent = JSON.stringify({ event: 'ready', data: { version: '1.0.0' } });
|
|
13
|
+
process.stdout.write(readyEvent + '\n');
|
|
14
|
+
|
|
15
|
+
const rl = createInterface({ input: process.stdin });
|
|
16
|
+
|
|
17
|
+
rl.on('line', (line) => {
|
|
18
|
+
let request;
|
|
19
|
+
try {
|
|
20
|
+
request = JSON.parse(line.trim());
|
|
21
|
+
} catch {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { id, method, params } = request;
|
|
26
|
+
|
|
27
|
+
if (method === 'echo') {
|
|
28
|
+
// Echo back the params as result
|
|
29
|
+
const response = JSON.stringify({ id, success: true, result: params });
|
|
30
|
+
process.stdout.write(response + '\n');
|
|
31
|
+
} else if (method === 'error') {
|
|
32
|
+
// Return an error
|
|
33
|
+
const response = JSON.stringify({ id, success: false, error: 'Test error message' });
|
|
34
|
+
process.stdout.write(response + '\n');
|
|
35
|
+
} else if (method === 'emitEvent') {
|
|
36
|
+
// Emit a custom event, then respond with success
|
|
37
|
+
const event = JSON.stringify({ event: params.eventName, data: params.eventData });
|
|
38
|
+
process.stdout.write(event + '\n');
|
|
39
|
+
const response = JSON.stringify({ id, success: true, result: null });
|
|
40
|
+
process.stdout.write(response + '\n');
|
|
41
|
+
} else if (method === 'slow') {
|
|
42
|
+
// Respond after a delay
|
|
43
|
+
setTimeout(() => {
|
|
44
|
+
const response = JSON.stringify({ id, success: true, result: { delayed: true } });
|
|
45
|
+
process.stdout.write(response + '\n');
|
|
46
|
+
}, 100);
|
|
47
|
+
} else if (method === 'exit') {
|
|
48
|
+
// Graceful exit
|
|
49
|
+
const response = JSON.stringify({ id, success: true, result: null });
|
|
50
|
+
process.stdout.write(response + '\n');
|
|
51
|
+
process.exit(0);
|
|
52
|
+
} else {
|
|
53
|
+
// Unknown command
|
|
54
|
+
const response = JSON.stringify({ id, success: false, error: `Unknown method: ${method}` });
|
|
55
|
+
process.stdout.write(response + '\n');
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Handle SIGTERM gracefully
|
|
60
|
+
process.on('SIGTERM', () => {
|
|
61
|
+
process.exit(0);
|
|
62
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import { RustBinaryLocator } from '../ts/classes.rustbinarylocator.js';
|
|
5
|
+
|
|
6
|
+
const testDir = path.resolve(path.dirname(new URL(import.meta.url).pathname));
|
|
7
|
+
|
|
8
|
+
tap.test('should return null when no binary is found', async () => {
|
|
9
|
+
const locator = new RustBinaryLocator({
|
|
10
|
+
binaryName: 'nonexistent-binary-xyz',
|
|
11
|
+
searchSystemPath: false,
|
|
12
|
+
});
|
|
13
|
+
const result = await locator.findBinary();
|
|
14
|
+
expect(result).toBeNull();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
tap.test('should use explicit binaryPath when provided', async () => {
|
|
18
|
+
const mockBinaryPath = path.join(testDir, 'helpers/mock-rust-binary.mjs');
|
|
19
|
+
const locator = new RustBinaryLocator({
|
|
20
|
+
binaryName: 'mock-rust-binary',
|
|
21
|
+
binaryPath: mockBinaryPath,
|
|
22
|
+
searchSystemPath: false,
|
|
23
|
+
});
|
|
24
|
+
const result = await locator.findBinary();
|
|
25
|
+
expect(result).toEqual(mockBinaryPath);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
tap.test('should cache the result', async () => {
|
|
29
|
+
const mockBinaryPath = path.join(testDir, 'helpers/mock-rust-binary.mjs');
|
|
30
|
+
const locator = new RustBinaryLocator({
|
|
31
|
+
binaryName: 'mock-rust-binary',
|
|
32
|
+
binaryPath: mockBinaryPath,
|
|
33
|
+
searchSystemPath: false,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const first = await locator.findBinary();
|
|
37
|
+
const second = await locator.findBinary();
|
|
38
|
+
expect(first).toEqual(second);
|
|
39
|
+
expect(first).toEqual(mockBinaryPath);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
tap.test('should clear cache', async () => {
|
|
43
|
+
const mockBinaryPath = path.join(testDir, 'helpers/mock-rust-binary.mjs');
|
|
44
|
+
const locator = new RustBinaryLocator({
|
|
45
|
+
binaryName: 'mock-rust-binary',
|
|
46
|
+
binaryPath: mockBinaryPath,
|
|
47
|
+
searchSystemPath: false,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const first = await locator.findBinary();
|
|
51
|
+
expect(first).toEqual(mockBinaryPath);
|
|
52
|
+
|
|
53
|
+
locator.clearCache();
|
|
54
|
+
// After clearing, next call should re-search and still find it
|
|
55
|
+
const second = await locator.findBinary();
|
|
56
|
+
expect(second).toEqual(mockBinaryPath);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
tap.test('should fall back to env var when binaryPath not set', async () => {
|
|
60
|
+
const mockBinaryPath = path.join(testDir, 'helpers/mock-rust-binary.mjs');
|
|
61
|
+
const envVar = 'TEST_SMARTRUST_BINARY_' + Date.now();
|
|
62
|
+
process.env[envVar] = mockBinaryPath;
|
|
63
|
+
|
|
64
|
+
const locator = new RustBinaryLocator({
|
|
65
|
+
binaryName: 'mock-rust-binary',
|
|
66
|
+
envVarName: envVar,
|
|
67
|
+
searchSystemPath: false,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const result = await locator.findBinary();
|
|
71
|
+
expect(result).toEqual(mockBinaryPath);
|
|
72
|
+
|
|
73
|
+
delete process.env[envVar];
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
tap.test('should find binary in local paths', async () => {
|
|
77
|
+
const mockBinaryPath = path.join(testDir, 'helpers/mock-rust-binary.mjs');
|
|
78
|
+
const locator = new RustBinaryLocator({
|
|
79
|
+
binaryName: 'mock-rust-binary',
|
|
80
|
+
localPaths: ['/nonexistent/path/binary', mockBinaryPath],
|
|
81
|
+
searchSystemPath: false,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const result = await locator.findBinary();
|
|
85
|
+
expect(result).toEqual(mockBinaryPath);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
tap.test('should find node in system PATH', async () => {
|
|
89
|
+
const locator = new RustBinaryLocator({
|
|
90
|
+
binaryName: 'node',
|
|
91
|
+
searchSystemPath: true,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const result = await locator.findBinary();
|
|
95
|
+
expect(result).not.toBeNull();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
export default tap.start();
|