socaity
Version:
SDK for Generative AI. Build AI-powered applications with ease
1,647 lines • 141 kB
JavaScript
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