/**
 * Transport-agnostic dispatch core shared by HTTP RPC and WebSocket
 * action dispatchers.
 *
 * `perform_action` runs the post-parse pipeline that every action-spec
 * handler must traverse:
 *
 * 1. **Pre-validation auth (401)** — short-circuits unauthenticated callers
 *    on `'required'` axes before input validation runs, so callers never
 *    see `invalid_params` for methods with required input.
 * 2. **Validate params (400)** — `spec.input.safeParse(raw_params)` with
 *    `z.void()` / `?? {}` rules. The validated input lands inside the
 *    function so the authorization phase reads `acting` as a typed Zod
 *    field.
 * 3. **Authorization phase** — when `auth.actor !== 'none'` (or
 *    `auth.account !== 'none' && actor === 'none'`), resolves the actor
 *    via `apply_authorization_phase` against the supplied `account_id`
 *    plus `validated_input.acting`. Failures fold into a JSON-RPC error
 *    envelope. The test-harness escape hatch lives in the caller — pass
 *    `preset.request_context` to skip the live phase and use a pre-baked
 *    context instead.
 * 4. **Post-authorization auth (403)** — gates `auth.credential_types` and
 *    `auth.roles` against the resolved context.
 * 5. **Rate limit (429)** — per-action IP / account throttling, throttle-
 *    requests semantics (every invocation records, regardless of outcome).
 * 6. **Dispatch + DEV-only output validation + error normalization** —
 *    `spec.side_effects` picks transaction (`deps.db.transaction`) vs
 *    pool. Handler throws roll back the transaction; the catch sits
 *    outside the transaction boundary. Handler outputs are validated
 *    against `spec.output` under DEV (logs an error on mismatch, never
 *    throws, never mutates the result).
 *
 * The function is pure data — it never touches a Hono context, so HTTP
 * RPC, REST bridge (when on the action surface), and WS dispatch all
 * call into it the same way and bind the discriminated
 * `PerformActionResult` to their wire shape.
 *
 * @module
 */
import type { Logger } from '@fuzdev/fuz_util/log.js';
import type { Uuid } from '@fuzdev/fuz_util/id.js';
import { type RequestContext } from '../auth/request_context.js';
import { type CredentialType } from '../hono_context.js';
import type { Db } from '../db/db.js';
import { type JsonrpcRequestId, type JsonrpcErrorObject } from '../http/jsonrpc.js';
import type { RateLimiter } from '../rate_limiter.js';
import type { RpcAction } from './action_rpc.js';
/**
 * Per-call inputs to `perform_action`. Each transport assembles this from
 * its wire envelope + connection identity.
 */
export interface PerformActionInput {
    /** The resolved spec + handler (transport does method lookup). */
    action: RpcAction;
    /** Raw params from the wire envelope (post-`JsonrpcRequest.parse`, pre-`spec.input.safeParse`). */
    raw_params: unknown;
    /** JSON-RPC request id — echoed onto the response. */
    request_id: JsonrpcRequestId;
    /** Authenticated account id, or `null` for anonymous. */
    account_id: string | null;
    /** Credential type the request arrived on, or `null` for anonymous. */
    credential_type: CredentialType | null;
    /** Resolved client IP (`'unknown'` if upstream couldn't resolve). */
    client_ip: string;
    /** Per-request abort signal. HTTP: `c.req.raw.signal`. WS: `AbortSignal.any([socket, request])`. */
    signal: AbortSignal;
    /** Send a request-scoped notification. HTTP: DEV-warn-and-drop. WS: socket-scoped. */
    notify: (method: string, params: unknown) => void;
    /** Stable per-socket id on WS; `undefined` on HTTP. */
    connection_id?: Uuid;
    /**
     * Test-harness escape hatch. When set, the live authorization phase is
     * skipped and `request_context` is used directly for post-authorization
     * checks + handler dispatch. Production callers leave this `undefined`.
     */
    preset?: {
        request_context: RequestContext | null;
    };
}
/**
 * Per-deps inputs to `perform_action`. Each transport supplies its own
 * pool-level `Db` and rate limiters; the dispatcher wraps in a transaction
 * iff `spec.side_effects` is true.
 *
 * Pool-resilient fire-and-forget effects (audit writes) run through
 * `AppDeps.audit.emit` from the action factory's closure — the dispatcher
 * never sees the audit emitter. The bound emitter owns the pool.
 */
export interface PerformActionDeps {
    /** Pool-level DB. The dispatcher wraps in `db.transaction` for `side_effects: true` actions. */
    db: Db;
    /**
     * Eager fire-and-forget pool-write queue, flushed by the transport's
     * `try/finally` via `flush_pending_effects`.
     */
    pending_effects: Array<Promise<void>>;
    /**
     * Deferred post-commit thunks pushed via `emit_after_commit`, flushed
     * by the transport's `try/finally` after the handler returns.
     */
    post_commit_effects: Array<() => void | Promise<void>>;
    /** Logger threaded into `ActionContext.log`. */
    log: Logger;
    /** Per-IP limiter (shared across transports). `null` disables. */
    action_ip_rate_limiter: RateLimiter | null;
    /** Per-account limiter (shared across transports). `null` disables. */
    action_account_rate_limiter: RateLimiter | null;
}
/**
 * Discriminated result of `perform_action`. Each transport binds this to
 * its wire shape: HTTP RPC folds the error into a JSON-RPC envelope and
 * returns via `c.json`; WS sends the response over the socket.
 */
export type PerformActionResult = {
    kind: 'ok';
    result: unknown;
} | {
    kind: 'error';
    error: JsonrpcErrorObject;
    status: number;
};
/**
 * The shared dispatch core. Pure data — no Hono context, no socket. Each
 * transport calls into this with pre-parsed inputs and binds the result
 * to its wire shape.
 *
 * Phase order: 401 → 400 → 403 → handler. On the test-preset path the
 * dispatcher skips the live authorization phase and uses the supplied
 * pre-baked context for post-authorization checks; pre-validation 401
 * still fires when the harness omits `account_id`.
 */
export declare const perform_action: (input: PerformActionInput, deps: PerformActionDeps) => Promise<PerformActionResult>;
/**
 * Build a JSON-RPC response envelope from a `PerformActionResult` for
 * transports that wire over the JSON-RPC 2.0 message shape (HTTP RPC + WS).
 */
export declare const perform_action_result_to_envelope: (id: JsonrpcRequestId, result: PerformActionResult) => {
    jsonrpc: string;
    id: JsonrpcRequestId;
} & ({
    result: unknown;
} | {
    error: JsonrpcErrorObject;
});
//# sourceMappingURL=perform_action.d.ts.map