/**
 * Vitest Configuration - Shared Base
 *
 * Exports shared configuration pieces that can be imported by
 * project-specific vitest.config.ts files. Reduces duplication between
 * typescript, nestjs, and other project type configurations.
 *
 * Published as part of the @codyswann/lisa npm package so downstream
 * projects can import these utilities directly from the package.
 * @see https://vitest.dev/config/
 * @module configs/vitest/base
 */
import { existsSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { isInsideWorktree } from "../worktrees.js";
/**
 * Default coverage thresholds used when not specified in project config.
 * Projects can override via vitest.thresholds.json.
 */
export const defaultThresholds = {
    global: {
        statements: 70,
        branches: 70,
        functions: 70,
        lines: 70,
    },
};
/**
 * Default patterns to exclude from coverage collection.
 * Common across all stacks — stack-specific configs extend this list.
 *
 * Unlike Jest's `!`-prefixed negation patterns in `collectCoverageFrom`,
 * Vitest uses separate `include`/`exclude` arrays without `!` prefixes.
 */
export const defaultCoverageExclusions = [
    "**/*.d.ts",
    "**/index.ts",
    "**/node_modules/**",
    "**/dist/**",
    "**/*.test.ts",
    "**/*.spec.ts",
    "**/*.mock.ts",
    "**/test/**",
    "**/tests/**",
    "**/__tests__/**",
    "**/__mocks__/**",
    "**/components/ui/**",
];
/**
 * Default patterns to exclude from test discovery across all stacks.
 *
 * The `.claude/worktrees/` exclusion is intentionally NOT baked in here —
 * it is cwd-conditional and supplied by {@link worktreeExclusions} so that
 * a vitest run launched from INSIDE a worktree can still discover its own
 * tests. Stack factories spread `worktreeExclusions()` alongside this list.
 */
export const defaultTestExclusions = [
    "**/node_modules/**",
    "**/dist/**",
];
/**
 * Returns the worktree exclusion globs a stack config should add to skip
 * test files / coverage that live inside an agent worktree.
 *
 * Lisa manages scratch worktrees for subagents under TWO roots —
 * `.claude/worktrees/` and a bare `.worktrees/` — and both must be covered.
 * Omitting the second one let sibling worktrees dominate test discovery from
 * the primary checkout (one host project measured 13,821 of 14,277 collected
 * files coming from other branches' worktrees), so the local suite reported
 * other agents' in-flight failures instead of this checkout's.
 *
 * When vitest runs from the primary checkout, tests inside those worktrees
 * should be skipped — each worktree has its own vitest run. When vitest runs
 * from INSIDE a worktree (the project root *is* the worktree), the same globs
 * match every path under root and vitest finds zero tests. This returns the
 * globs only when the current working directory is outside a worktree, so each
 * stack factory can spread them into its `exclude` arrays without hand-rolling
 * the conditional. Mirrors jest's `worktreeTestPathIgnorePatterns()`.
 * @returns The worktree exclude globs, or an empty array when already inside a worktree.
 */
export function worktreeExclusions() {
    return isInsideWorktree()
        ? []
        : ["**/.claude/worktrees/**", "**/.worktrees/**"];
}
/**
 * Resolves a sibling module of this file to an absolute path Vitest can load.
 *
 * `setupFiles` and `globalSetup` take file paths rather than functions, and this
 * module is consumed from two places with different extensions: downstream
 * projects import the compiled `dist/` copy, while Lisa's own unit tests import
 * the `src/` TypeScript directly. Probing for the built file first and falling
 * back to the source keeps one wiring correct on both.
 * @param basename - Sibling module name without extension
 * @returns Absolute path to the module.
 */
const resolveScratchModule = (basename) => {
    const built = fileURLToPath(new URL(`./${basename}.js`, import.meta.url));
    return existsSync(built)
        ? built
        : fileURLToPath(new URL(`./${basename}.ts`, import.meta.url));
};
/**
 * Setup files that bound and reclaim the suite's temporary directories.
 *
 * Every stack factory spreads this. Without it, each fixture's
 * `mkdtemp(os.tmpdir())` lands directly in the shared platform temp root, which
 * is never emptied while the machine is up and which no in-process cleanup can
 * protect once the runner is killed.
 * @returns Absolute paths to spread into `test.setupFiles`.
 * @see {@link module:configs/vitest/scratch} for the measurements behind this
 */
export function scratchSetupFiles() {
    return [resolveScratchModule("scratch-setup")];
}
/**
 * Global setup that reclaims residue before a run and audits the namespace after it.
 * @returns Absolute paths to spread into `test.globalSetup`.
 */
export function scratchGlobalSetup() {
    return [resolveScratchModule("scratch-global-setup")];
}
/**
 * Maps portable threshold format (with `global` wrapper) to Vitest's
 * flat threshold format. Projects store thresholds in the portable
 * format so the same JSON file works for both Jest and Vitest stacks.
 * @param thresholds - Portable thresholds with `global` wrapper
 * @returns Flat Vitest thresholds object
 */
export const mapThresholds = (thresholds) => ({
    ...(thresholds.global?.statements !== undefined
        ? { statements: thresholds.global.statements }
        : {}),
    ...(thresholds.global?.branches !== undefined
        ? { branches: thresholds.global.branches }
        : {}),
    ...(thresholds.global?.functions !== undefined
        ? { functions: thresholds.global.functions }
        : {}),
    ...(thresholds.global?.lines !== undefined
        ? { lines: thresholds.global.lines }
        : {}),
});
/**
 * Merges project-specific threshold overrides into default thresholds.
 * Allows projects to selectively raise or lower coverage requirements
 * via vitest.thresholds.json without replacing the entire threshold object.
 *
 * Uses the portable format (with `global` wrapper) for compatibility
 * with both Jest and Vitest threshold JSON files.
 * @param defaults - Base thresholds from the stack config
 * @param overrides - Project-specific overrides from vitest.thresholds.json
 * @returns Merged thresholds with overrides taking precedence
 */
export const mergeThresholds = (defaults, overrides) => ({
    ...defaults,
    ...overrides,
    global: {
        ...defaults.global,
        ...overrides.global,
    },
});
/**
 * Deep merges the `test` key of multiple Vitest UserConfig objects.
 * Arrays are concatenated and deduplicated. Nested objects (like
 * `coverage`) are shallow-merged. Scalar values from later configs
 * take precedence.
 * @param configs - Vitest UserConfig objects to merge in order of precedence
 * @returns Single merged UserConfig
 */
export const mergeVitestConfigs = (...configs) => {
    if (configs.length === 0) {
        return {};
    }
    const mergeObjects = (a, b) => Object.keys(b).reduce((acc, key) => {
        const accVal = acc[key];
        const bVal = b[key];
        const merged = Array.isArray(accVal) && Array.isArray(bVal)
            ? [...new Set([...accVal, ...bVal])]
            : typeof accVal === "object" &&
                accVal !== null &&
                !Array.isArray(accVal) &&
                typeof bVal === "object" &&
                bVal !== null &&
                !Array.isArray(bVal)
                ? {
                    ...accVal,
                    ...bVal,
                }
                : bVal;
        return { ...acc, [key]: merged };
    }, { ...a });
    return configs.reduce((acc, config) => {
        const accTest = (acc.test ?? {});
        const configTest = (config.test ?? {});
        return {
            ...acc,
            ...config,
            test: mergeObjects(accTest, configTest),
        };
    }, {});
};
//# sourceMappingURL=base.js.map