/**
 * AI-behavior library whitelist — single source of truth for which
 * external libraries AI-generated `behaviors/*.ai.ts` pure-function
 * bodies are allowed to dynamic-import.
 *
 * Three things keyed off this module:
 *
 *   1. The validator in `ai-behaviors-generator.ts` rejects any
 *      generated body whose `await import('LITERAL')` calls reference
 *      a literal not in `AI_BEHAVIOR_LIBRARY_WHITELIST`. Closes the
 *      gap where the validator previously failed equally on whitelisted
 *      and unwhitelisted imports (because the engines workspace lacks
 *      both), so non-whitelisted libs slipped through to AI-INVALID
 *      and only crashed at runtime in the realized backend.
 *
 *   2. `backend-package-json-generator.ts` calls
 *      `predictAiBehaviorLibraries(spec)` and only emits deps for libs
 *      whose triggers fire in the spec's step text. Avoids the dep
 *      bloat where every realized backend installed all 5 libs even
 *      when 97% of bodies needed none.
 *
 *   3. The behavior prompt YAML's "LIBRARY WHITELIST" section is the
 *      author-facing description — the trigger phrases here MUST stay
 *      in sync with the prompt. If you add or rename a trigger here,
 *      update `assets/prompts/core/standard/default/behavior.prompt.yaml`
 *      and bump `PROMPT_VERSION` in `ai-behaviors-generator.ts`.
 *
 * Triage status (2026-05-02 audit, #43K-B-review):
 *   jsonwebtoken — used in 2 of 49 cached bodies. KEEP.
 *   expr-eval    — used in 1 of 49 cached bodies. MONITORED.
 *   bcryptjs     — 0 of 49. MONITORED — drop after 2026-11 if no real spec uses it.
 *   crypto       — 0 of 49. MONITORED — drop after 2026-11 if no real spec uses it.
 *   uuid         — 0 of 49. MONITORED — drop after 2026-11 if no real spec uses it.
 */
export const AI_BEHAVIOR_LIBRARY_WHITELIST = [
    'jsonwebtoken',
    'bcryptjs',
    'uuid',
    'crypto',
    'expr-eval',
];
const WHITELIST_SET = new Set(AI_BEHAVIOR_LIBRARY_WHITELIST);
/** Is `name` a member of the whitelist? */
export function isWhitelistedLibrary(name) {
    return WHITELIST_SET.has(name);
}
/**
 * Trigger phrases per library — when any regex matches a step's text,
 * the corresponding library is predicted to be needed by the AI body
 * the realize engine will generate for that step.
 *
 * Kept in sync with `behavior.prompt.yaml`'s LIBRARY WHITELIST section.
 * If you change the prompt's triggers, update both places + bump
 * PROMPT_VERSION.
 */
export const AI_BEHAVIOR_LIBRARY_TRIGGERS = {
    // JWT: explicit lib name OR the abbrev OR signing/verifying intent.
    // \bJWT\b avoids matching "JWTRevoked" or "BJWT" — only the standalone token.
    jsonwebtoken: [
        /jsonwebtoken/i,
        /\bJWT\b/,
        /\bsign\s+tokens?\b/i,
        /\bverify\s+token\s+signature\b/i,
        /\bdecode\s+token\b/i,
    ],
    // bcryptjs (or just "bcrypt") + the hash/compare-password pattern.
    bcryptjs: [
        /\bbcryptjs?\b/i,
        /\bhash\s+password\b/i,
        /\bcompare\s+password\b/i,
    ],
    // Note: these triggers are deliberately specific. "uuid" alone would
    // match too aggressively (e.g. "userId: UUID required" in an attribute
    // declaration). Anchor on action-y phrasing instead.
    uuid: [
        /\bvia\s+uuid\b/i,
        /\bgenerate\s+uuid\b/i,
    ],
    // node:crypto built-in — only the explicit "via crypto" or hash-action
    // phrasings, since "crypto" alone would match "crypto-currency", "crypto
    // wallet", etc.
    crypto: [
        /\bvia\s+crypto\b/i,
        /\bcompute\s+(?:hash|checksum)\b/i,
    ],
    'expr-eval': [
        /\bexpr-eval\b/i,
        /\bevaluate\s+formula\b/i,
    ],
};
/**
 * Walk every step text in a SpecVerse spec and predict which whitelist
 * libraries the AI body generator will need. The set is the basis for
 * the realized backend's `package.json` deps — only libs predicted as
 * needed get added.
 *
 * Walks: spec.components[].(controllers / services).operations[].steps[]
 *        + spec.components[].models[].behaviors[].steps[]
 */
export function predictAiBehaviorLibraries(spec) {
    const found = new Set();
    if (!spec)
        return found;
    const allSteps = [];
    const collectFromOps = (ops) => {
        if (!ops)
            return;
        const entries = Array.isArray(ops) ? ops : Object.values(ops);
        for (const op of entries) {
            const steps = Array.isArray(op?.steps) ? op.steps : [];
            for (const step of steps) {
                const text = typeof step === 'string' ? step : (step?.step || step?.action);
                if (typeof text === 'string')
                    allSteps.push(text);
            }
        }
    };
    const collectFromModels = (models) => {
        if (!models)
            return;
        const entries = Array.isArray(models) ? models : Object.values(models);
        for (const m of entries) {
            collectFromOps(m?.behaviors);
        }
    };
    const components = Array.isArray(spec.components)
        ? spec.components
        : (spec.components ? Object.values(spec.components) : []);
    for (const comp of components) {
        if (!comp)
            continue;
        // Controllers may declare actions or behaviors-derived ops.
        const ctrls = comp.controllers;
        if (ctrls) {
            const ctrlEntries = Array.isArray(ctrls) ? ctrls : Object.values(ctrls);
            for (const c of ctrlEntries) {
                collectFromOps(c?.actions);
                collectFromOps(c?.behaviors);
            }
        }
        // Services have operations.
        const services = comp.services;
        if (services) {
            const svcEntries = Array.isArray(services) ? services : Object.values(services);
            for (const s of svcEntries) {
                collectFromOps(s?.operations);
            }
        }
        collectFromModels(comp.models);
    }
    // Run each lib's triggers against the collected step text.
    for (const lib of AI_BEHAVIOR_LIBRARY_WHITELIST) {
        const triggers = AI_BEHAVIOR_LIBRARY_TRIGGERS[lib];
        if (allSteps.some((step) => triggers.some((re) => re.test(step)))) {
            found.add(lib);
        }
    }
    return found;
}
/**
 * Extract the literal module specifiers from every `await import('X')`
 * (or bare `import('X')`) call in a TS source string. Used by the AI
 * body validator to enforce whitelist compliance — any returned name
 * not in `AI_BEHAVIOR_LIBRARY_WHITELIST` rejects the body.
 *
 * Walks char-by-char tracking string + comment context — ignores any
 * `import(...)` syntax that appears inside a comment or string literal,
 * so commented-out examples and string content can't false-positive.
 *
 * Computed specifiers (`import(varName)`) and template-string
 * specifiers (`` import(`name`) ``) are intentionally NOT extracted:
 * they're rejected by the validator anyway (only allow string-literal
 * specifiers) and the safe answer when we can't statically resolve a
 * specifier is "don't try."
 */
export function extractDynamicImports(code) {
    const out = [];
    let i = 0;
    let stringChar = null; // null when not in a string
    const len = code.length;
    while (i < len) {
        const c = code[i];
        // Inside a string — consume until matching closer (handle escapes).
        if (stringChar !== null) {
            if (c === '\\' && i + 1 < len) {
                i += 2;
                continue;
            }
            if (c === stringChar) {
                stringChar = null;
            }
            i++;
            continue;
        }
        // Block comment
        if (c === '/' && code[i + 1] === '*') {
            const end = code.indexOf('*/', i + 2);
            i = end < 0 ? len : end + 2;
            continue;
        }
        // Line comment
        if (c === '/' && code[i + 1] === '/') {
            const end = code.indexOf('\n', i + 2);
            i = end < 0 ? len : end;
            continue;
        }
        // String start
        if (c === '"' || c === "'" || c === '`') {
            stringChar = c;
            i++;
            continue;
        }
        // `import(` pattern — must be preceded by a non-identifier char so
        // we don't match `myimport(` or similar. Then skip whitespace,
        // capture the literal between matching quotes, verify it's followed
        // by `)` (modulo whitespace).
        if (c === 'i' && code.slice(i, i + 7) === 'import(') {
            const prev = i > 0 ? code[i - 1] : '\0';
            if (!/[a-zA-Z0-9_$]/.test(prev)) {
                let j = i + 7;
                while (j < len && /\s/.test(code[j]))
                    j++;
                const quote = code[j];
                if (quote === '"' || quote === "'") {
                    const endQuote = code.indexOf(quote, j + 1);
                    if (endQuote > 0) {
                        const lit = code.slice(j + 1, endQuote);
                        let k = endQuote + 1;
                        while (k < len && /\s/.test(code[k]))
                            k++;
                        if (code[k] === ')') {
                            out.push(lit);
                            i = k + 1;
                            continue;
                        }
                    }
                }
            }
        }
        i++;
    }
    return out;
}
/**
 * Validate that every dynamic import in `code` references a whitelisted
 * library. Returns a list of violating specifiers (empty list = clean).
 */
export function validateImportWhitelist(code) {
    return extractDynamicImports(code).filter((n) => !isWhitelistedLibrary(n));
}
//# sourceMappingURL=library-whitelist.js.map