( shell: ShellRPC, sync: Sync, id: string, wireEvents: ReadableStream<ExecEvent>, encoding: E | undefined, pushed: number, )
| 243 | // The user stream remains the only reader so backpressure reaches the backend. |
| 244 | // kill() requests a signal; result() or stream completion observes the exit. |
| 245 | function wrapHandle<E extends ExecEncoding>( |
| 246 | shell: ShellRPC, |
| 247 | sync: Sync, |
| 248 | id: string, |
| 249 | wireEvents: ReadableStream<ExecEvent>, |
| 250 | encoding: E | undefined, |
| 251 | pushed: number, |
| 252 | ): ExecHandle<E> { |
| 253 | const postPull = withPostPull(pipeEvents<E>(wireEvents, encoding), sync); |
| 254 | const stream = postPull.stream; |
| 255 | const handle = stream as ExecHandle<E>; |
| 256 | let resultPromise: Promise<ExecResult<E>> | undefined; |
| 257 | let resultReader: ReadableStreamDefaultReader<WorkspaceExecEvent<E>> | undefined; |
| 258 | // configurable: true on result/kill lets the Workspace-level |
| 259 | // router redefine them to add cross-cutting concerns (transport |
| 260 | // failure invalidation on result(); future kill hooks). The id |
| 261 | // slot stays non-configurable — nothing should rewrite it. |
| 262 | Object.defineProperties(handle, { |
| 263 | id: { value: id, enumerable: false, writable: false, configurable: false }, |
| 264 | result: { |
| 265 | value: () => { |
| 266 | resultPromise ??= drainToResult<E>(stream, encoding, pushed, postPull.outcome, (reader) => { |
| 267 | resultReader = reader; |
| 268 | }); |
| 269 | return resultPromise; |
| 270 | }, |
| 271 | enumerable: false, |
| 272 | writable: false, |
| 273 | configurable: true, |
| 274 | }, |
| 275 | kill: { |
| 276 | value: (signal?: KillSignal) => shell.killExec({ id, signal }), |
| 277 | enumerable: false, |
| 278 | writable: false, |
| 279 | configurable: true, |
| 280 | }, |
| 281 | [Symbol.dispose]: { |
| 282 | value: () => { |
| 283 | if (resultReader) void resultReader.cancel().catch(() => undefined); |
| 284 | else void stream.cancel().catch(() => undefined); |
| 285 | }, |
| 286 | }, |
| 287 | }); |
| 288 | return handle; |
| 289 | } |
| 290 | |
| 291 | function pipeEvents<E extends ExecEncoding>( |
| 292 | source: ReadableStream<ExecEvent>, |
no test coverage detected