//#region src/define.d.ts /** * Build a typed identity `defineConfig` for a specific config shape. * * Each consumer (`@theholocron/cli`, `@theholocron/astromech`) calls this * once with its own config type and re-exports the result, so config * files get autocomplete and type-checking with no runtime cost: * * ```ts * // @theholocron/astromech/config * export const defineConfig = createDefineConfig(); * ``` */ declare function createDefineConfig(): (config: C) => C; //#endregion //#region src/errors.d.ts /** * Thrown when a config file is found but cannot be loaded — malformed * JSON, a syntax error in a `.ts` / `.js` module, or no usable default * export. "Not found" is never an error: {@link loadConfigFile} returns * `null` for that. */ declare class ConfigFileError extends Error { /** Absolute path of the offending file, when known. */ readonly filepath?: string | undefined; name: string; constructor(message: string, /** Absolute path of the offending file, when known. */ filepath?: string | undefined); } //#endregion //#region src/load.d.ts /** * Discover and load `.config.*` files. Holocron-agnostic — no * schema, no validation, no defaults. Consumers layer those on top. * * Probe order is **TS-first**: `.ts` → `.js` → `.mjs` → `.cjs` → `.json`. * TS is loaded through `tsx`'s `tsImport` (a runtime dependency) so a * typed `defineConfig` file works with no build step. */ /** Default extension probe order, highest priority first. */ declare const DEFAULT_EXTENSIONS: readonly ["ts", "js", "mjs", "cjs", "json"]; interface LoadConfigFileOptions { /** Directory to look in. */ cwd: string; /** Base name — `"holocron"` resolves `holocron.config.{ts,js,mjs,cjs,json}`. */ name: string; /** Override the extension probe order. */ extensions?: readonly string[]; } interface Loaded { config: T; /** Absolute path of the file the value came from. */ filepath: string; } /** * Load the first `.config.` that exists in `cwd`. Returns * `null` when none is present; throws {@link ConfigFileError} when a file * exists but cannot be loaded. */ declare function loadConfigFile(opts: LoadConfigFileOptions): Promise | null>; interface LoadLayeredOptions extends LoadConfigFileOptions { /** * When no dedicated `.config.*` exists — or in addition to it — * read `.config.*` and take its `[key]`. The dedicated file (if * any) is merged on top. */ fallback?: { file: string; key: string; }; } interface LayeredResult { config: T; /** The dedicated file path if present, else the fallback file path, else `null`. */ filepath: string | null; /** Absolute paths actually merged, lowest priority first. */ sources: string[]; } /** * `.config.*` layered over the `[fallback.key]` of * `.config.*`. Either, both, or neither may be present; * the dedicated file wins on conflict (vitest-over-vite). Returns `null` * when neither source resolves. */ declare function loadLayered(opts: LoadLayeredOptions): Promise | null>; //#endregion //#region src/merge.d.ts /** * Deep-merge two config objects, `vite`-style: * * - plain objects merge recursively * - arrays concatenate (`base` first, then `override`) * - an `undefined` value in `override` is skipped (keeps `base`) * - every other `override` value replaces `base` * * Neither input is mutated. */ declare function mergeConfig(base: T, override: unknown): T; //#endregion export { ConfigFileError, DEFAULT_EXTENSIONS, type LayeredResult, type LoadConfigFileOptions, type LoadLayeredOptions, type Loaded, createDefineConfig, loadConfigFile, loadLayered, mergeConfig };