@akala/pm 14.0.20 → 14.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/changelog.md +6 -1
- package/dist/esm/commands/$init.js +5 -15
- package/dist/esm/commands/$init.js.map +1 -1
- package/dist/esm/commands/start.d.ts +0 -2
- package/dist/esm/commands/start.js +18 -22
- package/dist/esm/commands/start.js.map +1 -1
- package/dist/esm/commands/stop.js +2 -11
- package/dist/esm/commands/stop.js.map +1 -1
- package/dist/esm/messageport-adapter.d.ts +14 -0
- package/dist/esm/messageport-adapter.js +53 -0
- package/dist/esm/messageport-adapter.js.map +1 -0
- package/dist/esm/runtimes/child_process.d.ts +33 -0
- package/dist/esm/runtimes/child_process.js +44 -0
- package/dist/esm/runtimes/child_process.js.map +1 -0
- package/dist/esm/runtimes/process.d.ts +17 -0
- package/dist/esm/runtimes/process.js +26 -0
- package/dist/esm/runtimes/process.js.map +1 -0
- package/dist/esm/runtimes/shared.d.ts +24 -0
- package/dist/esm/runtimes/shared.js +2 -0
- package/dist/esm/runtimes/shared.js.map +1 -0
- package/dist/esm/runtimes/worker.d.ts +29 -0
- package/dist/esm/runtimes/worker.js +29 -0
- package/dist/esm/runtimes/worker.js.map +1 -0
- package/dist/esm/state.d.ts +3 -3
- package/dist/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +6 -6
- package/src/commands/$init.ts +6 -18
- package/src/commands/start.ts +24 -25
- package/src/commands/stop.ts +5 -15
- package/src/messageport-adapter.ts +81 -0
- package/src/runtimes/child_process.ts +71 -0
- package/src/runtimes/process.ts +34 -0
- package/src/runtimes/shared.ts +24 -0
- package/src/runtimes/worker.ts +38 -0
- package/src/state.ts +4 -4
package/package.json
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
"pm": "dist/esm/cli.js",
|
5
5
|
"pm-fork": "dist/esm/fork.js"
|
6
6
|
},
|
7
|
-
"version": "14.0
|
7
|
+
"version": "14.1.0",
|
8
8
|
"scripts": {
|
9
9
|
"test": "echo 1",
|
10
10
|
"generate": "ac generate dist/esm/commands commands.json --name pm && ac generate dist/esm/cli-commands cli-commands.json --name pm",
|
@@ -29,11 +29,11 @@
|
|
29
29
|
"src/fork.ts"
|
30
30
|
],
|
31
31
|
"dependencies": {
|
32
|
-
"@akala/cli": "^
|
33
|
-
"@akala/commands": "^11.
|
34
|
-
"@akala/config": "^5.0.
|
35
|
-
"@akala/core": "^23.
|
36
|
-
"@akala/json-rpc-ws": "^11.0.
|
32
|
+
"@akala/cli": "^5.0.0",
|
33
|
+
"@akala/commands": "^11.4.0",
|
34
|
+
"@akala/config": "^5.0.61",
|
35
|
+
"@akala/core": "^23.2.0",
|
36
|
+
"@akala/json-rpc-ws": "^11.0.24",
|
37
37
|
"reflect-metadata": "^0.2.2",
|
38
38
|
"source-map-support": "^0.5.21"
|
39
39
|
},
|
package/src/commands/$init.ts
CHANGED
@@ -3,11 +3,11 @@ import fs from 'fs/promises';
|
|
3
3
|
import pmContainer from '../container.js';
|
4
4
|
import { Container, Metadata, ignoredCommands, configure, SelfDefinedCommand } from '@akala/commands';
|
5
5
|
import { PassThrough } from 'stream';
|
6
|
-
import { EventEmitter } from 'events';
|
7
6
|
import { CliContext } from '@akala/cli';
|
8
7
|
import { Configuration } from '@akala/config';
|
9
8
|
import { fileURLToPath } from 'url';
|
10
9
|
import { eachAsync } from '@akala/core';
|
10
|
+
import Process from '../runtimes/process.js';
|
11
11
|
|
12
12
|
export async function metadata(container: Container<unknown>, deep?: boolean): Promise<Metadata.Container>
|
13
13
|
{
|
@@ -71,22 +71,10 @@ export default async function (this: State, container: RunningContainer & pmCont
|
|
71
71
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
72
72
|
return stdoutPT.write(...args as [any, BufferEncoding]);
|
73
73
|
} as typeof process.stdout.write;
|
74
|
-
container.process =
|
75
|
-
stdout: stdoutPT,
|
76
|
-
|
77
|
-
|
78
|
-
, spawnargs: process.argv
|
79
|
-
, spawnfile: null
|
80
|
-
, kill: process.exit.bind(process)
|
81
|
-
, send: null
|
82
|
-
, disconnect: null
|
83
|
-
, unref: null
|
84
|
-
, ref: null
|
85
|
-
, killed: false,
|
86
|
-
[Symbol.dispose]()
|
87
|
-
{
|
88
|
-
|
89
|
-
}
|
74
|
+
container.process = new Process({
|
75
|
+
stdout: stdoutPT,
|
76
|
+
stderr: stderrPT,
|
77
|
+
stdin: process.stdin
|
90
78
|
});
|
91
79
|
|
92
80
|
const configPath = context.options.configFile || './.pm.config.json';
|
@@ -166,4 +154,4 @@ export default async function (this: State, container: RunningContainer & pmCont
|
|
166
154
|
process.disconnect();
|
167
155
|
}
|
168
156
|
}
|
169
|
-
}
|
157
|
+
}
|
package/src/commands/start.ts
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
import { Container, Processors, Metadata, Cli, updateCommands } from "@akala/commands";
|
2
2
|
import State, { RunningContainer, SidecarMetadata } from '../state.js';
|
3
|
-
import { spawn, ChildProcess, StdioOptions } from "child_process";
|
4
3
|
import pmContainer from '../container.js';
|
5
4
|
import { eachAsync, Event } from "@akala/core";
|
6
|
-
import { NewLinePrefixer } from "../new-line-prefixer.js";
|
7
5
|
import { CliContext, unparseOptions } from "@akala/cli";
|
8
6
|
import { ErrorWithStatus } from "@akala/core";
|
9
7
|
import getRandomName from "./name.js";
|
10
8
|
import { ProxyConfiguration } from "@akala/config";
|
11
|
-
import { IpcAdapter } from "../ipc-adapter.js";
|
12
9
|
import path from 'path'
|
13
10
|
import { fileURLToPath } from 'url'
|
11
|
+
import { RuntimeInstance } from "../runtimes/shared.js";
|
12
|
+
import ChildProcess from "../runtimes/child_process.js";
|
13
|
+
import Worker from "../runtimes/worker.js";
|
14
14
|
|
15
15
|
//eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
16
16
|
//@ts-ignore
|
17
17
|
const _dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url))
|
18
18
|
|
19
|
-
export default async function start(this: State, pm: pmContainer.container & Container<State>, name: string, options?: CliContext<{ configFile?: string, new?: boolean, name: string, keepAttached?: boolean, inspect?: boolean, verbose?: boolean, wait?: boolean, autostart?: boolean }>['options'], context?: Pick<CliContext<{}>, 'args'>): Promise<void | { execPath: string, args: string[], cwd: string,
|
19
|
+
export default async function start(this: State, pm: pmContainer.container & Container<State>, name: string, options?: CliContext<{ configFile?: string, new?: boolean, name: string, keepAttached?: boolean, inspect?: boolean, verbose?: boolean, wait?: boolean, autostart?: boolean }>['options'], context?: Pick<CliContext<{}>, 'args'>): Promise<void | { execPath: string, args: string[], cwd: string, shell: boolean, windowsHide: boolean }>
|
20
20
|
{
|
21
21
|
let args: string[];
|
22
22
|
if (options)
|
@@ -87,25 +87,17 @@ export default async function start(this: State, pm: pmContainer.container & Con
|
|
87
87
|
if (options && options.verbose)
|
88
88
|
args.push('-v')
|
89
89
|
|
90
|
-
let cp: ChildProcess;
|
91
90
|
if (!this.isDaemon)
|
92
91
|
{
|
93
|
-
|
94
|
-
|
95
|
-
else
|
96
|
-
cp = spawn(process.execPath, args, { cwd: process.cwd(), detached: true, stdio: ['ignore', 'ignore', 'ignore', 'ipc'] });
|
97
|
-
cp.on('exit', function (...args: unknown[])
|
92
|
+
const cp = ChildProcess.build(args, { inheritStdio: true, name: options.name });
|
93
|
+
cp.on('exit', function ()
|
98
94
|
{
|
99
95
|
console.log(args);
|
100
96
|
})
|
101
|
-
|
102
|
-
{
|
103
|
-
console.log(message);
|
104
|
-
// cp.disconnect();
|
105
|
-
});
|
97
|
+
|
106
98
|
return new Promise<void>((resolve) =>
|
107
99
|
{
|
108
|
-
cp.on('
|
100
|
+
cp.on('runningChanged', () =>
|
109
101
|
{
|
110
102
|
if (!options.keepAttached)
|
111
103
|
cp.unref();
|
@@ -116,6 +108,8 @@ export default async function start(this: State, pm: pmContainer.container & Con
|
|
116
108
|
}
|
117
109
|
else
|
118
110
|
{
|
111
|
+
let cp: RuntimeInstance;
|
112
|
+
|
119
113
|
if (!container && def.dependencies?.length)
|
120
114
|
{
|
121
115
|
var missingDeps = def.dependencies.filter(d => !this.config.containers[d] && !this.config.mapping[d]);
|
@@ -125,17 +119,22 @@ export default async function start(this: State, pm: pmContainer.container & Con
|
|
125
119
|
await eachAsync(def.dependencies, async (dep) => { await pm.dispatch('start', dep, { name: options.name + '-' + dep, wait: true }) });
|
126
120
|
}
|
127
121
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
122
|
+
switch (def?.type)
|
123
|
+
{
|
124
|
+
default:
|
125
|
+
throw new ErrorWithStatus(400, `container with type ${this.config.containers[name]?.type} are not yet supported`);
|
126
|
+
case 'worker':
|
127
|
+
cp = Worker.build(args, options);
|
128
|
+
break;
|
129
|
+
case 'nodejs':
|
130
|
+
cp = ChildProcess.build(args, options);
|
131
|
+
break;
|
132
|
+
}
|
133
133
|
|
134
134
|
if (!container || !container.running)
|
135
135
|
{
|
136
|
-
const socket = new IpcAdapter(cp);
|
137
136
|
container = new Container(options.name, null) as RunningContainer;
|
138
|
-
const connection = Processors.JsonRpc.getConnection(
|
137
|
+
const connection = Processors.JsonRpc.getConnection(cp.adapter, pm, (params) =>
|
139
138
|
{
|
140
139
|
params.process = cp;
|
141
140
|
Object.defineProperty(params, 'connectionAsContainer', Object.assign({ value: container }));
|
@@ -206,6 +205,6 @@ export default async function start(this: State, pm: pmContainer.container & Con
|
|
206
205
|
}
|
207
206
|
if (options.wait)
|
208
207
|
await new Promise<void>(resolve => container.ready?.addListener(resolve));
|
209
|
-
return { execPath: process.execPath, args: args, cwd: process.cwd(),
|
208
|
+
return { execPath: process.execPath, args: args, cwd: process.cwd(), shell: false, windowsHide: true };
|
210
209
|
}
|
211
|
-
}
|
210
|
+
}
|
package/src/commands/stop.ts
CHANGED
@@ -5,7 +5,7 @@ export default async function stop(this: State, name: string, container: Contain
|
|
5
5
|
{
|
6
6
|
await Promise.all(Object.values(this.processes).filter(p => p.name == name && p.process).map(cp =>
|
7
7
|
{
|
8
|
-
return new Promise<
|
8
|
+
return new Promise<void>((resolve) =>
|
9
9
|
{
|
10
10
|
if (cp.process && cp.running)
|
11
11
|
{
|
@@ -20,22 +20,12 @@ export default async function stop(this: State, name: string, container: Contain
|
|
20
20
|
console.error(e);
|
21
21
|
}
|
22
22
|
}
|
23
|
-
const timeout = setTimeout(function ()
|
24
|
-
{
|
25
|
-
cp.process.kill();
|
26
|
-
}, 5000)
|
27
|
-
cp.process.on('exit', (_code, signal) =>
|
28
|
-
{
|
29
|
-
container.unregister(cp.name);
|
30
|
-
delete this.processes[cp.name];
|
31
|
-
clearTimeout(timeout);
|
32
|
-
resolve(signal);
|
33
|
-
})
|
34
23
|
|
35
|
-
cp.process.
|
24
|
+
cp.process.stop().then(() => resolve());
|
25
|
+
|
36
26
|
}
|
37
27
|
else
|
38
|
-
resolve(
|
28
|
+
resolve()
|
39
29
|
})
|
40
30
|
})).then(() =>
|
41
31
|
{
|
@@ -44,4 +34,4 @@ export default async function stop(this: State, name: string, container: Contain
|
|
44
34
|
process.emit('SIGINT', 'SIGINT');
|
45
35
|
}
|
46
36
|
});
|
47
|
-
}
|
37
|
+
}
|
@@ -0,0 +1,81 @@
|
|
1
|
+
import { SocketAdapter, SocketAdapterEventMap } from "@akala/json-rpc-ws";
|
2
|
+
import { MessagePort, Worker } from "worker_threads";
|
3
|
+
|
4
|
+
export class MessagePortAdapter implements SocketAdapter
|
5
|
+
{
|
6
|
+
private isOpen: boolean = true;
|
7
|
+
|
8
|
+
get open(): boolean { return this.isOpen; }
|
9
|
+
close(): void
|
10
|
+
{
|
11
|
+
if (this.cp instanceof Worker)
|
12
|
+
this.cp.terminate();
|
13
|
+
else
|
14
|
+
this.cp.close();
|
15
|
+
}
|
16
|
+
send(data: string): void
|
17
|
+
{
|
18
|
+
this.cp.postMessage(data);
|
19
|
+
}
|
20
|
+
on<K extends keyof SocketAdapterEventMap>(event: K, handler: (ev: SocketAdapterEventMap[K]) => void): void
|
21
|
+
{
|
22
|
+
switch (event)
|
23
|
+
{
|
24
|
+
case 'message':
|
25
|
+
this.cp.on('message', handler);
|
26
|
+
break;
|
27
|
+
case 'open':
|
28
|
+
handler(null);
|
29
|
+
break;
|
30
|
+
case 'close':
|
31
|
+
this.cp.on('disconnect', handler);
|
32
|
+
break;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
once<K extends keyof SocketAdapterEventMap>(event: K, handler: (ev: SocketAdapterEventMap[K]) => void): void
|
37
|
+
{
|
38
|
+
switch (event)
|
39
|
+
{
|
40
|
+
case 'message':
|
41
|
+
this.cp.once('message', handler);
|
42
|
+
break;
|
43
|
+
case 'open':
|
44
|
+
handler(null);
|
45
|
+
break;
|
46
|
+
case 'close':
|
47
|
+
this.cp.once('close', () => handler(null));
|
48
|
+
break;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
constructor(private cp: MessagePort | Worker)
|
53
|
+
{
|
54
|
+
cp.on('close', () => this.isOpen = false);
|
55
|
+
}
|
56
|
+
off<K extends keyof SocketAdapterEventMap>(event: K, handler?: (this: unknown, ev: SocketAdapterEventMap[K]) => void): void
|
57
|
+
{
|
58
|
+
this.cp.off(event, handler);
|
59
|
+
}
|
60
|
+
|
61
|
+
pipe(socket: SocketAdapter<unknown>)
|
62
|
+
{
|
63
|
+
this.on('message', (message) => socket.send(message));
|
64
|
+
this.on('close', () => socket.close());
|
65
|
+
}
|
66
|
+
|
67
|
+
// _write(chunk: string | Buffer, encoding: string, callback: (error?: any) => void)
|
68
|
+
// {
|
69
|
+
// // The underlying source only deals with strings.
|
70
|
+
// if (Buffer.isBuffer(chunk))
|
71
|
+
// chunk = chunk.toString('utf8');
|
72
|
+
// if (this.cp.send)
|
73
|
+
// this.cp.send(chunk + '\n', callback);
|
74
|
+
// else
|
75
|
+
// callback(new Error('there is no send method on this process'));
|
76
|
+
// }
|
77
|
+
|
78
|
+
// _read()
|
79
|
+
// {
|
80
|
+
// }
|
81
|
+
}
|
@@ -0,0 +1,71 @@
|
|
1
|
+
import { ChildProcess, SendHandle, Serializable, spawn } from "child_process";
|
2
|
+
import { Readable } from "stream";
|
3
|
+
import { IpcAdapter } from "../ipc-adapter.js";
|
4
|
+
import { NewLinePrefixer } from "../new-line-prefixer.js";
|
5
|
+
import { RuntimeEventMap, RuntimeInstance } from "./shared.js";
|
6
|
+
import { EventEmitter, IEvent } from "@akala/core";
|
7
|
+
import { fileURLToPath } from "url";
|
8
|
+
|
9
|
+
export type ChildProcessRuntimeEventMap = {
|
10
|
+
"close": IEvent<[code: number | null, signal: NodeJS.Signals | null], void>
|
11
|
+
"disconnect": IEvent<[], void>
|
12
|
+
"error": IEvent<[err: Error], void>
|
13
|
+
"exit": IEvent<[code: number | null, signal: NodeJS.Signals | null], void>
|
14
|
+
"message": IEvent<[message: Serializable, sendHandle: SendHandle], void>
|
15
|
+
"spawn": IEvent<[], void>
|
16
|
+
|
17
|
+
} & RuntimeEventMap;
|
18
|
+
|
19
|
+
export type ChildPRocessRuntimeOptions = { new?: boolean, name: string, keepAttached?: boolean, inspect?: boolean, verbose?: boolean, wait?: boolean, inheritStdio?: boolean }
|
20
|
+
|
21
|
+
export default class Runtime extends EventEmitter<ChildProcessRuntimeEventMap> implements RuntimeInstance
|
22
|
+
{
|
23
|
+
private readonly cp: ChildProcess;
|
24
|
+
public readonly adapter: IpcAdapter;
|
25
|
+
constructor(args: string[], options: ChildPRocessRuntimeOptions)
|
26
|
+
{
|
27
|
+
super();
|
28
|
+
args.unshift(fileURLToPath(new URL('../fork', import.meta.url)));
|
29
|
+
if (options.inspect)
|
30
|
+
args.unshift("--inspect-brk");
|
31
|
+
this.cp = spawn(process.execPath, [require.resolve('../fork')].concat(args), { cwd: process.cwd(), detached: !options.keepAttached, env: Object.assign({ DEBUG_COLORS: process.stdout.isTTY }, process.env), stdio: ['ignore', options.inheritStdio ? 'inherit' : 'pipe', options.inheritStdio ? 'inherit' : 'pipe', 'ipc'], shell: false, windowsHide: true });
|
32
|
+
if (!options.inheritStdio)
|
33
|
+
{
|
34
|
+
this.cp.stderr?.pipe(new NewLinePrefixer(options.name + ' ', { useColors: true })).pipe(process.stderr);
|
35
|
+
this.cp.stdout?.pipe(new NewLinePrefixer(options.name + ' ', { useColors: true })).pipe(process.stdout);
|
36
|
+
}
|
37
|
+
this.adapter = new IpcAdapter(this.cp);
|
38
|
+
this.cp.on('close', () => this.emit('exit'));
|
39
|
+
|
40
|
+
if (options.keepAttached)
|
41
|
+
this.cp.on('disconnect', () => this.emit('exit'));
|
42
|
+
}
|
43
|
+
stop(timeoutInMs?: number): Promise<number>
|
44
|
+
{
|
45
|
+
return new Promise((resolve) =>
|
46
|
+
{
|
47
|
+
this.cp.on('exit', (code, signal) =>
|
48
|
+
{
|
49
|
+
clearTimeout(timeout);
|
50
|
+
resolve(code);
|
51
|
+
})
|
52
|
+
this.cp.kill('SIGINT');
|
53
|
+
const timeout = setTimeout(() => { this.cp.kill() }, timeoutInMs || 5000)
|
54
|
+
})
|
55
|
+
}
|
56
|
+
|
57
|
+
public static build(args: string[], options: ChildPRocessRuntimeOptions)
|
58
|
+
{
|
59
|
+
return new Runtime(args, options);
|
60
|
+
}
|
61
|
+
|
62
|
+
public unref()
|
63
|
+
{
|
64
|
+
this.cp.unref();
|
65
|
+
}
|
66
|
+
|
67
|
+
get stderr(): Readable { return this.cp.stderr }
|
68
|
+
get stdout(): Readable { return this.cp.stdout }
|
69
|
+
|
70
|
+
get running() { return this.cp.exitCode === null }
|
71
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import { Readable, Writable } from "stream";
|
2
|
+
import { IpcAdapter } from "../ipc-adapter.js";
|
3
|
+
import { RuntimeEventMap, RuntimeInstance } from "./shared.js";
|
4
|
+
import { EventEmitter } from "@akala/core";
|
5
|
+
|
6
|
+
export default class Runtime extends EventEmitter<RuntimeEventMap> implements RuntimeInstance
|
7
|
+
{
|
8
|
+
public readonly adapter: IpcAdapter;
|
9
|
+
private stdio: { stderr: Readable, stdout: Readable, stdin: Writable }
|
10
|
+
constructor(stdio?: { stderr: Readable, stdout: Readable, stdin: Writable })
|
11
|
+
{
|
12
|
+
super();
|
13
|
+
this.adapter = new IpcAdapter(process);
|
14
|
+
if (!stdio)
|
15
|
+
this.stdio = process;
|
16
|
+
}
|
17
|
+
stop(timeoutInMs?: number): Promise<number>
|
18
|
+
{
|
19
|
+
return new Promise((resolve) =>
|
20
|
+
{
|
21
|
+
process.on('exit', (code, signal) =>
|
22
|
+
{
|
23
|
+
clearTimeout(timeout);
|
24
|
+
resolve(code);
|
25
|
+
})
|
26
|
+
process.kill(process.pid, 'SIGINT')
|
27
|
+
const timeout = setTimeout(() => { process.exit() }, timeoutInMs || 5000)
|
28
|
+
})
|
29
|
+
}
|
30
|
+
get stderr(): Readable { return this.stdio.stderr }
|
31
|
+
get stdout(): Readable { return this.stdio.stdout }
|
32
|
+
|
33
|
+
get running() { return true }
|
34
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { IEvent, EventEmitter } from "@akala/core";
|
2
|
+
import { SocketAdapter } from "@akala/json-rpc-ws";
|
3
|
+
import { Readable } from "stream";
|
4
|
+
|
5
|
+
export interface Runtime
|
6
|
+
{
|
7
|
+
build(args: string[], options: { new?: boolean, name: string, keepAttached?: boolean, inspect?: boolean, verbose?: boolean, wait?: boolean }): RuntimeInstance;
|
8
|
+
}
|
9
|
+
|
10
|
+
export type RuntimeEventMap = {
|
11
|
+
runningChanged: IEvent<[], void>;
|
12
|
+
exit: IEvent<[], void>;
|
13
|
+
}
|
14
|
+
|
15
|
+
export interface RuntimeInstance<T extends RuntimeEventMap = RuntimeEventMap> extends EventEmitter<T>
|
16
|
+
{
|
17
|
+
stop(): Promise<number>;
|
18
|
+
get adapter(): SocketAdapter;
|
19
|
+
|
20
|
+
get stderr(): Readable;
|
21
|
+
get stdout(): Readable;
|
22
|
+
|
23
|
+
get running(): boolean;
|
24
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { Worker } from "worker_threads";
|
2
|
+
import { Readable } from "stream";
|
3
|
+
import { NewLinePrefixer } from "../new-line-prefixer.js";
|
4
|
+
import { MessagePortAdapter } from "../messageport-adapter.js";
|
5
|
+
import { RuntimeEventMap, RuntimeInstance } from "./shared.js";
|
6
|
+
import module from 'module'
|
7
|
+
import { EventEmitter } from "@akala/core";
|
8
|
+
const require = module.createRequire(import.meta.url);
|
9
|
+
|
10
|
+
export default class Runtime extends EventEmitter<RuntimeEventMap> implements RuntimeInstance
|
11
|
+
{
|
12
|
+
private readonly cp: Worker;
|
13
|
+
public readonly adapter: MessagePortAdapter;
|
14
|
+
private _running: boolean;
|
15
|
+
constructor(args: string[], options: { new?: boolean, name: string, keepAttached?: boolean, inspect?: boolean, verbose?: boolean, wait?: boolean })
|
16
|
+
{
|
17
|
+
super();
|
18
|
+
this.cp = new Worker(require.resolve('../fork'), { argv: args, stderr: true, stdout: true })
|
19
|
+
this.cp.stderr?.pipe(new NewLinePrefixer(options.name + ' ', { useColors: true })).pipe(process.stderr);
|
20
|
+
this.cp.stdout?.pipe(new NewLinePrefixer(options.name + ' ', { useColors: true })).pipe(process.stdout);
|
21
|
+
this.adapter = new MessagePortAdapter(this.cp);
|
22
|
+
this.cp.on('exit', () => { this._running = false; this.emit('runningChanged') })
|
23
|
+
}
|
24
|
+
stop(): Promise<number>
|
25
|
+
{
|
26
|
+
return this.cp.terminate();
|
27
|
+
}
|
28
|
+
|
29
|
+
public static build(args: string[], options: { new?: boolean, name: string, keepAttached?: boolean, inspect?: boolean, verbose?: boolean, wait?: boolean })
|
30
|
+
{
|
31
|
+
return new Runtime(args, options);
|
32
|
+
}
|
33
|
+
|
34
|
+
get stderr(): Readable { return this.cp.stderr }
|
35
|
+
get stdout(): Readable { return this.cp.stdout }
|
36
|
+
|
37
|
+
get running() { return this._running; }
|
38
|
+
}
|
package/src/state.ts
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import { Container } from "@akala/commands";
|
2
|
-
import { ChildProcess } from "child_process";
|
3
2
|
import { Event, SerializableObject } from "@akala/core";
|
4
3
|
import { SocketAdapter } from "@akala/json-rpc-ws";
|
5
4
|
import { ProxyConfiguration } from "@akala/config";
|
5
|
+
import { RuntimeInstance } from "./runtimes/shared.js";
|
6
6
|
|
7
7
|
export default interface State
|
8
8
|
{
|
@@ -26,7 +26,7 @@ export interface SidecarMetadata
|
|
26
26
|
stateless: boolean;
|
27
27
|
dependencies?: string[];
|
28
28
|
commandable: boolean;
|
29
|
-
type?: 'nodejs';
|
29
|
+
type?: 'nodejs' | 'worker';
|
30
30
|
}
|
31
31
|
|
32
32
|
export interface SidecarConfiguration<T extends string | SerializableObject = SerializableObject>
|
@@ -42,8 +42,8 @@ export interface SidecarConfiguration<T extends string | SerializableObject = Se
|
|
42
42
|
//eslint-disable-next-line @typescript-eslint/no-explicit-any
|
43
43
|
export interface RunningContainer<T extends string | SerializableObject = any> extends Container<unknown>, SidecarConfiguration<T>, SidecarMetadata
|
44
44
|
{
|
45
|
-
process:
|
45
|
+
process: RuntimeInstance;
|
46
46
|
running?: boolean;
|
47
47
|
stateless: boolean;
|
48
48
|
ready?: Event<[void]>;
|
49
|
-
}
|
49
|
+
}
|