/**
 * Base types and interfaces for asset management
 */
/**
 * Content type mappings
 */
export const CONTENT_TYPES = {
    // Images
    png: 'image/png',
    jpg: 'image/jpeg',
    jpeg: 'image/jpeg',
    gif: 'image/gif',
    webp: 'image/webp',
    svg: 'image/svg+xml',
    ico: 'image/x-icon',
    // Videos
    mp4: 'video/mp4',
    mov: 'video/quicktime',
    webm: 'video/webm',
    avi: 'video/x-msvideo',
    // Audio
    mp3: 'audio/mpeg',
    wav: 'audio/wav',
    ogg: 'audio/ogg',
    m4a: 'audio/mp4',
    // Documents
    pdf: 'application/pdf',
    doc: 'application/msword',
    docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    xls: 'application/vnd.ms-excel',
    xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    ppt: 'application/vnd.ms-powerpoint',
    pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
    // Archives
    zip: 'application/zip',
    tar: 'application/x-tar',
    gz: 'application/gzip',
    // Design files
    fig: 'application/octet-stream',
    sketch: 'application/octet-stream',
    // Text
    txt: 'text/plain',
    json: 'application/json',
    xml: 'application/xml',
    csv: 'text/csv',
};
/**
 * Detect content type from filename
 */
export function detectContentType(filename) {
    const ext = filename.split('.').pop()?.toLowerCase() || '';
    return CONTENT_TYPES[ext] || 'application/octet-stream';
}
/**
 * Map a filename to the high-level `asset_kind` written into the asset
 * entity's frontmatter. Drives icon and viewer selection; coarser than
 * MIME so the UI doesn't need to enumerate every format. The kind set
 * is canonical in `ASSET_KINDS` in src/core/validation.ts.
 */
export function detectAssetKind(filename) {
    const ext = filename.split('.').pop()?.toLowerCase() || '';
    if (IMAGE_EXTS.has(ext))
        return 'image';
    if (VIDEO_EXTS.has(ext))
        return 'video';
    if (AUDIO_EXTS.has(ext))
        return 'audio';
    if (ext === 'pdf')
        return 'pdf';
    if (SPREADSHEET_EXTS.has(ext))
        return 'spreadsheet';
    return 'other';
}
// SVG is intentionally excluded — see docs/asset-architecture.md §security.
// Stored XSS risk: SVG can contain <script> and the GET /api/assets/* route
// serves files inline with `Content-Type: image/svg+xml`, which executes in
// the app origin. Users who need vector assets can paste source into a note
// or embed via the editor's per-entity attach (which also blocks SVG).
const IMAGE_EXTS = new Set(['jpg', 'jpeg', 'png', 'gif', 'webp', 'avif', 'heic']);
const VIDEO_EXTS = new Set(['mp4', 'mov', 'webm', 'avi', 'mkv', 'm4v']);
const AUDIO_EXTS = new Set(['mp3', 'wav', 'm4a', 'ogg', 'flac', 'aac']);
const SPREADSHEET_EXTS = new Set(['csv', 'xlsx', 'xls', 'numbers', 'tsv']);
// Other formerly-categorized extensions (indd, sketch, psd, ai, fig, doc/docx,
// pptx, key, pages, zip, tar, etc.) all fall to `other`. The product has no
// preview / extract support for them today, so the previous fine-grained
// kinds were noise. If preview support is added later, reintroduce a kind.
const OTHER_UPLOAD_EXTS = new Set([
    'indd', 'idml', 'sketch', 'psd', 'ai', 'fig',
    'doc', 'pptx', 'ppt', 'key', 'pages',
    'zip', 'tar', 'gz', '7z', 'rar',
]);
/**
 * Extension set the asset upload route accepts. Excludes the structured
 * formats handled elsewhere (.md/.txt → direct write via import-service,
 * .docx → mammoth workstream, .html → bookmark importer).
 */
export const ASSET_UPLOAD_EXTENSIONS = new Set([
    ...IMAGE_EXTS,
    ...VIDEO_EXTS,
    ...AUDIO_EXTS,
    'pdf',
    ...SPREADSHEET_EXTS,
    ...OTHER_UPLOAD_EXTS,
]);
export function isAssetUploadExtension(filename) {
    const ext = filename.split('.').pop()?.toLowerCase() || '';
    return ASSET_UPLOAD_EXTENSIONS.has(ext);
}
/**
 * Determine media type from content type
 */
export function getMediaType(contentType) {
    if (contentType.startsWith('image/'))
        return 'image';
    if (contentType.startsWith('video/'))
        return 'video';
    if (contentType.startsWith('audio/'))
        return 'audio';
    if (contentType.includes('pdf') ||
        contentType.includes('document') ||
        contentType.includes('word') ||
        contentType.includes('excel') ||
        contentType.includes('powerpoint') ||
        contentType.includes('presentation')) {
        return 'document';
    }
    return 'other';
}
/**
 * Validate filename for security
 */
export function validateFilename(filename) {
    if (!filename || filename.trim() === '') {
        throw new Error('Filename cannot be empty');
    }
    // Check for path traversal
    if (filename.includes('..') ||
        filename.includes('/') ||
        filename.includes('\\')) {
        throw new Error('Filename cannot contain path separators or traversal sequences');
    }
    // Check for hidden files
    if (filename.startsWith('.')) {
        throw new Error('Filename cannot start with a dot (hidden files not allowed)');
    }
    // Check for forbidden characters
    const forbiddenChars = /[<>:"|?*\x00-\x1f]/;
    if (forbiddenChars.test(filename)) {
        throw new Error('Filename contains forbidden characters');
    }
    // Check length
    if (filename.length > 255) {
        throw new Error('Filename too long (max 255 characters)');
    }
}
/**
 * Build asset key from components
 */
export function buildAssetKey(entityType, entityId, filename, keyPrefix) {
    return `${keyPrefix || entityType}/${entityId}/${filename}`;
}
/**
 * Build asset prefix for listing
 */
export function buildAssetPrefix(entityType, entityId, keyPrefix) {
    return `${keyPrefix || entityType}/${entityId}/`;
}
//# sourceMappingURL=base.js.map