1- import { poll , PollingOptions } from './polling' ;
1+ import { longPollUntil } from './polling' ;
22
33export interface DevboxStateWaitOptions < T > {
44 client : {
@@ -8,45 +8,37 @@ export interface DevboxStateWaitOptions<T> {
88 targetState : string ;
99 statesToCheck : string [ ] ;
1010 transitionStates : string [ ] ;
11- pollingOptions ?: Partial < PollingOptions < T > > | undefined ;
11+ /** Timeout in milliseconds for the long-poll operation. */
12+ timeoutMs ?: number | undefined ;
13+ /** Optional AbortSignal to cancel the long-poll loop externally. */
14+ signal ?: AbortSignal | null | undefined ;
1215 errorMessage : ( id : string , actualState : string ) => string ;
1316}
1417
1518/**
1619 * Shared utility for waiting for a devbox to reach a specific state.
17- * Uses the /wait_for_status endpoint with polling .
20+ * Uses the /wait_for_status long-poll endpoint .
1821 */
1922export async function awaitDevboxState < T extends { status : string } > (
2023 options : DevboxStateWaitOptions < T > ,
2124) : Promise < T > {
22- const { client, devboxId, targetState, statesToCheck, transitionStates, pollingOptions, errorMessage } =
23- options ;
25+ const { client, devboxId, targetState, statesToCheck, transitionStates, errorMessage } = options ;
2426
25- const longPoll = ( ) : Promise < T > => {
26- // This either returns a DevboxView when status matches one of statesToCheck;
27- // Otherwise it throws an 408 error when times out.
28- return client . post ( `/v1/devboxes/ ${ devboxId } /wait_for_status` , {
29- body : { statuses : statesToCheck } ,
30- } ) ;
31- } ;
32-
33- const finalResult = await poll (
34- ( ) => longPoll ( ) ,
35- ( ) => longPoll ( ) ,
27+ const finalResult = await longPollUntil (
28+ ( signal ) =>
29+ client . post ( `/v1/devboxes/ ${ devboxId } /wait_for_status` , {
30+ body : { statuses : statesToCheck } ,
31+ signal ,
32+ // Per-request HTTP timeout must exceed the server's max long-poll hold (30s)
33+ // so the server's 408 always arrives before the client aborts the connection.
34+ // The longPollUntil AbortSignal enforces the caller's actual deadline.
35+ timeout : 600000 ,
36+ maxRetries : 0 ,
37+ } ) ,
3638 {
37- ...pollingOptions ,
38- shouldStop : ( result ) => {
39- return ! transitionStates . includes ( result . status ) ;
40- } ,
41- onError : ( error : any ) => {
42- if ( error . status === 408 ) {
43- // Return a placeholder result to continue polling
44- return { status : transitionStates [ 0 ] } as T ;
45- }
46-
47- // For any other error, rethrow it
48- throw error ;
49- } ,
39+ timeoutMs : options . timeoutMs ,
40+ shouldStop : ( result ) => ! transitionStates . includes ( result . status ) ,
41+ signal : options . signal ,
5042 } ,
5143 ) ;
5244
0 commit comments