UNPKG

@fal-ai/serverless-client

Version:

Deprecation note: this library has been deprecated in favor of @fal-ai/client

264 lines 12.2 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.queue = void 0; exports.buildUrl = buildUrl; exports.send = send; exports.run = run; exports.subscribe = subscribe; const request_1 = require("./request"); const storage_1 = require("./storage"); const streaming_1 = require("./streaming"); const utils_1 = require("./utils"); /** * Builds the final url to run the function based on its `id` or alias and * a the options from `RunOptions<Input>`. * * @private * @param id the function id or alias * @param options the run options * @returns the final url to run the function */ function buildUrl(id, options = {}) { var _a, _b; const method = ((_a = options.method) !== null && _a !== void 0 ? _a : "post").toLowerCase(); const path = ((_b = options.path) !== null && _b !== void 0 ? _b : "").replace(/^\//, "").replace(/\/{2,}/, "/"); const input = options.input; const params = Object.assign(Object.assign({}, (options.query || {})), (method === "get" ? input : {})); const queryParams = Object.keys(params).length > 0 ? `?${new URLSearchParams(params).toString()}` : ""; // if a fal url is passed, just use it if ((0, utils_1.isValidUrl)(id)) { const url = id.endsWith("/") ? id : `${id}/`; return `${url}${path}${queryParams}`; } const appId = (0, utils_1.ensureAppIdFormat)(id); const subdomain = options.subdomain ? `${options.subdomain}.` : ""; const url = `https://${subdomain}fal.run/${appId}/${path}`; return `${url.replace(/\/$/, "")}${queryParams}`; } function send(id_1) { return __awaiter(this, arguments, void 0, function* (id, options = {}) { var _a; const input = options.input && options.autoUpload !== false ? yield storage_1.storageImpl.transformInput(options.input) : options.input; return (0, request_1.dispatchRequest)((_a = options.method) !== null && _a !== void 0 ? _a : "post", buildUrl(id, options), input); }); } /** * Runs a fal serverless function identified by its `id`. * * @param id the registered function revision id or alias. * @returns the remote function output */ function run(id_1) { return __awaiter(this, arguments, void 0, function* (id, options = {}) { return send(id, options); }); } const DEFAULT_POLL_INTERVAL = 500; /** * The fal run queue module. It allows to submit a function to the queue and get its result * on a separate call. This is useful for long running functions that can be executed * asynchronously and not . */ exports.queue = { submit(endpointId, options) { return __awaiter(this, void 0, void 0, function* () { const { webhookUrl, path = "" } = options, runOptions = __rest(options, ["webhookUrl", "path"]); return send(endpointId, Object.assign(Object.assign({}, runOptions), { subdomain: "queue", method: "post", path: path, query: webhookUrl ? { fal_webhook: webhookUrl } : undefined })); }); }, status(endpointId_1, _a) { return __awaiter(this, arguments, void 0, function* (endpointId, { requestId, logs = false }) { const appId = (0, utils_1.parseAppId)(endpointId); const prefix = appId.namespace ? `${appId.namespace}/` : ""; return send(`${prefix}${appId.owner}/${appId.alias}`, { subdomain: "queue", method: "get", path: `/requests/${requestId}/status`, input: { logs: logs ? "1" : "0", }, }); }); }, streamStatus(endpointId_1, _a) { return __awaiter(this, arguments, void 0, function* (endpointId, { requestId, logs = false, connectionMode }) { const appId = (0, utils_1.parseAppId)(endpointId); const prefix = appId.namespace ? `${appId.namespace}/` : ""; const queryParams = { logs: logs ? "1" : "0", }; const url = buildUrl(`${prefix}${appId.owner}/${appId.alias}`, { subdomain: "queue", path: `/requests/${requestId}/status/stream`, query: queryParams, }); return new streaming_1.FalStream(endpointId, { url, method: "get", connectionMode, queryParams, }); }); }, subscribeToStatus(endpointId, options) { return __awaiter(this, void 0, void 0, function* () { const requestId = options.requestId; const timeout = options.timeout; let timeoutId = undefined; const handleCancelError = () => { // Ignore errors as the client will follow through with the timeout // regardless of the server response. In case cancelation fails, we // still want to reject the promise and consider the client call canceled. }; if (options.mode === "streaming") { const status = yield exports.queue.streamStatus(endpointId, { requestId, logs: options.logs, connectionMode: "connectionMode" in options ? options.connectionMode : undefined, }); const logs = []; if (timeout) { timeoutId = setTimeout(() => { status.abort(); exports.queue.cancel(endpointId, { requestId }).catch(handleCancelError); // TODO this error cannot bubble up to the user since it's thrown in // a closure in the global scope due to setTimeout behavior. // User will get a platform error instead. We should find a way to // make this behavior aligned with polling. throw new Error(`Client timed out waiting for the request to complete after ${timeout}ms`); }, timeout); } status.on("data", (data) => { if (options.onQueueUpdate) { // accumulate logs to match previous polling behavior if ("logs" in data && Array.isArray(data.logs) && data.logs.length > 0) { logs.push(...data.logs); } options.onQueueUpdate("logs" in data ? Object.assign(Object.assign({}, data), { logs }) : data); } }); const doneStatus = yield status.done(); if (timeoutId) { clearTimeout(timeoutId); } return doneStatus; } // default to polling until status streaming is stable and faster return new Promise((resolve, reject) => { var _a; let pollingTimeoutId; // type resolution isn't great in this case, so check for its presence // and and type so the typechecker behaves as expected const pollInterval = "pollInterval" in options && typeof options.pollInterval === "number" ? ((_a = options.pollInterval) !== null && _a !== void 0 ? _a : DEFAULT_POLL_INTERVAL) : DEFAULT_POLL_INTERVAL; const clearScheduledTasks = () => { if (timeoutId) { clearTimeout(timeoutId); } if (pollingTimeoutId) { clearTimeout(pollingTimeoutId); } }; if (timeout) { timeoutId = setTimeout(() => { clearScheduledTasks(); exports.queue.cancel(endpointId, { requestId }).catch(handleCancelError); reject(new Error(`Client timed out waiting for the request to complete after ${timeout}ms`)); }, timeout); } const poll = () => __awaiter(this, void 0, void 0, function* () { var _a; try { const requestStatus = yield exports.queue.status(endpointId, { requestId, logs: (_a = options.logs) !== null && _a !== void 0 ? _a : false, }); if (options.onQueueUpdate) { options.onQueueUpdate(requestStatus); } if (requestStatus.status === "COMPLETED") { clearScheduledTasks(); resolve(requestStatus); return; } pollingTimeoutId = setTimeout(poll, pollInterval); } catch (error) { clearScheduledTasks(); reject(error); } }); poll().catch(reject); }); }); }, result(endpointId_1, _a) { return __awaiter(this, arguments, void 0, function* (endpointId, { requestId }) { const appId = (0, utils_1.parseAppId)(endpointId); const prefix = appId.namespace ? `${appId.namespace}/` : ""; return send(`${prefix}${appId.owner}/${appId.alias}`, { subdomain: "queue", method: "get", path: `/requests/${requestId}`, }); }); }, cancel(endpointId_1, _a) { return __awaiter(this, arguments, void 0, function* (endpointId, { requestId }) { const appId = (0, utils_1.parseAppId)(endpointId); const prefix = appId.namespace ? `${appId.namespace}/` : ""; yield send(`${prefix}${appId.owner}/${appId.alias}`, { subdomain: "queue", method: "put", path: `/requests/${requestId}/cancel`, }); }); }, }; /** * 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. * @returns A promise that resolves to the result of the request once it's completed. */ function subscribe(endpointId_1) { return __awaiter(this, arguments, void 0, function* (endpointId, options = {}) { const { request_id: requestId } = yield exports.queue.submit(endpointId, options); if (options.onEnqueue) { options.onEnqueue(requestId); } yield exports.queue.subscribeToStatus(endpointId, Object.assign({ requestId }, options)); return exports.queue.result(endpointId, { requestId }); }); } //# sourceMappingURL=function.js.map