import { type ApiData, type BlobRef, type Config, type EndpointInfo, type JsApiData, type DataType, Command, type Dependency, type ComponentMeta } from "../types"; import { FileData } from "../upload"; const is_node = typeof process !== "undefined" && process.versions && process.versions.node; export function update_object( object: { [x: string]: any }, newValue: any, stack: (string | number)[] ): void { while (stack.length > 1) { const key = stack.shift(); if (typeof key === "string" || typeof key === "number") { object = object[key]; } else { throw new Error("Invalid key type"); } } const key = stack.shift(); if (typeof key === "string" || typeof key === "number") { object[key] = newValue; } else { throw new Error("Invalid key type"); } } export async function walk_and_store_blobs( data: DataType, type: string | undefined = undefined, path: string[] = [], root = false, endpoint_info: EndpointInfo | undefined = undefined ): Promise { if (Array.isArray(data)) { let blob_refs: BlobRef[] = []; await Promise.all( data.map(async (_, index) => { let new_path = path.slice(); new_path.push(String(index)); const array_refs = await walk_and_store_blobs( data[index], root ? endpoint_info?.parameters[index]?.component || undefined : type, new_path, false, endpoint_info ); blob_refs = blob_refs.concat(array_refs); }) ); return blob_refs; } else if ( (globalThis.Buffer && data instanceof globalThis.Buffer) || data instanceof Blob ) { return [ { path: path, blob: new Blob([data]), type } ]; } else if (typeof data === "object" && data !== null) { let blob_refs: BlobRef[] = []; for (const key of Object.keys(data) as (keyof typeof data)[]) { const new_path = [...path, key]; const value = data[key]; blob_refs = blob_refs.concat( await walk_and_store_blobs( value, undefined, new_path, false, endpoint_info ) ); } return blob_refs; } return []; } export function skip_queue(id: number, config: Config): boolean { let fn_queue = config?.dependencies?.find((dep) => dep.id == id)?.queue; if (fn_queue != null) { return !fn_queue; } return !config.enable_queue; } // todo: add jsdoc for this function export function post_message( message: any, origin: string ): Promise { return new Promise((res, _rej) => { const channel = new MessageChannel(); channel.port1.onmessage = (({ data }) => { channel.port1.close(); res(data); }) as (ev: MessageEvent) => void; window.parent.postMessage(message, origin, [channel.port2]); }); } export function handle_file( file_or_url: File | string | Blob | Buffer ): FileData | Blob | Command { if (typeof file_or_url === "string") { if ( file_or_url.startsWith("http://") || file_or_url.startsWith("https://") ) { return { path: file_or_url, url: file_or_url, orig_name: file_or_url.split("/").pop() ?? "unknown", meta: { _type: "gradio.FileData" } }; } if (is_node) { // Handle local file paths return new Command("upload_file", { path: file_or_url, name: file_or_url, orig_path: file_or_url }); } } else if (typeof File !== "undefined" && file_or_url instanceof File) { return new Blob([file_or_url]); } else if (file_or_url instanceof Buffer) { return new Blob([file_or_url]); } else if (file_or_url instanceof Blob) { return file_or_url; } throw new Error( "Invalid input: must be a URL, File, Blob, or Buffer object." ); } /** * Handles the payload by filtering out state inputs and returning an array of resolved payload values. * We send null values for state inputs to the server, but we don't want to include them in the resolved payload. * * @param resolved_payload - The resolved payload values received from the client or the server * @param dependency - The dependency object. * @param components - The array of component metadata. * @param with_null_state - Optional. Specifies whether to include null values for state inputs. Default is false. * @returns An array of resolved payload values, filtered based on the dependency and component metadata. */ export function handle_payload( resolved_payload: unknown[], dependency: Dependency, components: ComponentMeta[], type: "input" | "output", with_null_state = false ): unknown[] { if (type === "input" && !with_null_state) { throw new Error("Invalid code path. Cannot skip state inputs for input."); } // data comes from the server with null state values so we skip if (type === "output" && with_null_state) { return resolved_payload; } let updated_payload: unknown[] = []; let payload_index = 0; const deps = type === "input" ? dependency.inputs : dependency.outputs; for (let i = 0; i < deps.length; i++) { const input_id = deps[i]; const component = components.find((c) => c.id === input_id); if (component?.type === "state") { // input + with_null_state needs us to fill state with null values if (with_null_state) { if (resolved_payload.length === deps.length) { const value = resolved_payload[payload_index]; updated_payload.push(value); payload_index++; } else { updated_payload.push(null); } } else { // this is output & !with_null_state, we skip state inputs // the server payload always comes with null state values so we move along the payload index payload_index++; continue; } // input & !with_null_state isn't a case we care about, server needs null continue; } else { const value = resolved_payload[payload_index]; updated_payload.push(value); payload_index++; } } return updated_payload; }