import * as plugins from '../plugins.js'; type IExecutionEnvironment = import('@design.estate/dees-catalog').IExecutionEnvironment; type IFileEntry = import('@design.estate/dees-catalog').IFileEntry; type IFileWatcher = import('@design.estate/dees-catalog').IFileWatcher; type IProcessHandle = import('@design.estate/dees-catalog').IProcessHandle; type TTypedRequestShape = { method: string; request: Record; response: Record; }; export class DeploymentExecutionEnvironment implements IExecutionEnvironment { public readonly type = 'backend' as const; private readyState = false; constructor( private deploymentId: string, private identity: plugins.interfaces.data.IIdentity, ) {} get ready(): boolean { return this.readyState; } public async init(): Promise { const result = await this.fireRequest('deploymentWorkspaceExists', { path: '/' }) as { exists: boolean }; if (!result.exists) { throw new Error(`Cannot access deployment filesystem for ${this.deploymentId}`); } this.readyState = true; } public async destroy(): Promise { this.readyState = false; } public async readFile(pathArg: string): Promise { const result = await this.fireRequest('deploymentWorkspaceReadFile', { path: pathArg }) as { content: string }; return result.content; } public async writeFile(pathArg: string, contentsArg: string): Promise { await this.fireRequest('deploymentWorkspaceWriteFile', { path: pathArg, content: contentsArg }); } public async readDir(pathArg: string): Promise { const result = await this.fireRequest('deploymentWorkspaceReadDir', { path: pathArg }) as { entries: IFileEntry[] }; return result.entries; } public async mkdir(pathArg: string): Promise { await this.fireRequest('deploymentWorkspaceMkdir', { path: pathArg }); } public async rm(pathArg: string, optionsArg?: { recursive?: boolean }): Promise { await this.fireRequest('deploymentWorkspaceRm', { path: pathArg, recursive: optionsArg?.recursive, }); } public async exists(pathArg: string): Promise { const result = await this.fireRequest('deploymentWorkspaceExists', { path: pathArg }) as { exists: boolean }; return result.exists; } public watch( _pathArg: string, _callbackArg: (eventArg: 'rename' | 'change', filenameArg: string | null) => void, _optionsArg?: { recursive?: boolean }, ): IFileWatcher { return { stop: () => {} }; } public async spawn(commandArg: string, argsArg: string[] = []): Promise { const result = await this.fireRequest('deploymentWorkspaceExec', { command: commandArg, args: argsArg, }) as { stdout?: string; stderr?: string; exitCode: number }; const output = new ReadableStream({ start(controllerArg) { if (result.stdout) controllerArg.enqueue(result.stdout); if (result.stderr) controllerArg.enqueue(result.stderr); controllerArg.close(); }, }); return { output, input: new WritableStream(), exit: Promise.resolve(result.exitCode), kill: () => {}, }; } private async fireRequest(methodArg: string, dataArg: Record) { const typedRequest = new plugins.typedrequest.TypedRequest( '/typedrequest', methodArg, ); return await typedRequest.fire({ identity: this.identity, deploymentId: this.deploymentId, ...dataArg, }); } }