Skip to content

Commit 5296aaa

Browse files
committed
Add missing files
1 parent be5cbc4 commit 5296aaa

13 files changed

Lines changed: 586 additions & 0 deletions

File tree

lib/host.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
const { Protocol } = require('./protocol')
2+
3+
class Process extends Protocol {
4+
constructor(stream) {
5+
super(stream)
6+
7+
this._ready = new Promise((resolve, reject) => {
8+
const onready = () => {
9+
this.off('ready', onready).off('error', onerror)
10+
resolve()
11+
}
12+
13+
const onerror = (err) => {
14+
this.off('ready', onready).off('error', onerror)
15+
reject(err)
16+
}
17+
18+
this.on('ready', onready).on('error', onerror)
19+
})
20+
21+
this._ready.catch(() => {})
22+
}
23+
24+
get ready() {
25+
return this._ready
26+
}
27+
28+
terminate() {
29+
return new Promise((resolve) => {
30+
const cleanup = () => {
31+
this.off('exit', onexit)
32+
this._stream.off('close', onclose)
33+
}
34+
35+
const onexit = (code) => {
36+
cleanup()
37+
resolve(code)
38+
}
39+
40+
const onclose = () => {
41+
cleanup()
42+
resolve(undefined)
43+
}
44+
45+
this.once('exit', onexit).send('terminate')
46+
this._stream.once('close', onclose)
47+
})
48+
}
49+
50+
_destroy(cb) {
51+
if (this._stream.destroyed) return cb(null)
52+
53+
this.send('terminate')
54+
this._stream.once('close', () => cb(null))
55+
}
56+
}
57+
58+
exports.Process = Process
59+
60+
exports.wrap = function wrap(stream) {
61+
return new Process(stream)
62+
}

lib/protocol.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
const { Duplex, Writable, isStreamx } = require('streamx')
2+
3+
const CONTROL = 0x00
4+
const USER = 0x01
5+
6+
exports.CONTROL = CONTROL
7+
exports.USER = USER
8+
9+
const HEADER_LENGTH = 5
10+
11+
class Protocol extends Duplex {
12+
constructor(stream) {
13+
super()
14+
15+
this._stream = stream
16+
this._buffer = []
17+
this._buffered = 0
18+
this._frame = -1
19+
20+
stream
21+
.on('data', (chunk) => this._ondata(chunk))
22+
.on('end', () => this.push(null))
23+
.on('close', () => this.destroy())
24+
.on('error', (err) => this.destroy(err))
25+
}
26+
27+
send(type, payload) {
28+
const frame = encodeFrame(CONTROL, Buffer.from(JSON.stringify({ type, ...payload })))
29+
30+
if (isStreamx(this._stream)) {
31+
this._stream.write(frame)
32+
33+
return Writable.drained(this._stream)
34+
}
35+
36+
return new Promise((resolve) => this._stream.write(frame, resolve))
37+
}
38+
39+
_write(data, cb) {
40+
const frame = encodeFrame(USER, data)
41+
42+
if (isStreamx(this._stream)) {
43+
const ok = this._stream.write(frame)
44+
45+
if (ok) return cb(null)
46+
47+
this._stream.once('drain', () => cb(null))
48+
} else {
49+
this._stream.write(frame, cb)
50+
}
51+
}
52+
53+
_destroy(cb) {
54+
this._stream.destroy()
55+
cb(null)
56+
}
57+
58+
_ondata(chunk) {
59+
this._buffer.push(chunk)
60+
this._buffered += chunk.byteLength
61+
62+
if (this._frame === -1) this._onbeforeframe()
63+
else this._onafterframe()
64+
}
65+
66+
_onbeforeframe() {
67+
if (this._buffered < 4) return
68+
69+
const buffer = this._buffer.length === 1 ? this._buffer[0] : Buffer.concat(this._buffer)
70+
71+
this._buffer = [buffer]
72+
this._frame = 4 + buffer.readUInt32LE(0)
73+
74+
this._onafterframe()
75+
}
76+
77+
_onafterframe() {
78+
if (this._buffered < this._frame) return
79+
80+
const buffer = this._buffer.length === 1 ? this._buffer[0] : Buffer.concat(this._buffer)
81+
const frame = this._frame
82+
83+
this._buffered -= frame
84+
this._buffer = this._buffered > 0 ? [buffer.subarray(frame)] : []
85+
this._frame = -1
86+
87+
this._onframe(buffer.subarray(0, frame))
88+
this._onbeforeframe()
89+
}
90+
91+
_onframe(frame) {
92+
const tag = frame.readUInt8(4)
93+
const payload = frame.subarray(HEADER_LENGTH)
94+
95+
if (tag === USER) {
96+
this.push(Buffer.from(payload))
97+
} else if (tag === CONTROL) {
98+
let message
99+
100+
try {
101+
message = JSON.parse(payload.toString())
102+
} catch {
103+
return
104+
}
105+
106+
if (message.type === 'error') {
107+
const err = new Error(message.message)
108+
109+
if (message.stack) err.stack = message.stack
110+
111+
this.destroy(err)
112+
} else if (message.type === 'exit') {
113+
this.emit('exit', message.code)
114+
} else {
115+
this.emit(message.type, message)
116+
}
117+
}
118+
}
119+
}
120+
121+
exports.Protocol = Protocol
122+
123+
exports.attach = function attach(stream) {
124+
return new Protocol(stream)
125+
}
126+
127+
function encodeFrame(tag, data) {
128+
const length = data.length + 1
129+
const frame = Buffer.alloc(HEADER_LENGTH + data.length)
130+
131+
frame.writeUInt32LE(length, 0)
132+
frame.writeUInt8(tag, 4)
133+
134+
data.copy(frame, HEADER_LENGTH)
135+
136+
return frame
137+
}

test/fixtures/cleanup/core.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = function start(ipc) {
2+
return async function () {
3+
await new Promise((resolve) => setTimeout(resolve, 50))
4+
5+
ipc.write(Buffer.from('goodbye'))
6+
}
7+
}

test/fixtures/echo/core.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = function start(ipc) {
2+
ipc.on('data', (data) => ipc.write(data))
3+
}

test/fixtures/error/core.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = function start() {
2+
throw new Error('boom')
3+
}

test/fixtures/load/core.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
throw new Error('load')

test/fixtures/ready/core.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports = function start(ipc, ready) {
2+
ipc.on('data', (data) => ipc.write(data))
3+
4+
ready()
5+
6+
return new Promise(() => {})
7+
}

test/fixtures/rejection/core.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = function start() {
2+
setTimeout(() => {
3+
Promise.reject(new Error('rejected'))
4+
}, 10)
5+
}

test/fixtures/stop/core.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = function start(ipc) {
2+
return function () {
3+
ipc.write(Buffer.from('goodbye'))
4+
}
5+
}

test/fixtures/uncaught/core.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module.exports = function start() {
2+
setTimeout(() => {
3+
throw new Error('uncaught')
4+
}, 10)
5+
}

0 commit comments

Comments
 (0)