| 1000 | // expects a WorkspaceShell without having to thread the union |
| 1001 | // type through. |
| 1002 | class WorkspaceShellRouter { |
| 1003 | readonly #defaultId: string; |
| 1004 | readonly #shellFor: (id: string) => Promise<{ shell: WorkspaceShell; handle: BackendHandle }>; |
| 1005 | readonly #resolveId: (id: string | undefined) => string; |
| 1006 | readonly #onError: (id: string, handle: BackendHandle, error: unknown) => void; |
| 1007 | |
| 1008 | constructor( |
| 1009 | defaultId: string, |
| 1010 | shellFor: (id: string) => Promise<{ shell: WorkspaceShell; handle: BackendHandle }>, |
| 1011 | resolveId: (id: string | undefined) => string, |
| 1012 | onError: (id: string, handle: BackendHandle, error: unknown) => void, |
| 1013 | ) { |
| 1014 | this.#defaultId = defaultId; |
| 1015 | this.#shellFor = shellFor; |
| 1016 | this.#resolveId = resolveId; |
| 1017 | this.#onError = onError; |
| 1018 | } |
| 1019 | |
| 1020 | async exec(command: string, options: { backend?: string } & Record<string, unknown> = {}) { |
| 1021 | const id = this.#resolveId(options.backend) || this.#defaultId; |
| 1022 | // Capture the BackendHandle here, at dispatch time. A late |
| 1023 | // failure from this command's stream must invalidate THIS |
| 1024 | // handle, not whatever the cache holds when the rejection |
| 1025 | // eventually fires; a concurrent reconnect may have already |
| 1026 | // swapped in a newer handle that we must not clobber. |
| 1027 | const { shell, handle: dispatchHandle } = await this.#shellFor(id); |
| 1028 | const { backend: _backend, ...rest } = options; |
| 1029 | let execHandle: unknown; |
| 1030 | try { |
| 1031 | execHandle = await (shell.exec as unknown as (c: string, o: typeof rest) => Promise<unknown>)( |
| 1032 | command, |
| 1033 | rest, |
| 1034 | ); |
| 1035 | } catch (error) { |
| 1036 | this.#onError(id, dispatchHandle, error); |
| 1037 | throw error; |
| 1038 | } |
| 1039 | return this.#wrapHandle(id, dispatchHandle, execHandle); |
| 1040 | } |
| 1041 | |
| 1042 | async get(id: string, options: { backend?: string } & Record<string, unknown> = {}) { |
| 1043 | const backendId = this.#resolveId(options.backend) || this.#defaultId; |
| 1044 | const { shell, handle: dispatchHandle } = await this.#shellFor(backendId); |
| 1045 | const { backend: _backend, ...rest } = options; |
| 1046 | let execHandle: unknown; |
| 1047 | try { |
| 1048 | execHandle = await (shell.get as unknown as (e: string, o: typeof rest) => Promise<unknown>)( |
| 1049 | id, |
| 1050 | rest, |
| 1051 | ); |
| 1052 | } catch (error) { |
| 1053 | this.#onError(backendId, dispatchHandle, error); |
| 1054 | throw error; |
| 1055 | } |
| 1056 | return this.#wrapHandle(backendId, dispatchHandle, execHandle); |
| 1057 | } |
| 1058 | |
| 1059 | async kill( |
nothing calls this directly
no outgoing calls
no test coverage detected