/**
 * install/find-workspace-framework-version.mjs — Bundle A.
 *
 * Locates the workspace `packages/framework/package.json` above `targetDir`
 * so the SessionStart worker and `apt-tools check-version` can detect the
 * "self-host contributor's workspace is ahead of the installed kernel" case
 * WITHOUT requiring the user to remember the `--from-source` flag.
 *
 * Mirrors the bounded-walk + semver-validation contract of the route-side
 * `detectSourceWorkspaceDrift` (route/drift-detect.mjs:32-107), but returns
 * just the source workspace metadata — the caller decides whether to flip
 * `update_available` (worker / check-version branch on the comparison).
 *
 * Safe for hot paths: synchronous, file-existence + JSON parse only,
 * bounded walk (6 segments max), no network, no spawn.
 *
 * Security: workspace `package.json:version` is validated against the same
 * `SEMVER_RE` as `update-chips.mjs` so a poisoned `package.json` cannot
 * propagate a non-semver value into downstream envelopes.
 */
import { existsSync, readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { SEMVER_RE } from '../util/semver.mjs';
/** Max directory frames to walk before giving up. Mirrors route/drift-detect.mjs:32. */
const MAX_WALK_DEPTH = 6;
/**
 * Walk up from `start`, returning every directory frame including `start`
 * itself. Stops at the filesystem root or at depth `MAX_WALK_DEPTH`
 * (whichever comes first).
 *
 * @param {string} start
 * @returns {string[]}
 */
function walkUp(start) {
    const frames = [];
    let cur = start;
    for (let i = 0; i < MAX_WALK_DEPTH; i++) {
        frames.push(cur);
        const parent = dirname(cur);
        if (parent === cur)
            break;
        cur = parent;
    }
    return frames;
}
/**
 * Find the workspace `@aperant/framework` package above `targetDir`.
 *
 * @param {string} targetDir  Absolute path to start the walk from.
 * @returns {null | { source_path: string, source_version: string }}
 *   `source_path` is the absolute path to `packages/framework/package.json`.
 *   `source_version` is the semver-validated `version` field. Returns null
 *   when no workspace package is found, when the workspace package name is
 *   not `@aperant/framework`, or when the workspace version fails the
 *   semver-shape check.
 */
export function findWorkspaceFrameworkVersion(targetDir) {
    if (!targetDir || typeof targetDir !== 'string')
        return null;
    for (const dir of walkUp(targetDir)) {
        const candidate = join(dir, 'packages', 'framework', 'package.json');
        if (!existsSync(candidate))
            continue;
        let pkg;
        try {
            pkg = JSON.parse(readFileSync(candidate, 'utf-8'));
        }
        catch {
            continue;
        }
        // Guard against false matches on unrelated monorepos that happen to
        // have a `packages/framework/` directory.
        if (pkg?.name !== '@aperant/framework')
            continue;
        const sourceVersion = pkg?.version;
        if (typeof sourceVersion !== 'string' || !SEMVER_RE.test(sourceVersion))
            continue;
        return {
            source_path: candidate,
            source_version: sourceVersion,
        };
    }
    return null;
}
//# sourceMappingURL=find-workspace-framework-version.mjs.map