"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.enumerateValidAccountIds = enumerateValidAccountIds;
exports.getAccountsDirFromEnv = getAccountsDirFromEnv;
exports._resetEnumerationCache = _resetEnumerationCache;
exports.validateAccountIdEnv = validateAccountIdEnv;
/**
 * account-enumeration — single source of truth for "which accountIds are
 * provisioned on disk for this install?".
 *
 * Doctrine — `.docs/neo4j.md` "Account isolation invariant": every writer
 * that stamps `n.accountId` must verify the value against
 * `${DATA_ROOT}/accounts/<id>/account.json` before write. Read-side scoping
 * (`WHERE n.accountId = $accountId`) silently hides leaks from every UI;
 * an unenforced writer can leak indefinitely with zero downstream symptom.
 *
 * Two consumers:
 *   1. `writeNodeWithEdges` (platform/lib/graph-write/) — central write-time
 *      floor; rejects any write whose `props.accountId` is not in the set
 *      returned here, unless `createdBy.agent === 'system'` (bootstrap).
 *   2. `resolvePlatformAccountId` (platform/ui/app/lib/whatsapp/platform-account-id.ts)
 *      — WhatsApp writer-side helper; loud-throws on zero or multi accounts.
 *
 * Per-process cache keyed on `accountsDir` — Phase 0 invariant has accounts
 * created at install time, so adding/removing accounts requires a service
 * restart anyway.
 */
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
const cache = new Map();
/**
 * Returns the list of valid account UUIDs found under `accountsDir`. A dir is
 * "valid" iff its name is a UUID AND it contains a parseable `account.json`.
 * Corruption-discipline: a present-but-unparseable account.json EXCLUDES the
 * dir (over-prune one suspect account beats under-prune the leak it might
 * be hiding).
 */
function enumerateValidAccountIds(accountsDir) {
    const cached = cache.get(accountsDir);
    if (cached !== undefined)
        return cached;
    let names;
    try {
        names = (0, node_fs_1.readdirSync)(accountsDir);
    }
    catch (err) {
        if (err.code === "ENOENT") {
            cache.set(accountsDir, []);
            return [];
        }
        throw err;
    }
    const valid = [];
    for (const name of names) {
        if (!UUID_RE.test(name))
            continue;
        const configPath = (0, node_path_1.resolve)(accountsDir, name, "account.json");
        try {
            JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
            valid.push(name);
        }
        catch (err) {
            const code = err.code;
            if (code === "ENOENT")
                continue;
            // SyntaxError (parse failure) and other read errors fall through:
            // exclude this dir, try the next.
        }
    }
    cache.set(accountsDir, valid);
    return valid;
}
/**
 * Resolves the canonical accounts dir from `MAXY_PLATFORM_ROOT`. Mirrors the
 * pattern at platform/ui/app/lib/claude-agent/account.ts:9
 * (`ACCOUNTS_DIR = resolve(PLATFORM_ROOT, "..", "data/accounts")`).
 *
 * Loud-throws when the env var is unset — graph-write's gate must not fall
 * open silently.
 */
function getAccountsDirFromEnv() {
    const root = process.env.MAXY_PLATFORM_ROOT;
    if (!root) {
        throw new Error("[graph-write] MAXY_PLATFORM_ROOT not set — cannot enforce accountId gate. " +
            "Set MAXY_PLATFORM_ROOT in the spawning process or pass `accountsDir` explicitly.");
    }
    return (0, node_path_1.resolve)(root, "..", "data/accounts");
}
/**
 * Test-only cache reset. Production callers must not invoke this — the cache
 * is a deliberate boot-time invariant (see module doc).
 */
function _resetEnumerationCache() {
    cache.clear();
}
function validateAccountIdEnv(envValue, diskIds) {
    if (!envValue) {
        return { ok: false, reason: "missing", envId: null, diskIds };
    }
    if (diskIds.length === 0) {
        return { ok: false, reason: "no-on-disk-account", envId: envValue, diskIds };
    }
    if (!diskIds.includes(envValue)) {
        return { ok: false, reason: "mismatch", envId: envValue, diskIds };
    }
    return { ok: true, envId: envValue, diskIds };
}
//# sourceMappingURL=index.js.map