import type { CliConfig } from "../base.ts"; import { type DeepPartial, type EffectiveConfig, type OmitPaths } from "../sparse.ts"; import { type DocumentOnlyLocalPath, type HostedSectionKey } from "./hosted-sections.ts"; /** * A deeply-readonly JSON value, the shape of everything under `_apiResponse` (frozen at attach * time). `Array.isArray` narrows to a mutable `any[]` (microsoft/TypeScript#17002), so narrow * arrays with a readonly-preserving guard instead, e.g. * `(v): v is ReadonlyArray => Array.isArray(v)`. */ export type ReadonlyJsonValue = string | number | boolean | null | ReadonlyArray | { readonly [key: string]: ReadonlyJsonValue; }; /** * The hosted-project subset of {@link CliConfig}: the sections a Management API project-config * response can speak for (`api`, `auth`, `db`, `realtime`, `storage`, `compute`, `experimental`), * never the local-only sections that only make sense for a checkout on disk. * * Sparse (`DeepPartial`) rather than schema-defaulted, since an API response never mentions a * field it doesn't manage and defaulting the rest would fabricate drift. * * `_apiResponse`, attached only by {@link fromApiProjectConfig}, is a non-enumerable, deep-frozen * copy of the raw API payload — invisible to serialization and to `../sparse.ts`'s structural * walks. It can include an HMAC digest of a secret value, so never log a `ProjectConfig` directly; * reattach a dropped `_apiResponse` with {@link attachApiResponse}. * * Not every field in the seven hosted sections is comparable on both arms; use * {@link comparableProjectConfigPaths}/{@link isComparableProjectConfigPath} to restrict a * comparison to fields both sides speak for. Per ADR 0021, values are canonicalized toward the * state `config push` would converge on rather than mirroring their source verbatim. * * Every {@link DOCUMENT_ONLY_LOCAL_PATHS} path is excluded from the shape itself, not just from * {@link fromConfigDocument}'s output. */ export type ProjectConfig = DeepPartial, DocumentOnlyLocalPath>> & { readonly _apiResponse?: { readonly [key: string]: ReadonlyJsonValue; }; }; /** * The two policies {@link fromConfigDocument} implements for reading a field absent from both the * raw file and the in-memory operand: * * - `"absent-is-default"`: the operand is a bare {@link EffectiveConfig}. Every value on it is * schema-materialized, so an absent field's value is asserted to be the schema default. * - `"absent-is-hands-off"`: the operand is a {@link CliConfigWithRawPresence} pair. For a fixed * list of paths, {@link applyRawPresenceMask} removes an absent raw field from the projection * instead of standing in the schema default. `config diff`/`config pull`/`config push` all use * this policy in production. * * The two policies agree everywhere except one cell: an absent field whose hosted value has been * customized. Under `"absent-is-default"`, that reads back as `remote_only` with `local = ` — treating `remote_only` as "push this" would silently revert the customization. * `"absent-is-hands-off"` closes this for its fixed path list; `diffProjectConfig`'s generic * `declared` classification closes it for every other comparable path. */ export type ConfigAbsencePolicy = "absent-is-default" | "absent-is-hands-off"; /** * A `{ config, document }` pair {@link fromConfigDocument} accepts as an alternative to a bare * {@link EffectiveConfig}. Supplying `document` — the raw, pre-decode document object * (`LoadedCliConfig.document`, `../config-document.ts`) — selects the `"absent-is-hands-off"` * {@link ConfigAbsencePolicy}, since decode alone can't distinguish "declared with a default * value" from "never mentioned". `LoadedCliConfig` satisfies this interface structurally, without * a cast. */ export interface CliConfigWithRawPresence { readonly config: EffectiveConfig; readonly document?: Record; } /** * Projects a {@link CliConfig} document (or any {@link EffectiveConfig} operand) down to its * hosted-section subset: copies each present hosted section, omitting every `x-secret` leaf and * every {@link DOCUMENT_ONLY_LOCAL_PATHS} entry, and canonicalizing fields a registry row's * `normalizeDocument` covers. The result is always a fresh copy and never carries `_apiResponse`. * * Not a verbatim projection of `config` (ADR 0021): this and {@link fromApiProjectConfig} both * build convergence projections so a local and hosted config compare like for like, applying * {@link applySmsProviderPrecedence} and {@link applyDisabledSentinels} in addition to secret * omission and canonicalization. Do not render this value to a user as "your local config". * * How an absent field reads depends on the selected {@link ConfigAbsencePolicy} — pass a * {@link CliConfigWithRawPresence} pair instead of a bare `config` whenever a `document` is * available (e.g. from `loadCliConfig`) to get the safer `"absent-is-hands-off"` policy; see that * type's docstring for the danger this avoids. * * @throws {@link ProjectConfigParseError} if `config` is not an object, or a normalized value is malformed. */ export declare function fromConfigDocument(config: EffectiveConfig): ProjectConfig; export declare function fromConfigDocument(loaded: CliConfigWithRawPresence): ProjectConfig; export declare function fromConfigDocument(source: EffectiveConfig | CliConfigWithRawPresence): ProjectConfig; /** * At most one SMS provider can be live on the platform; a push selects the first enabled provider * in this order and drops the rest, so this flips every later `enabled: true` to `false` to match. * Document-arm only — the API arm's five flags all derive from a single discriminator. */ export declare const SMS_PROVIDER_PUSH_PRECEDENCE: readonly ["twilio", "twilio_verify", "messagebird", "textlocal", "vonage"]; /** * Sibling fields of a container that go inert the moment that container's own `enabled` is * `false` — projecting them would fabricate drift between two representations of the identical * disabled state. Handles container-scalar siblings only; record-keyed per-entry sweeps * (`auth.external.*`, `auth.hook.*`, `auth.sms.*`) are {@link DISABLED_SENTINEL_ENTRY_SWEEPS} * below. Applied to both {@link fromConfigDocument} and {@link fromApiProjectConfig} output, so * the two stay symmetric. * * Excludes `auth`/`storage`'s own top-level `enabled`: that flag toggles the local Docker service, * not anything hosted, so pruning on it would hide a genuinely-configured section's real hosted * state whenever a user simply isn't running that service locally. */ export declare const DISABLED_SENTINEL_PRUNES: ReadonlyArray<{ readonly containerPath: ReadonlyArray; /** Keys to drop when `enabled === false`; absent = drop every key but `enabled`. */ readonly dropKeys?: ReadonlyArray; }>; /** Record-shaped containers whose per-entry `enabled: false` keeps only the flag. */ export declare const DISABLED_SENTINEL_ENTRY_SWEEPS: ReadonlyArray<{ readonly containerPath: ReadonlyArray; /** Restrict the sweep to these entry keys (a container mixing records and scalars). */ readonly entryKeys?: ReadonlyArray; }>; /** * Maps a Management API v2 project-config response into a {@link ProjectConfig}: unwraps whichever * envelope shape `input` is, decodes the attributes leniently (an API-ahead-of-package field never * fails decode, only a genuinely malformed mapped field does), walks the mapping registry * (`./registry.ts`) to populate the typed sections, and attaches a deep-frozen copy of the raw * attributes as a non-enumerable `_apiResponse` so {@link unmappedApiFields} can still reach * whatever the registry didn't map. * * Not a verbatim projection of the response (ADR 0021): a `null` on a gating boolean canonicalizes * to `enabled: false`, {@link applyDisabledSentinels} pruning runs here too, and an out-of-domain * value on a mapped field throws rather than canonicalizing to a wrong value — so an API-sourced * and a document-sourced `ProjectConfig` compare like for like. * * @throws {@link ProjectConfigParseError} if `input` isn't an object, the envelope is malformed, or a value fails to decode or map. */ export declare function fromApiProjectConfig(input: unknown): ProjectConfig; /** * Re-attaches `_apiResponse` to `config` after a spread, `structuredClone`, or state-store * round-trip drops it, since those operations are blind to non-enumerable properties. Returns a * new object — a shallow copy of `config` plus `rawAttributes` attached via the same * clone-and-freeze path {@link fromApiProjectConfig} uses — and never mutates `config` in place. * * @throws {@link ProjectConfigParseError} if `config` is not an object. */ export declare function attachApiResponse(config: ProjectConfig, rawAttributes: Record): ProjectConfig; /** * Either operand `toProjectConfig` accepts: a local {@link EffectiveConfig} or * {@link CliConfigWithRawPresence} pair to project down to the hosted subset, or a raw, * not-yet-decoded Management API v2 project-config response to map. */ export type ToProjectConfigSource = { readonly cliConfig: EffectiveConfig | CliConfigWithRawPresence; } | { readonly apiResponse: unknown; }; /** * Thin dispatcher over the two normalizers above: routes to {@link fromApiProjectConfig} when * `source` carries an own `apiResponse` property, otherwise to {@link fromConfigDocument} for an * own `cliConfig` property. * * @throws {@link ProjectConfigParseError} if `source` carries neither key or both. */ export declare function toProjectConfig(source: ToProjectConfigSource): ProjectConfig; /** * The subtree of `config._apiResponse` that {@link projectConfigMappingRows} does not map — * `{}` when `config` carries no `_apiResponse` at all, which does not mean "fully mapped". * Registry-derived, not a hand-maintained field list: a path is "mapped" when some row's `apiPath` * or `alsoConsumes` names it. Empty objects are pruned, so a fully-mapped subtree never shows up * as `{}` noise. * * Reports at registry `apiPath` granularity, not full recursive fidelity: a key nested inside a * consumed subtree is not itemized here either, though `_apiResponse` still carries it verbatim * for a consumer that needs full fidelity. * * The result can include the HMAC digest the API reports for a secret-typed key this package * doesn't know about yet. Callers must not render this result blindly. * * @throws {@link ProjectConfigParseError} if `_apiResponse` is nested more than 64 levels deep, or `config` is not a plain object. */ export declare function unmappedApiFields(config: ProjectConfig): { readonly [key: string]: ReadonlyJsonValue; }; /** * The deduped `configPath`s of every non-`isSecret` row in {@link projectConfigMappingRows} — the * fields `fromApiProjectConfig` can actually speak for. Exists so a diff consumer never * hand-maintains an equivalent field list; excludes secret rows and every field with no row at all. * * Only remedies the whole-section granularity gap, not the finer per-path gap: a path can be a * member of this list and still fabricate drift against a document operand that never declared its * containing sub-section at all. A caller doing that comparison must additionally intersect with * what the document side actually declared. */ export declare const comparableProjectConfigPaths: ReadonlyArray>; /** * Whether `path` is a member of {@link comparableProjectConfigPaths}, or a descendant of one (a * row mapping a container, e.g. a record, makes each of its leaves comparable too). A bare prefix * of a mapped path is not comparable — it names a section, not a mapped value. */ export declare function isComparableProjectConfigPath(path: ReadonlyArray): boolean; /** * Deduped `configPath`s of every `dualScope` row in {@link projectConfigMappingRows} — fields with * a legitimate different correct value for the local stack than the hosted project. `config pull` * uses this list to warn before overwriting one of these fields at the config root, since that * would reconfigure `supabase start` rather than record the hosted project's setting; a write into * a `[remotes.*]` block is unaffected. */ export declare const dualScopeProjectConfigPaths: ReadonlyArray>; //# sourceMappingURL=project-config.d.ts.map