socaity
Version:
SDK for Generative AI. Build AI-powered applications with ease
1,565 lines (1,564 loc) • 77.9 kB
JavaScript
var tt = Object.defineProperty;
var et = (n, t, e) => t in n ? tt(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e;
var a = (n, t, e) => et(n, typeof t != "symbol" ? t + "" : t, e);
class D extends Error {
constructor(t = "Invalid API key format. API keys should start with 'sk_' and be 67 characters long.") {
super(t), this.name = "ApiKeyError", Error.captureStackTrace && Error.captureStackTrace(this, D);
}
}
const P = class P {
constructor(t = {}) {
a(this, "apiKey");
a(this, "baseUrl");
a(this, "pollInterval");
a(this, "maxRetries");
this.apiKey = t.apiKey, this.baseUrl = "https://api.socaity.ai/v1", this.pollInterval = t.pollInterval || 5e3, this.maxRetries = t.maxRetries || 3;
}
/**
* Get the global configuration instance
*/
static getInstance() {
return P.instance || (P.instance = new P()), P.instance;
}
/**
* Updates global configuration with new values
*/
static update(t) {
if (t.apiKey !== void 0) {
if (!t.apiKey.startsWith("sk_") && !t.apiKey.startsWith("tk_") && !t.apiKey.startsWith("ey"))
throw new D("Invalid authentication token. Use either a Socaity API key from https://www.socaity.ai or a valid JWT token.");
if (t.apiKey.startsWith("sk_") && !t.apiKey.startsWith("tk_") && t.apiKey.length !== 67)
throw new D("Invalid API key format. API keys should be 67 characters long. Get your API key from https://www.socaity.ai");
}
const e = P.getInstance();
Object.assign(e, t);
}
};
a(P, "instance");
let T = P;
var u = /* @__PURE__ */ ((n) => (n.CREATED = "CREATED", n.QUEUED = "QUEUED", n.PROCESSING = "PROCESSING", n.COMPLETED = "COMPLETED", n.FAILED = "FAILED", n.UNKNOWN = "UNKNOWN", n))(u || {}), f = /* @__PURE__ */ ((n) => (n.INITIALIZING = "INITIALIZING", n.PREPARING = "PREPARING", n.SENDING = "SENDING", n.TRACKING = "TRACKING", n.PROCESSING_RESULT = "PROCESSING_RESULT", n.COMPLETED = "COMPLETED", n.FAILED = "FAILED", n))(f || {});
class rt {
/**
* Parse response into standardized job format
* @param response - API response object or string
* @returns Standardized SocaityJob object
*/
async parse(t) {
if (t == null)
return this.createErrorJob("No response received");
if (typeof t == "string")
try {
const e = JSON.parse(t);
return this.parseObject(e);
} catch {
return {
id: "",
status: u.COMPLETED,
progress: { progress: 1, message: null },
result: t,
createdAt: /* @__PURE__ */ new Date(),
updatedAt: /* @__PURE__ */ new Date()
};
}
return this.parseObject(t);
}
/**
* Parse object responses into SocaityJob
* @param response - Object to parse
*/
async parseObject(t) {
if (typeof t != "object" || t === null)
return this.createErrorJob("Invalid response format");
const e = t, r = typeof e.id == "string" ? e.id : "", s = this.parseStatus(e.status), i = this.parseProgress(e, s), o = typeof e.error == "string" ? e.error : null, l = typeof e.refresh_job_url == "string" ? e.refresh_job_url : void 0, h = this.parseDate(e.createdAt), c = this.parseDate(e.updatedAt);
return {
id: r,
status: s,
progress: i,
result: e.result,
error: o,
createdAt: h,
updatedAt: c,
refresh_job_url: l
};
}
/**
* Creates a standard error job response
* @param errorMessage - Error message to include
*/
createErrorJob(t) {
const e = /* @__PURE__ */ new Date();
return {
id: "",
status: u.FAILED,
progress: { progress: 0, message: t },
result: null,
error: t,
createdAt: e,
updatedAt: e
};
}
/**
* Parse date from various formats
* @param dateValue - Date value to parse
*/
parseDate(t) {
if (t instanceof Date)
return t;
if (typeof t == "string" || typeof t == "number") {
const e = new Date(t);
if (!isNaN(e.getTime()))
return e;
}
return /* @__PURE__ */ new Date();
}
/**
* Parse status from different API formats
* @param status - Status string to parse
*/
parseStatus(t) {
if (typeof t != "string" || !t)
return u.UNKNOWN;
const e = t.toUpperCase();
return {
COMPLETED: u.COMPLETED,
SUCCEEDED: u.COMPLETED,
FINISHED: u.COMPLETED,
CREATED: u.CREATED,
FAILED: u.FAILED,
ERROR: u.FAILED,
IN_PROGRESS: u.PROCESSING,
PROCESSING: u.PROCESSING,
RUNNING: u.PROCESSING,
BOOTING: u.PROCESSING,
QUEUED: u.QUEUED,
PENDING: u.QUEUED,
IN_QUEUE: u.QUEUED,
STARTING: u.QUEUED
}[e] || u.UNKNOWN;
}
/**
* Parse progress from different API formats
* @param response - Response object containing progress information
* @param status - Parsed job status
*/
parseProgress(t, e) {
let r = 0, s = null;
const i = t.progress;
if (typeof i == "number")
r = i;
else if (typeof i == "string")
try {
r = parseFloat(i);
} catch {
r = 0;
}
else if (i && typeof i == "object") {
const o = i;
if (typeof o.progress == "number")
r = o.progress;
else if (typeof o.progress == "string")
try {
r = parseFloat(o.progress);
} catch {
r = 0;
}
s = typeof o.message == "string" ? o.message : null;
}
return isNaN(r) && (r = 0), r = Math.max(0, Math.min(1, r)), e === u.COMPLETED && (r = 1), !s && typeof t.message == "string" && (s = t.message), {
progress: r,
message: s
};
}
}
var st = Object.defineProperty, it = (n, t, e) => t in n ? st(n, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : n[t] = e, k = (n, t, e) => it(n, typeof t != "symbol" ? t + "" : t, e);
function z(n) {
return n && typeof n == "object" && "file_name" in n && "content_type" in n && "content" in n;
}
const b = {
mp3: "audio/mpeg",
wav: "audio/wav",
ogg: "audio/ogg",
aac: "audio/aac",
flac: "audio/flac",
m4a: "audio/mp4",
webm: "audio/webm"
}, w = {
mp4: "video/mp4",
mov: "video/quicktime",
avi: "video/x-msvideo",
mkv: "video/x-matroska",
wmv: "video/x-ms-wmv",
webm: "video/webm",
"3gp": "video/3gpp",
flv: "video/x-flv"
}, E = {
jpg: "image/jpeg",
jpeg: "image/jpeg",
png: "image/png",
gif: "image/gif",
webp: "image/webp",
svg: "image/svg+xml",
bmp: "image/bmp",
ico: "image/x-icon",
tiff: "image/tiff",
tif: "image/tiff",
avif: "image/avif"
}, nt = {
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",
csv: "text/csv",
npz: "application/octet-stream"
}, K = {
...b,
...w,
...E,
...nt
};
function dt(n) {
const t = [];
switch (n) {
case "audio":
t.push(...Object.values(b)), t.push(...Object.keys(b).map((e) => `.${e}`));
break;
case "video":
t.push(...Object.values(w)), t.push(...Object.keys(w).map((e) => `.${e}`));
break;
case "image":
t.push(...Object.values(E)), t.push(...Object.keys(E).map((e) => `.${e}`));
break;
case "file":
default:
return ["*/*"];
}
return t;
}
function mt(n) {
return {
audio: "Audio",
video: "Video",
image: "Images",
file: "Files"
}[n] || n;
}
function gt(n) {
return Object.values(b).includes(n) ? "audio" : Object.values(w).includes(n) ? "video" : Object.values(E).includes(n) ? "image" : null;
}
function H(n) {
const t = (n.startsWith(".") ? n.slice(1) : n).toLowerCase();
return t in K ? K[t] : null;
}
const _ = typeof window > "u";
function B(n) {
try {
const t = new URL(n);
return t.protocol === "http:" || t.protocol === "https:";
} catch {
return !1;
}
}
function yt(n) {
if (typeof n != "string" || !B(n)) return null;
try {
const t = ((new URL(n).pathname || "").split("/").pop() || "").split("?")[0].split("#")[0].split("."), e = t.length > 1 ? t.pop().toLowerCase() : "";
return e ? H(e) : null;
} catch {
return null;
}
}
function ot(n) {
return n.startsWith("data:") || at(n);
}
function at(n) {
return /^[A-Za-z0-9+/=]+$/.test(n) && n.length % 4 === 0;
}
async function lt(n) {
if (!_)
return !1;
try {
return (await (await import("fs/promises")).stat(n)).isFile();
} catch {
return !1;
}
}
function V(n) {
return n && typeof n == "object" && typeof n.data == "string" && typeof n.type == "string" && typeof n.name == "string";
}
async function Q(n) {
if (n == null)
return null;
if (Array.isArray(n)) {
const t = n.map((e) => Q(e));
return Promise.all(t);
}
if (z(n))
try {
return await new p().fromDict(n);
} catch {
return n;
}
return n;
}
class L {
/**
* Create a MediaFile or specialized subclass from any supported data type.
* Automatically detects data type and instantiates the appropriate class.
*
* @param data - Data to load (file path, URL, base64 string, etc.)
* @returns Promise resolving to a MediaFile instance or specialized subclass
*/
static async create(t) {
if (t == null)
throw new Error("Cannot create MediaFile from null or undefined data");
const e = await L._detectContentType(t);
let r = I.default;
return e && e.startsWith("image/") ? r = I.image : e && e.startsWith("audio/") ? r = I.audio : e && e.startsWith("video/") && (r = I.video), new r().fromAny(t);
}
/**
* Detects content type from various data sources.
*
* @param data - Data to detect content type from
* @returns Promise resolving to the detected content type or null
* @private
*/
static async _detectContentType(t) {
var e;
if (t instanceof p)
return t.getContentType();
if (z(t))
return t.content_type;
if (V(t))
return t.type;
if (typeof t == "string" && B(t)) {
const r = (e = new URL(t).pathname.split(".").pop()) == null ? void 0 : e.toLowerCase();
if (r) {
const s = L._getMimeTypeFromExtension(r);
if (s) return s;
}
try {
const s = await fetch(t, {
method: "HEAD",
headers: { "User-Agent": "MediaFile/1.0.0" }
});
if (s.ok) {
const i = s.headers.get("content-type");
if (i) return i;
}
} catch {
}
}
if (typeof t == "string" && t.startsWith("data:")) {
const r = t.match(/^data:([^;,]+)/);
if (r && r[1])
return r[1];
}
if (typeof t == "string" && typeof window > "u")
try {
const r = await import("fs/promises"), s = await import("path");
await r.access(t);
const i = s.extname(t).slice(1).toLowerCase();
if (i) {
const o = L._getMimeTypeFromExtension(i);
if (o) return o;
}
} catch {
}
return typeof Blob < "u" && t instanceof Blob ? t.type : null;
}
/**
* Maps file extensions to MIME types.
*
* @param extension - File extension without the dot
* @returns The corresponding MIME type or null
* @private
*/
static _getMimeTypeFromExtension(t) {
return H(t);
}
}
class p {
/**
* Creates a new MediaFile instance.
*
* @param file_name - Default filename to use
* @param content_type - Default content type to use
*/
constructor(t = "file", e = "application/octet-stream") {
k(this, "content_type"), k(this, "file_name"), k(this, "_content", null), this.content_type = e, this.file_name = t;
}
/**
* Factory method to create a MediaFile from any supported data type.
* This is kept for backward compatibility. New code should use MediaFileFactory.create().
*
* @param data - Data to load (file path, URL, base64 string, etc.)
* @returns Promise resolving to a MediaFile instance or specialized subclass
* @deprecated Use MediaFileFactory.create() instead
*/
static async create(t) {
return L.create(t);
}
/**
* 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(t) {
if (t == null)
throw new Error("Cannot create MediaFile from null or undefined data");
if (t instanceof p)
return t;
if (V(t)) {
if (this.file_name = t.name || "file", this.content_type = t.type, typeof t.data == "string")
return this.fromBase64(t.data);
throw new Error("Invalid data format in FileReader object");
}
if (_ && this._isBuffer(t))
return this.fromBytes(t);
if (typeof File < "u" && t instanceof File) {
this.file_name = t.name, this.content_type = t.type || "application/octet-stream";
const e = await t.arrayBuffer();
return this.fromBytes(e);
}
if (typeof Blob < "u" && t instanceof Blob) {
const e = await t.arrayBuffer();
return this.fromBytes(e);
}
if (t instanceof ArrayBuffer || t instanceof Uint8Array)
return this.fromBytes(t);
if (typeof t == "string") {
if (B(t))
return await this.fromUrl(t);
if (ot(t))
return this.fromBase64(t);
if (await lt(t))
return await this.fromFile(t);
throw typeof t == "string" ? new Error("Invalid data type for MediaFile " + t) : new Error("Invalid data type for MediaFile");
}
return z(t) ? await this.fromDict(t) : 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(t) {
if (!_)
throw new Error("Loading from file path is only supported in Node.js environment");
try {
const e = await (await import("fs/promises")).readFile(t), r = await import("path");
return this.file_name = r.basename(t), this._content = this._bufferToArrayBuffer(e), this._setContentTypeFromFileName(), this;
} catch (e) {
throw new Error(`Failed to load file from path: ${t}. ${e.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(t, e) {
const r = async (s, i) => await fetch(s, {
headers: i || {
"User-Agent": "MediaFile/1.0.0"
}
});
try {
let s = await r(t, e);
if (!s.ok) {
if (s.status === 401 || s.status === 403) {
const o = new URL(t);
o.search = "", s = await r(o.toString(), e);
}
if (!s.ok)
throw new Error(`HTTP error! Status: ${s.status}`);
}
this.content_type = s.headers.get("content-type") || "application/octet-stream";
const i = s.headers.get("content-disposition");
if (i) {
const o = i.match(/filename=(?:['"]?)([^'";\n]+)/i);
o && o[1] && (this.file_name = o[1]);
}
if (!this.file_name || this.file_name === "file") {
const o = new URL(t).pathname.split("/"), l = o[o.length - 1];
l && l.trim() !== "" ? this.file_name = decodeURIComponent(l) : this.file_name = "downloaded_file";
}
return this._content = await s.arrayBuffer(), this;
} catch (s) {
throw new Error(`Failed to load file from URL: ${t}. ${s.message}`);
}
}
/**
* Load file from base64 encoded string.
*
* @param base64Data - Base64 encoded string, optionally with data URI prefix
* @returns The MediaFile instance
*/
fromBase64(t) {
const { data: e, mediaType: r } = this._parseBase64Uri(t);
r && (this.content_type = r);
try {
return this._content = _ ? this._decodeBase64NodeJs(e) : this._decodeBase64Browser(e), this;
} catch (s) {
throw new Error(`Failed to decode base64 data: ${s.message}`);
}
}
/**
* Load file from binary data.
*
* @param data - ArrayBuffer, Buffer, or Uint8Array containing the file data
* @returns The MediaFile instance
*/
fromBytes(t) {
if (typeof SharedArrayBuffer < "u" && t instanceof SharedArrayBuffer) {
const e = new Uint8Array(t), r = new ArrayBuffer(e.byteLength);
return new Uint8Array(r).set(e), this._content = r, this;
}
if (t instanceof Uint8Array)
if (typeof SharedArrayBuffer < "u" && t.buffer instanceof SharedArrayBuffer) {
const e = new Uint8Array(t);
this._content = e.buffer.slice(e.byteOffset, e.byteOffset + e.byteLength);
} else
this._content = t.buffer.slice(t.byteOffset, t.byteOffset + t.byteLength);
else if (_ && Buffer.isBuffer(t))
if (typeof SharedArrayBuffer < "u" && t.buffer instanceof SharedArrayBuffer) {
const e = new Uint8Array(t);
this._content = e.buffer.slice(e.byteOffset, e.byteOffset + e.byteLength);
} else
this._content = t.buffer.slice(t.byteOffset, t.byteOffset + t.byteLength);
else
this._content = new Uint8Array(t).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(t) {
if (!t.content)
throw new Error("Invalid FileResult object: missing content");
return this.file_name = t.file_name, this.content_type = t.content_type, await this.fromAny(t.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(), typeof SharedArrayBuffer < "u" && 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(), !_)
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(t = !0) {
this._ensureContent();
let e;
if (_)
e = Buffer.from(this._content).toString("base64");
else {
const r = new Uint8Array(this._content);
let s = "";
const i = 10240;
for (let o = 0; o < r.length; o += i) {
const l = r.subarray(o, Math.min(o + i, r.length));
s += String.fromCharCode.apply(null, Array.from(l));
}
e = btoa(s);
}
return t ? `data:${this.content_type};base64,${e}` : e;
}
/**
* 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(t) {
this._ensureContent();
const e = t || this.file_name;
if (_)
try {
const r = await import("fs/promises").then((o) => o.default || o), s = await import("path").then((o) => o.default || o);
if (!s || typeof s.dirname != "function")
throw new Error("Failed to load 'path' module.");
const i = s.dirname(e);
i !== "." && await r.mkdir(i, { recursive: !0 }).catch(() => {
}), await r.writeFile(e, Buffer.from(this._content));
} catch (r) {
throw new Error(`Failed to save file: ${r.message}`);
}
else {
const r = this.toBlob(), s = URL.createObjectURL(r), i = document.createElement("a");
i.href = s, i.download = e, document.body.appendChild(i), i.click(), setTimeout(() => {
document.body.removeChild(i), URL.revokeObjectURL(s);
}, 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(t = "bytes") {
if (!this._content)
return 0;
const e = this._content.byteLength;
switch (t) {
case "kb":
return e / 1024;
case "mb":
return e / (1024 * 1024);
case "gb":
return e / (1024 * 1024 * 1024);
default:
return e;
}
}
/**
* 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 t;
if (this.content_type && this.content_type !== "application/octet-stream") {
const e = {
"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",
npz: "npz"
};
if (this.content_type in e)
return e[this.content_type];
if (_)
try {
const r = require("mime-types").extension(this.content_type);
if (r) return r;
} catch {
}
}
return this.file_name && this.file_name.includes(".") && ((t = this.file_name.split(".").pop()) == null ? void 0 : t.toLowerCase()) || null;
}
/**
* Get the filename.
*/
getFileName() {
return this.file_name;
}
/**
* Set the filename.
*/
setFileName(t) {
this.file_name = t;
}
/**
* Get the content type.
*/
getContentType() {
return this.content_type;
}
/**
* Set the content type.
*/
setContentType(t) {
this.content_type = t;
}
/**
* 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 t;
if (!this.file_name) return;
const e = (t = this.file_name.split(".").pop()) == null ? void 0 : t.toLowerCase();
if (!e) return;
if (_)
try {
const s = require("mime-types").lookup(this.file_name);
if (s) {
this.content_type = s;
return;
}
} catch {
}
const r = {
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 r && (this.content_type = r[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(t) {
if (t.startsWith("data:")) {
const [e, r] = t.split(",", 2), s = e.match(/^data:([^;,]+)/), i = s ? s[1] : null;
return { data: r, mediaType: i };
}
return { data: t, 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 an object is a Buffer.
*
* @param obj - Object to check
* @returns Whether the object is a Buffer
* @private
*/
_isBuffer(t) {
return _ && Buffer.isBuffer(t);
}
/**
* Decode base64 string in Node.js environment.
*
* @param base64 - Base64 string to decode
* @returns ArrayBuffer containing the decoded data
* @private
*/
_decodeBase64NodeJs(t) {
const e = Buffer.from(t, "base64");
return this._bufferToArrayBuffer(e);
}
/**
* Decode base64 string in browser environment.
*
* @param base64 - Base64 string to decode
* @returns ArrayBuffer containing the decoded data
* @private
*/
_decodeBase64Browser(t) {
const e = atob(t), r = new Uint8Array(e.length);
for (let s = 0; s < e.length; s++)
r[s] = e.charCodeAt(s);
return r.buffer;
}
/**
* Convert a Node.js Buffer to an ArrayBuffer.
*
* @param buffer - Buffer to convert
* @returns ArrayBuffer containing the data
* @private
*/
_bufferToArrayBuffer(t) {
if (typeof SharedArrayBuffer < "u" && t.buffer instanceof SharedArrayBuffer) {
const e = new ArrayBuffer(t.byteLength);
return new Uint8Array(e).set(new Uint8Array(t)), e;
} else
return t.buffer.slice(t.byteOffset, t.byteOffset + t.byteLength);
}
}
class F extends p {
/**
* Creates a new ImageFile instance.
*
* @param file_name - Default filename to use
* @param content_type - Default content type to use (defaults to image/png)
*/
constructor(t = "image", e = "image/png") {
super(t, e);
}
/**
* Factory method to create an ImageFile from any supported data type.
*
* @param data - Data to load (file path, URL, base64 string, etc.)
* @returns Promise resolving to an ImageFile instance
*/
static async create(t) {
if (t == null)
throw new Error("Cannot create ImageFile from null or undefined data");
return new F().fromAny(t);
}
/**
* Override fromAny to ensure the content type is an image type.
*
* @param data - Data to load
* @returns Promise resolving to an ImageFile instance
*/
async fromAny(t) {
if (t instanceof F)
return t;
if (t instanceof p && !(t instanceof F)) {
if (t.isEmpty())
throw new Error("Cannot create ImageFile from empty MediaFile");
return this.file_name = t.getFileName() || "image", this.content_type = t.getContentType(), this._content = t.read(), this._validateImageContentType(), this;
}
const e = await super.fromAny(t);
return e.file_name = e.file_name || "image", e._validateImageContentType(), e;
}
/**
* Creates an HTML image element from the image data.
*
* @param options - Optional image element attributes (width, height, alt, className)
* @returns HTML string containing an image element
*/
toImageElement(t = {}) {
if (typeof window > "u")
throw new Error("Image elements are only available in browser environments");
this._ensureContent();
const e = this.toBase64(), { width: r, height: s, alt: i = "", className: o = "" } = t, l = r ? ` width="${r}"` : "", h = s ? ` height="${s}"` : "", c = o ? ` class="${o}"` : "";
return `<img src="${e}" alt="${i}"${l}${h}${c}>`;
}
/**
* Create an actual DOM Image element (browser only).
*
* @param options - Optional image element attributes
* @returns HTMLImageElement that can be added to the DOM
*/
toDOMImageElement(t = {}) {
if (typeof window > "u")
throw new Error("DOM Image elements are only available in browser environments");
this._ensureContent();
const e = this.toBlob(), r = URL.createObjectURL(e), s = new Image();
return t.width && (s.width = t.width), t.height && (s.height = t.height), t.alt && (s.alt = t.alt), t.className && (s.className = t.className), s.src = r, s.addEventListener("load", () => {
setTimeout(() => URL.revokeObjectURL(r), 1e3);
}), s;
}
/**
* Get image dimensions (browser only).
*
* @returns Promise resolving to an object with width and height
*/
async getDimensions() {
if (typeof window > "u")
throw new Error("Getting image dimensions is only available in browser environments");
return new Promise((t, e) => {
const r = new Image();
r.onload = () => {
t({ width: r.naturalWidth, height: r.naturalHeight });
}, r.onerror = () => {
e(new Error("Failed to load image for dimension calculation"));
};
const s = this.toBlob(), i = URL.createObjectURL(s);
r.src = i, r.addEventListener("load", () => {
URL.revokeObjectURL(i);
}, { once: !0 });
});
}
/**
* Validate that the content type is an image type.
* If not, attempt to correct it based on file extension.
*
* @private
*/
_validateImageContentType() {
var t;
if (this.content_type.startsWith("image/"))
return;
const e = (t = this.extension) == null ? void 0 : t.toLowerCase();
if (e && e in E) {
this.content_type = E[e];
return;
}
this._content && this._detectImageFormat();
}
/**
* Attempt to detect image format from file header.
* Updates content_type if a format is detected.
*
* @private
*/
_detectImageFormat() {
const t = new Uint8Array(this._content).slice(0, 12);
if (t[0] === 255 && t[1] === 216 && t[2] === 255) {
this.content_type = E.jpeg;
return;
}
if (t[0] === 137 && t[1] === 80 && t[2] === 78 && t[3] === 71 && t[4] === 13 && t[5] === 10 && t[6] === 26 && t[7] === 10) {
this.content_type = E.png;
return;
}
if (t[0] === 71 && t[1] === 73 && t[2] === 70 && t[3] === 56 && (t[4] === 55 || t[4] === 57) && t[5] === 97) {
this.content_type = E.gif;
return;
}
if (t[0] === 82 && t[1] === 73 && t[2] === 70 && t[3] === 70 && t[8] === 87 && t[9] === 69 && t[10] === 66 && t[11] === 80) {
this.content_type = E.webp;
return;
}
if (this._content.byteLength > 100 && new TextDecoder().decode(new Uint8Array(this._content.slice(0, 100))).indexOf("<svg") !== -1) {
this.content_type = E.svg;
return;
}
this.content_type = E.png;
}
}
class S extends p {
/**
* Creates a new AudioFile instance.
*
* @param file_name - Default filename to use
* @param content_type - Default content type to use (defaults to audio/mpeg)
*/
constructor(t = "audio", e = "audio/wav") {
super(t, e);
}
/**
* Factory method to create an AudioFile from any supported data type.
*
* @param data - Data to load (file path, URL, base64 string, etc.)
* @returns Promise resolving to an AudioFile instance
*/
static async create(t) {
if (t == null)
throw new Error("Cannot create AudioFile from null or undefined data");
return new S().fromAny(t);
}
/**
* Override fromAny to ensure the content type is an audio type.
*
* @param data - Data to load
* @returns Promise resolving to an AudioFile instance
*/
async fromAny(t) {
if (t instanceof S)
return t;
if (t instanceof p && !(t instanceof S)) {
if (t.isEmpty())
throw new Error("Cannot create AudioFile from empty MediaFile");
return this.file_name = t.getFileName() || "audio", this.content_type = t.getContentType(), this._content = t.read(), this._validateAudioContentType(), this;
}
const e = await super.fromAny(t);
return e.file_name = e.file_name || "audio", e._validateAudioContentType(), e;
}
/**
* Creates an HTML audio element from the audio data.
*
* @returns HTMLAudioElement that can be used for playback
*/
toAudioElement() {
if (typeof window > "u")
throw new Error("Audio elements are only available in browser environments");
this._ensureContent();
const t = this.toBlob(), e = URL.createObjectURL(t), r = new Audio(e);
return r.addEventListener("canplaythrough", () => {
setTimeout(() => URL.revokeObjectURL(e), 1e3);
}), r;
}
/**
* Play the audio (browser only).
*
* @returns Promise that resolves when audio playback starts
*/
async play() {
if (typeof window > "u")
throw new Error("Audio playback is only available in browser environments");
const t = this.toAudioElement();
return new Promise((e, r) => {
t.addEventListener("play", () => e()), t.addEventListener("error", (s) => r(new Error(`Audio playback error: ${s}`))), t.play().catch(r);
});
}
/**
* Get duration of the audio file in seconds (browser only).
*
* @returns Promise resolving to the duration in seconds
*/
async getDuration() {
if (typeof window > "u")
throw new Error("Getting audio duration is only available in browser environments");
const t = this.toAudioElement();
return new Promise((e) => {
if (t.duration && !isNaN(t.duration)) {
e(t.duration);
return;
}
t.addEventListener("loadedmetadata", () => {
e(t.duration);
});
});
}
/**
* Create an embedded HTML audio player.
*
* @param options - Options for the audio player (controls, autoplay, loop)
* @returns HTML string containing an audio element
*/
toHTMLPlayer(t = {}) {
const e = this.toBase64(), { controls: r = !0, autoplay: s = !1, loop: i = !1 } = t;
return `<audio
src="${e}"
${r ? "controls" : ""}
${s ? "autoplay" : ""}
${i ? "loop" : ""}>
Your browser does not support the audio element.
</audio>`;
}
/**
* Validate that the content type is an audio type.
* If not, attempt to correct it based on file extension.
*
* @private
*/
_validateAudioContentType() {
var t;
if (this.content_type.startsWith("audio/"))
return;
const e = (t = this.extension) == null ? void 0 : t.toLowerCase();
if (e && e in b) {
this.content_type = b[e];
return;
}
this._content && this._detectAudioFormat();
}
/**
* Attempt to detect audio format from file header.
* Updates content_type if a format is detected.
*
* @private
*/
_detectAudioFormat() {
const t = new Uint8Array(this._content).slice(0, 12);
if (t[0] === 73 && t[1] === 68 && t[2] === 51) {
this.content_type = b.mp3;
return;
}
if (t[0] === 255 && (t[1] & 224) === 224) {
this.content_type = b.mp3;
return;
}
if (t[0] === 82 && t[1] === 73 && t[2] === 70 && t[3] === 70 && t[8] === 87 && t[9] === 65 && t[10] === 86 && t[11] === 69) {
this.content_type = b.wav;
return;
}
if (t[0] === 79 && t[1] === 103 && t[2] === 103 && t[3] === 83) {
this.content_type = b.ogg;
return;
}
if (t[0] === 102 && t[1] === 76 && t[2] === 97 && t[3] === 67) {
this.content_type = b.flac;
return;
}
this.content_type = b.mp3;
}
}
class R extends p {
/**
* Creates a new VideoFile instance.
*
* @param file_name - Default filename to use
* @param content_type - Default content type to use (defaults to video/mp4)
*/
constructor(t = "video", e = "video/mp4") {
super(t, e);
}
/**
* Factory method to create a VideoFile from any supported data type.
*
* @param data - Data to load (file path, URL, base64 string, etc.)
* @returns Promise resolving to a VideoFile instance
*/
static async create(t) {
if (t == null)
throw new Error("Cannot create VideoFile from null or undefined data");
return new R().fromAny(t);
}
/**
* Override fromAny to ensure the content type is a video type.
*
* @param data - Data to load
* @returns Promise resolving to a VideoFile instance
*/
async fromAny(t) {
if (t instanceof R)
return t;
if (t instanceof p && !(t instanceof R)) {
if (t.isEmpty())
throw new Error("Cannot create VideoFile from empty MediaFile");
return this.file_name = t.getFileName() || "video", this.content_type = t.getContentType(), this._content = t.read(), this._validateVideoContentType(), this;
}
const e = await super.fromAny(t);
return e.file_name = e.file_name || "video", e._validateVideoContentType(), e;
}
/**
* Creates an HTML video element from the video data.
*
* @returns HTMLVideoElement that can be used for playback
*/
toVideoElement() {
if (typeof window > "u")
throw new Error("Video elements are only available in browser environments");
this._ensureContent();
const t = this.toBlob(), e = URL.createObjectURL(t), r = document.createElement("video");
return r.src = e, r.addEventListener("loadedmetadata", () => {
setTimeout(() => URL.revokeObjectURL(e), 1e3);
}), r;
}
/**
* Play the video (browser only).
*
* @returns Promise that resolves when video playback starts
*/
async play() {
if (typeof window > "u")
throw new Error("Video playback is only available in browser environments");
const t = this.toVideoElement();
return new Promise((e, r) => {
t.addEventListener("play", () => e()), t.addEventListener("error", (s) => r(new Error(`Video playback error: ${s}`))), t.play().catch(r);
});
}
/**
* Get duration of the video file in seconds (browser only).
*
* @returns Promise resolving to the duration in seconds
*/
async getDuration() {
if (typeof window > "u")
throw new Error("Getting video duration is only available in browser environments");
const t = this.toVideoElement();
return new Promise((e) => {
if (t.duration && !isNaN(t.duration)) {
e(t.duration);
return;
}
t.addEventListener("loadedmetadata", () => {
e(t.duration);
});
});
}
/**
* Get dimensions of the video (width and height).
*
* @returns Promise resolving to an object with width and height properties
*/
async getDimensions() {
if (typeof window > "u")
throw new Error("Getting video dimensions is only available in browser environments");
const t = this.toVideoElement();
return new Promise((e) => {
if (t.videoWidth && t.videoHeight) {
e({ width: t.videoWidth, height: t.videoHeight });
return;
}
t.addEventListener("loadedmetadata", () => {
e({ width: t.videoWidth, height: t.videoHeight });
});
});
}
/**
* Create a thumbnail from the video at a specific time point.
*
* @param timeSeconds - Time in seconds for the thumbnail (defaults to 0)
* @returns Promise resolving to a Blob containing the thumbnail image
*/
async createThumbnail(t = 0) {
if (typeof window > "u")
throw new Error("Creating thumbnails is only available in browser environments");
const e = this.toVideoElement();
return new Promise((r, s) => {
e.addEventListener("loadedmetadata", async () => {
try {
e.currentTime = t, await new Promise((l) => {
const h = () => {
e.removeEventListener("seeked", h), l();
};
e.addEventListener("seeked", h);
});
const i = document.createElement("canvas");
i.width = e.videoWidth, i.height = e.videoHeight;
const o = i.getContext("2d");
if (!o)
throw new Error("Failed to get canvas context");
o.drawImage(e, 0, 0, i.width, i.height), i.toBlob((l) => {
l ? r(l) : s(new Error("Failed to create thumbnail blob"));
}, "image/jpeg", 0.95);
} catch (i) {
s(i);
}
}), e.addEventListener("error", (i) => {
s(new Error(`Video loading error: ${i}`));
});
});
}
/**
* Create an embedded HTML video player.
*
* @param options - Options for the video player
* @returns HTML string containing a video element
*/
toHtmlPlayer(t = {}) {
const e = this.toBase64(), {
controls: r = !0,
autoplay: s = !1,
loop: i = !1,
muted: o = !1,
width: l,
height: h,
poster: c
} = t;
return `<video
src="${e}"
${r ? "controls" : ""}
${s ? "autoplay" : ""}
${i ? "loop" : ""}
${o ? "muted" : ""}
${l ? `width="${l}"` : ""}
${h ? `height="${h}"` : ""}
${c ? `poster="${c}"` : ""}>
Your browser does not support the video element.
</video>`;
}
/**
* Validate that the content type is a video type.
* If not, attempt to correct it based on file extension.
*
* @private
*/
_validateVideoContentType() {
var t;
if (this.content_type.startsWith("video/"))
return;
const e = (t = this.extension) == null ? void 0 : t.toLowerCase();
if (e && e in w) {
this.content_type = w[e];
return;
}
this._content && this._detectVideoFormat();
}
/**
* Attempt to detect video format from file header.
* Updates content_type if a format is detected.
*
* @private
*/
_detectVideoFormat() {
const t = new Uint8Array(this._content).slice(0, 16);
if (t[4] === 102 && t[5] === 116 && t[6] === 121 && t[7] === 112) {
this.content_type = w.mp4;
return;
}
if (t[4] === 119 && t[5] === 105 && t[6] === 100 && t[7] === 101 || t[4] === 109 && t[5] === 100 && t[6] === 97 && t[7] === 116 || t[4] === 102 && t[5] === 114 && t[6] === 101 && t[7] === 101 || t[4] === 112 && t[5] === 110 && t[6] === 111 && t[7] === 116 || t[4] === 115 && t[5] === 107 && t[6] === 105 && t[7] === 112 || t[4] === 109 && t[5] === 111 && t[6] === 111 && t[7] === 118) {
this.content_type = w.mov;
return;
}
if (t[0] === 26 && t[1] === 69 && t[2] === 223 && t[3] === 163) {
this.content_type = w.webm;
return;
}
if (t[0] === 82 && t[1] === 73 && t[2] === 70 && t[3] === 70 && t[8] === 65 && t[9] === 86 && t[10] === 73 && t[11] === 32) {
this.content_type = w.avi;
return;
}
if (t[0] === 70 && t[1] === 76 && t[2] === 86) {
this.content_type = w.flv;
return;
}
if (t[4] === 102 && t[5] === 116 && t[6] === 121 && t[7] === 112 && t[8] === 51 && t[9] === 103 && t[10] === 112) {
this.content_type = w["3gp"];
return;
}
this.content_type = w.mp4;
}
}
const I = {};
I.default = p;
I.image = F;
I.audio = S;
I.video = R;
I.media = p;
typeof window < "u" && (window.MediaFile = p, window.ImageFile = F, window.AudioFile = S, window.AudioFile = R);
typeof module < "u" && typeof module.exports < "u" && (module.exports = { MediaFile: p, ImageFile: F, AudioFile: S, VideoFile: R, MediaFileFactory: L });
class ct {
// 60 seconds timeout for uploads
/**
* Create a new FastCloud instance
* @param config FastCloud configuration
*/
constructor(t) {
a(this, "uploadEndpoint");
a(this, "apiKey");
a(this, "defaultTimeout", 6e4);
this.uploadEndpoint = t.uploadEndpoint || "https://api.socaity.ai/sdk/v1/files", this.apiKey = t.apiKey;
}
setApiKey(t) {
this.apiKey = t;
}
/**
* Get authentication headers for requests
* @returns Headers object with authentication
*/
getAuthHeaders() {
return { Authorization: `Bearer ${this.apiKey}` };
}
/**
* Process the upload response to extract file URLs
* @param response Response from the upload endpoint
* @returns Array of temporary upload URLs
*/
async processUploadResponse(t) {
if (t.status === 401)
throw new Error("Unauthorized: Invalid API key");
if (!t.ok)
throw console.error("Failed to get temporary upload URL:", t), new Error(`Failed to get temporary upload URL: ${t.status} ${t.statusText}`);
try {
const e = await t.text(), r = JSON.parse(e);
return Array.isArray(r) ? r : [r];
} catch (e) {
throw console.error("Error parsing response:", e), new Error("Failed to parse response body as JSON");
}
}
/**
* Upload a file to a temporary URL
* @param sasUrl Temporary URL for uploading
* @param file File to upload
*/
async uploadToTemporaryUrl(t, e) {
const r = {
"x-ms-blob-type": "BlockBlob",
"x-ms-if-none-match": "*"
}, s = e instanceof p ? await e.toArrayBuffer() : e, i = await fetch(t, {
method: "PUT",
headers: r,
body: s
});
if (i.status !== 201)
throw new Error(`Failed to upload to temporary URL ${t}. Response: ${i.statusText}`);
}
/**
* Upload a single file
* @param file File to upload
* @returns the URLs of the uploaded files. (If one file is uploaded, the return value is an array with one URL)
*/
async upload(t) {
const e = Array.isArray(t) ? t : [t], r = e.map((c) => c.extension).filter((c) => c !== null), s = {
"Content-Type": "application/json",
...this.getAuthHeaders()
}, i = await fetch(this.uploadEndpoint, {
method: "POST",
headers: s,
body: JSON.stringify({
n_files: e.length,
file_extensions: r.length > 0 ? r : void 0
}),
signal: AbortSignal.timeout(this.defaultTimeout)
}), o = await this.processUploadResponse(i), l = o.map(
(c, v) => this.uploadToTemporaryUrl(c, e[v])
);
return await Promise.all(l), o.map((c) => c.split("?")[0]);
}
/**
* 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(t, e) {
const r = await new p(t).fromUrl(t, this.getAuthHeaders());
return e ? (await r.save(e), e) : r;
}
}
class Z {
constructor() {
a(this, "config");
a(this, "uploadFileThresholdMB", 1);
a(this, "maxFileUploadLimitMB", 1e3);
a(this, "fastCloud");
a(this, "abortController");
a(this, "responseParser");
this.config = T.getInstance(), this.abortController = new AbortController(), this.responseParser = new rt(), this.fastCloud = new ct({
uploadEndpoint: `${this.config.baseUrl}/sdk/files`,
apiKey: this.config.apiKey ? this.config.apiKey : ""
});
}
/**
* Check if a parameter is a file type based on parameter definition and value
*/
isFileType(t, e) {
const r = ["file", "image", "audio", "video"];
if (typeof t.type == "string") {
const s = this.getBaseType(t.type);
if (r.includes(s))
return !0;
}
return Array.isArray(t.type) && t.type.some((i) => {
const o = this.getBaseType(i);
return r.includes(o);
}) || e instanceof p ? !0 : e == null ? !1 : !!(e instanceof File || e instanceof Blob || typeof e == "string" && B(e));
}
/**
* Extract base type from List[type] or type string
*/
getBaseType(t) {
if (!t) return "string";
const e = t.match(/^List\[(.+)\]$/i);
return e && e[1] ? e[1].toLowerCase() : t.toLowerCase();
}
/**
* Format request parameters similar to Python's format_request_params
* @param endpoint Endpoint metadata
* @param data Request parameters
* @returns Organized request data
*/
async formatRequestParams(t, e) {
const r = {
queryParams: {},
bodyParams: {},
fileParams: {},
headers: {},
url: ""
};
if (!t.parameters || t.parameters.length === 0)
return r.url = this.buildRequestUrl(t.path, r.queryParams), r;
for (const s of t.parameters) {
const i = e[s.name] ?? s.default;
if (i === void 0 && s.required)
throw new Error(`Required parameter '${s.name}' is missing`);
if (i === void 0)
continue;
if (this.isFileType(s, i)) {
if (i == null) {
r.bodyParams[s.name] = null;
continue;
}
if (typeof i == "string" && B(i))
r.fileParams[s.name] = i;
else
try {
const l = i instanceof p ? i : await L.create(i);
l instanceof p && (r.fileParams[s.name] = l);
} catch (l) {
console.warn(`Failed to convert ${s.name} to MediaFile:`, l), s.location === "query" ? r.queryParams[s.name] = i : r.bodyParams[s.name] = i;
}
} else
s.location === "query" ? r.queryParams[s.name] = i : r.bodyParams[s.name] = i;
}
return r.fileParams = await this.processFileParams(r.fileParams), r.url = this.buildRequestUrl(t.path, r.queryParams), r;
}
/**
* Process file parameters - handle uploads and conversions
*/
async processFileParams(t) {
const e = {};
let r = 0;
const s = {};
for (const [i, o] of Object.entries(t))
typeof o == "string" && B(o) ? e[i] = o : o instanceof p && (s[i] = o, r += o.fileSize("mb"));
if (r > this.maxFileUploadLimitMB)
throw new Error(`Total file size exceeds maximum limit of ${this.maxFileUploadLimitMB}MB`);
if (r > this.uploadFileThresholdMB) {