/** * `.fz/config.json` — what a project declares once, so nothing else needs flags. * * Every command that touches a Deployment needs the same four facts: which Deployment, * which environment, which secrets, and what to call them in the process. Passed * as flags they get typed differently by every person and every CI job; put in a * file they are reviewed like code and diff like code. * * ## JSON, not YAML * * A YAML parser is a third-party dependency, and this package claims to have * none — it ends up inside a tenant's production application, where every edge * in its dependency graph is one they did not choose. JSON is in the runtime. * (`.fz/deploy.plan.json` is a different file read by a different tool on our side of * the wire, where the dependency is ours to carry.) * * ## What is deliberately NOT in here * * No secrets, no API key, no endpoint. This file is committed. The credential * comes from the agent socket or `FORGEZERO_API_KEY`, and the day somebody adds * a `token` field here is the day the file starts leaking in pull requests. * `assertNoSecrets` fails the load rather than trusting that nobody will. */ export declare class ConfigError extends Error { readonly code: 'CONFIG_NOT_FOUND' | 'CONFIG_INVALID' | 'CONFIG_HAS_SECRET' | 'UNKNOWN_ENVIRONMENT'; constructor(code: 'CONFIG_NOT_FOUND' | 'CONFIG_INVALID' | 'CONFIG_HAS_SECRET' | 'UNKNOWN_ENVIRONMENT', message: string); } /** How a secret is named in the process, when it differs from the entry key. */ export interface SecretBinding { /** Vault entry key. */ entry: string; /** Environment variable name. Defaults to `entry`. */ as?: string; /** * Absent is a hard failure at boot rather than an undefined at first use. * * Default true, because the alternative is a service that starts, looks * healthy, and fails on whichever request first touches the missing value. */ required?: boolean; } export interface FzConfig { /** Native Deployment key in ForgeZero. */ deploymentKey: string; /** Default environment. Overridden by `FORGEZERO_ENVIRONMENT` or a flag. */ environment: string; /** * Which secrets to load, and what to call them. * * An explicit list rather than "everything in the project". A process that * pulls every secret it can reach holds credentials it never uses, and the * blast radius of a memory dump becomes the whole Deployment rather than the * five things this service actually needs. */ secrets: readonly (string | SecretBinding)[]; /** * Let the vault overwrite a variable that is already set. * * Default FALSE, matching dotenv: a value already in the environment wins, so * a developer can override one thing for an afternoon without editing config. * Anything shadowed is REPORTED at boot — a production secret silently losing * to a stale export is otherwise the hardest class of bug in this area. */ override?: boolean; /** Prefix applied to every variable name. `FZ_` → `FZ_DATABASE_URL`. */ prefix?: string; /** Per-environment overrides, merged over the top level. */ /** Environments may adjust secret bindings, never redirect a process to another Deployment. */ environments?: Record>>; /** Framework, when auto-setup needs to know. */ framework?: 'sveltekit' | 'nextjs' | 'node' | 'bun'; } export declare const CONFIG_PATHS: readonly [".fz/config.json", "fz.config.json"]; export declare function assertNoSecrets(raw: Record, path?: string): void; export declare function parseConfig(text: string): FzConfig; /** * Resolve for one environment. * * Environment blocks are merged over the top level rather than replacing it, so * `production` can change one field without restating the list — restating it is * how one environment ends up missing a secret the others have. */ export declare function resolveConfig(config: FzConfig, environment?: string): FzConfig; /** Normalise the two spellings into one, with the prefix applied. */ export declare function bindingsOf(config: FzConfig): readonly Required[]; export interface LoadOptions { /** Where to start looking. Walks up to the filesystem root. */ cwd?: string; readFile?: (path: string) => string | undefined; env?: Record; } /** * Find the config by walking up from `cwd`. * * Walking up rather than requiring an exact path, because the same command is * run from the repository root, from a workspace package and from a CI step with * a different working directory — and a tool that only works from one of those * gets wrapped in a script that hard-codes a path. * * `readFile` is injected so this stays runtime-agnostic: the same code has to * work under Bun, Node and a bundler that has no `fs` at all. */ export declare function loadConfig(options?: LoadOptions): { config: FzConfig; path: string; }; /** What `fz init` writes. Kept here so the tool and the reader agree on the shape. */ export declare function exampleConfig(args: { deploymentKey: string; framework?: FzConfig['framework']; }): string;