fix(app): terminal issues
This commit is contained in:
@@ -23,60 +23,6 @@ export namespace Pty {
|
||||
close: (code?: number, reason?: string) => void
|
||||
}
|
||||
|
||||
type Subscriber = {
|
||||
id: number
|
||||
token: unknown
|
||||
}
|
||||
|
||||
const sockets = new WeakMap<object, number>()
|
||||
const owners = new WeakMap<object, string>()
|
||||
let socketCounter = 0
|
||||
|
||||
const tagSocket = (ws: Socket) => {
|
||||
if (!ws || typeof ws !== "object") return
|
||||
const next = (socketCounter = (socketCounter + 1) % Number.MAX_SAFE_INTEGER)
|
||||
sockets.set(ws, next)
|
||||
return next
|
||||
}
|
||||
|
||||
const token = (ws: Socket) => {
|
||||
const data = ws.data
|
||||
if (data === undefined) return
|
||||
if (data === null) return
|
||||
if (typeof data !== "object") return data
|
||||
|
||||
const id = (data as { connId?: unknown }).connId
|
||||
if (typeof id === "number" || typeof id === "string") return id
|
||||
|
||||
const href = (data as { href?: unknown }).href
|
||||
if (typeof href === "string") return href
|
||||
|
||||
const url = (data as { url?: unknown }).url
|
||||
if (typeof url === "string") return url
|
||||
if (url && typeof url === "object") {
|
||||
const href = (url as { href?: unknown }).href
|
||||
if (typeof href === "string") return href
|
||||
return url
|
||||
}
|
||||
|
||||
const events = (data as { events?: unknown }).events
|
||||
if (typeof events === "number" || typeof events === "string") return events
|
||||
if (events && typeof events === "object") {
|
||||
const id = (events as { connId?: unknown }).connId
|
||||
if (typeof id === "number" || typeof id === "string") return id
|
||||
|
||||
const id2 = (events as { connection?: unknown }).connection
|
||||
if (typeof id2 === "number" || typeof id2 === "string") return id2
|
||||
|
||||
const id3 = (events as { id?: unknown }).id
|
||||
if (typeof id3 === "number" || typeof id3 === "string") return id3
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
// WebSocket control frame: 0x00 + UTF-8 JSON.
|
||||
const meta = (cursor: number) => {
|
||||
const json = JSON.stringify({ cursor })
|
||||
@@ -141,7 +87,7 @@ export namespace Pty {
|
||||
buffer: string
|
||||
bufferCursor: number
|
||||
cursor: number
|
||||
subscribers: Map<Socket, Subscriber>
|
||||
subscribers: Map<unknown, Socket>
|
||||
}
|
||||
|
||||
const state = Instance.state(
|
||||
@@ -151,9 +97,9 @@ export namespace Pty {
|
||||
try {
|
||||
session.process.kill()
|
||||
} catch {}
|
||||
for (const ws of session.subscribers.keys()) {
|
||||
for (const [key, ws] of session.subscribers.entries()) {
|
||||
try {
|
||||
ws.close()
|
||||
if (ws.data === key) ws.close()
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@@ -224,26 +170,21 @@ export namespace Pty {
|
||||
ptyProcess.onData((chunk) => {
|
||||
session.cursor += chunk.length
|
||||
|
||||
for (const [ws, sub] of session.subscribers) {
|
||||
for (const [key, ws] of session.subscribers.entries()) {
|
||||
if (ws.readyState !== 1) {
|
||||
session.subscribers.delete(ws)
|
||||
session.subscribers.delete(key)
|
||||
continue
|
||||
}
|
||||
|
||||
if (typeof ws === "object" && sockets.get(ws) !== sub.id) {
|
||||
session.subscribers.delete(ws)
|
||||
continue
|
||||
}
|
||||
|
||||
if (token(ws) !== sub.token) {
|
||||
session.subscribers.delete(ws)
|
||||
if (ws.data !== key) {
|
||||
session.subscribers.delete(key)
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
ws.send(chunk)
|
||||
} catch {
|
||||
session.subscribers.delete(ws)
|
||||
session.subscribers.delete(key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,9 +197,9 @@ export namespace Pty {
|
||||
ptyProcess.onExit(({ exitCode }) => {
|
||||
log.info("session exited", { id, exitCode })
|
||||
session.info.status = "exited"
|
||||
for (const ws of session.subscribers.keys()) {
|
||||
for (const [key, ws] of session.subscribers.entries()) {
|
||||
try {
|
||||
ws.close()
|
||||
if (ws.data === key) ws.close()
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@@ -291,9 +232,9 @@ export namespace Pty {
|
||||
try {
|
||||
session.process.kill()
|
||||
} catch {}
|
||||
for (const ws of session.subscribers.keys()) {
|
||||
for (const [key, ws] of session.subscribers.entries()) {
|
||||
try {
|
||||
ws.close()
|
||||
if (ws.data === key) ws.close()
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
@@ -325,23 +266,16 @@ export namespace Pty {
|
||||
}
|
||||
log.info("client connected to session", { id })
|
||||
|
||||
const socketId = tagSocket(ws)
|
||||
if (socketId === undefined) {
|
||||
ws.close()
|
||||
return
|
||||
}
|
||||
// Use ws.data as the unique key for this connection lifecycle.
|
||||
// If ws.data is undefined, fallback to ws object.
|
||||
const connectionKey = ws.data && typeof ws.data === "object" ? ws.data : ws
|
||||
|
||||
const previous = owners.get(ws)
|
||||
if (previous && previous !== id) {
|
||||
state().get(previous)?.subscribers.delete(ws)
|
||||
}
|
||||
|
||||
owners.set(ws, id)
|
||||
session.subscribers.set(ws, { id: socketId, token: token(ws) })
|
||||
// Optionally cleanup if the key somehow exists
|
||||
session.subscribers.delete(connectionKey)
|
||||
session.subscribers.set(connectionKey, ws)
|
||||
|
||||
const cleanup = () => {
|
||||
session.subscribers.delete(ws)
|
||||
if (owners.get(ws) === id) owners.delete(ws)
|
||||
session.subscribers.delete(connectionKey)
|
||||
}
|
||||
|
||||
const start = session.bufferCursor
|
||||
|
||||
Reference in New Issue
Block a user