UNPKG

@lenml/char-card-reader

Version:

SillyTavern character card info reader

156 lines (145 loc) 4.41 kB
export function toBase64( input: Blob | Buffer | ArrayBuffer | Uint8Array ): Promise<string> { if (typeof Buffer !== "undefined" && Buffer.isBuffer(input)) { // Node.js Buffer return Promise.resolve(input.toString("base64")); } if (typeof Blob !== "undefined" && input instanceof Blob) { // Browser Blob return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = () => { const result = reader.result as string; const base64 = result.split(",")[1]; // data:*/*;base64,... resolve(base64); }; reader.onerror = reject; reader.readAsDataURL(input); }); } // ArrayBuffer or Uint8Array let uint8: Uint8Array; if (input instanceof ArrayBuffer) { uint8 = new Uint8Array(input); } else if (input instanceof Uint8Array) { uint8 = input; } else { throw new TypeError("Unsupported input type"); } if (typeof Buffer !== "undefined") { // Node.js environment return Promise.resolve(Buffer.from(uint8).toString("base64")); } else { // Browser: use btoa let binary = ""; for (let i = 0; i < uint8.length; i++) { binary += String.fromCharCode(uint8[i]); } return Promise.resolve(btoa(binary)); } } export function isValidImageUrl(url: any) { if (typeof url !== "string") { return false; } // http 或者 data: if ( url.startsWith("http://") || url.startsWith("https://") || url.startsWith("data:") ) { return true; } return false; } export const deepClone: <T>(x: T) => T = globalThis.structuredClone ? globalThis.structuredClone : <T>(x: T): T => JSON.parse(JSON.stringify(x)); export class Base64 { static encode(input: string): string { if (typeof window !== "undefined" && typeof window.btoa === "function") { return window.btoa(unescape(encodeURIComponent(input))); } else if (typeof Buffer !== "undefined") { return Buffer.from(input, "utf-8").toString("base64"); } else { throw new Error("Base64.encode: Environment not supported."); } } static decode(base64: string): string { if (typeof window !== "undefined" && typeof window.atob === "function") { return decodeURIComponent(escape(window.atob(base64))); } else if (typeof Buffer !== "undefined") { return Buffer.from(base64, "base64").toString("utf-8"); } else { throw new Error("Base64.decode: Environment not supported."); } } } function isDeepEqual(a: any, b: any) { if (a === b) { return true; } if ( a === null || b === null || typeof a !== "object" || typeof b !== "object" ) { return false; } if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (!isDeepEqual(a[i], b[i])) { return false; } } return true; } const keys = Object.keys(a); if (keys.length !== Object.keys(b).length) { return false; } for (const key of keys) { if (!isDeepEqual(a[key], b[key])) { return false; } } return true; } function isObject(x: any): x is object { return typeof x === "object" && x !== null; } function isNoNone(x: any): x is any { return x !== undefined && x !== null; } /** * Recursively merge objects into a single object. * * If all values for a key are objects, merge them recursively. * If any value for a key is not an object, take the last non-null value. * @param {...any[]} objects objects to merge * @returns merged object */ export function mergeObjects(...objects: any[]): any { const keys = new Set(objects.flatMap(Object.keys)); return Object.fromEntries( Array.from(keys).map((key) => { const vals = objects.map((x) => x[key]); if (vals.some(Array.isArray)) { // NOTE: 直接用最后一个非空,数组没有合并逻辑 return [key, [...vals].reverse().filter(isNoNone).filter(Boolean)[0]]; } if (vals.some(isObject)) { return [key, mergeObjects(...vals.filter(isObject))]; } return [key, [...vals].reverse().filter(isNoNone)[0]]; }) ); } export function uniq<T>(arr: T[]): T[] { return Array.from(new Set(arr)); }