import { z } from "zod"; /** * Wire contract for the root `fjall-config.json` — the schemas, the canonical * serialiser, and the shape helpers the merge needs. * * Split out of `config.ts` so `rootConfigMerge.ts` stays a pure module: the * merge needs these schemas but must not drag `config.ts`'s fs/path/logger * graph in with them, because the webapp imports the merge from its own I/O * boundary. `config.ts` re-exports the schemas, their companion types, the * serialiser and ROOT_CONFIG_KEYS, so `@fjall/util/config` is unchanged for * consumers; `RootConfigKey`, `copyDefinedKey` and `domainEntriesEqual` are * merge-internal and deliberately not re-exported. */ /** * STRICT wire contract for one `domains[]` entry in fjall-config.json — * unknown keys are rejected, never silently carried. This is the write-path * schema shared with the webapp's domain-topology resolver; a scaffold or * saver emitting a key this schema rejects is exactly the drift it prevents. */ export declare const DomainConfigSchema: z.ZodObject<{ name: z.ZodString; type: z.ZodEnum<{ apex: "apex"; delegated: "delegated"; }>; parentDomain: z.ZodOptional; account: z.ZodOptional; region: z.ZodOptional; }, z.core.$strict>; export type DomainConfig = z.infer; /** * STRICT wire contract for the `organisation` key — the project's binding to * ONE Fjall organisation. * * This is the ONE organisation fact that belongs in fjall-config.json, and it * is not a mirror of API state: it is a project ASSERTION that says which * organisation this source tree deploys into. Two writers, both recording * stated intent: `fjall create organisation` writes it at scaffold time (the * organisation being created IS the command's subject — the project is born * bound), and `fjall org bind` records or deliberately re-records it for * projects that predate birth binding or genuinely move. Everything else * about an organisation (accounts, regions, tiers) is mutable API state that * a committed copy would misreport, which is why it stays out of this file. * * `id` is the only field ever compared. `name` is display-only, so renaming an * organisation neither trips the guard nor dirties a checkout. */ export declare const OrganisationBindingSchema: z.ZodObject<{ id: z.ZodString; name: z.ZodOptional; }, z.core.$strict>; export type OrganisationBinding = z.infer; export declare const RootConfigSchema: z.ZodObject<{ activeTarget: z.ZodOptional; domains: z.ZodOptional; parentDomain: z.ZodOptional; account: z.ZodOptional; region: z.ZodOptional; }, z.core.$strict>>>; capacityIdentity: z.ZodOptional; legacySgDescription: z.ZodOptional; }, z.core.$strict>>>>>; organisation: z.ZodOptional; }, z.core.$strict>>; }, z.core.$strict>; export type RootConfig = z.infer; /** * Tolerant READ schema for the deploy-time load path. Intentionally NOT * `.strict()`: a fjall-config.json written by a different fjall version, or a * legacy pre-config-split file, may carry top-level keys this version does not * know (`version`, `services`, `providerAccounts`, `generatorVersion`, …). * Stripping them and reading only the recognised keys keeps `fjall deploy` from * hard-failing on an otherwise-harmless older file. The strict RootConfigSchema * remains the contract for the WRITE/save path — never use this schema to * validate what you are about to write. Domain entries stay nested-strict * (DomainConfigSchema): tolerance applies to unknown TOP-LEVEL keys only. */ export declare const RootConfigReadSchema: z.ZodObject<{ activeTarget: z.ZodOptional; domains: z.ZodOptional; parentDomain: z.ZodOptional; account: z.ZodOptional; region: z.ZodOptional; }, z.core.$strict>>>; capacityIdentity: z.ZodOptional; legacySgDescription: z.ZodOptional; }, z.core.$strict>>>>>; organisation: z.ZodOptional; }, z.core.$strict>>; }, z.core.$strip>; export type RootConfigRead = z.infer; /** * Canonical serialiser for the root fjall-config.json - the single source of * truth for the file's on-disk shape. Both `Config.saveConfig` and the webapp * scaffold MUST route through this rather than hand-rolling the JSON, so the * two can never drift (a scaffold emitting a key the loader later rejects is * exactly the bug this prevents). The `RootConfig` input type keeps OUR keys * honest at compile time, but the emitted JSON is deliberately NOT * strict-validated: `mergeRootConfig` routes top-level keys another fjall * version wrote through the save verbatim (see its doc), so a merged save may * carry keys RootConfigSchema does not recognise. The round-trip test asserts * only that the emitted DEFAULT validates against RootConfigSchema. */ export declare function serialiseRootConfig(config?: RootConfig): string; export type RootConfigKey = keyof RootConfig; /** * Top-level keys this version recognises, derived from the strict write * schema so a key added there cannot silently start being classified as * another version's foreign key by the merge. */ export declare const ROOT_CONFIG_KEYS: readonly RootConfigKey[]; export declare function copyDefinedKey(target: RootConfig, source: RootConfig, key: K): void; /** * Exact equality for one `domains[]` entry. DomainConfigSchema is flat and * strict (every value a primitive string), so a key-union shallow compare is * a complete equality - revisit if the schema ever grows a nested key. */ export declare function domainEntriesEqual(a: DomainConfig, b: DomainConfig | undefined): boolean;