UNPKG

socaity

Version:

SDK for Generative AI. Build AI-powered applications with ease

1,647 lines 141 kB
var yt = Object.defineProperty; var _t = (r, e, t) => e in r ? yt(r, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : r[e] = t; var m = (r, e, t) => _t(r, typeof e != "symbol" ? e + "" : e, t); class Je extends Error { constructor(e = "Invalid API key format. API keys should start with 'sk_' and be 67 characters long.") { super(e), this.name = "ApiKeyError", Error.captureStackTrace && Error.captureStackTrace(this, Je); } } const J = class J { constructor(e = {}) { m(this, "apiKey"); m(this, "baseUrl"); m(this, "pollInterval"); m(this, "maxRetries"); this.apiKey = e.apiKey, this.baseUrl = "https://api.socaity.ai/v0", this.pollInterval = e.pollInterval || 5e3, this.maxRetries = e.maxRetries || 3; } /** * Get the global configuration instance */ static getInstance() { return J.instance || (J.instance = new J()), J.instance; } /** * Updates global configuration with new values */ static update(e) { if (e.apiKey !== void 0 && (!e.apiKey.startsWith("sk_") || e.apiKey.length != 67)) throw new Je("API key is wrong. Get your API key from the Socaity https://www.socaity.ai dashboard."); const t = J.getInstance(); Object.assign(t, e); } }; m(J, "instance"); let F = J; var x = /* @__PURE__ */ ((r) => (r.CREATED = "CREATED", r.QUEUED = "QUEUED", r.PROCESSING = "PROCESSING", r.COMPLETED = "COMPLETED", r.FAILED = "FAILED", r.UNKNOWN = "UNKNOWN", r))(x || {}), T = /* @__PURE__ */ ((r) => (r.INITIALIZING = "INITIALIZING", r.PREPARING = "PREPARING", r.SENDING = "SENDING", r.TRACKING = "TRACKING", r.PROCESSING_RESULT = "PROCESSING_RESULT", r.COMPLETED = "COMPLETED", r.FAILED = "FAILED", r))(T || {}); class vt { /** * Parse response into standardized job format * @param response - API response object or string * @returns Standardized SocaityJob object */ async parse(e) { if (e == null) return this.createErrorJob("No response received"); if (typeof e == "string") try { const t = JSON.parse(e); return this.parseObject(t); } catch { return { id: "", status: x.COMPLETED, progress: { progress: 1, message: null }, result: e, createdAt: /* @__PURE__ */ new Date(), updatedAt: /* @__PURE__ */ new Date() }; } return this.parseObject(e); } /** * Parse object responses into SocaityJob * @param response - Object to parse */ async parseObject(e) { if (typeof e != "object" || e === null) return this.createErrorJob("Invalid response format"); const t = e, s = typeof t.id == "string" ? t.id : "", n = this.parseStatus(t.status), a = this.parseProgress(t, n), i = typeof t.error == "string" ? t.error : null, o = this.parseDate(t.createdAt), l = this.parseDate(t.updatedAt); return { id: s, status: n, progress: a, result: t.result, error: i, createdAt: o, updatedAt: l }; } /** * Creates a standard error job response * @param errorMessage - Error message to include */ createErrorJob(e) { const t = /* @__PURE__ */ new Date(); return { id: "", status: x.FAILED, progress: { progress: 0, message: e }, result: null, error: e, createdAt: t, updatedAt: t }; } /** * Parse date from various formats * @param dateValue - Date value to parse */ parseDate(e) { if (e instanceof Date) return e; if (typeof e == "string" || typeof e == "number") { const t = new Date(e); if (!isNaN(t.getTime())) return t; } return /* @__PURE__ */ new Date(); } /** * Parse status from different API formats * @param status - Status string to parse */ parseStatus(e) { if (typeof e != "string" || !e) return x.UNKNOWN; const t = e.toUpperCase(); return { COMPLETED: x.COMPLETED, SUCCEEDED: x.COMPLETED, FINISHED: x.COMPLETED, CREATED: x.CREATED, FAILED: x.FAILED, ERROR: x.FAILED, IN_PROGRESS: x.PROCESSING, PROCESSING: x.PROCESSING, RUNNING: x.PROCESSING, BOOTING: x.PROCESSING, QUEUED: x.QUEUED, PENDING: x.QUEUED, IN_QUEUE: x.QUEUED, STARTING: x.QUEUED }[t] || x.UNKNOWN; } /** * Parse progress from different API formats * @param response - Response object containing progress information * @param status - Parsed job status */ parseProgress(e, t) { let s = 0, n = null; const a = e.progress; if (typeof a == "number") s = a; else if (typeof a == "string") try { s = parseFloat(a); } catch { s = 0; } else if (a && typeof a == "object") { const i = a; if (typeof i.progress == "number") s = i.progress; else if (typeof i.progress == "string") try { s = parseFloat(i.progress); } catch { s = 0; } n = typeof i.message == "string" ? i.message : null; } return isNaN(s) && (s = 0), s = Math.max(0, Math.min(1, s)), t === x.COMPLETED && (s = 1), !n && typeof e.message == "string" && (n = e.message), { progress: s, message: n }; } } function nt(r) { return r && typeof r == "object" && "file_name" in r && "content_type" in r && "content" in r; } function bt(r) { try { const e = new URL(r); return e.protocol === "http:" || e.protocol === "https:"; } catch { return !1; } } function wt(r) { return r.startsWith("data:") || xt(r); } function xt(r) { return /^[A-Za-z0-9+/=]+$/.test(r) && r.length % 4 === 0; } async function at(r) { if (r == null) return null; if (Array.isArray(r)) { const e = r.map((t) => at(t)); return Promise.all(e); } if (nt(r)) try { return await new E().fromDict(r); } catch { return r; } return r; } class E { /** * Creates a new MediaFile instance. * * @param file_name - Default filename to use * @param content_type - Default content type to use */ constructor(e = "file", t = "application/octet-stream") { m(this, "content_type"); m(this, "file_name"); m(this, "_content", null); m(this, "_isNode"); this.content_type = t, this.file_name = e, this._isNode = typeof window > "u"; } /** * Factory method to create a MediaFile from any supported data type. * Automatically detects data type and uses the appropriate method. * * @param data - Data to load (file path, URL, base64 string, etc.) * @returns Promise resolving to a MediaFile instance or null */ static async create(e) { if (e == null) throw new Error("Cannot create MediaFile from null or undefined data"); return new E().fromAny(e); } /** * Load a file from any supported data type. * * @param data - Data to load (file path, URL, base64 string, etc.) * @param websafe - Prevents loading from file paths and malformatted base64 strings. * @returns Promise resolving to a MediaFile instance or null */ async fromAny(e) { if (e instanceof E) return e; if (this._isNode && this._isBuffer(e)) return this.fromBytes(e); if (typeof Blob < "u" && e instanceof Blob) { const t = await e.arrayBuffer(); return this.fromBytes(t); } if (e instanceof ArrayBuffer || e instanceof Uint8Array) return this.fromBytes(e); if (typeof e == "string") { if (bt(e)) return await this.fromUrl(e); if (wt(e)) return this.fromBase64(e); if (await this._isValidFilePath(e)) return await this.fromFile(e); throw new Error("Invalid data type for MediaFile"); } return nt(e) ? await this.fromDict(e) : this; } /** * Load file from a file path (Node.js only). * * @param filePath - Path to the file * @returns Promise resolving to the MediaFile instance */ async fromFile(e) { if (!this._isNode) throw new Error("Loading from file path is only supported in Node.js environment"); try { const s = await (await import("fs/promises")).readFile(e), n = await import("path"); return this.file_name = n.basename(e), this._content = this._bufferToArrayBuffer(s), this._setContentTypeFromFileName(), this; } catch (t) { throw new Error(`Failed to load file from path: ${e}. ${t.message}`); } } /** * Load file from a URL. * * @param url - URL to fetch the file from * @param headers - Optional headers for the request * @returns Promise resolving to the MediaFile instance */ async fromUrl(e, t) { try { const s = await fetch(e, { headers: t || { "User-Agent": "MediaFile/1.0.0" } }); if (!s.ok) throw new Error(`HTTP error! Status: ${s.status}`); this.content_type = s.headers.get("content-type") || "application/octet-stream"; const n = s.headers.get("content-disposition"); if (n) { const a = n.match(/filename=(?:['"]?)([^'";\n]+)/i); a && a[1] && (this.file_name = a[1]); } if (!this.file_name || this.file_name === "file") { const i = new URL(e).pathname.split("/"), o = i[i.length - 1]; o && o.trim() !== "" ? this.file_name = decodeURIComponent(o) : this.file_name = "downloaded_file"; } return this._content = await s.arrayBuffer(), this; } catch (s) { throw new Error(`Failed to load file from URL: ${e}. ${s.message}`); } } /** * Load file from base64 encoded string. * * @param base64Data - Base64 encoded string, optionally with data URI prefix * @returns The MediaFile instance */ fromBase64(e) { const { data: t, mediaType: s } = this._parseBase64Uri(e); s && (this.content_type = s); try { return this._content = this._isNode ? this._decodeBase64NodeJs(t) : this._decodeBase64Browser(t), this; } catch (n) { throw new Error(`Failed to decode base64 data: ${n.message}`); } } /** * Load file from binary data. * * @param data - ArrayBuffer, Buffer, or Uint8Array containing the file data * @returns The MediaFile instance */ fromBytes(e) { if (e instanceof SharedArrayBuffer) { const t = new Uint8Array(e), s = new ArrayBuffer(t.byteLength); return new Uint8Array(s).set(t), this._content = s, this; } if (e instanceof Uint8Array) if (e.buffer instanceof SharedArrayBuffer) { const t = new Uint8Array(e); this._content = t.buffer.slice(t.byteOffset, t.byteOffset + t.byteLength); } else this._content = e.buffer.slice(e.byteOffset, e.byteOffset + e.byteLength); else if (this._isNode && Buffer.isBuffer(e)) if (e.buffer instanceof SharedArrayBuffer) { const t = new Uint8Array(e); this._content = t.buffer.slice(t.byteOffset, t.byteOffset + t.byteLength); } else this._content = e.buffer.slice(e.byteOffset, e.byteOffset + e.byteLength); else this._content = new Uint8Array(e).buffer; return this; } /** * Load file from a FileResult object. * * @param fileResult - FileResult object with file metadata and base64 content * @returns The MediaFile instance */ async fromDict(e) { if (!e.content) throw new Error("Invalid FileResult object: missing content"); return this.file_name = e.file_name, this.content_type = e.content_type, await this.fromAny(e.content); } /** * Convert the file to a Blob (browser only). * * @returns Blob object representing the file */ toBlob() { if (this._ensureContent(), typeof Blob > "u") throw new Error("Blob is not available in this environment"); return new Blob([new Uint8Array(this._content)], { type: this.content_type }); } /** * Convert the file to an ArrayBuffer. * * @returns ArrayBuffer containing the file data */ toArrayBuffer() { return this._ensureContent(), this._content instanceof SharedArrayBuffer ? this._content.slice(0) : this._content; } /** * Convert the file to a Uint8Array. * * @returns Uint8Array containing the file data */ toUint8Array() { return this._ensureContent(), new Uint8Array(this._content); } /** * Convert the file to a Node.js Buffer (Node.js only). * * @returns Buffer containing the file data */ toBuffer() { if (this._ensureContent(), !this._isNode) throw new Error("Buffer is only available in Node.js environment"); return Buffer.from(this._content); } /** * Convert the file to a base64 encoded string. * * @param includeDataUri - Whether to include the data URI prefix * @returns Base64 encoded string of the file data */ toBase64(e = !0) { this._ensureContent(); let t; if (this._isNode) t = Buffer.from(this._content).toString("base64"); else { const s = new Uint8Array(this._content); let n = ""; const a = 10240; for (let i = 0; i < s.length; i += a) { const o = s.subarray(i, Math.min(i + a, s.length)); n += String.fromCharCode.apply(null, Array.from(o)); } t = btoa(n); } return e ? `data:${this.content_type};base64,${t}` : t; } /** * Convert the file to a FileResult object. * * @returns FileResult object with file metadata and base64 content */ toJson() { return { file_name: this.file_name, content_type: this.content_type, content: this.toBase64() }; } /** * Save the file to disk (Node.js) or trigger download (browser). * * @param filePath - Optional file path (Node.js) or filename (browser) * @returns Promise that resolves when the file is saved */ async save(e) { this._ensureContent(); const t = e || this.file_name; if (this._isNode) try { const s = await import("fs/promises").then((i) => i.default || i), n = await import("path").then((i) => i.default || i); if (!n || typeof n.dirname != "function") throw new Error("Failed to load 'path' module."); const a = n.dirname(t); a !== "." && await s.mkdir(a, { recursive: !0 }).catch(() => { }), await s.writeFile(t, Buffer.from(this._content)); } catch (s) { throw new Error(`Failed to save file: ${s.message}`); } else { const s = this.toBlob(), n = URL.createObjectURL(s), a = document.createElement("a"); a.href = n, a.download = t, document.body.appendChild(a), a.click(), setTimeout(() => { document.body.removeChild(a), URL.revokeObjectURL(n); }, 100); } } /** * Get the file size in bytes. * * @param unit - Unit to return the size in ('bytes', 'kb', 'mb', or 'gb') * @returns File size in the specified unit */ fileSize(e = "bytes") { if (!this._content) return 0; const t = this._content.byteLength; switch (e) { case "kb": return t / 1024; case "mb": return t / (1024 * 1024); case "gb": return t / (1024 * 1024 * 1024); default: return t; } } /** * Get the file extension based on the content type or filename. * * @returns File extension without the leading dot, or null if it cannot be determined */ get extension() { var e; if (this.content_type && this.content_type !== "application/octet-stream") { const t = { "image/jpeg": "jpg", "image/png": "png", "image/gif": "gif", "image/webp": "webp", "image/svg+xml": "svg", "audio/mpeg": "mp3", "audio/wav": "wav", "audio/ogg": "ogg", "video/mp4": "mp4", "video/quicktime": "mov", "application/pdf": "pdf", "text/plain": "txt", "text/html": "html", "application/json": "json" }; if (this.content_type in t) return t[this.content_type]; if (this._isNode) try { const n = require("mime-types").extension(this.content_type); if (n) return n; } catch { } } return this.file_name && this.file_name.includes(".") && ((e = this.file_name.split(".").pop()) == null ? void 0 : e.toLowerCase()) || null; } /** * Get the filename. */ getFileName() { return this.file_name; } /** * Set the filename. */ setFileName(e) { this.file_name = e; } /** * Get the content type. */ getContentType() { return this.content_type; } /** * Set the content type. */ setContentType(e) { this.content_type = e; } /** * Read raw file data. * * @returns ArrayBuffer containing the file data */ read() { return this._ensureContent(), this._content; } /** * Check if the file is empty. * * @returns Boolean indicating if the file has content */ isEmpty() { return !this._content || this._content.byteLength === 0; } /** * Get info about the file. * * @returns Object with file information */ getInfo() { return { fileName: this.file_name, contentType: this.content_type, size: this.fileSize(), extension: this.extension }; } /** * Detect MIME type for the file based on file extension. * Updates the content_type property. * * @private */ _setContentTypeFromFileName() { var s; if (!this.file_name) return; const e = (s = this.file_name.split(".").pop()) == null ? void 0 : s.toLowerCase(); if (!e) return; if (this._isNode) try { const a = require("mime-types").lookup(this.file_name); if (a) { this.content_type = a; return; } } catch { } const t = { jpg: "image/jpeg", jpeg: "image/jpeg", png: "image/png", gif: "image/gif", webp: "image/webp", svg: "image/svg+xml", mp3: "audio/mpeg", wav: "audio/wav", ogg: "audio/ogg", mp4: "video/mp4", mov: "video/quicktime", pdf: "application/pdf", txt: "text/plain", html: "text/html", htm: "text/html", json: "application/json", js: "application/javascript", css: "text/css", xml: "application/xml", zip: "application/zip" }; e in t && (this.content_type = t[e]); } /** * Parse a base64 data URI into content and media type. * * @param data - Base64 string, potentially with data URI prefix * @returns Object containing the parsed data and media type * @private */ _parseBase64Uri(e) { if (e.startsWith("data:")) { const [t, s] = e.split(",", 2), n = t.match(/^data:([^;,]+)/), a = n ? n[1] : null; return { data: s, mediaType: a }; } return { data: e, mediaType: null }; } /** * Ensure content exists before operating on it. * * @private */ _ensureContent() { if (!this._content) throw new Error("No content available. Load content first using fromFile, fromUrl, etc."); } /** * Check if a string is a valid file path. * * @param path - Path to check * @returns Whether the path is valid * @private */ async _isValidFilePath(e) { if (!this._isNode) return !1; try { return (await (await import("fs/promises")).stat(e)).isFile(); } catch { return !1; } } /** * Check if an object is a Buffer. * * @param obj - Object to check * @returns Whether the object is a Buffer * @private */ _isBuffer(e) { return this._isNode && Buffer.isBuffer(e); } /** * Decode base64 string in Node.js environment. * * @param base64 - Base64 string to decode * @returns ArrayBuffer containing the decoded data * @private */ _decodeBase64NodeJs(e) { const t = Buffer.from(e, "base64"); return this._bufferToArrayBuffer(t); } /** * Decode base64 string in browser environment. * * @param base64 - Base64 string to decode * @returns ArrayBuffer containing the decoded data * @private */ _decodeBase64Browser(e) { const t = atob(e), s = new Uint8Array(t.length); for (let n = 0; n < t.length; n++) s[n] = t.charCodeAt(n); return s.buffer; } /** * Convert a Node.js Buffer to an ArrayBuffer. * * @param buffer - Buffer to convert * @returns ArrayBuffer containing the data * @private */ _bufferToArrayBuffer(e) { if (e.buffer instanceof SharedArrayBuffer) { const t = new ArrayBuffer(e.byteLength); return new Uint8Array(t).set(new Uint8Array(e)), t; } else return e.buffer.slice(e.byteOffset, e.byteOffset + e.byteLength); } } class kt { // 60 seconds timeout for uploads /** * Create a new FastCloud instance * @param config FastCloud configuration */ constructor(e) { m(this, "uploadEndpoint"); m(this, "apiKey"); m(this, "defaultTimeout", 6e4); this.uploadEndpoint = e.uploadEndpoint || "https://api.socaity.ai/v0/files", this.apiKey = e.apiKey; } /** * Get authentication headers for requests * @returns Headers object with authentication */ getAuthHeaders() { return { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" }; } /** * Process the upload response to extract file URLs * @param response Response from the upload endpoint * @returns Array of temporary upload URLs */ async processUploadResponse(e) { if (!e.ok) throw new Error(`Failed to get temporary upload URL: ${e.status} ${e.statusText}`); const t = await e.json(); return Array.isArray(t) ? t : [t]; } /** * Upload a file to a temporary URL * @param sasUrl Temporary URL for uploading * @param file File to upload */ async uploadToTemporaryUrl(e, t) { const s = { "x-ms-blob-type": "BlockBlob", "x-ms-if-none-match": "*" }, n = t instanceof E ? await t.toArrayBuffer() : t, a = await fetch(e, { method: "PUT", headers: s, body: n }); if (a.status !== 201) throw new Error(`Failed to upload to temporary URL ${e}. Response: ${a.statusText}`); } /** * Upload a single file * @param file File to upload * @returns URL of the uploaded file */ async upload(e) { const t = Array.isArray(e) ? e : [e], s = t.map((o) => o.extension).filter((o) => o !== null), n = await fetch(this.uploadEndpoint, { method: "POST", headers: this.getAuthHeaders(), body: JSON.stringify({ n_files: t.length, file_extensions: s.length > 0 ? s : void 0 }), signal: AbortSignal.timeout(this.defaultTimeout) }), a = await this.processUploadResponse(n), i = a.map( (o, l) => this.uploadToTemporaryUrl(o, t[l]) ); return await Promise.all(i), t.length === 1 ? a[0] : a; } /** * Download a file from a URL * @param url URL to download from * @param savePath Optional path to save the file to * @returns MediaFile object or save path if specified */ async download(e, t) { const s = await new E(e).fromUrl(e, this.getAuthHeaders()); return t ? (await s.save(t), t) : s; } } class Ve { constructor() { m(this, "config"); m(this, "uploadFileThresholdMB", 1); m(this, "maxFileUploadLimitMB", 1e3); m(this, "fastCloud"); m(this, "abortController"); m(this, "responseParser"); this.config = F.getInstance(), this.abortController = new AbortController(), this.responseParser = new vt(), this.fastCloud = new kt({ uploadEndpoint: `${this.config.baseUrl}/v0/files`, apiKey: this.config.apiKey ? this.config.apiKey : "" }); } /** * Match request parameters against defined parameters * @param definingParams Expected parameters * @param params Actual parameters */ matchParams(e, t) { const s = {}; for (const [n, a] of Object.entries(e)) n in t ? s[n] = t[n] : a !== void 0 && (s[n] = a); return s; } /** * Parse query parameters for the request * @param endpoint Endpoint metadata * @param params Request parameters */ parseQueryParams(e, t) { return e.queryParams ? this.matchParams(e.queryParams, t) : {}; } /** * Parse body parameters for the request * @param endpoint Endpoint metadata * @param params Request parameters */ parseBodyParams(e, t) { return e.bodyParams ? this.matchParams(e.bodyParams, t) : {}; } /** * Parse file parameters for the request * @param endpoint Endpoint metadata * @param params Request parameters * @returns Processed file parameters */ async parseFileParams(e, t) { if (!e.fileParams) return {}; const s = this.matchParams(e.fileParams, t), n = {}; try { let a = 0; const i = {}; for (const [o, l] of Object.entries(s)) if (typeof l == "string" && l.startsWith("http")) n[o] = l; else { let d = l; try { d = l instanceof E ? l : await E.create(l); } catch (p) { console.log(`Failed to convert ${o} to MediaFile`, p); } if (!(d instanceof E)) continue; i[o] = d, a += d.fileSize("mb"); } if (a > this.maxFileUploadLimitMB) throw new Error(`Total file size exceeds maximum limit of ${this.maxFileUploadLimitMB}MB`); if (a > this.uploadFileThresholdMB) if (Object.keys(i).length > 1) { const o = Object.values(i), l = await this.fastCloud.upload(o); Object.keys(i).forEach((d, p) => { n[d] = l[p]; }); } else for (const [o, l] of Object.entries(i)) n[o] = await this.fastCloud.upload(l); else for (const [o, l] of Object.entries(i)) n[o] = l.toBlob(); return n; } catch (a) { throw new Error(`File parameter processing failed: ${a instanceof Error ? a.message : String(a)}`); } } /** * Validates that an API key is available * @private */ validateAPIKey(e) { const t = e || this.config.apiKey; if (!t) throw new Error("API key not provided"); return t; } /** * Build query string from parameters * @private */ buildQueryString(e) { if (!e || Object.keys(e).length === 0) return ""; const t = new URLSearchParams(); for (const [n, a] of Object.entries(e)) a != null && (typeof a == "object" ? t.append(n, JSON.stringify(a)) : t.append(n, String(a))); const s = t.toString(); return s ? `?${s}` : ""; } /** * Format error response from fetch * @private */ async formatErrorResponse(e) { const t = e.status; let s; try { const n = e.headers.get("content-type"); if (n && n.includes("application/json")) { const a = await e.json(); s = JSON.stringify(a); } else s = await e.text(); } catch { s = "Could not parse error response"; } return new Error(`API error (${t}): ${s}`); } /** * Send a request to the API * @param path API endpoint path * @param method HTTP method * @param queryParams URL query parameters * @param bodyParams Request body parameters * @param apiKey API key for authentication * @param fileParams Optional files to upload */ async sendRequest(e, t = "POST", s = {}, n = {}, a = {}, i) { const o = this.validateAPIKey(i); this.abortController = new AbortController(); const { signal: l } = this.abortController, d = { Authorization: `Bearer ${o}` }; let p = `${this.config.baseUrl}/${e}`, A = null; if (t === "GET") { const S = { ...s, ...n, ...a }; p += this.buildQueryString(S); } else if (Object.keys(a).length > 0) { p += this.buildQueryString({ ...s, ...n }); const P = new FormData(); Object.entries(a).forEach(([Ae, Me]) => { Me != null && P.append(Ae, Me); }), A = P; } else p += this.buildQueryString(s), d["Content-Type"] = "application/json", A = Object.keys(n).length > 0 ? JSON.stringify(n) : null; try { const S = setTimeout(() => { this.abort(); }, 3e4), P = await fetch(p, { method: t, headers: d, body: A, signal: l }); if (clearTimeout(S), !P.ok) throw await this.formatErrorResponse(P); const Ae = P.headers.get("content-type"); return Ae && Ae.includes("application/json") ? await P.json() : await P.text(); } catch (S) { throw S instanceof DOMException && S.name === "AbortError" ? new Error("Request canceled") : S instanceof Error ? S : new Error(`Network error: ${String(S)}`); } } /** * Make a request to a specific endpoint * @param endpoint Endpoint metadata * @param params Request parameters * @param apiKey API key */ async request_endpoint(e, t, s) { const n = this.parseQueryParams(e, t), a = this.parseBodyParams(e, t), i = await this.parseFileParams(e, t), o = await this.sendRequest( e.path, e.method, n, a, i, s ); return this.responseParser.parse(o); } async refresh_status(e) { const t = await this.sendRequest("status", "POST", { job_id: e }); return this.responseParser.parse(t); } /** * Abort any ongoing requests */ abort() { this.abortController.abort(), this.abortController = new AbortController(); } } const W = class W { /** * Private constructor to enforce singleton pattern * @param requestHandler - RequestHandler for API communication */ constructor(e) { m(this, "requestHandler"); m(this, "config"); m(this, "jobs"); m(this, "mediaHandler"); this.requestHandler = e, this.config = F.getInstance(), this.jobs = /* @__PURE__ */ new Map(), this.mediaHandler = new E(); } /** * Get the singleton instance of JobManager * @param requestHandler - Optional RequestHandler to use when creating the instance * @returns The JobManager instance */ static getInstance(e) { return W.instance || (e || (e = new Ve()), W.instance = new W(e)), W.instance; } /** * Submit a new job to the API * @param endpoint - API endpoint to call * @param params - Parameters for the job * @param apiKey - Optional API key to use for this request * @param file - Optional file to include * @returns Promise resolving to the created job */ async submitJob(e, t, s) { try { const n = await this.requestHandler.request_endpoint(e, t, s); return this.jobs.set(n.id, n), n; } catch (n) { throw n instanceof Error ? n : new Error(`Failed to submit job: ${String(n)}`); } } /** * Get all tracked jobs */ getAllJobs() { return Array.from(this.jobs.values()); } /** * Get a specific job by ID * @param jobId - ID of the job to retrieve */ getJob(e) { return this.jobs.get(e); } /** * Track a job until it completes * @param job - The job to track * @returns Promise resolving to the final result of the job */ async trackJobToCompletion(e) { let t = 0; for (; t < this.config.maxRetries; ) try { const s = await this.requestHandler.refresh_status(e.id); if (this.jobs.set(e.id, s), s.status === x.COMPLETED) return s.result; if (s.status === x.FAILED) throw new Error(`Job failed: ${s.error}`); await new Promise((n) => setTimeout(n, this.config.pollInterval)); } catch (s) { if (t++, t >= this.config.maxRetries) throw s; await new Promise((n) => setTimeout(n, this.config.pollInterval * t)); } throw new Error("Max retries exceeded while tracking job"); } /** * Cancel a running job * @param jobId - ID of the job to cancel * @returns Promise resolving to true if the job was cancelled */ async cancelJob(e) { try { return await this.requestHandler.sendRequest("cancel", "GET", { job_id: e }), this.jobs.delete(e), !0; } catch { return !1; } } /** * Clear completed or failed jobs from tracking */ clearCompletedJobs() { for (const [e, t] of this.jobs.entries()) (t.status === x.COMPLETED || t.status === x.FAILED) && this.jobs.delete(e); } }; m(W, "instance"); let Ce = W; const de = typeof process < "u" && process.stdout && process.stdout.clearLine; let re; if (de) try { re = require("cli-progress"); } catch { console.warn("cli-progress package not found. Using basic console output for progress."); } const H = class H { constructor() { m(this, "multiBar"); m(this, "bars", /* @__PURE__ */ new Map()); m(this, "isInitialized", !1); de && re && (this.multiBar = new re.MultiBar({ clearOnComplete: !1, hideCursor: !0, format: "{bar} {percentage}% | {jobId} | {phase} | {message}" }), this.isInitialized = !0); } static getInstance() { return H.instance || (H.instance = new H()), H.instance; } createBar(e, t) { if (!this.isInitialized) return null; const s = this.multiBar.create(100, 0, { jobId: e, phase: t, message: "Starting..." }); return this.bars.set(e, s), s; } updateBar(e, t, s) { const n = this.bars.get(e); n && n.update(t, s); } removeBar(e) { const t = this.bars.get(e); t && (t.stop(), this.multiBar.remove(t), this.bars.delete(e)); } stopAll() { this.isInitialized && (this.multiBar.stop(), this.bars.clear()); } }; m(H, "instance"); let Be = H; class Tt { constructor(e, t, s, n, a = !0) { m(this, "apiJob"); m(this, "endpoint"); m(this, "processingState"); m(this, "jobManager"); m(this, "result", null); m(this, "error", null); m(this, "completed", !1); m(this, "resolvePromise"); m(this, "rejectPromise"); m(this, "promise"); m(this, "eventListeners", /* @__PURE__ */ new Map()); m(this, "verbose"); m(this, "progressBar", null); m(this, "progressBarManager"); m(this, "parseResultCallbacks", []); // Keep track of the last logged message to avoid duplicates m(this, "_lastLoggedMessage", null); this.apiJob = e, this.jobManager = t, this.endpoint = s, this.verbose = a, this.processingState = { phase: T.INITIALIZING, progress: 0 }, this.progressBarManager = Be.getInstance(), this.promise = new Promise((i, o) => { this.resolvePromise = i, this.rejectPromise = o; }), n && this.onParseResult(n), setTimeout(() => this.startTracking(), 0), this.verbose && this.initProgressDisplay(); } /** * Initialize the progress display */ initProgressDisplay() { this.verbose && (de && re ? (this.progressBar = this.progressBarManager.createBar( this.apiJob.id || "initializing", this.processingState.phase ), this.updateProgressDisplay()) : this.logProgress()); } /** * Update the progress display based on current state */ updateProgressDisplay() { if (!this.verbose) return; const e = this.processingState.phase, t = this.calculateProgressPercent(), s = this.formatProgressMessage(); de && re && this.progressBar ? this.progressBarManager.updateBar(this.apiJob.id, t, { phase: e, message: s.replace(/^.*\| /, "") // Remove the progress percentage part }) : this.logProgress(); } /** * Calculate progress percentage based on current state */ calculateProgressPercent() { var t; const e = this.processingState.phase; if (e === T.TRACKING && ((t = this.apiJob.progress) != null && t.progress)) return Math.round(this.apiJob.progress.progress * 100); switch (e) { case T.INITIALIZING: return 0; case T.PREPARING: return 5; case T.SENDING: return 10; case T.TRACKING: return 15; case T.PROCESSING_RESULT: return 90; case T.COMPLETED: return 100; case T.FAILED: return 100; default: return 0; } } /** * Format a human-readable progress message */ formatProgressMessage() { var s; const e = this.processingState.phase, t = this.apiJob.id || "initializing"; if (e === T.INITIALIZING || e === T.PREPARING || e === T.SENDING) return `${e} job | API: ${this.endpoint.path}`; if (e === T.TRACKING) { const n = this.apiJob.progress ? `${Math.round(this.apiJob.progress.progress * 100)}%` : "unknown"; let a = `Job ${t} | Progress: ${n} | API: ${this.endpoint.path}`; return (s = this.apiJob.progress) != null && s.message && (a += ` | ${this.apiJob.progress.message}`), a; } else return `Job ${t} | ${e} | API: ${this.endpoint.path}`; } /** * Log progress to console (for browser environments) */ logProgress() { const e = this.formatProgressMessage(); (!this._lastLoggedMessage || this._lastLoggedMessage !== e) && (console.log(e), this._lastLoggedMessage = e); } /** * Stop the progress display */ stopProgressDisplay() { if (!this.verbose) return; de && re && this.progressBar && (this.progressBarManager.removeBar(this.apiJob.id), this.progressBar = null); const e = this.processingState.phase; e === T.COMPLETED ? console.log(`✓ Job ${this.apiJob.id} completed successfully (${this.endpoint.path})`) : e === T.FAILED && console.log(`✗ Job ${this.apiJob.id} failed: ${this.processingState.message} (${this.endpoint.path})`); } /** * Add an event listener */ on(e, t) { return this.eventListeners.has(e) || this.eventListeners.set(e, []), this.eventListeners.get(e).push(t), this; } /** * Emit an event */ emit(e, ...t) { const s = this.eventListeners.get(e); s && s.forEach((n) => n(...t)); } /** * Poll for job status updates */ async pollJobStatus() { if (!(this.completed || !this.apiJob.id)) try { const e = await this.jobManager.requestHandler.refresh_status(this.apiJob.id); if (!e) { setTimeout(() => this.pollJobStatus(), this.jobManager.config.pollInterval); return; } if (this.apiJob = e, this.emitJobUpdate(), this.verbose && this.updateProgressDisplay(), e.status === x.COMPLETED) { this.updateProcessingState(T.PROCESSING_RESULT, 0.9, "Processing result"); try { this.result = await at(e.result), this.result = await this.runParseResultCallbacks(this.result), this.complete(); } catch (t) { this.fail(t instanceof Error ? t : new Error(String(t))); } } else e.status === x.FAILED ? (this.updateProcessingState(T.FAILED, 1, e.error || "Job failed"), this.fail(new Error(e.error || "Job failed with no error message"))) : setTimeout(() => this.pollJobStatus(), this.jobManager.config.pollInterval); } catch (e) { console.error("Error polling job status:", e), setTimeout(() => this.pollJobStatus(), this.jobManager.config.pollInterval); } } /** * Start tracking the job */ startTracking() { this.updateProcessingState(T.TRACKING, 0, "Tracking job status"), this.pollJobStatus(); } /** * Mark the job as completed */ complete() { this.updateProcessingState(T.COMPLETED, 1, "Job completed"), this.cleanup(), this.completed = !0, this.emit("completed", this.result), this.resolvePromise(this.result); } /** * Mark the job as failed */ fail(e) { this.updateProcessingState(T.FAILED, 1, e.message), this.cleanup(), this.completed = !0, this.error = e, this.emit("failed", e), this.rejectPromise(e); } /** * Clean up resources */ cleanup() { this.stopProgressDisplay(); } /** * Update the internal processing state */ updateProcessingState(e, t, s) { this.processingState = { phase: e, progress: t, message: s }, this.verbose && this.updateProgressDisplay(), this.emit("processingUpdated", this.processingState); } /** * Emit job update event */ emitJobUpdate() { this.emit("progressUpdated", this.apiJob.progress), this.emit("statusUpdated", this.apiJob.status); } /** * Set verbosity of progress display * @param verbose Whether to display progress information */ setVerbose(e) { return this.verbose = e, e && !this.progressBar && !this.completed ? this.initProgressDisplay() : !e && this.progressBar && this.stopProgressDisplay(), this; } /** * Register a callback for job completion */ onCompleted(e) { return this.on("completed", e), this.completed && this.result !== null && setTimeout(() => e(this.result), 0), this; } /** * Register a callback for job failure */ onFailed(e) { return this.on("failed", e), this.completed && this.error !== null && setTimeout(() => e(this.error), 0), this; } /** * Register a callback for job progress updates */ onProgress(e) { return this.on("progressUpdated", e), setTimeout(() => e(this.apiJob.progress), 0), this; } /** * Register a callback for job status updates */ onStatus(e) { return this.on("statusUpdated", e), setTimeout(() => e(this.apiJob.status), 0), this; } /** * Register a callback for internal processing state updates */ onProcessingUpdated(e) { return this.on("processingUpdated", e), setTimeout(() => e(this.processingState), 0), this; } /** * Register a callback for custom post-processing of the job result */ onParseResult(e) { return this.parseResultCallbacks.push(e), this; } /** * Run all registered parse result callbacks */ async runParseResultCallbacks(e) { let t = e; for (const s of this.parseResultCallbacks) { const n = await s(t); n !== void 0 && (t = n); } return t; } /** * Cancel the job */ async cancel() { try { const e = await this.jobManager.cancelJob(this.apiJob.id); return e && (this.cleanup(), this.fail(new Error("Job cancelled by user"))), e; } catch { return !1; } } /** * Get the current job ID */ get id() { return this.apiJob.id; } /** * Get the current job status */ get status() { return this.apiJob.status; } /** * Get the current job progress */ get progress() { return this.apiJob.progress || null; } /** * Get the current processing phase */ get phase() { return this.processingState.phase; } /** * Implementation of the PromiseLike interface */ then(e, t) { return this.promise.then(e, t); } } const N = class N { constructor() { m(this, "registeredClients", /* @__PURE__ */ new Map()); m(this, "clientConstructors", /* @__PURE__ */ new Map()); } static getInstance() { return N.instance || (N.instance = new N()), N.instance; } static registerClientType(e, t) { const s = N.getInstance(), n = N.normalizeClientName(e); s.clientConstructors.set(n, t); } static registerClient(e) { const t = N.getInstance(), s = N.normalizeClientName(e.name); t.registeredClients.set(s, e); } static getClient(e) { const t = N.getInstance(), s = N.normalizeClientName(e); let n = t.registeredClients.get(s); if (!n) { const a = t.clientConstructors.get(s); if (!a) throw new Error( `Client "${e}" not found. Available clients: ${Array.from(t.clientConstructors.keys()).join(", ")}` ); n = new a(), t.registeredClients.set(s, n); } return n; } static getAvailableClients() { const e = N.getInstance(); return Array.from(e.clientConstructors.keys()); } static normalizeClientName(e) { return e.toLowerCase().replace(/[^a-z0-9-]/g, ""); } }; m(N, "instance"); let R = N; class qe { constructor(e) { m(this, "requestHandler"); m(this, "config"); m(this, "endpoints"); m(this, "jobManager"); // Name of the API client. Has influence on path variable in the requests which will be formatted like /api/v1/{name}/{endpoint} m(this, "name"); this.name = this.sanitizePath(e), this.config = F.getInstance(), this.requestHandler = new Ve(), this.endpoints = /* @__PURE__ */ new Map(), this.jobManager = Ce.getInstance(this.requestHandler), this.registerEndpoints(), R.registerClient(this); } sanitizePath(e) { return !e || typeof e != "string" ? "" : e.toLowerCase().trim().replace(/\s+/g, " ").replace(/\//g, "-").replace(/[\s_]/g, "-").replace(/[^a-z0-9\-]/g, "").replace(/\-+/g, "-").replace(/^\-+|\-+$/g, "").trim(); } /** * Register a new endpoint with the client */ registerEndpoint(e) { const t = this.sanitizePath(e.path); e.path = this.name + "/" + t, this.endpoints.set(t, e); } /** * Get an endpoint by name */ getEndpoint(e) { e = this.sanitizePath(e); const t = this.endpoints.get(e); if (!t) throw new Error(`Unknown endpoint: ${e}`); return t; } /** * Update the client configuration */ updateConfig(e) { F.update(e); } /** * Submit a job to the API and return a TrackedJob for monitoring * @param endpoint - API endpoint path * @param params - Request parameters * @param apiKey - Optional API key to override the default * @param file - Optional file to upload * @returns A TrackedJob for monitoring the job */ async submitTrackedJob(e, t, s, n, a = !0) { const i = await this.jobManager.submitJob(e, t, n); return new Tt(i, this.jobManager, e, s, a); } /** * Get all active jobs for this client */ getJobs() { return this.jobManager.getAllJobs(); } /** * Get a specific job by ID */ getJob(e) { return this.jobManager.getJob(e); } /** * Cancel a running job */ async cancelJob(e) { return this.jobManager.cancelJob(e); } } var w; (function(r) { r.assertEqual = (n) => n; function e(n) { } r.assertIs = e; function t(n) { throw new Error(); } r.assertNever = t, r.arrayToEnum = (n) => { const a = {}; for (const i of n) a[i] = i; return a; }, r.getValidEnumValues = (n) => { const a = r.objectKeys(n).filter((o) => typeof n[n[o]] != "number"), i = {}; for (const o of a) i[o] = n[o]; return r.objectValues(i); }, r.objectValues = (n) => r.objectKeys(n).map(function(a) { return n[a]; }), r.objectKeys = typeof Object.keys == "function" ? (n) => Object.keys(n) : (n) => { const a = []; for (const i in n) Object.prototype.hasOwnProperty.call(n, i) && a.push(i); return a; }, r.find = (n, a) => { for (const i of n) if (a(i)) return i; }, r.isInteger = typeof Number.isInteger == "function" ? (n) => Number.isInteger(n) : (n) => typeof n == "number" && isFinite(n) && Math.floor(n) === n; function s(n, a = " | ") { return n.map((i) => typeof i == "string" ? `'${i}'` : i).join(a); } r.joinValues = s, r.jsonStringifyReplacer = (n, a) => typeof a == "bigint" ? a.toString() : a; })(w || (w = {})); var Le; (function(r) { r.mergeShapes = (e, t) => ({ ...e, ...t // second overwrites first }); })(Le || (Le = {})); const h = w.arrayToEnum([ "string", "nan", "number", "integer", "float", "boolean", "date", "bigint", "symbol", "function", "undefined", "null", "array", "object", "unknown", "promise", "void", "never", "map", "set" ]), U = (r) => { switch (typeof r) { case "undefined": return h.undefined; case "string": return h.string; case "number": return isNaN(r) ? h.nan : h.number; case "boolean": return h.boolean; case "function": return h.function; case "bigint": return h.bigint; case "symbol": return h.symbol; case "object": return Array.isArray(r) ? h.array : r === null ? h.null : r.then && typeof r.then == "function" && r.catch && typeof r.catch == "function" ? h.promise : typeof Map < "u" && r instanceof Map ? h.map : typeof Set < "u" && r instanceof Set ? h.set : typeof Date < "u" && r instanceof Date ? h.date : h.object; default: return h.unknown; } }, c = w.arrayToEnum([ "invalid_type", "invalid_literal", "custom", "invalid_union", "invalid_union_discriminator", "invalid_enum_value", "unrecognized_keys", "invalid_arguments", "invalid_return_type", "invalid_date", "invalid_string", "too_small", "too_big", "invalid_intersection_types", "not_multiple_of", "not_finite" ]), Et = (r) => JSON.stringify(r, null, 2).replace(/"([^"]+)":/g, "$1:"); class O extends Error { get errors() { return this.issues; } constructor(e) { super(), this.issues = [], this.addIssue = (s) => { this.issues = [...this.issues, s]; }, this.addIssues = (s = []) => { this.issues = [...this.issues, ...s]; }; const t = new.target.prototype; Object.setPrototypeOf ? Object.setPrototypeOf(this, t) : this.__proto__ = t, this.name = "ZodError", this.issues = e; } format(e) { const t = e || function(a) { return a.message; }, s = { _errors: [] }, n = (a) => { for (const i of a.issues) if (i.code === "invalid_union") i.unionErrors.map(n); else if (i.code === "invalid_return_type") n(i.returnTypeError); else if (i.code === "invalid_arguments") n(i.argumentsError); else if (i.path.length === 0) s._errors.push(t(i)); else { let o = s, l = 0; for (; l < i.path.length; ) { const d = i.path[l]; l === i.path.length - 1 ? (o[d] = o[d] || { _errors: [] }, o[d]._errors.push(t(i))) : o[d] = o[d] || { _errors: [] }, o = o[d], l++; } } }; return n(this), s; } static assert(e) { if (!(e instanceof O)) throw new Error(`Not a ZodError: ${e}`); } toString() { return this.message; } get message() { return JSON.stringify(this.issues, w.jsonStringifyReplacer, 2); } get isEmpty() { return this.issues.length === 0; } flatten