@character-foundry/character-foundry 0.1.6 → 0.1.8-dev.1765911776
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -32
- package/dist/app-framework.cjs +1 -1
- package/dist/app-framework.cjs.map +1 -1
- package/dist/app-framework.js +1 -1
- package/dist/app-framework.js.map +1 -1
- package/dist/charx.cjs.map +1 -1
- package/dist/charx.js.map +1 -1
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +1 -1
- package/dist/core.js.map +1 -1
- package/dist/exporter.cjs +3 -3
- package/dist/exporter.cjs.map +1 -1
- package/dist/exporter.js +3 -3
- package/dist/exporter.js.map +1 -1
- package/dist/federation.cjs +6 -6
- package/dist/federation.cjs.map +1 -1
- package/dist/federation.js +6 -6
- package/dist/federation.js.map +1 -1
- package/dist/image-utils.cjs +249 -0
- package/dist/image-utils.cjs.map +1 -0
- package/dist/image-utils.d.cts +136 -0
- package/dist/image-utils.d.ts +136 -0
- package/dist/image-utils.js +226 -0
- package/dist/image-utils.js.map +1 -0
- package/dist/index.cjs +7 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -7
- package/dist/index.js.map +1 -1
- package/dist/loader.cjs +4 -4
- package/dist/loader.cjs.map +1 -1
- package/dist/loader.js +4 -4
- package/dist/loader.js.map +1 -1
- package/dist/lorebook.cjs +5 -5
- package/dist/lorebook.cjs.map +1 -1
- package/dist/lorebook.js +5 -5
- package/dist/lorebook.js.map +1 -1
- package/dist/media.cjs.map +1 -1
- package/dist/media.js.map +1 -1
- package/dist/normalizer.cjs.map +1 -1
- package/dist/normalizer.js.map +1 -1
- package/dist/png.cjs +1 -1
- package/dist/png.cjs.map +1 -1
- package/dist/png.js +1 -1
- package/dist/png.js.map +1 -1
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.js.map +1 -1
- package/dist/tokenizers.cjs.map +1 -1
- package/dist/tokenizers.js.map +1 -1
- package/dist/voxta.cjs +1 -1
- package/dist/voxta.cjs.map +1 -1
- package/dist/voxta.js +1 -1
- package/dist/voxta.js.map +1 -1
- package/package.json +14 -3
package/dist/core.cjs
CHANGED
|
@@ -699,7 +699,7 @@ function buildDataURI(data, mimeType, isBase642 = true) {
|
|
|
699
699
|
}
|
|
700
700
|
return `data:${mimeType},${encodeURIComponent(data)}`;
|
|
701
701
|
}
|
|
702
|
-
function isAnimatedImage(data,
|
|
702
|
+
function isAnimatedImage(data, _mimeType) {
|
|
703
703
|
if (data.length > 12 && data[0] === 82 && data[1] === 73 && data[2] === 70 && data[3] === 70 && // RIFF
|
|
704
704
|
data[8] === 87 && data[9] === 69 && data[10] === 66 && data[11] === 80) {
|
|
705
705
|
if (data[12] === 86 && data[13] === 80 && data[14] === 56 && data[15] === 88) {
|
package/dist/core.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts","../../core/src/binary.ts","../../core/src/base64.ts","../../core/src/errors.ts","../../core/src/data-url.ts","../../core/src/uri.ts","../../core/src/image.ts","../../core/src/uuid.ts"],"sourcesContent":["export * from '@character-foundry/core';\n","/**\n * Binary Data Utilities\n *\n * Universal binary data operations using Uint8Array.\n * Works in both Node.js and browser environments.\n */\n\n/**\n * Universal binary data type (works in both environments)\n */\nexport type BinaryData = Uint8Array;\n\n/**\n * Read a 32-bit big-endian unsigned integer\n */\nexport function readUInt32BE(data: BinaryData, offset: number): number {\n return (\n (data[offset]! << 24) |\n (data[offset + 1]! << 16) |\n (data[offset + 2]! << 8) |\n data[offset + 3]!\n ) >>> 0;\n}\n\n/**\n * Write a 32-bit big-endian unsigned integer\n */\nexport function writeUInt32BE(data: BinaryData, value: number, offset: number): void {\n data[offset] = (value >>> 24) & 0xff;\n data[offset + 1] = (value >>> 16) & 0xff;\n data[offset + 2] = (value >>> 8) & 0xff;\n data[offset + 3] = value & 0xff;\n}\n\n/**\n * Read a 16-bit big-endian unsigned integer\n */\nexport function readUInt16BE(data: BinaryData, offset: number): number {\n return ((data[offset]! << 8) | data[offset + 1]!) >>> 0;\n}\n\n/**\n * Write a 16-bit big-endian unsigned integer\n */\nexport function writeUInt16BE(data: BinaryData, value: number, offset: number): void {\n data[offset] = (value >>> 8) & 0xff;\n data[offset + 1] = value & 0xff;\n}\n\n/**\n * Find a byte sequence in binary data\n */\nexport function indexOf(data: BinaryData, search: BinaryData, fromIndex = 0): number {\n outer: for (let i = fromIndex; i <= data.length - search.length; i++) {\n for (let j = 0; j < search.length; j++) {\n if (data[i + j] !== search[j]) continue outer;\n }\n return i;\n }\n return -1;\n}\n\n/**\n * Concatenate multiple binary arrays\n */\nexport function concat(...arrays: BinaryData[]): BinaryData {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n\n/**\n * Slice binary data (returns a view, not a copy)\n */\nexport function slice(data: BinaryData, start: number, end?: number): BinaryData {\n return data.subarray(start, end);\n}\n\n/**\n * Copy a portion of binary data (returns a new array)\n */\nexport function copy(data: BinaryData, start: number, end?: number): BinaryData {\n return data.slice(start, end);\n}\n\n/**\n * Convert string to binary (UTF-8)\n */\nexport function fromString(str: string): BinaryData {\n return new TextEncoder().encode(str);\n}\n\n/**\n * Convert binary to string (UTF-8)\n */\nexport function toString(data: BinaryData): string {\n return new TextDecoder().decode(data);\n}\n\n/**\n * Convert string to binary (Latin1 - for PNG keywords and similar)\n */\nexport function fromLatin1(str: string): BinaryData {\n const result = new Uint8Array(str.length);\n for (let i = 0; i < str.length; i++) {\n result[i] = str.charCodeAt(i) & 0xff;\n }\n return result;\n}\n\n/**\n * Convert binary to string (Latin1)\n */\nexport function toLatin1(data: BinaryData): string {\n let result = '';\n for (let i = 0; i < data.length; i++) {\n result += String.fromCharCode(data[i]!);\n }\n return result;\n}\n\n/**\n * Compare two binary arrays for equality\n */\nexport function equals(a: BinaryData, b: BinaryData): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Create a new Uint8Array filled with zeros\n */\nexport function alloc(size: number): BinaryData {\n return new Uint8Array(size);\n}\n\n/**\n * Create a Uint8Array from an array of numbers\n */\nexport function from(data: number[] | ArrayBuffer | BinaryData): BinaryData {\n if (data instanceof Uint8Array) {\n return data;\n }\n if (data instanceof ArrayBuffer) {\n return new Uint8Array(data);\n }\n return new Uint8Array(data);\n}\n\n/**\n * Check if value is a Uint8Array\n */\nexport function isBinaryData(value: unknown): value is BinaryData {\n return value instanceof Uint8Array;\n}\n\n/**\n * Convert Node.js Buffer to Uint8Array (no-op if already Uint8Array)\n * This provides compatibility when interfacing with Node.js code\n */\nexport function toUint8Array(data: BinaryData | Buffer): BinaryData {\n if (data instanceof Uint8Array) {\n // Buffer extends Uint8Array, but we want a plain Uint8Array\n // This ensures we get a proper Uint8Array in all cases\n if (Object.getPrototypeOf(data).constructor.name === 'Buffer') {\n return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);\n }\n return data;\n }\n return new Uint8Array(data);\n}\n\n/**\n * Convert binary data to hex string\n */\nexport function toHex(data: BinaryData): string {\n return Array.from(data)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Convert hex string to binary data\n */\nexport function fromHex(hex: string): BinaryData {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.substr(i * 2, 2), 16);\n }\n return bytes;\n}\n","/**\n * Universal Base64 Encoding/Decoding\n *\n * Works in both Node.js and browser environments.\n */\n\nimport type { BinaryData } from './binary.js';\n\n/**\n * Check if we're in a Node.js environment\n */\nconst isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n/**\n * Threshold for switching to chunked encoding in browsers (1MB)\n * Below this, simple string concatenation is fast enough.\n * Above this, quadratic string growth becomes a problem.\n */\nconst LARGE_BUFFER_THRESHOLD = 1024 * 1024;\n\n/**\n * Encode binary data to base64 string\n *\n * PERFORMANCE: For large buffers (>1MB) in browsers, this automatically\n * uses the chunked implementation to avoid quadratic string concatenation.\n */\nexport function encode(data: BinaryData): string {\n if (isNode) {\n // Node.js: Buffer handles large data efficiently\n return Buffer.from(data).toString('base64');\n }\n\n // Browser: use chunked encoding for large buffers to avoid O(n²) string growth\n if (data.length > LARGE_BUFFER_THRESHOLD) {\n return encodeChunked(data);\n }\n\n // Small buffers: simple approach is fast enough\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]!);\n }\n return btoa(binary);\n}\n\n/**\n * Decode base64 string to binary data\n */\nexport function decode(base64: string): BinaryData {\n if (isNode) {\n // Node.js: use Buffer\n return new Uint8Array(Buffer.from(base64, 'base64'));\n }\n\n // Browser: use atob\n const binary = atob(base64);\n const result = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n result[i] = binary.charCodeAt(i);\n }\n return result;\n}\n\n/**\n * Check if a string is valid base64\n */\nexport function isBase64(str: string): boolean {\n if (str.length === 0) return false;\n // Base64 regex: only valid base64 characters, length multiple of 4 (with padding)\n const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;\n return base64Regex.test(str) && str.length % 4 === 0;\n}\n\n/**\n * Encode binary data to URL-safe base64 string\n * Replaces + with -, / with _, and removes padding\n */\nexport function encodeUrlSafe(data: BinaryData): string {\n return encode(data)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Decode URL-safe base64 string to binary data\n */\nexport function decodeUrlSafe(base64: string): BinaryData {\n // Add back padding if needed\n let padded = base64\n .replace(/-/g, '+')\n .replace(/_/g, '/');\n\n while (padded.length % 4 !== 0) {\n padded += '=';\n }\n\n return decode(padded);\n}\n\n/**\n * Chunk size for encoding large buffers (64KB)\n * Prevents stack overflow when using String.fromCharCode with spread operator\n */\nconst ENCODE_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Encode binary data to base64 string with chunking for large buffers.\n * Handles buffers >10MB without stack overflow.\n *\n * @param data - Binary data to encode\n * @returns Base64 encoded string\n *\n * @example\n * ```typescript\n * const largeBuffer = new Uint8Array(20 * 1024 * 1024); // 20MB\n * const base64 = encodeChunked(largeBuffer); // No stack overflow\n * ```\n */\nexport function encodeChunked(data: BinaryData): string {\n if (isNode) {\n // Node.js: Buffer handles large data efficiently\n return Buffer.from(data).toString('base64');\n }\n\n // Browser: process in chunks to avoid stack overflow\n const chunks: string[] = [];\n\n for (let i = 0; i < data.length; i += ENCODE_CHUNK_SIZE) {\n const chunk = data.subarray(i, Math.min(i + ENCODE_CHUNK_SIZE, data.length));\n let binary = '';\n for (let j = 0; j < chunk.length; j++) {\n binary += String.fromCharCode(chunk[j]!);\n }\n chunks.push(binary);\n }\n\n return btoa(chunks.join(''));\n}\n","/**\n * Error Classes\n *\n * Specific error types for character card operations.\n * All errors extend FoundryError for consistent handling.\n */\n\n/** Symbol to identify FoundryError instances across ESM/CJS boundaries */\nconst FOUNDRY_ERROR_MARKER = Symbol.for('@character-foundry/core:FoundryError');\n\n/**\n * Base error class for all Character Foundry errors\n */\nexport class FoundryError extends Error {\n /** @internal Marker for cross-module identification */\n readonly [FOUNDRY_ERROR_MARKER] = true;\n\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = 'FoundryError';\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error during card parsing\n */\nexport class ParseError extends FoundryError {\n constructor(message: string, public readonly format?: string) {\n super(message, 'PARSE_ERROR');\n this.name = 'ParseError';\n }\n}\n\n/**\n * Error during card validation\n */\nexport class ValidationError extends FoundryError {\n constructor(message: string, public readonly field?: string) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Asset not found in card or archive\n */\nexport class AssetNotFoundError extends FoundryError {\n constructor(public readonly uri: string) {\n super(`Asset not found: ${uri}`, 'ASSET_NOT_FOUND');\n this.name = 'AssetNotFoundError';\n }\n}\n\n/**\n * Format not supported for operation\n */\nexport class FormatNotSupportedError extends FoundryError {\n constructor(public readonly format: string, operation?: string) {\n const msg = operation\n ? `Format '${format}' not supported for ${operation}`\n : `Format not supported: ${format}`;\n super(msg, 'FORMAT_NOT_SUPPORTED');\n this.name = 'FormatNotSupportedError';\n }\n}\n\n/**\n * File size exceeds limits\n */\nexport class SizeLimitError extends FoundryError {\n constructor(\n public readonly actualSize: number,\n public readonly maxSize: number,\n context?: string\n ) {\n const actualMB = (actualSize / 1024 / 1024).toFixed(2);\n const maxMB = (maxSize / 1024 / 1024).toFixed(2);\n const msg = context\n ? `${context}: Size ${actualMB}MB exceeds limit ${maxMB}MB`\n : `Size ${actualMB}MB exceeds limit ${maxMB}MB`;\n super(msg, 'SIZE_LIMIT_EXCEEDED');\n this.name = 'SizeLimitError';\n }\n}\n\n/**\n * Path traversal or unsafe path detected\n */\nexport class PathTraversalError extends FoundryError {\n constructor(public readonly path: string) {\n super(`Unsafe path detected: ${path}`, 'PATH_TRAVERSAL');\n this.name = 'PathTraversalError';\n }\n}\n\n/**\n * Export operation would lose data\n */\nexport class DataLossError extends FoundryError {\n constructor(\n public readonly lostFields: string[],\n public readonly targetFormat: string\n ) {\n const fields = lostFields.slice(0, 3).join(', ');\n const more = lostFields.length > 3 ? ` and ${lostFields.length - 3} more` : '';\n super(\n `Export to ${targetFormat} would lose: ${fields}${more}`,\n 'DATA_LOSS'\n );\n this.name = 'DataLossError';\n }\n}\n\n/**\n * Check if an error is a FoundryError\n *\n * Uses Symbol.for() marker instead of instanceof to handle dual ESM/CJS package loading.\n * In dual-package environments, instanceof can fail if the error comes from a different\n * module instance (e.g., ESM vs CJS version of the same package). Symbol.for() creates\n * a global symbol shared across all module instances.\n */\nexport function isFoundryError(error: unknown): error is FoundryError {\n return (\n error instanceof Error &&\n FOUNDRY_ERROR_MARKER in error &&\n (error as Record<symbol, unknown>)[FOUNDRY_ERROR_MARKER] === true\n );\n}\n\n/**\n * Wrap unknown errors in a FoundryError\n */\nexport function wrapError(error: unknown, context?: string): FoundryError {\n if (isFoundryError(error)) {\n return error;\n }\n\n const message = error instanceof Error\n ? error.message\n : String(error);\n\n return new FoundryError(\n context ? `${context}: ${message}` : message,\n 'UNKNOWN_ERROR'\n );\n}\n","/**\n * Data URL Utilities\n *\n * Convert between Uint8Array buffers and data URLs.\n * Handles large buffers (>10MB) without stack overflow by processing in chunks.\n */\n\nimport type { BinaryData } from './binary.js';\nimport { encodeChunked as base64Encode, decode as base64Decode } from './base64.js';\nimport { ValidationError } from './errors.js';\n\n/**\n * Convert Uint8Array to data URL.\n * Handles large buffers (>10MB) without stack overflow by processing in chunks.\n *\n * @param buffer - Binary data to encode\n * @param mimeType - MIME type for the data URL (e.g., 'image/png', 'application/octet-stream')\n * @returns Data URL string\n *\n * @example\n * ```typescript\n * const png = new Uint8Array([...]);\n * const dataUrl = toDataURL(png, 'image/png');\n * // => \"data:image/png;base64,iVBORw0KGgo...\"\n * ```\n */\nexport function toDataURL(buffer: BinaryData, mimeType: string): string {\n // Use chunked encoding to handle large buffers without stack overflow\n const base64 = base64Encode(buffer);\n return `data:${mimeType};base64,${base64}`;\n}\n\n/**\n * Parse a data URL back to buffer and MIME type.\n * Validates the data URL format before parsing.\n *\n * @param dataUrl - Data URL string to parse\n * @returns Object containing the decoded buffer and MIME type\n * @throws Error if the data URL format is invalid\n *\n * @example\n * ```typescript\n * const { buffer, mimeType } = fromDataURL('data:image/png;base64,iVBORw0KGgo...');\n * // buffer: Uint8Array\n * // mimeType: 'image/png'\n * ```\n */\nexport function fromDataURL(dataUrl: string): { buffer: Uint8Array; mimeType: string } {\n // Validate data URL format\n if (!dataUrl.startsWith('data:')) {\n throw new ValidationError('Invalid data URL: must start with \"data:\"', 'dataUrl');\n }\n\n const commaIndex = dataUrl.indexOf(',');\n if (commaIndex === -1) {\n throw new ValidationError('Invalid data URL: missing comma separator', 'dataUrl');\n }\n\n const header = dataUrl.slice(5, commaIndex); // Skip 'data:'\n const data = dataUrl.slice(commaIndex + 1);\n\n // Parse header: [<mediatype>][;base64]\n let mimeType = 'text/plain';\n let isBase64 = false;\n\n const parts = header.split(';');\n for (const part of parts) {\n if (part === 'base64') {\n isBase64 = true;\n } else if (part && !part.includes('=')) {\n // MIME type (not a parameter like charset=utf-8)\n mimeType = part;\n }\n }\n\n if (!isBase64) {\n // URL-encoded text data\n throw new ValidationError('Non-base64 data URLs are not supported', 'dataUrl');\n }\n\n const buffer = base64Decode(data);\n return { buffer, mimeType };\n}\n\n/**\n * Check if a string is a valid data URL\n *\n * @param str - String to check\n * @returns true if the string is a valid data URL format\n */\nexport function isDataURL(str: string): boolean {\n if (!str.startsWith('data:')) return false;\n const commaIndex = str.indexOf(',');\n if (commaIndex === -1) return false;\n const header = str.slice(5, commaIndex);\n return header.includes('base64');\n}\n","/**\n * URI Utilities\n *\n * Handles different asset URI schemes used in character cards.\n * Supports: embeded://, embedded://, ccdefault:, https://, http://,\n * data:, file://, __asset:, asset:, chara-ext-asset_\n */\n\nexport type URIScheme =\n | 'embeded' // embeded:// (CharX standard, note intentional typo)\n | 'ccdefault' // ccdefault:\n | 'https' // https://\n | 'http' // http://\n | 'data' // data:mime;base64,...\n | 'file' // file://\n | 'internal' // Internal asset ID (UUID/string)\n | 'pngchunk' // PNG chunk reference (__asset:, asset:, chara-ext-asset_)\n | 'unknown';\n\nexport interface ParsedURI {\n scheme: URIScheme;\n originalUri: string;\n normalizedUri: string; // Normalized form of the URI\n path?: string; // For embeded://, file://\n url?: string; // For http://, https://\n data?: string; // For data: URIs\n mimeType?: string; // For data: URIs\n encoding?: string; // For data: URIs (e.g., base64)\n chunkKey?: string; // For pngchunk - the key/index to look up\n chunkCandidates?: string[]; // For pngchunk - all possible chunk keys to search\n}\n\n/**\n * Normalize a URI to its canonical form\n * Handles common typos and variant formats\n */\nexport function normalizeURI(uri: string): string {\n const trimmed = uri.trim();\n\n // Fix embedded:// -> embeded:// (common typo, CharX spec uses single 'd')\n if (trimmed.startsWith('embedded://')) {\n return 'embeded://' + trimmed.substring('embedded://'.length);\n }\n\n // Normalize PNG chunk references to pngchunk: scheme\n if (trimmed.startsWith('__asset:')) {\n const id = trimmed.substring('__asset:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('asset:')) {\n const id = trimmed.substring('asset:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('chara-ext-asset_:')) {\n const id = trimmed.substring('chara-ext-asset_:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('chara-ext-asset_')) {\n const id = trimmed.substring('chara-ext-asset_'.length);\n return `pngchunk:${id}`;\n }\n\n return trimmed;\n}\n\n/**\n * Parse a URI and determine its scheme and components\n */\nexport function parseURI(uri: string): ParsedURI {\n const trimmed = uri.trim();\n const normalized = normalizeURI(trimmed);\n\n // PNG chunk references (__asset:, asset:, chara-ext-asset_, pngchunk:)\n if (\n trimmed.startsWith('__asset:') ||\n trimmed.startsWith('asset:') ||\n trimmed.startsWith('chara-ext-asset_') ||\n trimmed.startsWith('pngchunk:')\n ) {\n let assetId: string;\n if (trimmed.startsWith('__asset:')) {\n assetId = trimmed.substring('__asset:'.length);\n } else if (trimmed.startsWith('asset:')) {\n assetId = trimmed.substring('asset:'.length);\n } else if (trimmed.startsWith('chara-ext-asset_:')) {\n assetId = trimmed.substring('chara-ext-asset_:'.length);\n } else if (trimmed.startsWith('pngchunk:')) {\n assetId = trimmed.substring('pngchunk:'.length);\n } else {\n assetId = trimmed.substring('chara-ext-asset_'.length);\n }\n\n // Generate all possible chunk key variations for lookup\n const candidates = [\n assetId, // \"0\" or \"filename.png\"\n trimmed, // Original URI\n `asset:${assetId}`, // \"asset:0\"\n `__asset:${assetId}`, // \"__asset:0\"\n `__asset_${assetId}`, // \"__asset_0\"\n `chara-ext-asset_${assetId}`, // \"chara-ext-asset_0\"\n `chara-ext-asset_:${assetId}`, // \"chara-ext-asset_:0\"\n `pngchunk:${assetId}`, // \"pngchunk:0\"\n ];\n\n return {\n scheme: 'pngchunk',\n originalUri: uri,\n normalizedUri: normalized,\n chunkKey: assetId,\n chunkCandidates: candidates,\n };\n }\n\n // ccdefault: - use default asset\n if (trimmed === 'ccdefault:' || trimmed.startsWith('ccdefault:')) {\n return {\n scheme: 'ccdefault',\n originalUri: uri,\n normalizedUri: normalized,\n };\n }\n\n // embeded:// or embedded:// (normalize typo)\n if (trimmed.startsWith('embeded://') || trimmed.startsWith('embedded://')) {\n const path = trimmed.startsWith('embeded://')\n ? trimmed.substring('embeded://'.length)\n : trimmed.substring('embedded://'.length);\n return {\n scheme: 'embeded',\n originalUri: uri,\n normalizedUri: normalized,\n path,\n };\n }\n\n // https://\n if (trimmed.startsWith('https://')) {\n return {\n scheme: 'https',\n originalUri: uri,\n normalizedUri: normalized,\n url: trimmed,\n };\n }\n\n // http://\n if (trimmed.startsWith('http://')) {\n return {\n scheme: 'http',\n originalUri: uri,\n normalizedUri: normalized,\n url: trimmed,\n };\n }\n\n // data: URIs\n if (trimmed.startsWith('data:')) {\n const parsed = parseDataURI(trimmed);\n return {\n scheme: 'data',\n originalUri: uri,\n normalizedUri: normalized,\n ...parsed,\n };\n }\n\n // file://\n if (trimmed.startsWith('file://')) {\n const path = trimmed.substring('file://'.length);\n return {\n scheme: 'file',\n originalUri: uri,\n normalizedUri: normalized,\n path,\n };\n }\n\n // Internal asset ID (alphanumeric/UUID format)\n if (/^[a-zA-Z0-9_-]+$/.test(trimmed)) {\n return {\n scheme: 'internal',\n originalUri: uri,\n normalizedUri: normalized,\n path: trimmed,\n };\n }\n\n // Unknown scheme\n return {\n scheme: 'unknown',\n originalUri: uri,\n normalizedUri: normalized,\n };\n}\n\n/**\n * Parse a data URI into its components\n * Format: data:[<mediatype>][;base64],<data>\n */\nfunction parseDataURI(uri: string): { mimeType?: string; encoding?: string; data?: string } {\n const match = uri.match(/^data:([^;,]+)?(;base64)?,(.*)$/);\n\n if (!match) {\n return {};\n }\n\n return {\n mimeType: match[1] || 'text/plain',\n encoding: match[2] ? 'base64' : undefined,\n data: match[3],\n };\n}\n\n/**\n * Check if extension is an image format\n */\nexport function isImageExt(ext: string): boolean {\n const imageExts = ['png', 'jpg', 'jpeg', 'webp', 'gif', 'avif', 'bmp', 'svg'];\n return imageExts.includes(ext.toLowerCase());\n}\n\n/**\n * Check if extension is an audio format\n */\nexport function isAudioExt(ext: string): boolean {\n const audioExts = ['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac'];\n return audioExts.includes(ext.toLowerCase());\n}\n\n/**\n * Check if extension is a video format\n */\nexport function isVideoExt(ext: string): boolean {\n const videoExts = ['mp4', 'webm', 'avi', 'mov', 'mkv'];\n return videoExts.includes(ext.toLowerCase());\n}\n\n/** Safe MIME types for data: URIs that can be used in href/src */\nconst SAFE_DATA_URI_MIME_TYPES = new Set([\n // Images (safe for img src)\n 'image/png',\n 'image/jpeg',\n 'image/gif',\n 'image/webp',\n 'image/avif',\n 'image/bmp',\n 'image/x-icon',\n // Audio (safe for audio src)\n 'audio/mpeg',\n 'audio/wav',\n 'audio/ogg',\n 'audio/flac',\n 'audio/mp4',\n 'audio/aac',\n // Video (safe for video src)\n 'video/mp4',\n 'video/webm',\n // Text/data (generally safe)\n 'text/plain',\n 'application/json',\n 'application/octet-stream',\n]);\n\n/** Potentially dangerous MIME types that should NOT be used in href/src */\nconst DANGEROUS_DATA_URI_MIME_TYPES = new Set([\n // Executable/script content\n 'text/html',\n 'text/javascript',\n 'application/javascript',\n 'application/x-javascript',\n 'text/css',\n 'image/svg+xml', // SVG can contain scripts\n 'application/xhtml+xml',\n 'application/xml',\n]);\n\n/**\n * Options for URI safety validation\n */\nexport interface URISafetyOptions {\n /** Allow http:// URIs (default: false) */\n allowHttp?: boolean;\n /** Allow file:// URIs (default: false) */\n allowFile?: boolean;\n /**\n * Allowed MIME types for data: URIs (default: all safe types).\n * Set to empty array to reject all data: URIs.\n * Set to undefined to use default safe list.\n */\n allowedDataMimes?: string[];\n}\n\n/**\n * Result of URI safety check with detailed information\n */\nexport interface URISafetyResult {\n /** Whether the URI is safe to use */\n safe: boolean;\n /** Reason if unsafe */\n reason?: string;\n /** Detected scheme */\n scheme: URIScheme;\n /** MIME type for data: URIs */\n mimeType?: string;\n}\n\n/**\n * Validate if a URI is safe to use (detailed version)\n *\n * @param uri - URI to validate\n * @param options - Safety options\n * @returns Detailed safety result\n */\nexport function checkURISafety(uri: string, options: URISafetyOptions = {}): URISafetyResult {\n const parsed = parseURI(uri);\n\n switch (parsed.scheme) {\n case 'embeded':\n case 'ccdefault':\n case 'internal':\n case 'https':\n case 'pngchunk':\n return { safe: true, scheme: parsed.scheme };\n\n case 'data': {\n const mimeType = parsed.mimeType || 'text/plain';\n\n // Check for explicitly dangerous MIME types\n if (DANGEROUS_DATA_URI_MIME_TYPES.has(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Data URI with potentially dangerous MIME type: ${mimeType}`,\n };\n }\n\n // If custom allowed list is provided, check against it\n if (options.allowedDataMimes !== undefined) {\n if (options.allowedDataMimes.length === 0) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: 'Data URIs are not allowed',\n };\n }\n if (!options.allowedDataMimes.includes(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Data URI MIME type not in allowed list: ${mimeType}`,\n };\n }\n }\n\n // Otherwise use default safe list\n if (!SAFE_DATA_URI_MIME_TYPES.has(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Unknown data URI MIME type: ${mimeType}`,\n };\n }\n\n return { safe: true, scheme: parsed.scheme, mimeType };\n }\n\n case 'http':\n if (options.allowHttp === true) {\n return { safe: true, scheme: parsed.scheme };\n }\n return { safe: false, scheme: parsed.scheme, reason: 'HTTP URIs are not allowed' };\n\n case 'file':\n if (options.allowFile === true) {\n return { safe: true, scheme: parsed.scheme };\n }\n return { safe: false, scheme: parsed.scheme, reason: 'File URIs are not allowed' };\n\n case 'unknown':\n default:\n return { safe: false, scheme: parsed.scheme, reason: 'Unknown URI scheme' };\n }\n}\n\n/**\n * Validate if a URI is safe to use (simple boolean version for backwards compatibility)\n *\n * @deprecated Use checkURISafety() for detailed safety information\n */\nexport function isURISafe(uri: string, options: { allowHttp?: boolean; allowFile?: boolean } = {}): boolean {\n return checkURISafety(uri, options).safe;\n}\n\n/**\n * Extract file extension from URI\n */\nexport function getExtensionFromURI(uri: string): string {\n const parsed = parseURI(uri);\n\n if (parsed.path) {\n const parts = parsed.path.split('.');\n if (parts.length > 1) {\n return parts[parts.length - 1]!.toLowerCase();\n }\n }\n\n if (parsed.url) {\n const urlParts = parsed.url.split('?')[0]!.split('.');\n if (urlParts.length > 1) {\n return urlParts[urlParts.length - 1]!.toLowerCase();\n }\n }\n\n if (parsed.mimeType) {\n return getExtFromMimeType(parsed.mimeType);\n }\n\n return 'unknown';\n}\n\n/**\n * Get MIME type from file extension\n */\nexport function getMimeTypeFromExt(ext: string): string {\n const extToMime: Record<string, string> = {\n // Images\n 'png': 'image/png',\n 'jpg': 'image/jpeg',\n 'jpeg': 'image/jpeg',\n 'webp': 'image/webp',\n 'gif': 'image/gif',\n 'avif': 'image/avif',\n 'svg': 'image/svg+xml',\n 'bmp': 'image/bmp',\n 'ico': 'image/x-icon',\n\n // Audio\n 'mp3': 'audio/mpeg',\n 'wav': 'audio/wav',\n 'ogg': 'audio/ogg',\n 'flac': 'audio/flac',\n 'm4a': 'audio/mp4',\n 'aac': 'audio/aac',\n\n // Video\n 'mp4': 'video/mp4',\n 'webm': 'video/webm',\n 'avi': 'video/x-msvideo',\n 'mov': 'video/quicktime',\n 'mkv': 'video/x-matroska',\n\n // Text/Data\n 'json': 'application/json',\n 'txt': 'text/plain',\n 'html': 'text/html',\n 'css': 'text/css',\n 'js': 'application/javascript',\n };\n\n return extToMime[ext.toLowerCase()] || 'application/octet-stream';\n}\n\n/**\n * Get file extension from MIME type\n */\nexport function getExtFromMimeType(mimeType: string): string {\n const mimeToExt: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n 'image/avif': 'avif',\n 'image/svg+xml': 'svg',\n 'image/bmp': 'bmp',\n 'image/x-icon': 'ico',\n 'audio/mpeg': 'mp3',\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/flac': 'flac',\n 'audio/mp4': 'm4a',\n 'audio/aac': 'aac',\n 'video/mp4': 'mp4',\n 'video/webm': 'webm',\n 'video/x-msvideo': 'avi',\n 'video/quicktime': 'mov',\n 'video/x-matroska': 'mkv',\n 'application/json': 'json',\n 'text/plain': 'txt',\n 'text/html': 'html',\n 'text/css': 'css',\n 'application/javascript': 'js',\n };\n\n return mimeToExt[mimeType] || 'bin';\n}\n\n/**\n * Build a data URI from binary data and MIME type\n */\nexport function buildDataURI(data: string, mimeType: string, isBase64 = true): string {\n if (isBase64) {\n return `data:${mimeType};base64,${data}`;\n }\n return `data:${mimeType},${encodeURIComponent(data)}`;\n}\n","/**\n * Image Analysis Utilities\n *\n * Detect properties of image files from binary data.\n */\n\nimport {\n type BinaryData,\n readUInt32BE,\n indexOf,\n fromLatin1,\n slice,\n toLatin1\n} from './binary.js';\n\n/**\n * Check if an image buffer contains animation data.\n * Supports: APNG, WebP (Animated), GIF\n */\nexport function isAnimatedImage(data: BinaryData, mimeType?: string): boolean {\n // 1. WebP Detection\n // RIFF .... WEBP\n if (\n data.length > 12 &&\n data[0] === 0x52 && data[1] === 0x49 && data[2] === 0x46 && data[3] === 0x46 && // RIFF\n data[8] === 0x57 && data[9] === 0x45 && data[10] === 0x42 && data[11] === 0x50 // WEBP\n ) {\n // Check for VP8X chunk\n // VP8X chunk header: 'VP8X' (bytes 12-15)\n if (\n data[12] === 0x56 && data[13] === 0x50 && data[14] === 0x38 && data[15] === 0x58\n ) {\n // Flags byte is at offset 20 (16 + 4 bytes chunk size)\n // Animation bit is bit 1 (0x02)\n const flags = data[20];\n return (flags! & 0x02) !== 0;\n }\n return false;\n }\n\n // 2. PNG/APNG Detection\n // Signature: 89 50 4E 47 0D 0A 1A 0A\n if (\n data.length > 8 &&\n data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4E && data[3] === 0x47\n ) {\n // Search for 'acTL' chunk (Animation Control)\n // It must appear before IDAT.\n // Simple search: indexOf('acTL')\n // Note: theoretically 'acTL' string could appear in other data, but highly unlikely in valid PNG structure before IDAT\n // We can iterate chunks to be safe, but indexOf is faster for a quick check\n const actlSig = fromLatin1('acTL');\n const idatSig = fromLatin1('IDAT');\n \n const actlIndex = indexOf(data, actlSig);\n if (actlIndex === -1) return false;\n\n const idatIndex = indexOf(data, idatSig);\n // If acTL exists and is before the first IDAT (or IDAT not found yet), it's APNG\n return idatIndex === -1 || actlIndex < idatIndex;\n }\n\n // 3. GIF Detection\n // Signature: GIF87a or GIF89a\n if (\n data.length > 6 &&\n data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46 // GIF\n ) {\n // Check for NETSCAPE2.0 extension (looping animation)\n // This is a heuristic. Static GIFs are rare in this domain but possible.\n // Full frame counting is expensive. Presence of NETSCAPE block is a strong indicator.\n const netscape = fromLatin1('NETSCAPE2.0');\n return indexOf(data, netscape) !== -1;\n }\n\n return false;\n}\n","/**\n * UUID Generation Utilities\n *\n * Provides crypto-grade UUID v4 generation that works in Node.js,\n * browsers (secure contexts), and falls back gracefully.\n */\n\n/**\n * Format 16 random bytes as a UUID v4 string\n */\nfunction formatUUID(bytes: Uint8Array): string {\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;\n}\n\n/**\n * Fallback UUID generation using Math.random()\n * Only used when crypto APIs are unavailable (rare)\n */\nfunction mathRandomUUID(): string {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Generate a cryptographically secure UUID v4.\n *\n * Uses crypto.randomUUID() when available (Node.js 19+, modern browsers).\n * Falls back to crypto.getRandomValues() if randomUUID is unavailable.\n * Last resort uses Math.random() (non-secure, emits warning in dev).\n *\n * @returns A valid RFC 4122 UUID v4 string\n *\n * @example\n * ```typescript\n * const id = generateUUID();\n * // => \"550e8400-e29b-41d4-a716-446655440000\"\n * ```\n */\nexport function generateUUID(): string {\n // Node.js 19+ or browser with secure context\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n\n // Fallback using crypto.getRandomValues (older Node/browsers)\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n // Set version (4) and variant (RFC 4122)\n bytes[6] = (bytes[6]! & 0x0f) | 0x40; // Version 4\n bytes[8] = (bytes[8]! & 0x3f) | 0x80; // Variant 1\n return formatUUID(bytes);\n }\n\n // Last resort - non-secure fallback\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') {\n console.warn('[character-foundry/core] generateUUID: Using insecure Math.random() fallback');\n }\n return mathRandomUUID();\n}\n\n/**\n * Validate if a string is a valid UUID v4\n *\n * @param uuid - String to validate\n * @returns true if valid UUID v4 format\n */\nexport function isValidUUID(uuid: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);\n}\n"],"mappings":"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","names":["isBase64"]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../../core/src/binary.ts","../../core/src/base64.ts","../../core/src/errors.ts","../../core/src/data-url.ts","../../core/src/uri.ts","../../core/src/image.ts","../../core/src/uuid.ts"],"sourcesContent":["export * from '@character-foundry/core';\n","/**\n * Binary Data Utilities\n *\n * Universal binary data operations using Uint8Array.\n * Works in both Node.js and browser environments.\n */\n\n/**\n * Universal binary data type (works in both environments)\n */\nexport type BinaryData = Uint8Array;\n\n/**\n * Read a 32-bit big-endian unsigned integer\n */\nexport function readUInt32BE(data: BinaryData, offset: number): number {\n return (\n (data[offset]! << 24) |\n (data[offset + 1]! << 16) |\n (data[offset + 2]! << 8) |\n data[offset + 3]!\n ) >>> 0;\n}\n\n/**\n * Write a 32-bit big-endian unsigned integer\n */\nexport function writeUInt32BE(data: BinaryData, value: number, offset: number): void {\n data[offset] = (value >>> 24) & 0xff;\n data[offset + 1] = (value >>> 16) & 0xff;\n data[offset + 2] = (value >>> 8) & 0xff;\n data[offset + 3] = value & 0xff;\n}\n\n/**\n * Read a 16-bit big-endian unsigned integer\n */\nexport function readUInt16BE(data: BinaryData, offset: number): number {\n return ((data[offset]! << 8) | data[offset + 1]!) >>> 0;\n}\n\n/**\n * Write a 16-bit big-endian unsigned integer\n */\nexport function writeUInt16BE(data: BinaryData, value: number, offset: number): void {\n data[offset] = (value >>> 8) & 0xff;\n data[offset + 1] = value & 0xff;\n}\n\n/**\n * Find a byte sequence in binary data\n */\nexport function indexOf(data: BinaryData, search: BinaryData, fromIndex = 0): number {\n outer: for (let i = fromIndex; i <= data.length - search.length; i++) {\n for (let j = 0; j < search.length; j++) {\n if (data[i + j] !== search[j]) continue outer;\n }\n return i;\n }\n return -1;\n}\n\n/**\n * Concatenate multiple binary arrays\n */\nexport function concat(...arrays: BinaryData[]): BinaryData {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n\n/**\n * Slice binary data (returns a view, not a copy)\n */\nexport function slice(data: BinaryData, start: number, end?: number): BinaryData {\n return data.subarray(start, end);\n}\n\n/**\n * Copy a portion of binary data (returns a new array)\n */\nexport function copy(data: BinaryData, start: number, end?: number): BinaryData {\n return data.slice(start, end);\n}\n\n/**\n * Convert string to binary (UTF-8)\n */\nexport function fromString(str: string): BinaryData {\n return new TextEncoder().encode(str);\n}\n\n/**\n * Convert binary to string (UTF-8)\n */\nexport function toString(data: BinaryData): string {\n return new TextDecoder().decode(data);\n}\n\n/**\n * Convert string to binary (Latin1 - for PNG keywords and similar)\n */\nexport function fromLatin1(str: string): BinaryData {\n const result = new Uint8Array(str.length);\n for (let i = 0; i < str.length; i++) {\n result[i] = str.charCodeAt(i) & 0xff;\n }\n return result;\n}\n\n/**\n * Convert binary to string (Latin1)\n */\nexport function toLatin1(data: BinaryData): string {\n let result = '';\n for (let i = 0; i < data.length; i++) {\n result += String.fromCharCode(data[i]!);\n }\n return result;\n}\n\n/**\n * Compare two binary arrays for equality\n */\nexport function equals(a: BinaryData, b: BinaryData): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Create a new Uint8Array filled with zeros\n */\nexport function alloc(size: number): BinaryData {\n return new Uint8Array(size);\n}\n\n/**\n * Create a Uint8Array from an array of numbers\n */\nexport function from(data: number[] | ArrayBuffer | BinaryData): BinaryData {\n if (data instanceof Uint8Array) {\n return data;\n }\n if (data instanceof ArrayBuffer) {\n return new Uint8Array(data);\n }\n return new Uint8Array(data);\n}\n\n/**\n * Check if value is a Uint8Array\n */\nexport function isBinaryData(value: unknown): value is BinaryData {\n return value instanceof Uint8Array;\n}\n\n/**\n * Convert Node.js Buffer to Uint8Array (no-op if already Uint8Array)\n * This provides compatibility when interfacing with Node.js code\n */\nexport function toUint8Array(data: BinaryData | Buffer): BinaryData {\n if (data instanceof Uint8Array) {\n // Buffer extends Uint8Array, but we want a plain Uint8Array\n // This ensures we get a proper Uint8Array in all cases\n if (Object.getPrototypeOf(data).constructor.name === 'Buffer') {\n return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);\n }\n return data;\n }\n return new Uint8Array(data);\n}\n\n/**\n * Convert binary data to hex string\n */\nexport function toHex(data: BinaryData): string {\n return Array.from(data)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Convert hex string to binary data\n */\nexport function fromHex(hex: string): BinaryData {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.substr(i * 2, 2), 16);\n }\n return bytes;\n}\n","/**\n * Universal Base64 Encoding/Decoding\n *\n * Works in both Node.js and browser environments.\n */\n\nimport type { BinaryData } from './binary.js';\n\n/**\n * Check if we're in a Node.js environment\n */\nconst isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n/**\n * Threshold for switching to chunked encoding in browsers (1MB)\n * Below this, simple string concatenation is fast enough.\n * Above this, quadratic string growth becomes a problem.\n */\nconst LARGE_BUFFER_THRESHOLD = 1024 * 1024;\n\n/**\n * Encode binary data to base64 string\n *\n * PERFORMANCE: For large buffers (>1MB) in browsers, this automatically\n * uses the chunked implementation to avoid quadratic string concatenation.\n */\nexport function encode(data: BinaryData): string {\n if (isNode) {\n // Node.js: Buffer handles large data efficiently\n return Buffer.from(data).toString('base64');\n }\n\n // Browser: use chunked encoding for large buffers to avoid O(n²) string growth\n if (data.length > LARGE_BUFFER_THRESHOLD) {\n return encodeChunked(data);\n }\n\n // Small buffers: simple approach is fast enough\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]!);\n }\n return btoa(binary);\n}\n\n/**\n * Decode base64 string to binary data\n */\nexport function decode(base64: string): BinaryData {\n if (isNode) {\n // Node.js: use Buffer\n return new Uint8Array(Buffer.from(base64, 'base64'));\n }\n\n // Browser: use atob\n const binary = atob(base64);\n const result = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n result[i] = binary.charCodeAt(i);\n }\n return result;\n}\n\n/**\n * Check if a string is valid base64\n */\nexport function isBase64(str: string): boolean {\n if (str.length === 0) return false;\n // Base64 regex: only valid base64 characters, length multiple of 4 (with padding)\n const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;\n return base64Regex.test(str) && str.length % 4 === 0;\n}\n\n/**\n * Encode binary data to URL-safe base64 string\n * Replaces + with -, / with _, and removes padding\n */\nexport function encodeUrlSafe(data: BinaryData): string {\n return encode(data)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Decode URL-safe base64 string to binary data\n */\nexport function decodeUrlSafe(base64: string): BinaryData {\n // Add back padding if needed\n let padded = base64\n .replace(/-/g, '+')\n .replace(/_/g, '/');\n\n while (padded.length % 4 !== 0) {\n padded += '=';\n }\n\n return decode(padded);\n}\n\n/**\n * Chunk size for encoding large buffers (64KB)\n * Prevents stack overflow when using String.fromCharCode with spread operator\n */\nconst ENCODE_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Encode binary data to base64 string with chunking for large buffers.\n * Handles buffers >10MB without stack overflow.\n *\n * @param data - Binary data to encode\n * @returns Base64 encoded string\n *\n * @example\n * ```typescript\n * const largeBuffer = new Uint8Array(20 * 1024 * 1024); // 20MB\n * const base64 = encodeChunked(largeBuffer); // No stack overflow\n * ```\n */\nexport function encodeChunked(data: BinaryData): string {\n if (isNode) {\n // Node.js: Buffer handles large data efficiently\n return Buffer.from(data).toString('base64');\n }\n\n // Browser: process in chunks to avoid stack overflow\n const chunks: string[] = [];\n\n for (let i = 0; i < data.length; i += ENCODE_CHUNK_SIZE) {\n const chunk = data.subarray(i, Math.min(i + ENCODE_CHUNK_SIZE, data.length));\n let binary = '';\n for (let j = 0; j < chunk.length; j++) {\n binary += String.fromCharCode(chunk[j]!);\n }\n chunks.push(binary);\n }\n\n return btoa(chunks.join(''));\n}\n","/**\n * Error Classes\n *\n * Specific error types for character card operations.\n * All errors extend FoundryError for consistent handling.\n */\n\n/** Symbol to identify FoundryError instances across ESM/CJS boundaries */\nconst FOUNDRY_ERROR_MARKER = Symbol.for('@character-foundry/core:FoundryError');\n\n/**\n * Base error class for all Character Foundry errors\n */\nexport class FoundryError extends Error {\n /** @internal Marker for cross-module identification */\n readonly [FOUNDRY_ERROR_MARKER] = true;\n\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = 'FoundryError';\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error during card parsing\n */\nexport class ParseError extends FoundryError {\n constructor(message: string, public readonly format?: string) {\n super(message, 'PARSE_ERROR');\n this.name = 'ParseError';\n }\n}\n\n/**\n * Error during card validation\n */\nexport class ValidationError extends FoundryError {\n constructor(message: string, public readonly field?: string) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Asset not found in card or archive\n */\nexport class AssetNotFoundError extends FoundryError {\n constructor(public readonly uri: string) {\n super(`Asset not found: ${uri}`, 'ASSET_NOT_FOUND');\n this.name = 'AssetNotFoundError';\n }\n}\n\n/**\n * Format not supported for operation\n */\nexport class FormatNotSupportedError extends FoundryError {\n constructor(public readonly format: string, operation?: string) {\n const msg = operation\n ? `Format '${format}' not supported for ${operation}`\n : `Format not supported: ${format}`;\n super(msg, 'FORMAT_NOT_SUPPORTED');\n this.name = 'FormatNotSupportedError';\n }\n}\n\n/**\n * File size exceeds limits\n */\nexport class SizeLimitError extends FoundryError {\n constructor(\n public readonly actualSize: number,\n public readonly maxSize: number,\n context?: string\n ) {\n const actualMB = (actualSize / 1024 / 1024).toFixed(2);\n const maxMB = (maxSize / 1024 / 1024).toFixed(2);\n const msg = context\n ? `${context}: Size ${actualMB}MB exceeds limit ${maxMB}MB`\n : `Size ${actualMB}MB exceeds limit ${maxMB}MB`;\n super(msg, 'SIZE_LIMIT_EXCEEDED');\n this.name = 'SizeLimitError';\n }\n}\n\n/**\n * Path traversal or unsafe path detected\n */\nexport class PathTraversalError extends FoundryError {\n constructor(public readonly path: string) {\n super(`Unsafe path detected: ${path}`, 'PATH_TRAVERSAL');\n this.name = 'PathTraversalError';\n }\n}\n\n/**\n * Export operation would lose data\n */\nexport class DataLossError extends FoundryError {\n constructor(\n public readonly lostFields: string[],\n public readonly targetFormat: string\n ) {\n const fields = lostFields.slice(0, 3).join(', ');\n const more = lostFields.length > 3 ? ` and ${lostFields.length - 3} more` : '';\n super(\n `Export to ${targetFormat} would lose: ${fields}${more}`,\n 'DATA_LOSS'\n );\n this.name = 'DataLossError';\n }\n}\n\n/**\n * Check if an error is a FoundryError\n *\n * Uses Symbol.for() marker instead of instanceof to handle dual ESM/CJS package loading.\n * In dual-package environments, instanceof can fail if the error comes from a different\n * module instance (e.g., ESM vs CJS version of the same package). Symbol.for() creates\n * a global symbol shared across all module instances.\n */\nexport function isFoundryError(error: unknown): error is FoundryError {\n return (\n error instanceof Error &&\n FOUNDRY_ERROR_MARKER in error &&\n (error as Record<symbol, unknown>)[FOUNDRY_ERROR_MARKER] === true\n );\n}\n\n/**\n * Wrap unknown errors in a FoundryError\n */\nexport function wrapError(error: unknown, context?: string): FoundryError {\n if (isFoundryError(error)) {\n return error;\n }\n\n const message = error instanceof Error\n ? error.message\n : String(error);\n\n return new FoundryError(\n context ? `${context}: ${message}` : message,\n 'UNKNOWN_ERROR'\n );\n}\n","/**\n * Data URL Utilities\n *\n * Convert between Uint8Array buffers and data URLs.\n * Handles large buffers (>10MB) without stack overflow by processing in chunks.\n */\n\nimport type { BinaryData } from './binary.js';\nimport { encodeChunked as base64Encode, decode as base64Decode } from './base64.js';\nimport { ValidationError } from './errors.js';\n\n/**\n * Convert Uint8Array to data URL.\n * Handles large buffers (>10MB) without stack overflow by processing in chunks.\n *\n * @param buffer - Binary data to encode\n * @param mimeType - MIME type for the data URL (e.g., 'image/png', 'application/octet-stream')\n * @returns Data URL string\n *\n * @example\n * ```typescript\n * const png = new Uint8Array([...]);\n * const dataUrl = toDataURL(png, 'image/png');\n * // => \"data:image/png;base64,iVBORw0KGgo...\"\n * ```\n */\nexport function toDataURL(buffer: BinaryData, mimeType: string): string {\n // Use chunked encoding to handle large buffers without stack overflow\n const base64 = base64Encode(buffer);\n return `data:${mimeType};base64,${base64}`;\n}\n\n/**\n * Parse a data URL back to buffer and MIME type.\n * Validates the data URL format before parsing.\n *\n * @param dataUrl - Data URL string to parse\n * @returns Object containing the decoded buffer and MIME type\n * @throws Error if the data URL format is invalid\n *\n * @example\n * ```typescript\n * const { buffer, mimeType } = fromDataURL('data:image/png;base64,iVBORw0KGgo...');\n * // buffer: Uint8Array\n * // mimeType: 'image/png'\n * ```\n */\nexport function fromDataURL(dataUrl: string): { buffer: Uint8Array; mimeType: string } {\n // Validate data URL format\n if (!dataUrl.startsWith('data:')) {\n throw new ValidationError('Invalid data URL: must start with \"data:\"', 'dataUrl');\n }\n\n const commaIndex = dataUrl.indexOf(',');\n if (commaIndex === -1) {\n throw new ValidationError('Invalid data URL: missing comma separator', 'dataUrl');\n }\n\n const header = dataUrl.slice(5, commaIndex); // Skip 'data:'\n const data = dataUrl.slice(commaIndex + 1);\n\n // Parse header: [<mediatype>][;base64]\n let mimeType = 'text/plain';\n let isBase64 = false;\n\n const parts = header.split(';');\n for (const part of parts) {\n if (part === 'base64') {\n isBase64 = true;\n } else if (part && !part.includes('=')) {\n // MIME type (not a parameter like charset=utf-8)\n mimeType = part;\n }\n }\n\n if (!isBase64) {\n // URL-encoded text data\n throw new ValidationError('Non-base64 data URLs are not supported', 'dataUrl');\n }\n\n const buffer = base64Decode(data);\n return { buffer, mimeType };\n}\n\n/**\n * Check if a string is a valid data URL\n *\n * @param str - String to check\n * @returns true if the string is a valid data URL format\n */\nexport function isDataURL(str: string): boolean {\n if (!str.startsWith('data:')) return false;\n const commaIndex = str.indexOf(',');\n if (commaIndex === -1) return false;\n const header = str.slice(5, commaIndex);\n return header.includes('base64');\n}\n","/**\n * URI Utilities\n *\n * Handles different asset URI schemes used in character cards.\n * Supports: embeded://, embedded://, ccdefault:, https://, http://,\n * data:, file://, __asset:, asset:, chara-ext-asset_\n */\n\nexport type URIScheme =\n | 'embeded' // embeded:// (CharX standard, note intentional typo)\n | 'ccdefault' // ccdefault:\n | 'https' // https://\n | 'http' // http://\n | 'data' // data:mime;base64,...\n | 'file' // file://\n | 'internal' // Internal asset ID (UUID/string)\n | 'pngchunk' // PNG chunk reference (__asset:, asset:, chara-ext-asset_)\n | 'unknown';\n\nexport interface ParsedURI {\n scheme: URIScheme;\n originalUri: string;\n normalizedUri: string; // Normalized form of the URI\n path?: string; // For embeded://, file://\n url?: string; // For http://, https://\n data?: string; // For data: URIs\n mimeType?: string; // For data: URIs\n encoding?: string; // For data: URIs (e.g., base64)\n chunkKey?: string; // For pngchunk - the key/index to look up\n chunkCandidates?: string[]; // For pngchunk - all possible chunk keys to search\n}\n\n/**\n * Normalize a URI to its canonical form\n * Handles common typos and variant formats\n */\nexport function normalizeURI(uri: string): string {\n const trimmed = uri.trim();\n\n // Fix embedded:// -> embeded:// (common typo, CharX spec uses single 'd')\n if (trimmed.startsWith('embedded://')) {\n return 'embeded://' + trimmed.substring('embedded://'.length);\n }\n\n // Normalize PNG chunk references to pngchunk: scheme\n if (trimmed.startsWith('__asset:')) {\n const id = trimmed.substring('__asset:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('asset:')) {\n const id = trimmed.substring('asset:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('chara-ext-asset_:')) {\n const id = trimmed.substring('chara-ext-asset_:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('chara-ext-asset_')) {\n const id = trimmed.substring('chara-ext-asset_'.length);\n return `pngchunk:${id}`;\n }\n\n return trimmed;\n}\n\n/**\n * Parse a URI and determine its scheme and components\n */\nexport function parseURI(uri: string): ParsedURI {\n const trimmed = uri.trim();\n const normalized = normalizeURI(trimmed);\n\n // PNG chunk references (__asset:, asset:, chara-ext-asset_, pngchunk:)\n if (\n trimmed.startsWith('__asset:') ||\n trimmed.startsWith('asset:') ||\n trimmed.startsWith('chara-ext-asset_') ||\n trimmed.startsWith('pngchunk:')\n ) {\n let assetId: string;\n if (trimmed.startsWith('__asset:')) {\n assetId = trimmed.substring('__asset:'.length);\n } else if (trimmed.startsWith('asset:')) {\n assetId = trimmed.substring('asset:'.length);\n } else if (trimmed.startsWith('chara-ext-asset_:')) {\n assetId = trimmed.substring('chara-ext-asset_:'.length);\n } else if (trimmed.startsWith('pngchunk:')) {\n assetId = trimmed.substring('pngchunk:'.length);\n } else {\n assetId = trimmed.substring('chara-ext-asset_'.length);\n }\n\n // Generate all possible chunk key variations for lookup\n const candidates = [\n assetId, // \"0\" or \"filename.png\"\n trimmed, // Original URI\n `asset:${assetId}`, // \"asset:0\"\n `__asset:${assetId}`, // \"__asset:0\"\n `__asset_${assetId}`, // \"__asset_0\"\n `chara-ext-asset_${assetId}`, // \"chara-ext-asset_0\"\n `chara-ext-asset_:${assetId}`, // \"chara-ext-asset_:0\"\n `pngchunk:${assetId}`, // \"pngchunk:0\"\n ];\n\n return {\n scheme: 'pngchunk',\n originalUri: uri,\n normalizedUri: normalized,\n chunkKey: assetId,\n chunkCandidates: candidates,\n };\n }\n\n // ccdefault: - use default asset\n if (trimmed === 'ccdefault:' || trimmed.startsWith('ccdefault:')) {\n return {\n scheme: 'ccdefault',\n originalUri: uri,\n normalizedUri: normalized,\n };\n }\n\n // embeded:// or embedded:// (normalize typo)\n if (trimmed.startsWith('embeded://') || trimmed.startsWith('embedded://')) {\n const path = trimmed.startsWith('embeded://')\n ? trimmed.substring('embeded://'.length)\n : trimmed.substring('embedded://'.length);\n return {\n scheme: 'embeded',\n originalUri: uri,\n normalizedUri: normalized,\n path,\n };\n }\n\n // https://\n if (trimmed.startsWith('https://')) {\n return {\n scheme: 'https',\n originalUri: uri,\n normalizedUri: normalized,\n url: trimmed,\n };\n }\n\n // http://\n if (trimmed.startsWith('http://')) {\n return {\n scheme: 'http',\n originalUri: uri,\n normalizedUri: normalized,\n url: trimmed,\n };\n }\n\n // data: URIs\n if (trimmed.startsWith('data:')) {\n const parsed = parseDataURI(trimmed);\n return {\n scheme: 'data',\n originalUri: uri,\n normalizedUri: normalized,\n ...parsed,\n };\n }\n\n // file://\n if (trimmed.startsWith('file://')) {\n const path = trimmed.substring('file://'.length);\n return {\n scheme: 'file',\n originalUri: uri,\n normalizedUri: normalized,\n path,\n };\n }\n\n // Internal asset ID (alphanumeric/UUID format)\n if (/^[a-zA-Z0-9_-]+$/.test(trimmed)) {\n return {\n scheme: 'internal',\n originalUri: uri,\n normalizedUri: normalized,\n path: trimmed,\n };\n }\n\n // Unknown scheme\n return {\n scheme: 'unknown',\n originalUri: uri,\n normalizedUri: normalized,\n };\n}\n\n/**\n * Parse a data URI into its components\n * Format: data:[<mediatype>][;base64],<data>\n */\nfunction parseDataURI(uri: string): { mimeType?: string; encoding?: string; data?: string } {\n const match = uri.match(/^data:([^;,]+)?(;base64)?,(.*)$/);\n\n if (!match) {\n return {};\n }\n\n return {\n mimeType: match[1] || 'text/plain',\n encoding: match[2] ? 'base64' : undefined,\n data: match[3],\n };\n}\n\n/**\n * Check if extension is an image format\n */\nexport function isImageExt(ext: string): boolean {\n const imageExts = ['png', 'jpg', 'jpeg', 'webp', 'gif', 'avif', 'bmp', 'svg'];\n return imageExts.includes(ext.toLowerCase());\n}\n\n/**\n * Check if extension is an audio format\n */\nexport function isAudioExt(ext: string): boolean {\n const audioExts = ['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac'];\n return audioExts.includes(ext.toLowerCase());\n}\n\n/**\n * Check if extension is a video format\n */\nexport function isVideoExt(ext: string): boolean {\n const videoExts = ['mp4', 'webm', 'avi', 'mov', 'mkv'];\n return videoExts.includes(ext.toLowerCase());\n}\n\n/** Safe MIME types for data: URIs that can be used in href/src */\nconst SAFE_DATA_URI_MIME_TYPES = new Set([\n // Images (safe for img src)\n 'image/png',\n 'image/jpeg',\n 'image/gif',\n 'image/webp',\n 'image/avif',\n 'image/bmp',\n 'image/x-icon',\n // Audio (safe for audio src)\n 'audio/mpeg',\n 'audio/wav',\n 'audio/ogg',\n 'audio/flac',\n 'audio/mp4',\n 'audio/aac',\n // Video (safe for video src)\n 'video/mp4',\n 'video/webm',\n // Text/data (generally safe)\n 'text/plain',\n 'application/json',\n 'application/octet-stream',\n]);\n\n/** Potentially dangerous MIME types that should NOT be used in href/src */\nconst DANGEROUS_DATA_URI_MIME_TYPES = new Set([\n // Executable/script content\n 'text/html',\n 'text/javascript',\n 'application/javascript',\n 'application/x-javascript',\n 'text/css',\n 'image/svg+xml', // SVG can contain scripts\n 'application/xhtml+xml',\n 'application/xml',\n]);\n\n/**\n * Options for URI safety validation\n */\nexport interface URISafetyOptions {\n /** Allow http:// URIs (default: false) */\n allowHttp?: boolean;\n /** Allow file:// URIs (default: false) */\n allowFile?: boolean;\n /**\n * Allowed MIME types for data: URIs (default: all safe types).\n * Set to empty array to reject all data: URIs.\n * Set to undefined to use default safe list.\n */\n allowedDataMimes?: string[];\n}\n\n/**\n * Result of URI safety check with detailed information\n */\nexport interface URISafetyResult {\n /** Whether the URI is safe to use */\n safe: boolean;\n /** Reason if unsafe */\n reason?: string;\n /** Detected scheme */\n scheme: URIScheme;\n /** MIME type for data: URIs */\n mimeType?: string;\n}\n\n/**\n * Validate if a URI is safe to use (detailed version)\n *\n * @param uri - URI to validate\n * @param options - Safety options\n * @returns Detailed safety result\n */\nexport function checkURISafety(uri: string, options: URISafetyOptions = {}): URISafetyResult {\n const parsed = parseURI(uri);\n\n switch (parsed.scheme) {\n case 'embeded':\n case 'ccdefault':\n case 'internal':\n case 'https':\n case 'pngchunk':\n return { safe: true, scheme: parsed.scheme };\n\n case 'data': {\n const mimeType = parsed.mimeType || 'text/plain';\n\n // Check for explicitly dangerous MIME types\n if (DANGEROUS_DATA_URI_MIME_TYPES.has(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Data URI with potentially dangerous MIME type: ${mimeType}`,\n };\n }\n\n // If custom allowed list is provided, check against it\n if (options.allowedDataMimes !== undefined) {\n if (options.allowedDataMimes.length === 0) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: 'Data URIs are not allowed',\n };\n }\n if (!options.allowedDataMimes.includes(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Data URI MIME type not in allowed list: ${mimeType}`,\n };\n }\n }\n\n // Otherwise use default safe list\n if (!SAFE_DATA_URI_MIME_TYPES.has(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Unknown data URI MIME type: ${mimeType}`,\n };\n }\n\n return { safe: true, scheme: parsed.scheme, mimeType };\n }\n\n case 'http':\n if (options.allowHttp === true) {\n return { safe: true, scheme: parsed.scheme };\n }\n return { safe: false, scheme: parsed.scheme, reason: 'HTTP URIs are not allowed' };\n\n case 'file':\n if (options.allowFile === true) {\n return { safe: true, scheme: parsed.scheme };\n }\n return { safe: false, scheme: parsed.scheme, reason: 'File URIs are not allowed' };\n\n case 'unknown':\n default:\n return { safe: false, scheme: parsed.scheme, reason: 'Unknown URI scheme' };\n }\n}\n\n/**\n * Validate if a URI is safe to use (simple boolean version for backwards compatibility)\n *\n * @deprecated Use checkURISafety() for detailed safety information\n */\nexport function isURISafe(uri: string, options: { allowHttp?: boolean; allowFile?: boolean } = {}): boolean {\n return checkURISafety(uri, options).safe;\n}\n\n/**\n * Extract file extension from URI\n */\nexport function getExtensionFromURI(uri: string): string {\n const parsed = parseURI(uri);\n\n if (parsed.path) {\n const parts = parsed.path.split('.');\n if (parts.length > 1) {\n return parts[parts.length - 1]!.toLowerCase();\n }\n }\n\n if (parsed.url) {\n const urlParts = parsed.url.split('?')[0]!.split('.');\n if (urlParts.length > 1) {\n return urlParts[urlParts.length - 1]!.toLowerCase();\n }\n }\n\n if (parsed.mimeType) {\n return getExtFromMimeType(parsed.mimeType);\n }\n\n return 'unknown';\n}\n\n/**\n * Get MIME type from file extension\n */\nexport function getMimeTypeFromExt(ext: string): string {\n const extToMime: Record<string, string> = {\n // Images\n 'png': 'image/png',\n 'jpg': 'image/jpeg',\n 'jpeg': 'image/jpeg',\n 'webp': 'image/webp',\n 'gif': 'image/gif',\n 'avif': 'image/avif',\n 'svg': 'image/svg+xml',\n 'bmp': 'image/bmp',\n 'ico': 'image/x-icon',\n\n // Audio\n 'mp3': 'audio/mpeg',\n 'wav': 'audio/wav',\n 'ogg': 'audio/ogg',\n 'flac': 'audio/flac',\n 'm4a': 'audio/mp4',\n 'aac': 'audio/aac',\n\n // Video\n 'mp4': 'video/mp4',\n 'webm': 'video/webm',\n 'avi': 'video/x-msvideo',\n 'mov': 'video/quicktime',\n 'mkv': 'video/x-matroska',\n\n // Text/Data\n 'json': 'application/json',\n 'txt': 'text/plain',\n 'html': 'text/html',\n 'css': 'text/css',\n 'js': 'application/javascript',\n };\n\n return extToMime[ext.toLowerCase()] || 'application/octet-stream';\n}\n\n/**\n * Get file extension from MIME type\n */\nexport function getExtFromMimeType(mimeType: string): string {\n const mimeToExt: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n 'image/avif': 'avif',\n 'image/svg+xml': 'svg',\n 'image/bmp': 'bmp',\n 'image/x-icon': 'ico',\n 'audio/mpeg': 'mp3',\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/flac': 'flac',\n 'audio/mp4': 'm4a',\n 'audio/aac': 'aac',\n 'video/mp4': 'mp4',\n 'video/webm': 'webm',\n 'video/x-msvideo': 'avi',\n 'video/quicktime': 'mov',\n 'video/x-matroska': 'mkv',\n 'application/json': 'json',\n 'text/plain': 'txt',\n 'text/html': 'html',\n 'text/css': 'css',\n 'application/javascript': 'js',\n };\n\n return mimeToExt[mimeType] || 'bin';\n}\n\n/**\n * Build a data URI from binary data and MIME type\n */\nexport function buildDataURI(data: string, mimeType: string, isBase64 = true): string {\n if (isBase64) {\n return `data:${mimeType};base64,${data}`;\n }\n return `data:${mimeType},${encodeURIComponent(data)}`;\n}\n","/**\n * Image Analysis Utilities\n *\n * Detect properties of image files from binary data.\n */\n\nimport {\n type BinaryData,\n indexOf,\n fromLatin1,\n} from './binary.js';\n\n/**\n * Check if an image buffer contains animation data.\n * Supports: APNG, WebP (Animated), GIF\n */\nexport function isAnimatedImage(data: BinaryData, _mimeType?: string): boolean {\n // 1. WebP Detection\n // RIFF .... WEBP\n if (\n data.length > 12 &&\n data[0] === 0x52 && data[1] === 0x49 && data[2] === 0x46 && data[3] === 0x46 && // RIFF\n data[8] === 0x57 && data[9] === 0x45 && data[10] === 0x42 && data[11] === 0x50 // WEBP\n ) {\n // Check for VP8X chunk\n // VP8X chunk header: 'VP8X' (bytes 12-15)\n if (\n data[12] === 0x56 && data[13] === 0x50 && data[14] === 0x38 && data[15] === 0x58\n ) {\n // Flags byte is at offset 20 (16 + 4 bytes chunk size)\n // Animation bit is bit 1 (0x02)\n const flags = data[20];\n return (flags! & 0x02) !== 0;\n }\n return false;\n }\n\n // 2. PNG/APNG Detection\n // Signature: 89 50 4E 47 0D 0A 1A 0A\n if (\n data.length > 8 &&\n data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4E && data[3] === 0x47\n ) {\n // Search for 'acTL' chunk (Animation Control)\n // It must appear before IDAT.\n // Simple search: indexOf('acTL')\n // Note: theoretically 'acTL' string could appear in other data, but highly unlikely in valid PNG structure before IDAT\n // We can iterate chunks to be safe, but indexOf is faster for a quick check\n const actlSig = fromLatin1('acTL');\n const idatSig = fromLatin1('IDAT');\n \n const actlIndex = indexOf(data, actlSig);\n if (actlIndex === -1) return false;\n\n const idatIndex = indexOf(data, idatSig);\n // If acTL exists and is before the first IDAT (or IDAT not found yet), it's APNG\n return idatIndex === -1 || actlIndex < idatIndex;\n }\n\n // 3. GIF Detection\n // Signature: GIF87a or GIF89a\n if (\n data.length > 6 &&\n data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46 // GIF\n ) {\n // Check for NETSCAPE2.0 extension (looping animation)\n // This is a heuristic. Static GIFs are rare in this domain but possible.\n // Full frame counting is expensive. Presence of NETSCAPE block is a strong indicator.\n const netscape = fromLatin1('NETSCAPE2.0');\n return indexOf(data, netscape) !== -1;\n }\n\n return false;\n}\n","/**\n * UUID Generation Utilities\n *\n * Provides crypto-grade UUID v4 generation that works in Node.js,\n * browsers (secure contexts), and falls back gracefully.\n */\n\n/**\n * Format 16 random bytes as a UUID v4 string\n */\nfunction formatUUID(bytes: Uint8Array): string {\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;\n}\n\n/**\n * Fallback UUID generation using Math.random()\n * Only used when crypto APIs are unavailable (rare)\n */\nfunction mathRandomUUID(): string {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Generate a cryptographically secure UUID v4.\n *\n * Uses crypto.randomUUID() when available (Node.js 19+, modern browsers).\n * Falls back to crypto.getRandomValues() if randomUUID is unavailable.\n * Last resort uses Math.random() (non-secure, emits warning in dev).\n *\n * @returns A valid RFC 4122 UUID v4 string\n *\n * @example\n * ```typescript\n * const id = generateUUID();\n * // => \"550e8400-e29b-41d4-a716-446655440000\"\n * ```\n */\nexport function generateUUID(): string {\n // Node.js 19+ or browser with secure context\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n\n // Fallback using crypto.getRandomValues (older Node/browsers)\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n // Set version (4) and variant (RFC 4122)\n bytes[6] = (bytes[6]! & 0x0f) | 0x40; // Version 4\n bytes[8] = (bytes[8]! & 0x3f) | 0x80; // Variant 1\n return formatUUID(bytes);\n }\n\n // Last resort - non-secure fallback\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') {\n console.warn('[character-foundry/core] generateUUID: Using insecure Math.random() fallback');\n }\n return mathRandomUUID();\n}\n\n/**\n * Validate if a string is a valid UUID v4\n *\n * @param uuid - String to validate\n * @returns true if valid UUID v4 format\n */\nexport function isValidUUID(uuid: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);\n}\n"],"mappings":"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","names":["isBase64"]}
|
package/dist/core.d.cts
CHANGED
|
@@ -361,7 +361,7 @@ export declare function wrapError(error: unknown, context?: string): FoundryErro
|
|
|
361
361
|
* Check if an image buffer contains animation data.
|
|
362
362
|
* Supports: APNG, WebP (Animated), GIF
|
|
363
363
|
*/
|
|
364
|
-
export declare function isAnimatedImage(data: BinaryData,
|
|
364
|
+
export declare function isAnimatedImage(data: BinaryData, _mimeType?: string): boolean;
|
|
365
365
|
/**
|
|
366
366
|
* UUID Generation Utilities
|
|
367
367
|
*
|
package/dist/core.d.ts
CHANGED
|
@@ -361,7 +361,7 @@ export declare function wrapError(error: unknown, context?: string): FoundryErro
|
|
|
361
361
|
* Check if an image buffer contains animation data.
|
|
362
362
|
* Supports: APNG, WebP (Animated), GIF
|
|
363
363
|
*/
|
|
364
|
-
export declare function isAnimatedImage(data: BinaryData,
|
|
364
|
+
export declare function isAnimatedImage(data: BinaryData, _mimeType?: string): boolean;
|
|
365
365
|
/**
|
|
366
366
|
* UUID Generation Utilities
|
|
367
367
|
*
|
package/dist/core.js
CHANGED
|
@@ -622,7 +622,7 @@ function buildDataURI(data, mimeType, isBase642 = true) {
|
|
|
622
622
|
}
|
|
623
623
|
return `data:${mimeType},${encodeURIComponent(data)}`;
|
|
624
624
|
}
|
|
625
|
-
function isAnimatedImage(data,
|
|
625
|
+
function isAnimatedImage(data, _mimeType) {
|
|
626
626
|
if (data.length > 12 && data[0] === 82 && data[1] === 73 && data[2] === 70 && data[3] === 70 && // RIFF
|
|
627
627
|
data[8] === 87 && data[9] === 69 && data[10] === 66 && data[11] === 80) {
|
|
628
628
|
if (data[12] === 86 && data[13] === 80 && data[14] === 56 && data[15] === 88) {
|
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../core/src/binary.ts","../../core/src/base64.ts","../../core/src/errors.ts","../../core/src/data-url.ts","../../core/src/uri.ts","../../core/src/image.ts","../../core/src/uuid.ts"],"sourcesContent":["/**\n * Binary Data Utilities\n *\n * Universal binary data operations using Uint8Array.\n * Works in both Node.js and browser environments.\n */\n\n/**\n * Universal binary data type (works in both environments)\n */\nexport type BinaryData = Uint8Array;\n\n/**\n * Read a 32-bit big-endian unsigned integer\n */\nexport function readUInt32BE(data: BinaryData, offset: number): number {\n return (\n (data[offset]! << 24) |\n (data[offset + 1]! << 16) |\n (data[offset + 2]! << 8) |\n data[offset + 3]!\n ) >>> 0;\n}\n\n/**\n * Write a 32-bit big-endian unsigned integer\n */\nexport function writeUInt32BE(data: BinaryData, value: number, offset: number): void {\n data[offset] = (value >>> 24) & 0xff;\n data[offset + 1] = (value >>> 16) & 0xff;\n data[offset + 2] = (value >>> 8) & 0xff;\n data[offset + 3] = value & 0xff;\n}\n\n/**\n * Read a 16-bit big-endian unsigned integer\n */\nexport function readUInt16BE(data: BinaryData, offset: number): number {\n return ((data[offset]! << 8) | data[offset + 1]!) >>> 0;\n}\n\n/**\n * Write a 16-bit big-endian unsigned integer\n */\nexport function writeUInt16BE(data: BinaryData, value: number, offset: number): void {\n data[offset] = (value >>> 8) & 0xff;\n data[offset + 1] = value & 0xff;\n}\n\n/**\n * Find a byte sequence in binary data\n */\nexport function indexOf(data: BinaryData, search: BinaryData, fromIndex = 0): number {\n outer: for (let i = fromIndex; i <= data.length - search.length; i++) {\n for (let j = 0; j < search.length; j++) {\n if (data[i + j] !== search[j]) continue outer;\n }\n return i;\n }\n return -1;\n}\n\n/**\n * Concatenate multiple binary arrays\n */\nexport function concat(...arrays: BinaryData[]): BinaryData {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n\n/**\n * Slice binary data (returns a view, not a copy)\n */\nexport function slice(data: BinaryData, start: number, end?: number): BinaryData {\n return data.subarray(start, end);\n}\n\n/**\n * Copy a portion of binary data (returns a new array)\n */\nexport function copy(data: BinaryData, start: number, end?: number): BinaryData {\n return data.slice(start, end);\n}\n\n/**\n * Convert string to binary (UTF-8)\n */\nexport function fromString(str: string): BinaryData {\n return new TextEncoder().encode(str);\n}\n\n/**\n * Convert binary to string (UTF-8)\n */\nexport function toString(data: BinaryData): string {\n return new TextDecoder().decode(data);\n}\n\n/**\n * Convert string to binary (Latin1 - for PNG keywords and similar)\n */\nexport function fromLatin1(str: string): BinaryData {\n const result = new Uint8Array(str.length);\n for (let i = 0; i < str.length; i++) {\n result[i] = str.charCodeAt(i) & 0xff;\n }\n return result;\n}\n\n/**\n * Convert binary to string (Latin1)\n */\nexport function toLatin1(data: BinaryData): string {\n let result = '';\n for (let i = 0; i < data.length; i++) {\n result += String.fromCharCode(data[i]!);\n }\n return result;\n}\n\n/**\n * Compare two binary arrays for equality\n */\nexport function equals(a: BinaryData, b: BinaryData): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Create a new Uint8Array filled with zeros\n */\nexport function alloc(size: number): BinaryData {\n return new Uint8Array(size);\n}\n\n/**\n * Create a Uint8Array from an array of numbers\n */\nexport function from(data: number[] | ArrayBuffer | BinaryData): BinaryData {\n if (data instanceof Uint8Array) {\n return data;\n }\n if (data instanceof ArrayBuffer) {\n return new Uint8Array(data);\n }\n return new Uint8Array(data);\n}\n\n/**\n * Check if value is a Uint8Array\n */\nexport function isBinaryData(value: unknown): value is BinaryData {\n return value instanceof Uint8Array;\n}\n\n/**\n * Convert Node.js Buffer to Uint8Array (no-op if already Uint8Array)\n * This provides compatibility when interfacing with Node.js code\n */\nexport function toUint8Array(data: BinaryData | Buffer): BinaryData {\n if (data instanceof Uint8Array) {\n // Buffer extends Uint8Array, but we want a plain Uint8Array\n // This ensures we get a proper Uint8Array in all cases\n if (Object.getPrototypeOf(data).constructor.name === 'Buffer') {\n return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);\n }\n return data;\n }\n return new Uint8Array(data);\n}\n\n/**\n * Convert binary data to hex string\n */\nexport function toHex(data: BinaryData): string {\n return Array.from(data)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Convert hex string to binary data\n */\nexport function fromHex(hex: string): BinaryData {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.substr(i * 2, 2), 16);\n }\n return bytes;\n}\n","/**\n * Universal Base64 Encoding/Decoding\n *\n * Works in both Node.js and browser environments.\n */\n\nimport type { BinaryData } from './binary.js';\n\n/**\n * Check if we're in a Node.js environment\n */\nconst isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n/**\n * Threshold for switching to chunked encoding in browsers (1MB)\n * Below this, simple string concatenation is fast enough.\n * Above this, quadratic string growth becomes a problem.\n */\nconst LARGE_BUFFER_THRESHOLD = 1024 * 1024;\n\n/**\n * Encode binary data to base64 string\n *\n * PERFORMANCE: For large buffers (>1MB) in browsers, this automatically\n * uses the chunked implementation to avoid quadratic string concatenation.\n */\nexport function encode(data: BinaryData): string {\n if (isNode) {\n // Node.js: Buffer handles large data efficiently\n return Buffer.from(data).toString('base64');\n }\n\n // Browser: use chunked encoding for large buffers to avoid O(n²) string growth\n if (data.length > LARGE_BUFFER_THRESHOLD) {\n return encodeChunked(data);\n }\n\n // Small buffers: simple approach is fast enough\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]!);\n }\n return btoa(binary);\n}\n\n/**\n * Decode base64 string to binary data\n */\nexport function decode(base64: string): BinaryData {\n if (isNode) {\n // Node.js: use Buffer\n return new Uint8Array(Buffer.from(base64, 'base64'));\n }\n\n // Browser: use atob\n const binary = atob(base64);\n const result = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n result[i] = binary.charCodeAt(i);\n }\n return result;\n}\n\n/**\n * Check if a string is valid base64\n */\nexport function isBase64(str: string): boolean {\n if (str.length === 0) return false;\n // Base64 regex: only valid base64 characters, length multiple of 4 (with padding)\n const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;\n return base64Regex.test(str) && str.length % 4 === 0;\n}\n\n/**\n * Encode binary data to URL-safe base64 string\n * Replaces + with -, / with _, and removes padding\n */\nexport function encodeUrlSafe(data: BinaryData): string {\n return encode(data)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Decode URL-safe base64 string to binary data\n */\nexport function decodeUrlSafe(base64: string): BinaryData {\n // Add back padding if needed\n let padded = base64\n .replace(/-/g, '+')\n .replace(/_/g, '/');\n\n while (padded.length % 4 !== 0) {\n padded += '=';\n }\n\n return decode(padded);\n}\n\n/**\n * Chunk size for encoding large buffers (64KB)\n * Prevents stack overflow when using String.fromCharCode with spread operator\n */\nconst ENCODE_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Encode binary data to base64 string with chunking for large buffers.\n * Handles buffers >10MB without stack overflow.\n *\n * @param data - Binary data to encode\n * @returns Base64 encoded string\n *\n * @example\n * ```typescript\n * const largeBuffer = new Uint8Array(20 * 1024 * 1024); // 20MB\n * const base64 = encodeChunked(largeBuffer); // No stack overflow\n * ```\n */\nexport function encodeChunked(data: BinaryData): string {\n if (isNode) {\n // Node.js: Buffer handles large data efficiently\n return Buffer.from(data).toString('base64');\n }\n\n // Browser: process in chunks to avoid stack overflow\n const chunks: string[] = [];\n\n for (let i = 0; i < data.length; i += ENCODE_CHUNK_SIZE) {\n const chunk = data.subarray(i, Math.min(i + ENCODE_CHUNK_SIZE, data.length));\n let binary = '';\n for (let j = 0; j < chunk.length; j++) {\n binary += String.fromCharCode(chunk[j]!);\n }\n chunks.push(binary);\n }\n\n return btoa(chunks.join(''));\n}\n","/**\n * Error Classes\n *\n * Specific error types for character card operations.\n * All errors extend FoundryError for consistent handling.\n */\n\n/** Symbol to identify FoundryError instances across ESM/CJS boundaries */\nconst FOUNDRY_ERROR_MARKER = Symbol.for('@character-foundry/core:FoundryError');\n\n/**\n * Base error class for all Character Foundry errors\n */\nexport class FoundryError extends Error {\n /** @internal Marker for cross-module identification */\n readonly [FOUNDRY_ERROR_MARKER] = true;\n\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = 'FoundryError';\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error during card parsing\n */\nexport class ParseError extends FoundryError {\n constructor(message: string, public readonly format?: string) {\n super(message, 'PARSE_ERROR');\n this.name = 'ParseError';\n }\n}\n\n/**\n * Error during card validation\n */\nexport class ValidationError extends FoundryError {\n constructor(message: string, public readonly field?: string) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Asset not found in card or archive\n */\nexport class AssetNotFoundError extends FoundryError {\n constructor(public readonly uri: string) {\n super(`Asset not found: ${uri}`, 'ASSET_NOT_FOUND');\n this.name = 'AssetNotFoundError';\n }\n}\n\n/**\n * Format not supported for operation\n */\nexport class FormatNotSupportedError extends FoundryError {\n constructor(public readonly format: string, operation?: string) {\n const msg = operation\n ? `Format '${format}' not supported for ${operation}`\n : `Format not supported: ${format}`;\n super(msg, 'FORMAT_NOT_SUPPORTED');\n this.name = 'FormatNotSupportedError';\n }\n}\n\n/**\n * File size exceeds limits\n */\nexport class SizeLimitError extends FoundryError {\n constructor(\n public readonly actualSize: number,\n public readonly maxSize: number,\n context?: string\n ) {\n const actualMB = (actualSize / 1024 / 1024).toFixed(2);\n const maxMB = (maxSize / 1024 / 1024).toFixed(2);\n const msg = context\n ? `${context}: Size ${actualMB}MB exceeds limit ${maxMB}MB`\n : `Size ${actualMB}MB exceeds limit ${maxMB}MB`;\n super(msg, 'SIZE_LIMIT_EXCEEDED');\n this.name = 'SizeLimitError';\n }\n}\n\n/**\n * Path traversal or unsafe path detected\n */\nexport class PathTraversalError extends FoundryError {\n constructor(public readonly path: string) {\n super(`Unsafe path detected: ${path}`, 'PATH_TRAVERSAL');\n this.name = 'PathTraversalError';\n }\n}\n\n/**\n * Export operation would lose data\n */\nexport class DataLossError extends FoundryError {\n constructor(\n public readonly lostFields: string[],\n public readonly targetFormat: string\n ) {\n const fields = lostFields.slice(0, 3).join(', ');\n const more = lostFields.length > 3 ? ` and ${lostFields.length - 3} more` : '';\n super(\n `Export to ${targetFormat} would lose: ${fields}${more}`,\n 'DATA_LOSS'\n );\n this.name = 'DataLossError';\n }\n}\n\n/**\n * Check if an error is a FoundryError\n *\n * Uses Symbol.for() marker instead of instanceof to handle dual ESM/CJS package loading.\n * In dual-package environments, instanceof can fail if the error comes from a different\n * module instance (e.g., ESM vs CJS version of the same package). Symbol.for() creates\n * a global symbol shared across all module instances.\n */\nexport function isFoundryError(error: unknown): error is FoundryError {\n return (\n error instanceof Error &&\n FOUNDRY_ERROR_MARKER in error &&\n (error as Record<symbol, unknown>)[FOUNDRY_ERROR_MARKER] === true\n );\n}\n\n/**\n * Wrap unknown errors in a FoundryError\n */\nexport function wrapError(error: unknown, context?: string): FoundryError {\n if (isFoundryError(error)) {\n return error;\n }\n\n const message = error instanceof Error\n ? error.message\n : String(error);\n\n return new FoundryError(\n context ? `${context}: ${message}` : message,\n 'UNKNOWN_ERROR'\n );\n}\n","/**\n * Data URL Utilities\n *\n * Convert between Uint8Array buffers and data URLs.\n * Handles large buffers (>10MB) without stack overflow by processing in chunks.\n */\n\nimport type { BinaryData } from './binary.js';\nimport { encodeChunked as base64Encode, decode as base64Decode } from './base64.js';\nimport { ValidationError } from './errors.js';\n\n/**\n * Convert Uint8Array to data URL.\n * Handles large buffers (>10MB) without stack overflow by processing in chunks.\n *\n * @param buffer - Binary data to encode\n * @param mimeType - MIME type for the data URL (e.g., 'image/png', 'application/octet-stream')\n * @returns Data URL string\n *\n * @example\n * ```typescript\n * const png = new Uint8Array([...]);\n * const dataUrl = toDataURL(png, 'image/png');\n * // => \"data:image/png;base64,iVBORw0KGgo...\"\n * ```\n */\nexport function toDataURL(buffer: BinaryData, mimeType: string): string {\n // Use chunked encoding to handle large buffers without stack overflow\n const base64 = base64Encode(buffer);\n return `data:${mimeType};base64,${base64}`;\n}\n\n/**\n * Parse a data URL back to buffer and MIME type.\n * Validates the data URL format before parsing.\n *\n * @param dataUrl - Data URL string to parse\n * @returns Object containing the decoded buffer and MIME type\n * @throws Error if the data URL format is invalid\n *\n * @example\n * ```typescript\n * const { buffer, mimeType } = fromDataURL('data:image/png;base64,iVBORw0KGgo...');\n * // buffer: Uint8Array\n * // mimeType: 'image/png'\n * ```\n */\nexport function fromDataURL(dataUrl: string): { buffer: Uint8Array; mimeType: string } {\n // Validate data URL format\n if (!dataUrl.startsWith('data:')) {\n throw new ValidationError('Invalid data URL: must start with \"data:\"', 'dataUrl');\n }\n\n const commaIndex = dataUrl.indexOf(',');\n if (commaIndex === -1) {\n throw new ValidationError('Invalid data URL: missing comma separator', 'dataUrl');\n }\n\n const header = dataUrl.slice(5, commaIndex); // Skip 'data:'\n const data = dataUrl.slice(commaIndex + 1);\n\n // Parse header: [<mediatype>][;base64]\n let mimeType = 'text/plain';\n let isBase64 = false;\n\n const parts = header.split(';');\n for (const part of parts) {\n if (part === 'base64') {\n isBase64 = true;\n } else if (part && !part.includes('=')) {\n // MIME type (not a parameter like charset=utf-8)\n mimeType = part;\n }\n }\n\n if (!isBase64) {\n // URL-encoded text data\n throw new ValidationError('Non-base64 data URLs are not supported', 'dataUrl');\n }\n\n const buffer = base64Decode(data);\n return { buffer, mimeType };\n}\n\n/**\n * Check if a string is a valid data URL\n *\n * @param str - String to check\n * @returns true if the string is a valid data URL format\n */\nexport function isDataURL(str: string): boolean {\n if (!str.startsWith('data:')) return false;\n const commaIndex = str.indexOf(',');\n if (commaIndex === -1) return false;\n const header = str.slice(5, commaIndex);\n return header.includes('base64');\n}\n","/**\n * URI Utilities\n *\n * Handles different asset URI schemes used in character cards.\n * Supports: embeded://, embedded://, ccdefault:, https://, http://,\n * data:, file://, __asset:, asset:, chara-ext-asset_\n */\n\nexport type URIScheme =\n | 'embeded' // embeded:// (CharX standard, note intentional typo)\n | 'ccdefault' // ccdefault:\n | 'https' // https://\n | 'http' // http://\n | 'data' // data:mime;base64,...\n | 'file' // file://\n | 'internal' // Internal asset ID (UUID/string)\n | 'pngchunk' // PNG chunk reference (__asset:, asset:, chara-ext-asset_)\n | 'unknown';\n\nexport interface ParsedURI {\n scheme: URIScheme;\n originalUri: string;\n normalizedUri: string; // Normalized form of the URI\n path?: string; // For embeded://, file://\n url?: string; // For http://, https://\n data?: string; // For data: URIs\n mimeType?: string; // For data: URIs\n encoding?: string; // For data: URIs (e.g., base64)\n chunkKey?: string; // For pngchunk - the key/index to look up\n chunkCandidates?: string[]; // For pngchunk - all possible chunk keys to search\n}\n\n/**\n * Normalize a URI to its canonical form\n * Handles common typos and variant formats\n */\nexport function normalizeURI(uri: string): string {\n const trimmed = uri.trim();\n\n // Fix embedded:// -> embeded:// (common typo, CharX spec uses single 'd')\n if (trimmed.startsWith('embedded://')) {\n return 'embeded://' + trimmed.substring('embedded://'.length);\n }\n\n // Normalize PNG chunk references to pngchunk: scheme\n if (trimmed.startsWith('__asset:')) {\n const id = trimmed.substring('__asset:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('asset:')) {\n const id = trimmed.substring('asset:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('chara-ext-asset_:')) {\n const id = trimmed.substring('chara-ext-asset_:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('chara-ext-asset_')) {\n const id = trimmed.substring('chara-ext-asset_'.length);\n return `pngchunk:${id}`;\n }\n\n return trimmed;\n}\n\n/**\n * Parse a URI and determine its scheme and components\n */\nexport function parseURI(uri: string): ParsedURI {\n const trimmed = uri.trim();\n const normalized = normalizeURI(trimmed);\n\n // PNG chunk references (__asset:, asset:, chara-ext-asset_, pngchunk:)\n if (\n trimmed.startsWith('__asset:') ||\n trimmed.startsWith('asset:') ||\n trimmed.startsWith('chara-ext-asset_') ||\n trimmed.startsWith('pngchunk:')\n ) {\n let assetId: string;\n if (trimmed.startsWith('__asset:')) {\n assetId = trimmed.substring('__asset:'.length);\n } else if (trimmed.startsWith('asset:')) {\n assetId = trimmed.substring('asset:'.length);\n } else if (trimmed.startsWith('chara-ext-asset_:')) {\n assetId = trimmed.substring('chara-ext-asset_:'.length);\n } else if (trimmed.startsWith('pngchunk:')) {\n assetId = trimmed.substring('pngchunk:'.length);\n } else {\n assetId = trimmed.substring('chara-ext-asset_'.length);\n }\n\n // Generate all possible chunk key variations for lookup\n const candidates = [\n assetId, // \"0\" or \"filename.png\"\n trimmed, // Original URI\n `asset:${assetId}`, // \"asset:0\"\n `__asset:${assetId}`, // \"__asset:0\"\n `__asset_${assetId}`, // \"__asset_0\"\n `chara-ext-asset_${assetId}`, // \"chara-ext-asset_0\"\n `chara-ext-asset_:${assetId}`, // \"chara-ext-asset_:0\"\n `pngchunk:${assetId}`, // \"pngchunk:0\"\n ];\n\n return {\n scheme: 'pngchunk',\n originalUri: uri,\n normalizedUri: normalized,\n chunkKey: assetId,\n chunkCandidates: candidates,\n };\n }\n\n // ccdefault: - use default asset\n if (trimmed === 'ccdefault:' || trimmed.startsWith('ccdefault:')) {\n return {\n scheme: 'ccdefault',\n originalUri: uri,\n normalizedUri: normalized,\n };\n }\n\n // embeded:// or embedded:// (normalize typo)\n if (trimmed.startsWith('embeded://') || trimmed.startsWith('embedded://')) {\n const path = trimmed.startsWith('embeded://')\n ? trimmed.substring('embeded://'.length)\n : trimmed.substring('embedded://'.length);\n return {\n scheme: 'embeded',\n originalUri: uri,\n normalizedUri: normalized,\n path,\n };\n }\n\n // https://\n if (trimmed.startsWith('https://')) {\n return {\n scheme: 'https',\n originalUri: uri,\n normalizedUri: normalized,\n url: trimmed,\n };\n }\n\n // http://\n if (trimmed.startsWith('http://')) {\n return {\n scheme: 'http',\n originalUri: uri,\n normalizedUri: normalized,\n url: trimmed,\n };\n }\n\n // data: URIs\n if (trimmed.startsWith('data:')) {\n const parsed = parseDataURI(trimmed);\n return {\n scheme: 'data',\n originalUri: uri,\n normalizedUri: normalized,\n ...parsed,\n };\n }\n\n // file://\n if (trimmed.startsWith('file://')) {\n const path = trimmed.substring('file://'.length);\n return {\n scheme: 'file',\n originalUri: uri,\n normalizedUri: normalized,\n path,\n };\n }\n\n // Internal asset ID (alphanumeric/UUID format)\n if (/^[a-zA-Z0-9_-]+$/.test(trimmed)) {\n return {\n scheme: 'internal',\n originalUri: uri,\n normalizedUri: normalized,\n path: trimmed,\n };\n }\n\n // Unknown scheme\n return {\n scheme: 'unknown',\n originalUri: uri,\n normalizedUri: normalized,\n };\n}\n\n/**\n * Parse a data URI into its components\n * Format: data:[<mediatype>][;base64],<data>\n */\nfunction parseDataURI(uri: string): { mimeType?: string; encoding?: string; data?: string } {\n const match = uri.match(/^data:([^;,]+)?(;base64)?,(.*)$/);\n\n if (!match) {\n return {};\n }\n\n return {\n mimeType: match[1] || 'text/plain',\n encoding: match[2] ? 'base64' : undefined,\n data: match[3],\n };\n}\n\n/**\n * Check if extension is an image format\n */\nexport function isImageExt(ext: string): boolean {\n const imageExts = ['png', 'jpg', 'jpeg', 'webp', 'gif', 'avif', 'bmp', 'svg'];\n return imageExts.includes(ext.toLowerCase());\n}\n\n/**\n * Check if extension is an audio format\n */\nexport function isAudioExt(ext: string): boolean {\n const audioExts = ['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac'];\n return audioExts.includes(ext.toLowerCase());\n}\n\n/**\n * Check if extension is a video format\n */\nexport function isVideoExt(ext: string): boolean {\n const videoExts = ['mp4', 'webm', 'avi', 'mov', 'mkv'];\n return videoExts.includes(ext.toLowerCase());\n}\n\n/** Safe MIME types for data: URIs that can be used in href/src */\nconst SAFE_DATA_URI_MIME_TYPES = new Set([\n // Images (safe for img src)\n 'image/png',\n 'image/jpeg',\n 'image/gif',\n 'image/webp',\n 'image/avif',\n 'image/bmp',\n 'image/x-icon',\n // Audio (safe for audio src)\n 'audio/mpeg',\n 'audio/wav',\n 'audio/ogg',\n 'audio/flac',\n 'audio/mp4',\n 'audio/aac',\n // Video (safe for video src)\n 'video/mp4',\n 'video/webm',\n // Text/data (generally safe)\n 'text/plain',\n 'application/json',\n 'application/octet-stream',\n]);\n\n/** Potentially dangerous MIME types that should NOT be used in href/src */\nconst DANGEROUS_DATA_URI_MIME_TYPES = new Set([\n // Executable/script content\n 'text/html',\n 'text/javascript',\n 'application/javascript',\n 'application/x-javascript',\n 'text/css',\n 'image/svg+xml', // SVG can contain scripts\n 'application/xhtml+xml',\n 'application/xml',\n]);\n\n/**\n * Options for URI safety validation\n */\nexport interface URISafetyOptions {\n /** Allow http:// URIs (default: false) */\n allowHttp?: boolean;\n /** Allow file:// URIs (default: false) */\n allowFile?: boolean;\n /**\n * Allowed MIME types for data: URIs (default: all safe types).\n * Set to empty array to reject all data: URIs.\n * Set to undefined to use default safe list.\n */\n allowedDataMimes?: string[];\n}\n\n/**\n * Result of URI safety check with detailed information\n */\nexport interface URISafetyResult {\n /** Whether the URI is safe to use */\n safe: boolean;\n /** Reason if unsafe */\n reason?: string;\n /** Detected scheme */\n scheme: URIScheme;\n /** MIME type for data: URIs */\n mimeType?: string;\n}\n\n/**\n * Validate if a URI is safe to use (detailed version)\n *\n * @param uri - URI to validate\n * @param options - Safety options\n * @returns Detailed safety result\n */\nexport function checkURISafety(uri: string, options: URISafetyOptions = {}): URISafetyResult {\n const parsed = parseURI(uri);\n\n switch (parsed.scheme) {\n case 'embeded':\n case 'ccdefault':\n case 'internal':\n case 'https':\n case 'pngchunk':\n return { safe: true, scheme: parsed.scheme };\n\n case 'data': {\n const mimeType = parsed.mimeType || 'text/plain';\n\n // Check for explicitly dangerous MIME types\n if (DANGEROUS_DATA_URI_MIME_TYPES.has(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Data URI with potentially dangerous MIME type: ${mimeType}`,\n };\n }\n\n // If custom allowed list is provided, check against it\n if (options.allowedDataMimes !== undefined) {\n if (options.allowedDataMimes.length === 0) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: 'Data URIs are not allowed',\n };\n }\n if (!options.allowedDataMimes.includes(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Data URI MIME type not in allowed list: ${mimeType}`,\n };\n }\n }\n\n // Otherwise use default safe list\n if (!SAFE_DATA_URI_MIME_TYPES.has(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Unknown data URI MIME type: ${mimeType}`,\n };\n }\n\n return { safe: true, scheme: parsed.scheme, mimeType };\n }\n\n case 'http':\n if (options.allowHttp === true) {\n return { safe: true, scheme: parsed.scheme };\n }\n return { safe: false, scheme: parsed.scheme, reason: 'HTTP URIs are not allowed' };\n\n case 'file':\n if (options.allowFile === true) {\n return { safe: true, scheme: parsed.scheme };\n }\n return { safe: false, scheme: parsed.scheme, reason: 'File URIs are not allowed' };\n\n case 'unknown':\n default:\n return { safe: false, scheme: parsed.scheme, reason: 'Unknown URI scheme' };\n }\n}\n\n/**\n * Validate if a URI is safe to use (simple boolean version for backwards compatibility)\n *\n * @deprecated Use checkURISafety() for detailed safety information\n */\nexport function isURISafe(uri: string, options: { allowHttp?: boolean; allowFile?: boolean } = {}): boolean {\n return checkURISafety(uri, options).safe;\n}\n\n/**\n * Extract file extension from URI\n */\nexport function getExtensionFromURI(uri: string): string {\n const parsed = parseURI(uri);\n\n if (parsed.path) {\n const parts = parsed.path.split('.');\n if (parts.length > 1) {\n return parts[parts.length - 1]!.toLowerCase();\n }\n }\n\n if (parsed.url) {\n const urlParts = parsed.url.split('?')[0]!.split('.');\n if (urlParts.length > 1) {\n return urlParts[urlParts.length - 1]!.toLowerCase();\n }\n }\n\n if (parsed.mimeType) {\n return getExtFromMimeType(parsed.mimeType);\n }\n\n return 'unknown';\n}\n\n/**\n * Get MIME type from file extension\n */\nexport function getMimeTypeFromExt(ext: string): string {\n const extToMime: Record<string, string> = {\n // Images\n 'png': 'image/png',\n 'jpg': 'image/jpeg',\n 'jpeg': 'image/jpeg',\n 'webp': 'image/webp',\n 'gif': 'image/gif',\n 'avif': 'image/avif',\n 'svg': 'image/svg+xml',\n 'bmp': 'image/bmp',\n 'ico': 'image/x-icon',\n\n // Audio\n 'mp3': 'audio/mpeg',\n 'wav': 'audio/wav',\n 'ogg': 'audio/ogg',\n 'flac': 'audio/flac',\n 'm4a': 'audio/mp4',\n 'aac': 'audio/aac',\n\n // Video\n 'mp4': 'video/mp4',\n 'webm': 'video/webm',\n 'avi': 'video/x-msvideo',\n 'mov': 'video/quicktime',\n 'mkv': 'video/x-matroska',\n\n // Text/Data\n 'json': 'application/json',\n 'txt': 'text/plain',\n 'html': 'text/html',\n 'css': 'text/css',\n 'js': 'application/javascript',\n };\n\n return extToMime[ext.toLowerCase()] || 'application/octet-stream';\n}\n\n/**\n * Get file extension from MIME type\n */\nexport function getExtFromMimeType(mimeType: string): string {\n const mimeToExt: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n 'image/avif': 'avif',\n 'image/svg+xml': 'svg',\n 'image/bmp': 'bmp',\n 'image/x-icon': 'ico',\n 'audio/mpeg': 'mp3',\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/flac': 'flac',\n 'audio/mp4': 'm4a',\n 'audio/aac': 'aac',\n 'video/mp4': 'mp4',\n 'video/webm': 'webm',\n 'video/x-msvideo': 'avi',\n 'video/quicktime': 'mov',\n 'video/x-matroska': 'mkv',\n 'application/json': 'json',\n 'text/plain': 'txt',\n 'text/html': 'html',\n 'text/css': 'css',\n 'application/javascript': 'js',\n };\n\n return mimeToExt[mimeType] || 'bin';\n}\n\n/**\n * Build a data URI from binary data and MIME type\n */\nexport function buildDataURI(data: string, mimeType: string, isBase64 = true): string {\n if (isBase64) {\n return `data:${mimeType};base64,${data}`;\n }\n return `data:${mimeType},${encodeURIComponent(data)}`;\n}\n","/**\n * Image Analysis Utilities\n *\n * Detect properties of image files from binary data.\n */\n\nimport {\n type BinaryData,\n readUInt32BE,\n indexOf,\n fromLatin1,\n slice,\n toLatin1\n} from './binary.js';\n\n/**\n * Check if an image buffer contains animation data.\n * Supports: APNG, WebP (Animated), GIF\n */\nexport function isAnimatedImage(data: BinaryData, mimeType?: string): boolean {\n // 1. WebP Detection\n // RIFF .... WEBP\n if (\n data.length > 12 &&\n data[0] === 0x52 && data[1] === 0x49 && data[2] === 0x46 && data[3] === 0x46 && // RIFF\n data[8] === 0x57 && data[9] === 0x45 && data[10] === 0x42 && data[11] === 0x50 // WEBP\n ) {\n // Check for VP8X chunk\n // VP8X chunk header: 'VP8X' (bytes 12-15)\n if (\n data[12] === 0x56 && data[13] === 0x50 && data[14] === 0x38 && data[15] === 0x58\n ) {\n // Flags byte is at offset 20 (16 + 4 bytes chunk size)\n // Animation bit is bit 1 (0x02)\n const flags = data[20];\n return (flags! & 0x02) !== 0;\n }\n return false;\n }\n\n // 2. PNG/APNG Detection\n // Signature: 89 50 4E 47 0D 0A 1A 0A\n if (\n data.length > 8 &&\n data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4E && data[3] === 0x47\n ) {\n // Search for 'acTL' chunk (Animation Control)\n // It must appear before IDAT.\n // Simple search: indexOf('acTL')\n // Note: theoretically 'acTL' string could appear in other data, but highly unlikely in valid PNG structure before IDAT\n // We can iterate chunks to be safe, but indexOf is faster for a quick check\n const actlSig = fromLatin1('acTL');\n const idatSig = fromLatin1('IDAT');\n \n const actlIndex = indexOf(data, actlSig);\n if (actlIndex === -1) return false;\n\n const idatIndex = indexOf(data, idatSig);\n // If acTL exists and is before the first IDAT (or IDAT not found yet), it's APNG\n return idatIndex === -1 || actlIndex < idatIndex;\n }\n\n // 3. GIF Detection\n // Signature: GIF87a or GIF89a\n if (\n data.length > 6 &&\n data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46 // GIF\n ) {\n // Check for NETSCAPE2.0 extension (looping animation)\n // This is a heuristic. Static GIFs are rare in this domain but possible.\n // Full frame counting is expensive. Presence of NETSCAPE block is a strong indicator.\n const netscape = fromLatin1('NETSCAPE2.0');\n return indexOf(data, netscape) !== -1;\n }\n\n return false;\n}\n","/**\n * UUID Generation Utilities\n *\n * Provides crypto-grade UUID v4 generation that works in Node.js,\n * browsers (secure contexts), and falls back gracefully.\n */\n\n/**\n * Format 16 random bytes as a UUID v4 string\n */\nfunction formatUUID(bytes: Uint8Array): string {\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;\n}\n\n/**\n * Fallback UUID generation using Math.random()\n * Only used when crypto APIs are unavailable (rare)\n */\nfunction mathRandomUUID(): string {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Generate a cryptographically secure UUID v4.\n *\n * Uses crypto.randomUUID() when available (Node.js 19+, modern browsers).\n * Falls back to crypto.getRandomValues() if randomUUID is unavailable.\n * Last resort uses Math.random() (non-secure, emits warning in dev).\n *\n * @returns A valid RFC 4122 UUID v4 string\n *\n * @example\n * ```typescript\n * const id = generateUUID();\n * // => \"550e8400-e29b-41d4-a716-446655440000\"\n * ```\n */\nexport function generateUUID(): string {\n // Node.js 19+ or browser with secure context\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n\n // Fallback using crypto.getRandomValues (older Node/browsers)\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n // Set version (4) and variant (RFC 4122)\n bytes[6] = (bytes[6]! & 0x0f) | 0x40; // Version 4\n bytes[8] = (bytes[8]! & 0x3f) | 0x80; // Variant 1\n return formatUUID(bytes);\n }\n\n // Last resort - non-secure fallback\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') {\n console.warn('[character-foundry/core] generateUUID: Using insecure Math.random() fallback');\n }\n return mathRandomUUID();\n}\n\n/**\n * Validate if a string is a valid UUID v4\n *\n * @param uuid - String to validate\n * @returns true if valid UUID v4 format\n */\nexport function isValidUUID(uuid: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);\n}\n"],"mappings":"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","names":["isBase64"]}
|
|
1
|
+
{"version":3,"sources":["../../core/src/binary.ts","../../core/src/base64.ts","../../core/src/errors.ts","../../core/src/data-url.ts","../../core/src/uri.ts","../../core/src/image.ts","../../core/src/uuid.ts"],"sourcesContent":["/**\n * Binary Data Utilities\n *\n * Universal binary data operations using Uint8Array.\n * Works in both Node.js and browser environments.\n */\n\n/**\n * Universal binary data type (works in both environments)\n */\nexport type BinaryData = Uint8Array;\n\n/**\n * Read a 32-bit big-endian unsigned integer\n */\nexport function readUInt32BE(data: BinaryData, offset: number): number {\n return (\n (data[offset]! << 24) |\n (data[offset + 1]! << 16) |\n (data[offset + 2]! << 8) |\n data[offset + 3]!\n ) >>> 0;\n}\n\n/**\n * Write a 32-bit big-endian unsigned integer\n */\nexport function writeUInt32BE(data: BinaryData, value: number, offset: number): void {\n data[offset] = (value >>> 24) & 0xff;\n data[offset + 1] = (value >>> 16) & 0xff;\n data[offset + 2] = (value >>> 8) & 0xff;\n data[offset + 3] = value & 0xff;\n}\n\n/**\n * Read a 16-bit big-endian unsigned integer\n */\nexport function readUInt16BE(data: BinaryData, offset: number): number {\n return ((data[offset]! << 8) | data[offset + 1]!) >>> 0;\n}\n\n/**\n * Write a 16-bit big-endian unsigned integer\n */\nexport function writeUInt16BE(data: BinaryData, value: number, offset: number): void {\n data[offset] = (value >>> 8) & 0xff;\n data[offset + 1] = value & 0xff;\n}\n\n/**\n * Find a byte sequence in binary data\n */\nexport function indexOf(data: BinaryData, search: BinaryData, fromIndex = 0): number {\n outer: for (let i = fromIndex; i <= data.length - search.length; i++) {\n for (let j = 0; j < search.length; j++) {\n if (data[i + j] !== search[j]) continue outer;\n }\n return i;\n }\n return -1;\n}\n\n/**\n * Concatenate multiple binary arrays\n */\nexport function concat(...arrays: BinaryData[]): BinaryData {\n const totalLength = arrays.reduce((sum, arr) => sum + arr.length, 0);\n const result = new Uint8Array(totalLength);\n let offset = 0;\n for (const arr of arrays) {\n result.set(arr, offset);\n offset += arr.length;\n }\n return result;\n}\n\n/**\n * Slice binary data (returns a view, not a copy)\n */\nexport function slice(data: BinaryData, start: number, end?: number): BinaryData {\n return data.subarray(start, end);\n}\n\n/**\n * Copy a portion of binary data (returns a new array)\n */\nexport function copy(data: BinaryData, start: number, end?: number): BinaryData {\n return data.slice(start, end);\n}\n\n/**\n * Convert string to binary (UTF-8)\n */\nexport function fromString(str: string): BinaryData {\n return new TextEncoder().encode(str);\n}\n\n/**\n * Convert binary to string (UTF-8)\n */\nexport function toString(data: BinaryData): string {\n return new TextDecoder().decode(data);\n}\n\n/**\n * Convert string to binary (Latin1 - for PNG keywords and similar)\n */\nexport function fromLatin1(str: string): BinaryData {\n const result = new Uint8Array(str.length);\n for (let i = 0; i < str.length; i++) {\n result[i] = str.charCodeAt(i) & 0xff;\n }\n return result;\n}\n\n/**\n * Convert binary to string (Latin1)\n */\nexport function toLatin1(data: BinaryData): string {\n let result = '';\n for (let i = 0; i < data.length; i++) {\n result += String.fromCharCode(data[i]!);\n }\n return result;\n}\n\n/**\n * Compare two binary arrays for equality\n */\nexport function equals(a: BinaryData, b: BinaryData): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n/**\n * Create a new Uint8Array filled with zeros\n */\nexport function alloc(size: number): BinaryData {\n return new Uint8Array(size);\n}\n\n/**\n * Create a Uint8Array from an array of numbers\n */\nexport function from(data: number[] | ArrayBuffer | BinaryData): BinaryData {\n if (data instanceof Uint8Array) {\n return data;\n }\n if (data instanceof ArrayBuffer) {\n return new Uint8Array(data);\n }\n return new Uint8Array(data);\n}\n\n/**\n * Check if value is a Uint8Array\n */\nexport function isBinaryData(value: unknown): value is BinaryData {\n return value instanceof Uint8Array;\n}\n\n/**\n * Convert Node.js Buffer to Uint8Array (no-op if already Uint8Array)\n * This provides compatibility when interfacing with Node.js code\n */\nexport function toUint8Array(data: BinaryData | Buffer): BinaryData {\n if (data instanceof Uint8Array) {\n // Buffer extends Uint8Array, but we want a plain Uint8Array\n // This ensures we get a proper Uint8Array in all cases\n if (Object.getPrototypeOf(data).constructor.name === 'Buffer') {\n return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);\n }\n return data;\n }\n return new Uint8Array(data);\n}\n\n/**\n * Convert binary data to hex string\n */\nexport function toHex(data: BinaryData): string {\n return Array.from(data)\n .map(b => b.toString(16).padStart(2, '0'))\n .join('');\n}\n\n/**\n * Convert hex string to binary data\n */\nexport function fromHex(hex: string): BinaryData {\n const bytes = new Uint8Array(hex.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = parseInt(hex.substr(i * 2, 2), 16);\n }\n return bytes;\n}\n","/**\n * Universal Base64 Encoding/Decoding\n *\n * Works in both Node.js and browser environments.\n */\n\nimport type { BinaryData } from './binary.js';\n\n/**\n * Check if we're in a Node.js environment\n */\nconst isNode = typeof process !== 'undefined' &&\n process.versions != null &&\n process.versions.node != null;\n\n/**\n * Threshold for switching to chunked encoding in browsers (1MB)\n * Below this, simple string concatenation is fast enough.\n * Above this, quadratic string growth becomes a problem.\n */\nconst LARGE_BUFFER_THRESHOLD = 1024 * 1024;\n\n/**\n * Encode binary data to base64 string\n *\n * PERFORMANCE: For large buffers (>1MB) in browsers, this automatically\n * uses the chunked implementation to avoid quadratic string concatenation.\n */\nexport function encode(data: BinaryData): string {\n if (isNode) {\n // Node.js: Buffer handles large data efficiently\n return Buffer.from(data).toString('base64');\n }\n\n // Browser: use chunked encoding for large buffers to avoid O(n²) string growth\n if (data.length > LARGE_BUFFER_THRESHOLD) {\n return encodeChunked(data);\n }\n\n // Small buffers: simple approach is fast enough\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]!);\n }\n return btoa(binary);\n}\n\n/**\n * Decode base64 string to binary data\n */\nexport function decode(base64: string): BinaryData {\n if (isNode) {\n // Node.js: use Buffer\n return new Uint8Array(Buffer.from(base64, 'base64'));\n }\n\n // Browser: use atob\n const binary = atob(base64);\n const result = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n result[i] = binary.charCodeAt(i);\n }\n return result;\n}\n\n/**\n * Check if a string is valid base64\n */\nexport function isBase64(str: string): boolean {\n if (str.length === 0) return false;\n // Base64 regex: only valid base64 characters, length multiple of 4 (with padding)\n const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;\n return base64Regex.test(str) && str.length % 4 === 0;\n}\n\n/**\n * Encode binary data to URL-safe base64 string\n * Replaces + with -, / with _, and removes padding\n */\nexport function encodeUrlSafe(data: BinaryData): string {\n return encode(data)\n .replace(/\\+/g, '-')\n .replace(/\\//g, '_')\n .replace(/=+$/, '');\n}\n\n/**\n * Decode URL-safe base64 string to binary data\n */\nexport function decodeUrlSafe(base64: string): BinaryData {\n // Add back padding if needed\n let padded = base64\n .replace(/-/g, '+')\n .replace(/_/g, '/');\n\n while (padded.length % 4 !== 0) {\n padded += '=';\n }\n\n return decode(padded);\n}\n\n/**\n * Chunk size for encoding large buffers (64KB)\n * Prevents stack overflow when using String.fromCharCode with spread operator\n */\nconst ENCODE_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Encode binary data to base64 string with chunking for large buffers.\n * Handles buffers >10MB without stack overflow.\n *\n * @param data - Binary data to encode\n * @returns Base64 encoded string\n *\n * @example\n * ```typescript\n * const largeBuffer = new Uint8Array(20 * 1024 * 1024); // 20MB\n * const base64 = encodeChunked(largeBuffer); // No stack overflow\n * ```\n */\nexport function encodeChunked(data: BinaryData): string {\n if (isNode) {\n // Node.js: Buffer handles large data efficiently\n return Buffer.from(data).toString('base64');\n }\n\n // Browser: process in chunks to avoid stack overflow\n const chunks: string[] = [];\n\n for (let i = 0; i < data.length; i += ENCODE_CHUNK_SIZE) {\n const chunk = data.subarray(i, Math.min(i + ENCODE_CHUNK_SIZE, data.length));\n let binary = '';\n for (let j = 0; j < chunk.length; j++) {\n binary += String.fromCharCode(chunk[j]!);\n }\n chunks.push(binary);\n }\n\n return btoa(chunks.join(''));\n}\n","/**\n * Error Classes\n *\n * Specific error types for character card operations.\n * All errors extend FoundryError for consistent handling.\n */\n\n/** Symbol to identify FoundryError instances across ESM/CJS boundaries */\nconst FOUNDRY_ERROR_MARKER = Symbol.for('@character-foundry/core:FoundryError');\n\n/**\n * Base error class for all Character Foundry errors\n */\nexport class FoundryError extends Error {\n /** @internal Marker for cross-module identification */\n readonly [FOUNDRY_ERROR_MARKER] = true;\n\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = 'FoundryError';\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Error during card parsing\n */\nexport class ParseError extends FoundryError {\n constructor(message: string, public readonly format?: string) {\n super(message, 'PARSE_ERROR');\n this.name = 'ParseError';\n }\n}\n\n/**\n * Error during card validation\n */\nexport class ValidationError extends FoundryError {\n constructor(message: string, public readonly field?: string) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\n/**\n * Asset not found in card or archive\n */\nexport class AssetNotFoundError extends FoundryError {\n constructor(public readonly uri: string) {\n super(`Asset not found: ${uri}`, 'ASSET_NOT_FOUND');\n this.name = 'AssetNotFoundError';\n }\n}\n\n/**\n * Format not supported for operation\n */\nexport class FormatNotSupportedError extends FoundryError {\n constructor(public readonly format: string, operation?: string) {\n const msg = operation\n ? `Format '${format}' not supported for ${operation}`\n : `Format not supported: ${format}`;\n super(msg, 'FORMAT_NOT_SUPPORTED');\n this.name = 'FormatNotSupportedError';\n }\n}\n\n/**\n * File size exceeds limits\n */\nexport class SizeLimitError extends FoundryError {\n constructor(\n public readonly actualSize: number,\n public readonly maxSize: number,\n context?: string\n ) {\n const actualMB = (actualSize / 1024 / 1024).toFixed(2);\n const maxMB = (maxSize / 1024 / 1024).toFixed(2);\n const msg = context\n ? `${context}: Size ${actualMB}MB exceeds limit ${maxMB}MB`\n : `Size ${actualMB}MB exceeds limit ${maxMB}MB`;\n super(msg, 'SIZE_LIMIT_EXCEEDED');\n this.name = 'SizeLimitError';\n }\n}\n\n/**\n * Path traversal or unsafe path detected\n */\nexport class PathTraversalError extends FoundryError {\n constructor(public readonly path: string) {\n super(`Unsafe path detected: ${path}`, 'PATH_TRAVERSAL');\n this.name = 'PathTraversalError';\n }\n}\n\n/**\n * Export operation would lose data\n */\nexport class DataLossError extends FoundryError {\n constructor(\n public readonly lostFields: string[],\n public readonly targetFormat: string\n ) {\n const fields = lostFields.slice(0, 3).join(', ');\n const more = lostFields.length > 3 ? ` and ${lostFields.length - 3} more` : '';\n super(\n `Export to ${targetFormat} would lose: ${fields}${more}`,\n 'DATA_LOSS'\n );\n this.name = 'DataLossError';\n }\n}\n\n/**\n * Check if an error is a FoundryError\n *\n * Uses Symbol.for() marker instead of instanceof to handle dual ESM/CJS package loading.\n * In dual-package environments, instanceof can fail if the error comes from a different\n * module instance (e.g., ESM vs CJS version of the same package). Symbol.for() creates\n * a global symbol shared across all module instances.\n */\nexport function isFoundryError(error: unknown): error is FoundryError {\n return (\n error instanceof Error &&\n FOUNDRY_ERROR_MARKER in error &&\n (error as Record<symbol, unknown>)[FOUNDRY_ERROR_MARKER] === true\n );\n}\n\n/**\n * Wrap unknown errors in a FoundryError\n */\nexport function wrapError(error: unknown, context?: string): FoundryError {\n if (isFoundryError(error)) {\n return error;\n }\n\n const message = error instanceof Error\n ? error.message\n : String(error);\n\n return new FoundryError(\n context ? `${context}: ${message}` : message,\n 'UNKNOWN_ERROR'\n );\n}\n","/**\n * Data URL Utilities\n *\n * Convert between Uint8Array buffers and data URLs.\n * Handles large buffers (>10MB) without stack overflow by processing in chunks.\n */\n\nimport type { BinaryData } from './binary.js';\nimport { encodeChunked as base64Encode, decode as base64Decode } from './base64.js';\nimport { ValidationError } from './errors.js';\n\n/**\n * Convert Uint8Array to data URL.\n * Handles large buffers (>10MB) without stack overflow by processing in chunks.\n *\n * @param buffer - Binary data to encode\n * @param mimeType - MIME type for the data URL (e.g., 'image/png', 'application/octet-stream')\n * @returns Data URL string\n *\n * @example\n * ```typescript\n * const png = new Uint8Array([...]);\n * const dataUrl = toDataURL(png, 'image/png');\n * // => \"data:image/png;base64,iVBORw0KGgo...\"\n * ```\n */\nexport function toDataURL(buffer: BinaryData, mimeType: string): string {\n // Use chunked encoding to handle large buffers without stack overflow\n const base64 = base64Encode(buffer);\n return `data:${mimeType};base64,${base64}`;\n}\n\n/**\n * Parse a data URL back to buffer and MIME type.\n * Validates the data URL format before parsing.\n *\n * @param dataUrl - Data URL string to parse\n * @returns Object containing the decoded buffer and MIME type\n * @throws Error if the data URL format is invalid\n *\n * @example\n * ```typescript\n * const { buffer, mimeType } = fromDataURL('data:image/png;base64,iVBORw0KGgo...');\n * // buffer: Uint8Array\n * // mimeType: 'image/png'\n * ```\n */\nexport function fromDataURL(dataUrl: string): { buffer: Uint8Array; mimeType: string } {\n // Validate data URL format\n if (!dataUrl.startsWith('data:')) {\n throw new ValidationError('Invalid data URL: must start with \"data:\"', 'dataUrl');\n }\n\n const commaIndex = dataUrl.indexOf(',');\n if (commaIndex === -1) {\n throw new ValidationError('Invalid data URL: missing comma separator', 'dataUrl');\n }\n\n const header = dataUrl.slice(5, commaIndex); // Skip 'data:'\n const data = dataUrl.slice(commaIndex + 1);\n\n // Parse header: [<mediatype>][;base64]\n let mimeType = 'text/plain';\n let isBase64 = false;\n\n const parts = header.split(';');\n for (const part of parts) {\n if (part === 'base64') {\n isBase64 = true;\n } else if (part && !part.includes('=')) {\n // MIME type (not a parameter like charset=utf-8)\n mimeType = part;\n }\n }\n\n if (!isBase64) {\n // URL-encoded text data\n throw new ValidationError('Non-base64 data URLs are not supported', 'dataUrl');\n }\n\n const buffer = base64Decode(data);\n return { buffer, mimeType };\n}\n\n/**\n * Check if a string is a valid data URL\n *\n * @param str - String to check\n * @returns true if the string is a valid data URL format\n */\nexport function isDataURL(str: string): boolean {\n if (!str.startsWith('data:')) return false;\n const commaIndex = str.indexOf(',');\n if (commaIndex === -1) return false;\n const header = str.slice(5, commaIndex);\n return header.includes('base64');\n}\n","/**\n * URI Utilities\n *\n * Handles different asset URI schemes used in character cards.\n * Supports: embeded://, embedded://, ccdefault:, https://, http://,\n * data:, file://, __asset:, asset:, chara-ext-asset_\n */\n\nexport type URIScheme =\n | 'embeded' // embeded:// (CharX standard, note intentional typo)\n | 'ccdefault' // ccdefault:\n | 'https' // https://\n | 'http' // http://\n | 'data' // data:mime;base64,...\n | 'file' // file://\n | 'internal' // Internal asset ID (UUID/string)\n | 'pngchunk' // PNG chunk reference (__asset:, asset:, chara-ext-asset_)\n | 'unknown';\n\nexport interface ParsedURI {\n scheme: URIScheme;\n originalUri: string;\n normalizedUri: string; // Normalized form of the URI\n path?: string; // For embeded://, file://\n url?: string; // For http://, https://\n data?: string; // For data: URIs\n mimeType?: string; // For data: URIs\n encoding?: string; // For data: URIs (e.g., base64)\n chunkKey?: string; // For pngchunk - the key/index to look up\n chunkCandidates?: string[]; // For pngchunk - all possible chunk keys to search\n}\n\n/**\n * Normalize a URI to its canonical form\n * Handles common typos and variant formats\n */\nexport function normalizeURI(uri: string): string {\n const trimmed = uri.trim();\n\n // Fix embedded:// -> embeded:// (common typo, CharX spec uses single 'd')\n if (trimmed.startsWith('embedded://')) {\n return 'embeded://' + trimmed.substring('embedded://'.length);\n }\n\n // Normalize PNG chunk references to pngchunk: scheme\n if (trimmed.startsWith('__asset:')) {\n const id = trimmed.substring('__asset:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('asset:')) {\n const id = trimmed.substring('asset:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('chara-ext-asset_:')) {\n const id = trimmed.substring('chara-ext-asset_:'.length);\n return `pngchunk:${id}`;\n }\n if (trimmed.startsWith('chara-ext-asset_')) {\n const id = trimmed.substring('chara-ext-asset_'.length);\n return `pngchunk:${id}`;\n }\n\n return trimmed;\n}\n\n/**\n * Parse a URI and determine its scheme and components\n */\nexport function parseURI(uri: string): ParsedURI {\n const trimmed = uri.trim();\n const normalized = normalizeURI(trimmed);\n\n // PNG chunk references (__asset:, asset:, chara-ext-asset_, pngchunk:)\n if (\n trimmed.startsWith('__asset:') ||\n trimmed.startsWith('asset:') ||\n trimmed.startsWith('chara-ext-asset_') ||\n trimmed.startsWith('pngchunk:')\n ) {\n let assetId: string;\n if (trimmed.startsWith('__asset:')) {\n assetId = trimmed.substring('__asset:'.length);\n } else if (trimmed.startsWith('asset:')) {\n assetId = trimmed.substring('asset:'.length);\n } else if (trimmed.startsWith('chara-ext-asset_:')) {\n assetId = trimmed.substring('chara-ext-asset_:'.length);\n } else if (trimmed.startsWith('pngchunk:')) {\n assetId = trimmed.substring('pngchunk:'.length);\n } else {\n assetId = trimmed.substring('chara-ext-asset_'.length);\n }\n\n // Generate all possible chunk key variations for lookup\n const candidates = [\n assetId, // \"0\" or \"filename.png\"\n trimmed, // Original URI\n `asset:${assetId}`, // \"asset:0\"\n `__asset:${assetId}`, // \"__asset:0\"\n `__asset_${assetId}`, // \"__asset_0\"\n `chara-ext-asset_${assetId}`, // \"chara-ext-asset_0\"\n `chara-ext-asset_:${assetId}`, // \"chara-ext-asset_:0\"\n `pngchunk:${assetId}`, // \"pngchunk:0\"\n ];\n\n return {\n scheme: 'pngchunk',\n originalUri: uri,\n normalizedUri: normalized,\n chunkKey: assetId,\n chunkCandidates: candidates,\n };\n }\n\n // ccdefault: - use default asset\n if (trimmed === 'ccdefault:' || trimmed.startsWith('ccdefault:')) {\n return {\n scheme: 'ccdefault',\n originalUri: uri,\n normalizedUri: normalized,\n };\n }\n\n // embeded:// or embedded:// (normalize typo)\n if (trimmed.startsWith('embeded://') || trimmed.startsWith('embedded://')) {\n const path = trimmed.startsWith('embeded://')\n ? trimmed.substring('embeded://'.length)\n : trimmed.substring('embedded://'.length);\n return {\n scheme: 'embeded',\n originalUri: uri,\n normalizedUri: normalized,\n path,\n };\n }\n\n // https://\n if (trimmed.startsWith('https://')) {\n return {\n scheme: 'https',\n originalUri: uri,\n normalizedUri: normalized,\n url: trimmed,\n };\n }\n\n // http://\n if (trimmed.startsWith('http://')) {\n return {\n scheme: 'http',\n originalUri: uri,\n normalizedUri: normalized,\n url: trimmed,\n };\n }\n\n // data: URIs\n if (trimmed.startsWith('data:')) {\n const parsed = parseDataURI(trimmed);\n return {\n scheme: 'data',\n originalUri: uri,\n normalizedUri: normalized,\n ...parsed,\n };\n }\n\n // file://\n if (trimmed.startsWith('file://')) {\n const path = trimmed.substring('file://'.length);\n return {\n scheme: 'file',\n originalUri: uri,\n normalizedUri: normalized,\n path,\n };\n }\n\n // Internal asset ID (alphanumeric/UUID format)\n if (/^[a-zA-Z0-9_-]+$/.test(trimmed)) {\n return {\n scheme: 'internal',\n originalUri: uri,\n normalizedUri: normalized,\n path: trimmed,\n };\n }\n\n // Unknown scheme\n return {\n scheme: 'unknown',\n originalUri: uri,\n normalizedUri: normalized,\n };\n}\n\n/**\n * Parse a data URI into its components\n * Format: data:[<mediatype>][;base64],<data>\n */\nfunction parseDataURI(uri: string): { mimeType?: string; encoding?: string; data?: string } {\n const match = uri.match(/^data:([^;,]+)?(;base64)?,(.*)$/);\n\n if (!match) {\n return {};\n }\n\n return {\n mimeType: match[1] || 'text/plain',\n encoding: match[2] ? 'base64' : undefined,\n data: match[3],\n };\n}\n\n/**\n * Check if extension is an image format\n */\nexport function isImageExt(ext: string): boolean {\n const imageExts = ['png', 'jpg', 'jpeg', 'webp', 'gif', 'avif', 'bmp', 'svg'];\n return imageExts.includes(ext.toLowerCase());\n}\n\n/**\n * Check if extension is an audio format\n */\nexport function isAudioExt(ext: string): boolean {\n const audioExts = ['mp3', 'wav', 'ogg', 'flac', 'm4a', 'aac'];\n return audioExts.includes(ext.toLowerCase());\n}\n\n/**\n * Check if extension is a video format\n */\nexport function isVideoExt(ext: string): boolean {\n const videoExts = ['mp4', 'webm', 'avi', 'mov', 'mkv'];\n return videoExts.includes(ext.toLowerCase());\n}\n\n/** Safe MIME types for data: URIs that can be used in href/src */\nconst SAFE_DATA_URI_MIME_TYPES = new Set([\n // Images (safe for img src)\n 'image/png',\n 'image/jpeg',\n 'image/gif',\n 'image/webp',\n 'image/avif',\n 'image/bmp',\n 'image/x-icon',\n // Audio (safe for audio src)\n 'audio/mpeg',\n 'audio/wav',\n 'audio/ogg',\n 'audio/flac',\n 'audio/mp4',\n 'audio/aac',\n // Video (safe for video src)\n 'video/mp4',\n 'video/webm',\n // Text/data (generally safe)\n 'text/plain',\n 'application/json',\n 'application/octet-stream',\n]);\n\n/** Potentially dangerous MIME types that should NOT be used in href/src */\nconst DANGEROUS_DATA_URI_MIME_TYPES = new Set([\n // Executable/script content\n 'text/html',\n 'text/javascript',\n 'application/javascript',\n 'application/x-javascript',\n 'text/css',\n 'image/svg+xml', // SVG can contain scripts\n 'application/xhtml+xml',\n 'application/xml',\n]);\n\n/**\n * Options for URI safety validation\n */\nexport interface URISafetyOptions {\n /** Allow http:// URIs (default: false) */\n allowHttp?: boolean;\n /** Allow file:// URIs (default: false) */\n allowFile?: boolean;\n /**\n * Allowed MIME types for data: URIs (default: all safe types).\n * Set to empty array to reject all data: URIs.\n * Set to undefined to use default safe list.\n */\n allowedDataMimes?: string[];\n}\n\n/**\n * Result of URI safety check with detailed information\n */\nexport interface URISafetyResult {\n /** Whether the URI is safe to use */\n safe: boolean;\n /** Reason if unsafe */\n reason?: string;\n /** Detected scheme */\n scheme: URIScheme;\n /** MIME type for data: URIs */\n mimeType?: string;\n}\n\n/**\n * Validate if a URI is safe to use (detailed version)\n *\n * @param uri - URI to validate\n * @param options - Safety options\n * @returns Detailed safety result\n */\nexport function checkURISafety(uri: string, options: URISafetyOptions = {}): URISafetyResult {\n const parsed = parseURI(uri);\n\n switch (parsed.scheme) {\n case 'embeded':\n case 'ccdefault':\n case 'internal':\n case 'https':\n case 'pngchunk':\n return { safe: true, scheme: parsed.scheme };\n\n case 'data': {\n const mimeType = parsed.mimeType || 'text/plain';\n\n // Check for explicitly dangerous MIME types\n if (DANGEROUS_DATA_URI_MIME_TYPES.has(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Data URI with potentially dangerous MIME type: ${mimeType}`,\n };\n }\n\n // If custom allowed list is provided, check against it\n if (options.allowedDataMimes !== undefined) {\n if (options.allowedDataMimes.length === 0) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: 'Data URIs are not allowed',\n };\n }\n if (!options.allowedDataMimes.includes(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Data URI MIME type not in allowed list: ${mimeType}`,\n };\n }\n }\n\n // Otherwise use default safe list\n if (!SAFE_DATA_URI_MIME_TYPES.has(mimeType)) {\n return {\n safe: false,\n scheme: parsed.scheme,\n mimeType,\n reason: `Unknown data URI MIME type: ${mimeType}`,\n };\n }\n\n return { safe: true, scheme: parsed.scheme, mimeType };\n }\n\n case 'http':\n if (options.allowHttp === true) {\n return { safe: true, scheme: parsed.scheme };\n }\n return { safe: false, scheme: parsed.scheme, reason: 'HTTP URIs are not allowed' };\n\n case 'file':\n if (options.allowFile === true) {\n return { safe: true, scheme: parsed.scheme };\n }\n return { safe: false, scheme: parsed.scheme, reason: 'File URIs are not allowed' };\n\n case 'unknown':\n default:\n return { safe: false, scheme: parsed.scheme, reason: 'Unknown URI scheme' };\n }\n}\n\n/**\n * Validate if a URI is safe to use (simple boolean version for backwards compatibility)\n *\n * @deprecated Use checkURISafety() for detailed safety information\n */\nexport function isURISafe(uri: string, options: { allowHttp?: boolean; allowFile?: boolean } = {}): boolean {\n return checkURISafety(uri, options).safe;\n}\n\n/**\n * Extract file extension from URI\n */\nexport function getExtensionFromURI(uri: string): string {\n const parsed = parseURI(uri);\n\n if (parsed.path) {\n const parts = parsed.path.split('.');\n if (parts.length > 1) {\n return parts[parts.length - 1]!.toLowerCase();\n }\n }\n\n if (parsed.url) {\n const urlParts = parsed.url.split('?')[0]!.split('.');\n if (urlParts.length > 1) {\n return urlParts[urlParts.length - 1]!.toLowerCase();\n }\n }\n\n if (parsed.mimeType) {\n return getExtFromMimeType(parsed.mimeType);\n }\n\n return 'unknown';\n}\n\n/**\n * Get MIME type from file extension\n */\nexport function getMimeTypeFromExt(ext: string): string {\n const extToMime: Record<string, string> = {\n // Images\n 'png': 'image/png',\n 'jpg': 'image/jpeg',\n 'jpeg': 'image/jpeg',\n 'webp': 'image/webp',\n 'gif': 'image/gif',\n 'avif': 'image/avif',\n 'svg': 'image/svg+xml',\n 'bmp': 'image/bmp',\n 'ico': 'image/x-icon',\n\n // Audio\n 'mp3': 'audio/mpeg',\n 'wav': 'audio/wav',\n 'ogg': 'audio/ogg',\n 'flac': 'audio/flac',\n 'm4a': 'audio/mp4',\n 'aac': 'audio/aac',\n\n // Video\n 'mp4': 'video/mp4',\n 'webm': 'video/webm',\n 'avi': 'video/x-msvideo',\n 'mov': 'video/quicktime',\n 'mkv': 'video/x-matroska',\n\n // Text/Data\n 'json': 'application/json',\n 'txt': 'text/plain',\n 'html': 'text/html',\n 'css': 'text/css',\n 'js': 'application/javascript',\n };\n\n return extToMime[ext.toLowerCase()] || 'application/octet-stream';\n}\n\n/**\n * Get file extension from MIME type\n */\nexport function getExtFromMimeType(mimeType: string): string {\n const mimeToExt: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/webp': 'webp',\n 'image/gif': 'gif',\n 'image/avif': 'avif',\n 'image/svg+xml': 'svg',\n 'image/bmp': 'bmp',\n 'image/x-icon': 'ico',\n 'audio/mpeg': 'mp3',\n 'audio/wav': 'wav',\n 'audio/ogg': 'ogg',\n 'audio/flac': 'flac',\n 'audio/mp4': 'm4a',\n 'audio/aac': 'aac',\n 'video/mp4': 'mp4',\n 'video/webm': 'webm',\n 'video/x-msvideo': 'avi',\n 'video/quicktime': 'mov',\n 'video/x-matroska': 'mkv',\n 'application/json': 'json',\n 'text/plain': 'txt',\n 'text/html': 'html',\n 'text/css': 'css',\n 'application/javascript': 'js',\n };\n\n return mimeToExt[mimeType] || 'bin';\n}\n\n/**\n * Build a data URI from binary data and MIME type\n */\nexport function buildDataURI(data: string, mimeType: string, isBase64 = true): string {\n if (isBase64) {\n return `data:${mimeType};base64,${data}`;\n }\n return `data:${mimeType},${encodeURIComponent(data)}`;\n}\n","/**\n * Image Analysis Utilities\n *\n * Detect properties of image files from binary data.\n */\n\nimport {\n type BinaryData,\n indexOf,\n fromLatin1,\n} from './binary.js';\n\n/**\n * Check if an image buffer contains animation data.\n * Supports: APNG, WebP (Animated), GIF\n */\nexport function isAnimatedImage(data: BinaryData, _mimeType?: string): boolean {\n // 1. WebP Detection\n // RIFF .... WEBP\n if (\n data.length > 12 &&\n data[0] === 0x52 && data[1] === 0x49 && data[2] === 0x46 && data[3] === 0x46 && // RIFF\n data[8] === 0x57 && data[9] === 0x45 && data[10] === 0x42 && data[11] === 0x50 // WEBP\n ) {\n // Check for VP8X chunk\n // VP8X chunk header: 'VP8X' (bytes 12-15)\n if (\n data[12] === 0x56 && data[13] === 0x50 && data[14] === 0x38 && data[15] === 0x58\n ) {\n // Flags byte is at offset 20 (16 + 4 bytes chunk size)\n // Animation bit is bit 1 (0x02)\n const flags = data[20];\n return (flags! & 0x02) !== 0;\n }\n return false;\n }\n\n // 2. PNG/APNG Detection\n // Signature: 89 50 4E 47 0D 0A 1A 0A\n if (\n data.length > 8 &&\n data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4E && data[3] === 0x47\n ) {\n // Search for 'acTL' chunk (Animation Control)\n // It must appear before IDAT.\n // Simple search: indexOf('acTL')\n // Note: theoretically 'acTL' string could appear in other data, but highly unlikely in valid PNG structure before IDAT\n // We can iterate chunks to be safe, but indexOf is faster for a quick check\n const actlSig = fromLatin1('acTL');\n const idatSig = fromLatin1('IDAT');\n \n const actlIndex = indexOf(data, actlSig);\n if (actlIndex === -1) return false;\n\n const idatIndex = indexOf(data, idatSig);\n // If acTL exists and is before the first IDAT (or IDAT not found yet), it's APNG\n return idatIndex === -1 || actlIndex < idatIndex;\n }\n\n // 3. GIF Detection\n // Signature: GIF87a or GIF89a\n if (\n data.length > 6 &&\n data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46 // GIF\n ) {\n // Check for NETSCAPE2.0 extension (looping animation)\n // This is a heuristic. Static GIFs are rare in this domain but possible.\n // Full frame counting is expensive. Presence of NETSCAPE block is a strong indicator.\n const netscape = fromLatin1('NETSCAPE2.0');\n return indexOf(data, netscape) !== -1;\n }\n\n return false;\n}\n","/**\n * UUID Generation Utilities\n *\n * Provides crypto-grade UUID v4 generation that works in Node.js,\n * browsers (secure contexts), and falls back gracefully.\n */\n\n/**\n * Format 16 random bytes as a UUID v4 string\n */\nfunction formatUUID(bytes: Uint8Array): string {\n const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;\n}\n\n/**\n * Fallback UUID generation using Math.random()\n * Only used when crypto APIs are unavailable (rare)\n */\nfunction mathRandomUUID(): string {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0;\n const v = c === 'x' ? r : (r & 0x3) | 0x8;\n return v.toString(16);\n });\n}\n\n/**\n * Generate a cryptographically secure UUID v4.\n *\n * Uses crypto.randomUUID() when available (Node.js 19+, modern browsers).\n * Falls back to crypto.getRandomValues() if randomUUID is unavailable.\n * Last resort uses Math.random() (non-secure, emits warning in dev).\n *\n * @returns A valid RFC 4122 UUID v4 string\n *\n * @example\n * ```typescript\n * const id = generateUUID();\n * // => \"550e8400-e29b-41d4-a716-446655440000\"\n * ```\n */\nexport function generateUUID(): string {\n // Node.js 19+ or browser with secure context\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return crypto.randomUUID();\n }\n\n // Fallback using crypto.getRandomValues (older Node/browsers)\n if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n // Set version (4) and variant (RFC 4122)\n bytes[6] = (bytes[6]! & 0x0f) | 0x40; // Version 4\n bytes[8] = (bytes[8]! & 0x3f) | 0x80; // Variant 1\n return formatUUID(bytes);\n }\n\n // Last resort - non-secure fallback\n if (typeof process !== 'undefined' && process.env?.NODE_ENV === 'development') {\n console.warn('[character-foundry/core] generateUUID: Using insecure Math.random() fallback');\n }\n return mathRandomUUID();\n}\n\n/**\n * Validate if a string is a valid UUID v4\n *\n * @param uuid - String to validate\n * @returns true if valid UUID v4 format\n */\nexport function isValidUUID(uuid: string): boolean {\n return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid);\n}\n"],"mappings":"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","names":["isBase64"]}
|
package/dist/exporter.cjs
CHANGED
|
@@ -7042,7 +7042,7 @@ function sanitizeName2(name, ext) {
|
|
|
7042
7042
|
return safeName;
|
|
7043
7043
|
}
|
|
7044
7044
|
function writeVoxta(card, assets, options = {}) {
|
|
7045
|
-
|
|
7045
|
+
const { compressionLevel = 6, includePackageJson = false } = options;
|
|
7046
7046
|
const cardData = card.data;
|
|
7047
7047
|
const extensions = cardData.extensions;
|
|
7048
7048
|
const voxtaExt = extensions?.voxta;
|
|
@@ -7242,7 +7242,7 @@ function checkPngLoss(card, assets) {
|
|
|
7242
7242
|
if (soundAssets.length > 0) {
|
|
7243
7243
|
warnings.push(`${soundAssets.length} sound assets cannot be embedded in PNG`);
|
|
7244
7244
|
}
|
|
7245
|
-
const
|
|
7245
|
+
const _extensions = card.data.extensions;
|
|
7246
7246
|
if (card.data.assets && card.data.assets.length > 1) {
|
|
7247
7247
|
warnings.push("Card has multiple asset references - only main will be available");
|
|
7248
7248
|
}
|
|
@@ -7254,7 +7254,7 @@ function checkPngLoss(card, assets) {
|
|
|
7254
7254
|
isLossless: lostFields.length === 0 && lostAssets.length === 0
|
|
7255
7255
|
};
|
|
7256
7256
|
}
|
|
7257
|
-
function checkCharxLoss(card,
|
|
7257
|
+
function checkCharxLoss(card, _assets) {
|
|
7258
7258
|
const lostFields = [];
|
|
7259
7259
|
const lostAssets = [];
|
|
7260
7260
|
const warnings = [];
|