-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
3.3.0 remove a layer of indirection (unnecessary passthrough) #49
Changes from all commits
3c2c102
272d0a1
e3446de
32f2e9b
f6878ef
df00722
4c14313
904fc94
a815dc2
c945f5e
8771cf9
00dadbb
1e37466
ac4acb3
0630403
e71303f
04a136f
fe13ced
c4c2857
6168286
9a6d8e4
1210e93
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "@replit/ruspty-darwin-arm64", | ||
"version": "3.2.4", | ||
"version": "3.3.0", | ||
"os": [ | ||
"darwin" | ||
], | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "@replit/ruspty-darwin-x64", | ||
"version": "3.2.4", | ||
"version": "3.3.0", | ||
"os": [ | ||
"darwin" | ||
], | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "@replit/ruspty-linux-x64-gnu", | ||
"version": "3.2.4", | ||
"version": "3.3.0", | ||
"os": [ | ||
"linux" | ||
], | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "@replit/ruspty", | ||
"version": "3.2.4", | ||
"version": "3.3.0", | ||
"main": "dist/wrapper.js", | ||
"types": "dist/wrapper.d.ts", | ||
"author": "Szymon Kaliski <[email protected]>", | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -1,4 +1,4 @@ | ||||||
import { PassThrough, type Readable, type Writable } from 'node:stream'; | ||||||
import { type Readable, Writable } from 'node:stream'; | ||||||
import { ReadStream } from 'node:tty'; | ||||||
import { | ||||||
Pty as RawPty, | ||||||
|
@@ -45,21 +45,30 @@ type ExitResult = { | |||||
export class Pty { | ||||||
#pty: RawPty; | ||||||
#fd: number; | ||||||
#fdEnded: boolean = false; | ||||||
|
||||||
#handledClose: boolean = false; | ||||||
#handledEndOfData: boolean = false; | ||||||
|
||||||
#socket: ReadStream; | ||||||
get read(): Readable { | ||||||
return this.#socket; | ||||||
} | ||||||
|
||||||
read: Readable; | ||||||
write: Writable; | ||||||
|
||||||
constructor(options: PtyOptions) { | ||||||
const realExit = options.onExit; | ||||||
|
||||||
let resolve: (value: ExitResult) => void; | ||||||
let exitResult: Promise<ExitResult> = new Promise((res) => { | ||||||
resolve = res; | ||||||
let markExited: (value: ExitResult) => void; | ||||||
let exitResult: Promise<ExitResult> = new Promise((resolve) => { | ||||||
markExited = resolve; | ||||||
}); | ||||||
let markFdClosed: () => void; | ||||||
let fdClosed = new Promise<void>((resolve) => { | ||||||
markFdClosed = resolve; | ||||||
}); | ||||||
const mockedExit = (error: NodeJS.ErrnoException | null, code: number) => { | ||||||
resolve({ error, code }); | ||||||
markExited({ error, code }); | ||||||
}; | ||||||
|
||||||
// when pty exits, we should wait until the fd actually ends (end OR error) | ||||||
|
@@ -70,27 +79,29 @@ export class Pty { | |||||
// Transfer ownership of the FD to us. | ||||||
this.#fd = this.#pty.takeFd(); | ||||||
|
||||||
this.#socket = new ReadStream(this.#fd); | ||||||
const userFacingRead = new PassThrough(); | ||||||
const userFacingWrite = new PassThrough(); | ||||||
this.#socket.pipe(userFacingRead); | ||||||
userFacingWrite.pipe(this.#socket); | ||||||
this.read = userFacingRead; | ||||||
this.write = userFacingWrite; | ||||||
this.#socket = new ReadStream(this.#fd) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
ewwww semicolonless code. |
||||||
this.write = new Writable({ | ||||||
write: this.#socket.write.bind(this.#socket), | ||||||
}); | ||||||
Comment on lines
+83
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this looks disgusting. i love it. |
||||||
|
||||||
// catch end events | ||||||
const handleClose = () => { | ||||||
if (this.#fdEnded) { | ||||||
const handleEnd = async () => { | ||||||
if (this.#handledEndOfData) { | ||||||
return; | ||||||
} | ||||||
|
||||||
this.#fdEnded = true; | ||||||
exitResult.then((result) => { | ||||||
realExit(result.error, result.code) | ||||||
}); | ||||||
userFacingRead.end(); | ||||||
}; | ||||||
this.#socket.on('close', handleClose); | ||||||
this.#handledEndOfData = true; | ||||||
|
||||||
// must wait for fd close and exit result before calling real exit | ||||||
await fdClosed; | ||||||
const result = await exitResult; | ||||||
realExit(result.error, result.code) | ||||||
} | ||||||
|
||||||
this.read.on('end', handleEnd); | ||||||
this.read.on('close', () => { | ||||||
markFdClosed(); | ||||||
}); | ||||||
|
||||||
// PTYs signal their done-ness with an EIO error. we therefore need to filter them out (as well as | ||||||
// cleaning up other spurious errors) so that the user doesn't need to handle them and be in | ||||||
|
@@ -108,25 +119,26 @@ export class Pty { | |||||
// EIO only happens when the child dies. It is therefore our only true signal that there | ||||||
// is nothing left to read and we can start tearing things down. If we hadn't received an | ||||||
// error so far, we are considered to be in good standing. | ||||||
this.#socket.off('error', handleError); | ||||||
this.#socket.end(); | ||||||
this.read.off('error', handleError); | ||||||
handleEnd(); | ||||||
return; | ||||||
} | ||||||
} | ||||||
|
||||||
this.read.emit('error', err); | ||||||
}; | ||||||
this.#socket.on('error', handleError); | ||||||
|
||||||
this.read.on('error', handleError); | ||||||
} | ||||||
|
||||||
close() { | ||||||
this.#handledClose = true; | ||||||
|
||||||
// end instead of destroy so that the user can read the last bits of data | ||||||
// and allow graceful close event to mark the fd as ended | ||||||
this.#socket.end(); | ||||||
} | ||||||
|
||||||
resize(size: Size) { | ||||||
if (this.#fdEnded) { | ||||||
if (this.#handledClose || this.#handledEndOfData) { | ||||||
return; | ||||||
} | ||||||
|
||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we also have this be a getter for symmetry?