/**
 * Options for {@link LocalizedMessageSet}.
 */
type LocalizedMessageSetOptions = {
    /**
     * The guaranteed-fallback locale, made explicit (no hidden default). After
     * canonicalization it must have an entry in `messages`.
     */
    readonly baseLocale: string;
    /**
     * Locale tag to message text. Keys are canonicalized (BCP 47); message
     * contents are preserved verbatim and never trimmed or modified.
     */
    readonly messages: Readonly<Record<string, string>>;
};
/**
 * An immutable, canonicalized set of localized messages keyed by BCP 47 locale.
 *
 * Construction enforces the write-side invariants: every key is canonicalized
 * with `Intl.getCanonicalLocales` (invalid tags throw), keys that collide after
 * canonicalization throw, every message must contain at least one non-whitespace
 * character, and an entry for the (canonical) `baseLocale` must exist. Lookups
 * are exact, canonical matches with no parent fallback: walking up a tag
 * (`de-DE` to `de`) and choosing between preferences is the resolver's job, not
 * the set's. On the read side an invalid requested tag is a miss, never a throw.
 */
declare class LocalizedMessageSet {
    #private;
    /** Canonical BCP 47 tag of the guaranteed-fallback locale. */
    readonly baseLocale: string;
    constructor(options: LocalizedMessageSetOptions);
    /**
     * Whether an exact (canonical) entry exists for `locale`. No parent fallback.
     * An invalid tag is a miss.
     */
    has(locale: string): boolean;
    /**
     * The exact (canonical) message for `locale`, or `undefined`. No parent
     * fallback. An invalid tag yields `undefined`.
     */
    get(locale: string): string | undefined;
    /**
     * Fast-path lookup for a key that is **already canonical**. Skips
     * canonicalization, so the caller is responsible for passing a canonical tag
     * (a non-canonical spelling misses). Used by the resolver, which already
     * canonicalizes once and walks canonical truncation tags; prefer {@link get}
     * for untrusted input.
     */
    getCanonical(canonicalLocale: string): string | undefined;
    /** A copy of the entries as `[canonicalLocale, message]` pairs. */
    entries(): ReadonlyArray<readonly [string, string]>;
}

/**
 * The outcome of resolving a localized message: the text plus the locale that
 * actually matched and how. `matchedPreferenceIndex` and `match` are diagnostic
 * (tests, finding missing translations) and need not reach a public view.
 */
type ResolvedUserMessage = {
    /** Canonical BCP 47 tag that actually matched. */
    locale: string;
    /** The message for that locale. */
    message: string;
    /**
     * Index into the supplied `locales` whose tag (or one of its parents)
     * matched. `undefined` only when the match came from the appended baseLocale.
     */
    matchedPreferenceIndex?: number;
    /**
     * `exact` when the canonical supplied tag matched, `parent` when one of its
     * truncations matched, `base` only when the appended baseLocale matched.
     */
    match: "exact" | "parent" | "base";
};
/**
 * Resolves a single localized message from `set` against an ordered list of
 * locale preferences (RFC 4647 lookup). For each supplied locale, in order, the
 * canonical tag and its truncation chain are tried; the first present entry
 * wins. Candidates are deduped preserving first-seen order, so a duplicate is
 * attributed to the earlier preference's chain. The baseLocale is consulted only
 * after every supplied preference, and a match against it alone is reported as
 * `base`. An invalid supplied tag is skipped (a miss), never a throw.
 *
 * This never returns `undefined`: a {@link LocalizedMessageSet} always has an
 * entry for its baseLocale, which is the guaranteed floor.
 */
declare function resolveUserMessage(set: LocalizedMessageSet, options?: {
    locales?: readonly string[];
}): ResolvedUserMessage;

/**
 * A single, vetted field-level fault. The common validation case made
 * first-class so it need not be smuggled through ad-hoc extensions. `field` is a
 * client-meaningful path, `code` a stable, localizable reason (never a raw
 * message). RFC 9457 does not define this member; it is a documented extension
 * (`fields`) the transport adapter writes by default.
 */
type FieldFault = {
    readonly field: string;
    readonly code: string;
};
/**
 * The single source of truth for one public error code. One registration feeds
 * all three stages: curation ({@link project}), localization ({@link localize}),
 * and transport ({@link toProblem}). There is no second adapter map.
 *
 * Curation is a security boundary: nothing of the internal error reaches a view
 * automatically. `category`/`retryable` here are the *public* values, declared
 * by the registrant, deliberately distinct from the technical
 * `StructuredError.category`/`retryable` (which may reveal infrastructure, e.g.
 * a `DEADLOCK`). The internal taxonomy is never the wire taxonomy, exactly as
 * `publicCode` is distinct from the internal `code`.
 *
 * `userMessages` is optional: an app that localizes entirely on the client omits
 * it and still gets a machine-complete view and problem body.
 */
type PublicErrorDescriptor<TError = unknown, TDetails = never, TPublicCode extends string = string> = {
    /** Stable public code for the wire. */
    readonly publicCode: TPublicCode;
    /** Transport status (HTTP / RFC 9457). Read only by {@link toProblem}. */
    readonly status: number;
    /** Optional RFC 9457 problem type URI (ideally dereferences to docs). */
    readonly type?: string;
    /**
     * Optional static, developer-facing summary of the problem type (RFC 9457
     * `title`). Audience is the API consumer reading the JSON, not the end user;
     * stable per code, not localized. Distinct from {@link userMessages}, which is
     * the localized end-user text. `toProblem` emits a localized message as the
     * title when present, otherwise this static one.
     */
    readonly title?: string;
    /**
     * Curated public category. Never the internal `StructuredError.category`. An
     * advisory coarse grouping for telemetry and soft UX, NOT an exhaustive branch
     * key: branch on `publicCode`, which is the typed contract. Declare
     * `categories` on the catalog to enforce a closed vocabulary and catch drift.
     */
    readonly category?: string;
    /** Declared retryability hint; overridable per occurrence by {@link projectRetryable}. */
    readonly retryable?: boolean;
    /** Optional client-safe localized messages. Omit for client-side i18n. */
    readonly userMessages?: LocalizedMessageSet;
    /**
     * Explicit projection of a vetted, typed subset onto `details`. Never
     * spreads the error. Return **fresh** data (a new object built from vetted
     * values), not a reference into the error: `details` is deliberately not
     * cloned at this stage (the in-process view may hold rich values; `toProblem`
     * is the wire boundary), so a returned internal reference couples the view to
     * internal error state and later mutation reaches it.
     */
    readonly projectDetails?: (error: TError) => TDetails;
    /** Optional per-occurrence retryability, falling back to {@link retryable} if it throws. */
    readonly projectRetryable?: (error: TError) => boolean;
    /**
     * Optional per-occurrence retry delay in whole seconds, read from the error
     * (e.g. a rate limiter's window). Surfaced as the view's `retryAfter` and, by
     * `toProblem`, as the HTTP `Retry-After` header. A non-integer/negative result
     * or a throw is ignored.
     */
    readonly projectRetryAfter?: (error: TError) => number | undefined;
    /**
     * Optional projection of vetted field faults. Validation's common path.
     * The result is normalized into a frozen copy of exactly `{ field, code }`
     * per fault: foreign extra properties are stripped and the view is decoupled
     * from the returned objects.
     */
    readonly projectFields?: (error: TError) => readonly FieldFault[];
};
/**
 * The curated, transport-neutral, message-free machine view. Carries public
 * meaning only: a `publicCode`, optional curated `category`/`retryable`, and
 * explicitly projected `details`/`fields`. No status (transport's job), no
 * message (localization's job). The output of {@link project}, total over
 * `unknown`.
 */
type PublicError<TDetails = unknown, TCode extends string = string> = {
    readonly code: TCode;
    /** Advisory coarse grouping (see the descriptor). Branch on `code`, not this. */
    readonly category?: string;
    readonly retryable?: boolean;
    /** Neutral retry-delay hint in whole seconds (e.g. for a 429/503 occurrence). */
    readonly retryAfter?: number;
    readonly details?: TDetails;
    readonly fields?: readonly FieldFault[];
};
/** A {@link PublicError} after the optional {@link localize} stage attached human text. */
type LocalizedPublicError<TDetails = unknown, TCode extends string = string> = PublicError<TDetails, TCode> & {
    readonly message: string;
    readonly locale: string;
};
/**
 * How the `details`/`fields` projection went: `none` when the descriptor has no
 * projector, `succeeded` when one ran cleanly, `failed` when one threw (the view
 * still stands without that member). Surfaced for debugging a silently missing
 * `details`.
 */
type ProjectionStatus = "none" | "succeeded" | "failed";
/**
 * What a `project` did, for fire-and-forget observability: whether the error
 * matched a descriptor (and how) or fell back, plus the projection status. A
 * fallback caused by a throwing matcher is `matcher_failed`, distinct from a
 * genuine `no_match`.
 */
type ProjectionOutcome = {
    readonly kind: "matched";
    readonly via: "code" | "predicate";
    readonly projection: ProjectionStatus;
} | {
    readonly kind: "fallback";
    readonly reason: "no_match" | "matcher_failed";
    readonly projection: ProjectionStatus;
};
/**
 * Fire-and-forget observer invoked once per {@link project}. The central place
 * to log the technical error alongside the emitted public code and outcome. If
 * it throws, the projector swallows it: telemetry must never break totality.
 */
type OnProject = (error: unknown, view: PublicError, outcome: ProjectionOutcome) => void;

/** A descriptor with its types erased, as stored and returned by the catalog. */
type AnyDescriptor = PublicErrorDescriptor<unknown, unknown>;
/**
 * The static wire metadata of a public code, as read by {@link toProblem}:
 * status, the RFC 9457 type, and the static developer-facing title.
 */
type Transport = {
    readonly status: number;
    readonly type?: string;
    /** Static, developer-facing problem-type summary (RFC 9457 `title`). */
    readonly title?: string;
};
/**
 * The outcome of resolving an error against the catalog. `matcherThrew` lets a
 * caller distinguish a genuine miss from a broken matcher, mirroring the
 * presentation registry.
 */
type CatalogResolution = {
    readonly found: true;
    readonly via: "code" | "predicate";
    readonly descriptor: AnyDescriptor;
    readonly matcherThrew: boolean;
} | {
    readonly found: false;
    readonly matcherThrew: boolean;
};
/**
 * The single source of truth: one descriptor per public error code, addressable
 * both by the internal error `code`/predicate (for {@link project}) and by
 * public code (for {@link toProblem}'s transport facet and {@link localize}'s
 * messages). Unifies what used to be a registry (messages/details) plus a
 * separate adapter map (status/type), so the two cannot drift.
 *
 * Resolution order matches the presentation registry: exact internal `code`,
 * then predicate matchers in registration order, then a miss (the `fallback`).
 */
declare class PublicErrorCatalog<TPublicCode extends string = string> {
    #private;
    /** The generic descriptor used for any unmatched error. */
    readonly fallback: AnyDescriptor;
    constructor(options: {
        fallback: PublicErrorDescriptor<never, never, TPublicCode>;
        /** Fire-and-forget observer invoked once per {@link project} through this catalog. */
        onProject?: OnProject;
        /**
         * Optional closed vocabulary for the advisory public `category`. When given,
         * every descriptor's `category` must be a member, validated at registration
         * to prevent drift. `category` remains advisory: branch on `publicCode`.
         */
        categories?: readonly string[];
    });
    /**
     * Invokes the configured {@link OnProject} observer, swallowing any error so
     * telemetry can never break projection totality. Called by `project`.
     */
    observeProjection(error: unknown, view: PublicError, outcome: ProjectionOutcome): void;
    /**
     * Registers a descriptor keyed by an exact internal error `code`. Returns a
     * catalog widened with the new public code, so a chain of registrations
     * accumulates the public-code union for end-to-end typing.
     */
    registerByCode<TError = unknown, TDetails = never, const TNewCode extends string = string>(code: string, descriptor: PublicErrorDescriptor<TError, TDetails, TNewCode>): PublicErrorCatalog<TPublicCode | TNewCode>;
    /** Registers a descriptor guarded by a type-guard matcher, tried after code matches. */
    register<TError, TDetails = never, const TNewCode extends string = string>(entry: {
        match: (error: unknown) => error is TError;
        descriptor: PublicErrorDescriptor<TError, TDetails, TNewCode>;
    }): PublicErrorCatalog<TPublicCode | TNewCode>;
    /** Resolves the descriptor for `error`, or a miss. */
    resolve(error: unknown): CatalogResolution;
    /**
     * The static wire metadata (status/type/title) for a registered public code,
     * or `undefined` if the code is not registered (including the fallback, which
     * is indexed at construction). An unknown code is a foreign/stale view that
     * must not be paired with this catalog's fallback status; the caller decides.
     */
    transportFor(publicCode: string): Transport | undefined;
    /** The localized messages registered for a public code, if any. */
    messagesFor(publicCode: string): LocalizedMessageSet | undefined;
    /**
     * Asserts that every code in `knownCodes` has a `registerByCode` descriptor.
     * An opt-in completeness check for the consumer's composition root.
     */
    assertCoverage(knownCodes: readonly string[]): void;
}
/** The union of public codes a {@link PublicErrorCatalog} can produce. */
type PublicCodeOf<TCatalog> = TCatalog extends PublicErrorCatalog<infer TPublicCode> ? TPublicCode : never;
/**
 * Builds a catalog whose public-code union is inferred from the fallback (and
 * grows as you chain `registerByCode`/`register`). Prefer this over `new` when
 * you want the UI to switch exhaustively on `code` at compile time; `new`
 * leaves the union as the open `string`.
 */
declare function definePublicErrors<const TCode extends string>(options: {
    fallback: PublicErrorDescriptor<never, never, TCode>;
    onProject?: OnProject;
    categories?: readonly string[];
}): PublicErrorCatalog<TCode>;

/**
 * Stage 1: curation as a security boundary. Turns an unknown technical error
 * into a curated, transport-neutral, message-free {@link PublicError}. Total
 * over `unknown`: an unmatched error degrades to the catalog's fallback rather
 * than leaking or throwing. Nothing of the error reaches the view automatically;
 * only declared (`category`/`retryable`) or explicitly projected
 * (`details`/`fields`) values appear, and a throwing projector is contained.
 *
 * Invokes the catalog's {@link OnProject} observer once, so a single place can
 * log the technical error alongside the emitted code and outcome.
 */
declare function project<TPublicCode extends string>(catalog: PublicErrorCatalog<TPublicCode>, error: unknown): PublicError<unknown, TPublicCode>;
/**
 * Catalog-free projection against a single descriptor the caller already chose.
 * The same curation rules as {@link project}, without resolution or the
 * observer: use this when you bring your own matching, or have no catalog.
 */
declare function projectWithDescriptor<TCode extends string>(descriptor: PublicErrorDescriptor<never, unknown, TCode>, error: unknown): PublicError<unknown, TCode>;

/**
 * Stage 2: localization, deliberately optional and orthogonal. Attaches human
 * text to a {@link PublicError}, resolving `messages` against ordered locale
 * preferences. The `messages` set is keyed on the public code by the caller: a
 * backend passes `catalog.messagesFor(view.code)`, a client passes its own
 * catalog for the same public code. A client-localizing app simply never calls
 * this stage and renders text from `view.code` itself.
 */
declare function localize<TDetails, TCode extends string = string>(view: PublicError<TDetails, TCode>, messages: LocalizedMessageSet, options?: {
    locales?: readonly string[];
}): LocalizedPublicError<TDetails, TCode>;

/**
 * Shared JSON-safety helper. A value that crosses a wire (an HTTP body, an RPC
 * boundary, `postMessage`) must survive `JSON.stringify` losslessly and must not
 * carry a hostile prototype. This module is the single clone-and-freeze
 * implementation for the public-error transport stage (`toProblem`), so the
 * wire-safety guarantee lives at exactly one place.
 */
/** The subset of values that round-trips through JSON without loss. */
type JsonSafeValue = null | boolean | number | string | readonly JsonSafeValue[] | {
    readonly [key: string]: JsonSafeValue;
};

/**
 * Shared RFC 9457 / HTTP helpers. One definition of "a valid problem status", "a
 * usable type/title string", and the problem media type, reused across the
 * public-error pipeline (projection, catalog registration, and the transport
 * stage) so the rules cannot drift between them.
 */
/** Media type for RFC 9457 JSON problem details. */
declare const PROBLEM_DETAILS_JSON: "application/problem+json";

/** A dynamic body member dropped because it was not JSON-safe. */
type OmittedMember = "details" | "fields" | "extensions";
/** Body members the adapter owns; an extension may not collide with them. */
declare const RESERVED_BODY_FIELDS: readonly ["type", "title", "status", "detail", "instance", "code", "category", "retryable", "retryAfter", "fields", "details"];
type ReservedBodyField = (typeof RESERVED_BODY_FIELDS)[number];
/** Every extension value must be JSON-safe; a non-JSON-safe field is `never`. */
type JsonSafeExtensionShape<TExtensions extends object> = {
    readonly [K in keyof TExtensions]: Pick<TExtensions, K> extends Required<Pick<TExtensions, K>> ? TExtensions[K] extends JsonSafeValue ? TExtensions[K] : never : Exclude<TExtensions[K], undefined> extends JsonSafeValue ? TExtensions[K] : never;
};
/** Extensions must be string-keyed (symbol/number keys are rejected). */
type StringKeyedExtensionShape<TExtensions extends object> = Exclude<keyof TExtensions, string> extends never ? unknown : never;
/**
 * Per-occurrence members added while mapping one view to a problem. `extensions`
 * are additional top-level body members; they are compile-time constrained to be
 * JSON-safe, string-keyed, and free of reserved field names, and re-validated at
 * runtime (a non-JSON-safe or colliding set drops to `outcome.omitted`).
 */
type ToProblemContext<TExtensions extends object = Record<never, never>> = {
    /** RFC 9457 occurrence URI. */
    readonly instance?: string;
    /** RFC 9457 occurrence-specific explanation (distinct from the per-type title). */
    readonly detail?: string;
    /**
     * Retry delay in whole seconds, overriding the view's `retryAfter`. For a
     * boundary that knows the value (a rate limiter) rather than the error. A
     * non-integer/negative value is ignored.
     */
    readonly retryAfter?: number;
    /** Additional JSON-safe top-level body members, keyed by a non-reserved name. */
    readonly extensions?: TExtensions & JsonSafeExtensionShape<TExtensions> & StringKeyedExtensionShape<TExtensions> & {
        readonly [K in ReservedBodyField]?: never;
    };
};
/**
 * An RFC 9457 problem body. `type`/`title`/`status`/`detail`/`instance` are the
 * reserved members (`title` present only when a message was localized);
 * `code`/`category`/`retryable`/`fields`/`details` are documented extension
 * members the adapter writes by default. The body has a null prototype and is
 * deeply frozen, so it is safe to serialize and cannot carry prototype
 * pollution.
 */
type ProblemDetails<TDetails = unknown, TCode extends string = string, TExtensions extends object = Record<never, never>> = {
    readonly type?: string;
    readonly title?: string;
    readonly status: number;
    readonly detail?: string;
    readonly instance?: string;
    readonly code: TCode;
    readonly category?: string;
    readonly retryable?: boolean;
    readonly retryAfter?: number;
    readonly fields?: readonly FieldFault[];
    readonly details?: TDetails;
} & Readonly<Partial<TExtensions>>;
/** Mapping diagnostics retained outside the serialized body. */
type ProblemDetailsOutcome = {
    /** Dynamic members dropped because they were not JSON-safe. */
    readonly omitted: readonly OmittedMember[];
};
/** Framework-neutral status, headers, body, and diagnostics. */
type ProblemDetailsResult<TDetails = unknown, TCode extends string = string, TExtensions extends object = Record<never, never>> = {
    readonly status: number;
    readonly headers: Readonly<{
        "content-type": typeof PROBLEM_DETAILS_JSON;
        "content-language"?: string;
        "retry-after"?: string;
    }>;
    readonly body: ProblemDetails<TDetails, TCode, TExtensions>;
    readonly outcome: ProblemDetailsOutcome;
};
/**
 * Stage 3: transport. Maps a (possibly localized) {@link PublicError} to an RFC
 * 9457 result. The transport `source` is either a {@link PublicErrorCatalog}
 * (looks up `status`/`type` by public code) or an explicit {@link Transport}
 * `{ status, type? }` for catalog-free use; the machine members ride from the
 * view. A `title` and a `content-language` header appear only when the view was
 * localized, so the structure-only path is a first-class, RFC-valid response.
 *
 * This is the wire boundary: `details` and `fields` are deep-cloned into a
 * frozen, JSON-safe structure (a `Date`, `BigInt`, circular reference, or other
 * non-serializable value drops that member and records it in `outcome.omitted`
 * rather than throwing or leaking a value the next serializer would choke on).
 */
declare function toProblem<TDetails, TCode extends string = string, const TExtensions extends object = Record<never, never>>(source: PublicErrorCatalog | Transport, view: PublicError<TDetails, TCode> | LocalizedPublicError<TDetails, TCode>, context?: ToProblemContext<TExtensions>): ProblemDetailsResult<TDetails, TCode, TExtensions>;

export { type CatalogResolution, type FieldFault, LocalizedMessageSet, type LocalizedMessageSetOptions, type LocalizedPublicError, type OmittedMember, type OnProject, PROBLEM_DETAILS_JSON, type ProblemDetails, type ProblemDetailsOutcome, type ProblemDetailsResult, type ProjectionOutcome, type ProjectionStatus, type PublicCodeOf, type PublicError, PublicErrorCatalog, type PublicErrorDescriptor, type ResolvedUserMessage, type ToProblemContext, type Transport, definePublicErrors, localize, project, projectWithDescriptor, resolveUserMessage, toProblem };
