// engine/well-known/discovery.ts — SPEC-162: Parse and validate a .well-known/planu.json manifest
/** Validate that a value is a non-empty string. */
function isNonEmptyString(value) {
    return typeof value === 'string' && value.trim().length > 0;
}
/** Validate a single ManifestSpec entry. Returns error strings or empty array. */
function validateManifestSpec(spec, index) {
    const errors = [];
    const prefix = `specs[${String(index)}]`;
    if (spec === null || typeof spec !== 'object') {
        return [`${prefix}: must be an object`];
    }
    const s = spec;
    if (!isNonEmptyString(s.id)) {
        errors.push(`${prefix}.id: must be a non-empty string`);
    }
    else if (!/^SPEC-\d+$/.test(s.id)) {
        errors.push(`${prefix}.id: must match pattern SPEC-NNN (e.g. SPEC-001)`);
    }
    if (!isNonEmptyString(s.title)) {
        errors.push(`${prefix}.title: must be a non-empty string`);
    }
    if (!isNonEmptyString(s.status)) {
        errors.push(`${prefix}.status: must be a non-empty string`);
    }
    if (!isNonEmptyString(s.type)) {
        errors.push(`${prefix}.type: must be a non-empty string`);
    }
    if (typeof s.description !== 'string') {
        errors.push(`${prefix}.description: must be a string`);
    }
    if (!Array.isArray(s.tags)) {
        errors.push(`${prefix}.tags: must be an array`);
    }
    if (!isNonEmptyString(s.created)) {
        errors.push(`${prefix}.created: must be a non-empty string`);
    }
    return errors;
}
/** Validate the top-level structure of a parsed manifest object. */
function validateManifestShape(raw) {
    const errors = [];
    if (raw === null || typeof raw !== 'object') {
        return ['manifest: must be a JSON object'];
    }
    const m = raw;
    if (m.version !== 1) {
        errors.push(`version: must be 1, got ${String(m.version)}`);
    }
    if (!isNonEmptyString(m.name)) {
        errors.push('name: must be a non-empty string');
    }
    if (typeof m.specCount !== 'number' || !Number.isInteger(m.specCount) || m.specCount < 0) {
        errors.push('specCount: must be a non-negative integer');
    }
    if (!Array.isArray(m.specs)) {
        errors.push('specs: must be an array');
    }
    else {
        for (let i = 0; i < m.specs.length; i++) {
            const specErrors = validateManifestSpec(m.specs[i], i);
            errors.push(...specErrors);
        }
    }
    if (!Array.isArray(m.capabilities)) {
        errors.push('capabilities: must be an array');
    }
    else if (!m.capabilities.every((c) => typeof c === 'string')) {
        errors.push('capabilities: all items must be strings');
    }
    if (!isNonEmptyString(m.generatedAt)) {
        errors.push('generatedAt: must be a non-empty string');
    }
    return errors;
}
/**
 * Parse and validate a .well-known/planu.json manifest from a raw JSON string.
 * Returns a DiscoveryResult with the parsed manifest or validation errors.
 */
export function parseManifest(source, rawJson) {
    let parsed;
    try {
        parsed = JSON.parse(rawJson);
    }
    catch (err) {
        const message = err instanceof Error ? err.message : String(err);
        return {
            valid: false,
            errors: [`Failed to parse JSON: ${message}`],
            source,
        };
    }
    const errors = validateManifestShape(parsed);
    if (errors.length > 0) {
        return { valid: false, errors, source };
    }
    // Safe cast: validateManifestShape confirmed structure is correct
    const m = parsed;
    const manifest = {
        version: 1,
        name: m.name,
        specCount: m.specCount,
        specs: m.specs.map((s) => s),
        capabilities: m.capabilities,
        generatedAt: m.generatedAt,
    };
    return { valid: true, manifest, source };
}
//# sourceMappingURL=discovery.js.map