| import { spawn, ChildProcess } from 'child_process'; |
| import * as path from 'path'; |
| import * as fs from 'fs'; |
| import { detectPlatform, getBinaryName, getBinaryPath } from './platform'; |
| import { |
| SnapshotParams, |
| SnapshotResponse, |
| TabClickParams, |
| TabLockParams, |
| TabUnlockParams, |
| CreateTabParams, |
| CreateTabResponse, |
| PinchtabOptions, |
| } from './types'; |
|
|
| export * from './types'; |
| export * from './platform'; |
|
|
| export class Pinchtab { |
| private baseUrl: string; |
| private timeout: number; |
| private port: number; |
| private process: ChildProcess | null = null; |
| private binaryPath: string | null = null; |
|
|
| constructor(options: PinchtabOptions = {}) { |
| this.port = options.port || 9867; |
| this.baseUrl = options.baseUrl || `http://localhost:${this.port}`; |
| this.timeout = options.timeout || 30000; |
| } |
|
|
| |
| |
| |
| async start(binaryPath?: string): Promise<void> { |
| if (this.process) { |
| throw new Error('Pinchtab process already running'); |
| } |
|
|
| if (!binaryPath) { |
| binaryPath = await this.getBinaryPathInternal(); |
| } |
|
|
| this.binaryPath = binaryPath; |
|
|
| return new Promise((resolve, reject) => { |
| this.process = spawn(binaryPath, ['serve', `--port=${this.port}`], { |
| stdio: 'inherit', |
| }); |
|
|
| this.process.on('error', (err) => { |
| reject(new Error(`Failed to start Pinchtab: ${err.message}`)); |
| }); |
|
|
| |
| setTimeout(resolve, 500); |
| }); |
| } |
|
|
| |
| |
| |
| async stop(): Promise<void> { |
| if (this.process) { |
| return new Promise((resolve) => { |
| this.process?.kill(); |
| this.process = null; |
| resolve(); |
| }); |
| } |
| } |
|
|
| |
| |
| |
| async snapshot(params?: SnapshotParams): Promise<SnapshotResponse> { |
| return this.request<SnapshotResponse>('/snapshot', params); |
| } |
|
|
| |
| |
| |
| async click(params: TabClickParams): Promise<void> { |
| await this.request('/tab/click', params); |
| } |
|
|
| |
| |
| |
| async lock(params: TabLockParams): Promise<void> { |
| await this.request('/tab/lock', params); |
| } |
|
|
| |
| |
| |
| async unlock(params: TabUnlockParams): Promise<void> { |
| await this.request('/tab/unlock', params); |
| } |
|
|
| |
| |
| |
| async createTab(params: CreateTabParams): Promise<CreateTabResponse> { |
| return this.request<CreateTabResponse>('/tab/create', params); |
| } |
|
|
| |
| |
| |
| |
| private async request<T = any>(path: string, body?: any): Promise<T> { |
| const url = `${this.baseUrl}${path}`; |
|
|
| const controller = new AbortController(); |
| const timeoutId = setTimeout(() => controller.abort(), this.timeout); |
|
|
| try { |
| const response = await fetch(url, { |
| method: 'POST', |
| headers: { |
| 'Content-Type': 'application/json', |
| }, |
| body: body ? JSON.stringify(body) : undefined, |
| signal: controller.signal as AbortSignal, |
| }); |
|
|
| if (!response.ok) { |
| const error = await response.text(); |
| throw new Error(`${response.status}: ${error}`); |
| } |
|
|
| return response.json() as Promise<T>; |
| } finally { |
| clearTimeout(timeoutId); |
| } |
| } |
|
|
| |
| |
| |
| private async getBinaryPathInternal(): Promise<string> { |
| const platform = detectPlatform(); |
| const binaryName = getBinaryName(platform); |
|
|
| |
| const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf-8')); |
| const binaryPath = getBinaryPath(binaryName, pkg.version); |
|
|
| if (!fs.existsSync(binaryPath)) { |
| throw new Error( |
| `Pinchtab binary not found at ${binaryPath}.\n` + |
| `Please run: npm rebuild pinchtab\n` + |
| `Or set PINCHTAB_BINARY_PATH=/path/to/binary` |
| ); |
| } |
|
|
| return binaryPath; |
| } |
| } |
|
|
| export default Pinchtab; |
|
|