/**
 * Plugin Status
 */
export var PluginStatus;
(function (PluginStatus) {
    PluginStatus["LOADED"] = "loaded";
    PluginStatus["ACTIVE"] = "active";
    PluginStatus["DISABLED"] = "disabled";
    PluginStatus["ERROR"] = "error";
})(PluginStatus || (PluginStatus = {}));
/**
 * Manifest Validation Error
 */
export class ManifestValidationError extends Error {
    field;
    value;
    constructor(message, field, value) {
        super(message);
        this.field = field;
        this.value = value;
        this.name = 'ManifestValidationError';
    }
}
/**
 * Validate a plugin manifest
 * Ensures all required fields are present and have valid formats
 */
export function validateManifest(manifest) {
    const errors = [];
    if (!manifest || typeof manifest !== 'object') {
        return { valid: false, errors: ['Manifest must be a valid object'] };
    }
    const m = manifest;
    // Required string fields
    const requiredStrings = ['id', 'name', 'version', 'description'];
    for (const field of requiredStrings) {
        if (typeof m[field] !== 'string' || m[field].trim() === '') {
            errors.push(`Missing or invalid required field: ${field}`);
        }
    }
    // Validate ID format (alphanumeric, dashes, underscores only)
    if (typeof m.id === 'string' && !/^[a-zA-Z0-9_-]+$/.test(m.id)) {
        errors.push('Plugin ID must contain only alphanumeric characters, dashes, and underscores');
    }
    // Validate ID length (prevent path traversal attempts)
    if (typeof m.id === 'string' && (m.id.length < 2 || m.id.length > 64)) {
        errors.push('Plugin ID must be between 2 and 64 characters');
    }
    // Validate version format (semver-like)
    if (typeof m.version === 'string' && !/^\d+\.\d+\.\d+(-[\w.]+)?$/.test(m.version)) {
        errors.push('Version must be in semver format (e.g., 1.0.0)');
    }
    // Validate optional string fields if present
    const optionalStrings = ['author', 'license', 'homepage', 'repository', 'minApiVersion'];
    for (const field of optionalStrings) {
        if (m[field] !== undefined && typeof m[field] !== 'string') {
            errors.push(`Invalid field type for ${field}: expected string`);
        }
    }
    // Validate permissions if present
    if (m.permissions !== undefined) {
        const permResult = validatePermissions(m.permissions);
        if (!permResult.valid) {
            errors.push(...permResult.errors.map(e => `permissions: ${e}`));
        }
    }
    // Validate isolated field if present
    if (m.isolated !== undefined && typeof m.isolated !== 'boolean') {
        errors.push('Field "isolated" must be a boolean');
    }
    // Validate defaultConfig if present
    if (m.defaultConfig !== undefined) {
        if (typeof m.defaultConfig !== 'object' || m.defaultConfig === null || Array.isArray(m.defaultConfig)) {
            errors.push('Field "defaultConfig" must be an object');
        }
    }
    // Validate configSchema if present
    if (m.configSchema !== undefined) {
        const schemaResult = validateConfigSchema(m.configSchema);
        if (!schemaResult.valid) {
            errors.push(...schemaResult.errors.map(e => `configSchema: ${e}`));
        }
    }
    // Security: Check for suspicious fields that shouldn't exist
    const allowedFields = new Set([
        'id', 'name', 'version', 'description', 'author', 'license',
        'homepage', 'repository', 'minApiVersion', 'permissions', 'isolated',
        'defaultConfig', 'configSchema'
    ]);
    for (const key of Object.keys(m)) {
        if (!allowedFields.has(key)) {
            errors.push(`Unknown field in manifest: ${key}`);
        }
    }
    return { valid: errors.length === 0, errors };
}
/**
 * Validate plugin permissions object
 */
export function validatePermissions(permissions) {
    const errors = [];
    if (!permissions || typeof permissions !== 'object') {
        return { valid: false, errors: ['Permissions must be an object'] };
    }
    const p = permissions;
    // Validate filesystem permission
    if (p.filesystem !== undefined) {
        if (typeof p.filesystem !== 'boolean' && !isStringArray(p.filesystem)) {
            errors.push('filesystem must be boolean or array of path strings');
        }
        if (Array.isArray(p.filesystem)) {
            for (const path of p.filesystem) {
                if (typeof path !== 'string') {
                    errors.push('filesystem paths must be strings');
                    break;
                }
                // Security: Prevent path traversal
                if (path.includes('..') || path.startsWith('/')) {
                    errors.push(`Invalid filesystem path: ${path} (no .. or absolute paths allowed)`);
                }
            }
        }
    }
    // Validate network permission
    if (p.network !== undefined) {
        if (typeof p.network !== 'boolean' && !isStringArray(p.network)) {
            errors.push('network must be boolean or array of domain strings');
        }
        if (Array.isArray(p.network)) {
            for (const domain of p.network) {
                if (typeof domain !== 'string') {
                    errors.push('network domains must be strings');
                    break;
                }
                // Basic domain validation
                if (!/^[a-zA-Z0-9.-]+$/.test(domain)) {
                    errors.push(`Invalid network domain: ${domain}`);
                }
            }
        }
    }
    // Validate shell permission
    if (p.shell !== undefined && typeof p.shell !== 'boolean') {
        errors.push('shell must be a boolean');
    }
    // Validate env permission
    if (p.env !== undefined && typeof p.env !== 'boolean') {
        errors.push('env must be a boolean');
    }
    // Security: Check for unknown permission fields
    const allowedPermFields = new Set(['filesystem', 'network', 'shell', 'env']);
    for (const key of Object.keys(p)) {
        if (!allowedPermFields.has(key)) {
            errors.push(`Unknown permission field: ${key}`);
        }
    }
    return { valid: errors.length === 0, errors };
}
/**
 * Helper to check if value is an array of strings
 */
function isStringArray(value) {
    return Array.isArray(value) && value.every(item => typeof item === 'string');
}
/**
 * Check if a plugin has a specific permission
 */
export function hasPermission(permissions, type, target) {
    if (!permissions) {
        return false;
    }
    const perm = permissions[type];
    if (perm === undefined || perm === false) {
        return false;
    }
    if (perm === true) {
        return true;
    }
    // For array permissions, check if target is in the list
    if (Array.isArray(perm) && target) {
        if (type === 'filesystem') {
            // For filesystem, check if path starts with any allowed path
            return perm.some(allowed => target.startsWith(allowed));
        }
        if (type === 'network') {
            // For network, check exact domain match or subdomain
            return perm.some(allowed => target === allowed || target.endsWith(`.${allowed}`));
        }
    }
    return false;
}
/**
 * Blocked module list for sandboxing
 * These modules should never be accessible to plugins without explicit permission
 */
export const BLOCKED_MODULES = {
    always: ['cluster', 'dgram', 'dns', 'tls', 'v8', 'vm', 'worker_threads', 'repl'],
    withoutShell: ['child_process'],
    withoutFilesystem: ['fs', 'fs/promises', 'fs-extra'],
    withoutNetwork: ['net', 'http', 'https', 'http2'],
};
/**
 * Get list of blocked modules based on permissions
 */
export function getBlockedModules(permissions) {
    const blocked = [...BLOCKED_MODULES.always];
    if (!permissions.shell) {
        blocked.push(...BLOCKED_MODULES.withoutShell);
    }
    if (!permissions.filesystem) {
        blocked.push(...BLOCKED_MODULES.withoutFilesystem);
    }
    if (!permissions.network) {
        blocked.push(...BLOCKED_MODULES.withoutNetwork);
    }
    return blocked;
}
/**
 * Validate a plugin configuration schema
 */
export function validateConfigSchema(schema) {
    const errors = [];
    if (!schema || typeof schema !== 'object') {
        return { valid: false, errors: ['Config schema must be an object'] };
    }
    const s = schema;
    if (s.type !== 'object') {
        errors.push('Config schema type must be "object"');
    }
    if (s.properties !== undefined) {
        if (typeof s.properties !== 'object' || s.properties === null || Array.isArray(s.properties)) {
            errors.push('Config schema properties must be an object');
        }
        else {
            const props = s.properties;
            for (const [key, value] of Object.entries(props)) {
                if (!isValidPropertySchema(value)) {
                    errors.push(`Invalid property schema for "${key}"`);
                }
            }
        }
    }
    if (s.required !== undefined && !isStringArray(s.required)) {
        errors.push('Config schema required must be an array of strings');
    }
    return { valid: errors.length === 0, errors };
}
/**
 * Check if a value is a valid property schema
 */
function isValidPropertySchema(schema) {
    if (!schema || typeof schema !== 'object') {
        return false;
    }
    const s = schema;
    const validTypes = ['string', 'number', 'boolean', 'array', 'object'];
    if (!validTypes.includes(s.type)) {
        return false;
    }
    return true;
}
/**
 * Validate a configuration value against a schema
 */
export function validateConfigValue(value, schema, path = '') {
    const errors = [];
    const fullPath = path || 'config';
    // Type validation
    switch (schema.type) {
        case 'string':
            if (typeof value !== 'string') {
                errors.push(`${fullPath}: expected string, got ${typeof value}`);
            }
            else {
                if (schema.minLength !== undefined && value.length < schema.minLength) {
                    errors.push(`${fullPath}: string too short (min ${schema.minLength})`);
                }
                if (schema.maxLength !== undefined && value.length > schema.maxLength) {
                    errors.push(`${fullPath}: string too long (max ${schema.maxLength})`);
                }
                if (schema.pattern !== undefined) {
                    try {
                        const regex = new RegExp(schema.pattern);
                        if (!regex.test(value)) {
                            errors.push(`${fullPath}: string does not match pattern "${schema.pattern}"`);
                        }
                    }
                    catch {
                        // Invalid regex in schema - skip pattern validation
                    }
                }
                if (schema.enum !== undefined && !schema.enum.includes(value)) {
                    errors.push(`${fullPath}: value must be one of: ${schema.enum.join(', ')}`);
                }
            }
            break;
        case 'number':
            if (typeof value !== 'number' || isNaN(value)) {
                errors.push(`${fullPath}: expected number, got ${typeof value}`);
            }
            else {
                if (schema.minimum !== undefined && value < schema.minimum) {
                    errors.push(`${fullPath}: number too small (min ${schema.minimum})`);
                }
                if (schema.maximum !== undefined && value > schema.maximum) {
                    errors.push(`${fullPath}: number too large (max ${schema.maximum})`);
                }
                if (schema.enum !== undefined && !schema.enum.includes(value)) {
                    errors.push(`${fullPath}: value must be one of: ${schema.enum.join(', ')}`);
                }
            }
            break;
        case 'boolean':
            if (typeof value !== 'boolean') {
                errors.push(`${fullPath}: expected boolean, got ${typeof value}`);
            }
            if (schema.enum !== undefined && !schema.enum.includes(value)) {
                errors.push(`${fullPath}: value must be one of: ${schema.enum.join(', ')}`);
            }
            break;
        case 'array':
            if (!Array.isArray(value)) {
                errors.push(`${fullPath}: expected array, got ${typeof value}`);
            }
            else if (schema.items) {
                for (let i = 0; i < value.length; i++) {
                    const itemResult = validateConfigValue(value[i], schema.items, `${fullPath}[${i}]`);
                    errors.push(...itemResult.errors);
                }
            }
            break;
        case 'object':
            if (typeof value !== 'object' || value === null || Array.isArray(value)) {
                errors.push(`${fullPath}: expected object, got ${typeof value}`);
            }
            break;
    }
    return { valid: errors.length === 0, errors };
}
/**
 * Validate a complete plugin configuration against its schema
 */
export function validatePluginConfig(config, schema) {
    const errors = [];
    // Check required fields
    if (schema.required) {
        for (const field of schema.required) {
            if (config[field] === undefined) {
                errors.push(`Missing required config field: ${field}`);
            }
        }
    }
    // Validate each property
    for (const [key, value] of Object.entries(config)) {
        const propertySchema = schema.properties[key];
        if (propertySchema) {
            const result = validateConfigValue(value, propertySchema, key);
            errors.push(...result.errors);
        }
        // Allow unknown properties for forward compatibility
    }
    return { valid: errors.length === 0, errors };
}
//# sourceMappingURL=types.js.map