diff --git a/libs/client/package.json b/libs/client/package.json index e7a4831..4601abb 100644 --- a/libs/client/package.json +++ b/libs/client/package.json @@ -1,7 +1,7 @@ { "name": "@fal-ai/serverless-client", "description": "The fal serverless JS/TS client", - "version": "0.14.0-alpha.1", + "version": "0.14.0-alpha.2", "license": "MIT", "repository": { "type": "git", diff --git a/libs/client/src/function.ts b/libs/client/src/function.ts index e74e8d9..aa306a0 100644 --- a/libs/client/src/function.ts +++ b/libs/client/src/function.ts @@ -123,6 +123,8 @@ export async function run( return send(id, options); } +type TimeoutId = ReturnType; + const DEFAULT_POLL_INTERVAL = 500; /** @@ -140,6 +142,16 @@ export async function subscribe( if (options.onEnqueue) { options.onEnqueue(requestId); } + const timeout = options.timeout; + let timeoutId: TimeoutId = undefined; + if (timeout) { + timeoutId = setTimeout(() => { + queue.cancel(id, { requestId }).catch(console.warn); + throw new Error( + `Client timed out waiting for the request to complete after ${timeout}ms` + ); + }, timeout); + } if (options.mode === 'streaming') { const status = await queue.streamStatus(id, { requestId, @@ -160,6 +172,9 @@ export async function subscribe( } }); await status.done(); + if (timeoutId) { + clearTimeout(timeoutId); + } return queue.result(id, { requestId }); } // default to polling until status streaming is stable and faster @@ -228,6 +243,15 @@ type QueueSubscribeOptions = { */ logs?: boolean; + /** + * The timeout (in milliseconds) for the request. If the request is not + * completed within this time, the subscription will be cancelled. + * + * Note: currently, the timeout is not enforced and the default is `undefined`. + * This behavior might change in the future. + */ + timeout?: number; + /** * The URL to send a webhook notification to when the request is completed. * @see WebHookResponse @@ -283,7 +307,7 @@ interface Queue { /** * Submits a request to the queue. * - * @param endpointId - The ID or URL of the function web endpoint. + * @param endpointId - The ID of the function web endpoint. * @param options - Options to configure how the request is run. * @returns A promise that resolves to the result of enqueuing the request. */ @@ -295,7 +319,7 @@ interface Queue { /** * Retrieves the status of a specific request in the queue. * - * @param endpointId - The ID or URL of the function web endpoint. + * @param endpointId - The ID of the function web endpoint. * @param options - Options to configure how the request is run. * @returns A promise that resolves to the status of the request. */ @@ -304,7 +328,7 @@ interface Queue { /** * Retrieves the result of a specific request from the queue. * - * @param endpointId - The ID or URL of the function web endpoint. + * @param endpointId - The ID of the function web endpoint. * @param options - Options to configure how the request is run. * @returns A promise that resolves to the result of the request. */ @@ -321,10 +345,27 @@ interface Queue { options: RunOptions & QueueSubscribeOptions ): Promise; + /** + * Subscribes to updates for a specific request in the queue. + * + * @param endpointId - The ID of the function web endpoint. + * @param options - Options to configure how the request is run and how updates are received. + */ streamStatus( endpointId: string, options: QueueStatusOptions ): Promise>; + + /** + * Cancels a request in the queue. + * + * @param endpointId - The ID of the function web endpoint. + * @param options - Options to configure how the request + * is run and how updates are received. + * @returns A promise that resolves once the request is cancelled. + * @throws {Error} If the request cannot be cancelled. + */ + cancel(endpointId: string, options: BaseQueueOptions): Promise; } /** @@ -395,5 +436,17 @@ export const queue: Queue = { path: `/requests/${requestId}`, }); }, + async cancel( + endpointId: string, + { requestId }: BaseQueueOptions + ): Promise { + const appId = parseAppId(endpointId); + const prefix = appId.namespace ? `${appId.namespace}/` : ''; + await send(`${prefix}${appId.owner}/${appId.alias}`, { + subdomain: 'queue', + method: 'put', + path: `/requests/${requestId}/cancel`, + }); + }, subscribe, }; diff --git a/libs/proxy/package.json b/libs/proxy/package.json index 3842c52..2ce4d32 100644 --- a/libs/proxy/package.json +++ b/libs/proxy/package.json @@ -1,6 +1,6 @@ { "name": "@fal-ai/serverless-proxy", - "version": "0.7.4", + "version": "0.7.5", "license": "MIT", "repository": { "type": "git", diff --git a/libs/proxy/src/nextjs.ts b/libs/proxy/src/nextjs.ts index 7195a71..4f941b7 100644 --- a/libs/proxy/src/nextjs.ts +++ b/libs/proxy/src/nextjs.ts @@ -55,4 +55,5 @@ export const route = { handler: routeHandler, GET: routeHandler, POST: routeHandler, + PUT: routeHandler, }; diff --git a/libs/proxy/src/svelte.ts b/libs/proxy/src/svelte.ts index 80e5df6..e820919 100644 --- a/libs/proxy/src/svelte.ts +++ b/libs/proxy/src/svelte.ts @@ -44,5 +44,6 @@ export const createRequestHandler = ({ requestHandler: handler, GET: handler, POST: handler, + PUT: handler, }; };