import { Observable, Subject } from 'rxjs' import { Logger } from 'tabby-core' import { LoginScriptProcessor, LoginScriptsOptions } from './middleware/loginScriptProcessing' import { OSCProcessor } from './middleware/oscProcessing' import { SessionMiddlewareStack } from './api/middleware' /** * A session object for a [[BaseTerminalTabComponent]] * Extend this to implement custom I/O and process management for your terminal tab */ export abstract class BaseSession { open: boolean readonly oscProcessor = new OSCProcessor() readonly middleware = new SessionMiddlewareStack() protected output = new Subject() protected binaryOutput = new Subject() protected closed = new Subject() protected destroyed = new Subject() protected loginScriptProcessor: LoginScriptProcessor | null = null protected reportedCWD?: string private initialDataBuffer = Buffer.from('') private initialDataBufferReleased = false get output$ (): Observable { return this.output } get binaryOutput$ (): Observable { return this.binaryOutput } get closed$ (): Observable { return this.closed } get destroyed$ (): Observable { return this.destroyed } constructor (protected logger: Logger) { this.middleware.push(this.oscProcessor) this.oscProcessor.cwdReported$.subscribe(cwd => { this.reportedCWD = cwd }) this.middleware.outputToTerminal$.subscribe(data => { if (!this.initialDataBufferReleased) { this.initialDataBuffer = Buffer.concat([this.initialDataBuffer, data]) } else { this.output.next(data.toString()) this.binaryOutput.next(data) } }) this.middleware.outputToSession$.subscribe(data => this.write(data)) } feedFromTerminal (data: Buffer): void { this.middleware.feedFromTerminal(data) } protected emitOutput (data: Buffer): void { this.middleware.feedFromSession(data) } releaseInitialDataBuffer (): void { this.initialDataBufferReleased = true this.output.next(this.initialDataBuffer.toString()) this.binaryOutput.next(this.initialDataBuffer) this.initialDataBuffer = Buffer.from('') } setLoginScriptsOptions (options: LoginScriptsOptions): void { const newProcessor = new LoginScriptProcessor(this.logger, options) if (this.loginScriptProcessor) { this.middleware.replace(this.loginScriptProcessor, newProcessor) } else { this.middleware.push(newProcessor) } this.loginScriptProcessor = newProcessor } async destroy (): Promise { if (this.open) { this.logger.info('Destroying') this.open = false this.closed.next() this.destroyed.next() await this.gracefullyKillProcess() } this.middleware.close() this.closed.complete() this.destroyed.complete() this.output.complete() this.binaryOutput.complete() } abstract start (options: unknown): Promise abstract resize (columns: number, rows: number): void abstract write (data: Buffer): void abstract kill (signal?: string): void abstract gracefullyKillProcess (): Promise abstract supportsWorkingDirectory (): boolean abstract getWorkingDirectory (): Promise }