import { StandardSchemaV1 } from '@standard-schema/spec';

/**
 * Call names that cannot be used in {@link defineSocka} `calls` because they would
 * make {@link SockaSession} `send` look Promise-like or clash with ordinary object
 * shape (`constructor`, `Object.prototype`). Call names live only under `send`, so
 * session fields like `client` / `close` need not be reserved.
 */
declare const RESERVED_SOCKA_PROCEDURE_NAMES: readonly ["then", "catch", "finally", "constructor", "toString", "valueOf", "toLocaleString", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable"];
/** Union of {@link RESERVED_SOCKA_PROCEDURE_NAMES}. */
type ReservedSockaProcedureName = (typeof RESERVED_SOCKA_PROCEDURE_NAMES)[number];

/**
 * Defines one client-initiated call: optional `input` and optional `output` schemas
 * (Standard Schema v1: Zod v4, Valibot, ArkType, etc.).
 *
 * - **`output` present** (including `z.void()`): request/response RPC; the server sends
 *   a validated `serverResponse` on success.
 * - **`output` omitted**: fire-and-forget on success (no `serverResponse`); the client
 *   `send` method resolves after the request is sent. Use `output: z.void()` when you
 *   still want a correlated ack. See the package README and {@link defineSocka}.
 */
type SockaProcedureDef = {
    readonly input?: StandardSchemaV1;
    readonly output?: StandardSchemaV1;
};
/** Configuration object accepted by {@link defineSocka}. */
type SockaContractConfig = {
    readonly calls: Record<string, SockaProcedureDef>;
    readonly pushes?: Record<string, StandardSchemaV1>;
};
/** Runtime contract returned by {@link defineSocka}, preserving full generic types. */
type SockaContract<T extends SockaContractConfig = SockaContractConfig> = {
    readonly calls: T["calls"];
    readonly pushes: T extends {
        pushes: Record<string, StandardSchemaV1>;
    } ? T["pushes"] : Record<string, never>;
};
/**
 * Wide config shape for generic *bounds* on APIs that must accept any `defineSocka`
 * result, including contracts with **server pushes**.
 *
 * `SockaContract` instantiated with {@link SockaContractConfig} alone resolves
 * `pushes` to `Record<string, never>` (because `pushes` is optional, so the
 * conditional in {@link SockaContract} does not keep concrete push fields). A generic
 * bound of `extends SockaContract<SockaContractConfig>` therefore **rejects** real
 * contracts with named server pushes. Use {@link SockaContractBound} (equivalently
 * `extends SockaContract<SockaContractConfigBound>`) for those constraints.
 */
type SockaContractConfigBound = {
    readonly calls: Record<string, SockaProcedureDef>;
    readonly pushes: Record<string, StandardSchemaV1>;
};
/**
 * Widen any concrete `defineSocka` contract for use in `extends` constraints
 * (e.g. `SockaDoSession`, `SockaSession`, `useSockaSession`).
 */
type SockaContractBound = SockaContract<SockaContractConfigBound>;
/**
 * When call keys are a **narrow** object type, rejects keys in
 * {@link ReservedSockaProcedureName} (thenable / `Object.prototype` hazards on
 * `session.send`). Wide `Record<string, SockaProcedureDef>` is unchanged so
 * dynamic maps still typecheck; use runtime validation (see {@link SockaSession}
 * `send`).
 */
type ValidateSockaCallKeys<P extends Record<string, SockaProcedureDef>> = string extends keyof P ? P : keyof P & ReservedSockaProcedureName extends never ? P : never;
/** Inferred client return type for a call: payload type or `void` when `output` is omitted. */
type InferSockaCallReturn<P extends SockaProcedureDef> = P["output"] extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<P["output"]> : void;
type CallFn<P extends SockaProcedureDef> = P extends {
    input: infer I extends StandardSchemaV1;
} ? (input: StandardSchemaV1.InferInput<I>) => Promise<InferSockaCallReturn<P>> : () => Promise<InferSockaCallReturn<P>>;
/**
 * Infers the typed `session.send.*` method map for a contract.
 */
type InferSockaSend<C extends SockaContractBound> = {
    [K in keyof C["calls"]]: CallFn<C["calls"][K]>;
};
type HandlerOut<P extends SockaProcedureDef> = P["output"] extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<P["output"]> | Promise<StandardSchemaV1.InferOutput<P["output"]>> : void | Promise<void>;
type HandlerFn<P extends SockaProcedureDef, TSession> = P extends {
    input: infer I extends StandardSchemaV1;
} ? (input: StandardSchemaV1.InferInput<I>, session: TSession) => HandlerOut<P> : (session: TSession) => HandlerOut<P>;
/**
 * Infers the typed server handler map for a contract. Handlers with an input
 * schema take `(input, session)`; calls without input take `(session)` only.
 * When `output` is present, the return value is validated and sent as `serverResponse`.
 * When `output` is omitted (fire-and-forget), the handler should return `void`; the
 * server does not send a success response.
 */
type InferSockaHandlers<C extends SockaContractBound, TSession> = {
    [K in keyof C["calls"]]: HandlerFn<C["calls"][K], TSession>;
};
type InferPushPayload<S extends StandardSchemaV1> = StandardSchemaV1.InferOutput<S>;
/**
 * Payload type for a contract push (output of the push's Standard Schema).
 */
type InferSockaPushPayload<C extends SockaContractBound, K extends keyof C["pushes"]> = C["pushes"][K] extends StandardSchemaV1 ? InferPushPayload<C["pushes"][K]> : never;
/**
 * Infers the typed push subscription handler map for a contract's `pushes`.
 */
type InferSockaPushHandlers<C extends SockaContractBound> = {
    [K in keyof C["pushes"]]: C["pushes"][K] extends StandardSchemaV1 ? (payload: InferPushPayload<C["pushes"][K]>) => void | Promise<void> : never;
};
/**
 * Creates a socka contract from call and push definitions. Pass Zod, Valibot,
 * ArkType, or any Standard Schema v1 schemas directly — no adapters needed.
 *
 * ```ts
 * export const myContract = defineSocka({
 *   calls: {
 *     list: { output: z.array(itemSchema) },
 *     insert: { input: z.object({ item: itemSchema }), output: z.void() },
 *     notify: { input: z.object({ text: z.string() }) },
 *   },
 * });
 * ```
 *
 * Call names must not be {@link ReservedSockaProcedureName} — they would make
 * `session.send` thenable or unsafe as a plain method bag.
 */
declare function defineSocka<const T extends SockaContractConfig>(config: T & {
    calls: ValidateSockaCallKeys<T["calls"]>;
}): SockaContract<T>;

/**
 * Versioned socka wire framing. After JSON parse or msgpack unpack, every frame
 * must satisfy {@link decodeSockaWire}; procedure bodies are validated with Standard Schema on each side.
 */
declare const SOCKA_WIRE_VERSION: 1;
declare class SockaWireError extends Error {
    readonly name = "SockaWireError";
}
type SockaClientRequestFrame = {
    readonly socka: "clientRequest";
    readonly v: typeof SOCKA_WIRE_VERSION;
    readonly id: string;
    readonly rpc: string;
    readonly body: Record<string, unknown>;
};
type SockaServerResponseFrame = {
    readonly socka: "serverResponse";
    readonly v: typeof SOCKA_WIRE_VERSION;
    readonly id: string;
    readonly rpc: string;
    readonly body: unknown;
};
type SockaServerErrorFrame = {
    readonly socka: "serverError";
    readonly v: typeof SOCKA_WIRE_VERSION;
    readonly id: string;
    readonly error: string;
    /** Procedure name when the error relates to a client RPC (helps clients without a pending entry). */
    readonly rpc?: string;
    /** Optional machine-readable code (e.g. `FORBIDDEN`). */
    readonly code?: string;
    /** Optional structured detail for clients; keep small and JSON-serializable. */
    readonly data?: unknown;
};
type SockaServerEventFrame = {
    readonly socka: "serverEvent";
    readonly v: typeof SOCKA_WIRE_VERSION;
    readonly event: string;
    readonly body: unknown;
};
type SockaWireFrame = SockaClientRequestFrame | SockaServerResponseFrame | SockaServerErrorFrame | SockaServerEventFrame;
type DecodedSockaWire = {
    readonly kind: "clientRequest";
    readonly frame: SockaClientRequestFrame;
} | {
    readonly kind: "serverResponse";
    readonly frame: SockaServerResponseFrame;
} | {
    readonly kind: "serverError";
    readonly frame: SockaServerErrorFrame;
} | {
    readonly kind: "serverEvent";
    readonly frame: SockaServerEventFrame;
};
/**
 * Decodes a parsed wire object (from JSON or msgpack). Throws {@link SockaWireError}
 * if the payload is not a valid socka v1 frame.
 */
declare function decodeSockaWire(parsed: unknown): DecodedSockaWire;
/** Builds a socka v1 client request frame. */
declare function encodeClientRequest(id: string, rpc: string, body: Record<string, unknown>): SockaClientRequestFrame;
/** Builds a socka v1 server response frame. */
declare function encodeServerResponse(id: string, rpc: string, body: unknown): SockaServerResponseFrame;
/** Builds a socka v1 server error frame. */
declare function encodeServerError(id: string, error: string, extra?: {
    readonly code?: string;
    readonly data?: unknown;
    readonly rpc?: string;
}): SockaServerErrorFrame;
/** Builds a socka v1 server event frame. */
declare function encodeServerEvent(event: string, body: unknown): SockaServerEventFrame;

/**
 * JSON text frames vs msgpack binary frames for the same socka v1 object graph.
 * Matches {@link decodeSockaWire} after parse/unpack.
 */

/** Wire encoding: UTF-8 JSON strings (default) or msgpack `ArrayBuffer` frames. */
type SockaWireFormat = "json" | "msgpack";
/**
 * Encodes a socka frame for the wire. JSON returns a string; msgpack returns bytes
 * suitable for `WebSocket.send`.
 */
declare function encodeSockaWire(frame: SockaWireFrame, format: SockaWireFormat, serializeJson?: (value: unknown) => string): string | Uint8Array;
/**
 * Decodes a wire payload to a plain object before {@link decodeSockaWire}.
 * Msgpack mode accepts `ArrayBuffer` or `Uint8Array` (e.g. from `msgpackr` / `WebSocket`).
 */
declare function parseWirePayload(data: string | ArrayBuffer | Uint8Array, format: SockaWireFormat, deserializeJson?: (raw: string) => unknown): unknown;

/**
 * Thrown on the server and surfaced on the client when the wire uses a shared
 * `{ type: "error", id, error }` envelope for correlated RPC failures.
 */
declare class SockaError extends Error {
    readonly requestId?: string;
    /** Procedure name when provided on the wire (`serverError.rpc`). */
    readonly rpc?: string;
    readonly code?: string;
    readonly data?: unknown;
    constructor(message: string, options?: {
        requestId?: string;
        rpc?: string;
        code?: string;
        data?: unknown;
        cause?: unknown;
    });
    /** Builds a {@link SockaError} from a standard RPC error envelope. */
    static fromWire(msg: {
        id: string;
        error: string;
        rpc?: string;
        code?: string;
        data?: unknown;
    }): SockaError;
}

/**
 * Single discriminated union for optional `reportError` on session config and
 * `SockaSession` options: `kind` narrows context; `error` is what was thrown or rejected.
 */
type SockaReportError = {
    kind: "clientEventListener";
    eventName: string;
    error: unknown;
} | {
    kind: "clientEventValidation";
    eventName: string;
    error: unknown;
}
/**
 * `serverError` for a fire-and-forget call (no pending client promise). Prefer
 * setting `reportError` on the session when using output-less procedures.
 */
 | {
    kind: "clientFireAndForgetRpcError";
    error: SockaError;
}
/**
 * `serverError` with no matching pending entry for a call that expects a response
 * (e.g. stale id after reconnect or duplicate frame).
 */
 | {
    kind: "clientOrphanServerError";
    error: SockaError;
}
/**
 * Server sent `serverResponse` for a procedure with no `output` (misbehaving server).
 */
 | {
    kind: "clientUnexpectedServerResponse";
    rpc: string;
    requestId: string;
} | {
    kind: "serverOnAttached";
    error: unknown;
} | {
    kind: "serverInboundMessage";
    /** `hono` uses the same log line as the Hono adapters; others use attach-style. */
    adapter: "attach" | "hono" | "bun";
    error: unknown;
} | {
    kind: "serverHandleClose";
    error: unknown;
} | {
    kind: "serverShutdown";
    adapter: "attach" | "hono";
    error: unknown;
};
/** Default `console.error` behavior; same messages as pre–`reportError` socka. */
declare function defaultReportError(event: SockaReportError): void;
/** Invokes the optional `reportError` callback when provided, otherwise `defaultReportError`. */
declare function reportSockaError(reportError: ((event: SockaReportError) => void) | undefined, event: SockaReportError): void;

export { parseWirePayload as A, reportSockaError as B, type DecodedSockaWire as D, type InferSockaHandlers as I, RESERVED_SOCKA_PROCEDURE_NAMES as R, type SockaContractBound as S, type ValidateSockaCallKeys as V, type SockaWireFormat as a, type InferSockaPushHandlers as b, type InferSockaPushPayload as c, type InferSockaSend as d, type ReservedSockaProcedureName as e, SOCKA_WIRE_VERSION as f, type SockaClientRequestFrame as g, type SockaContract as h, type SockaContractConfig as i, type SockaContractConfigBound as j, SockaError as k, type SockaProcedureDef as l, type SockaReportError as m, type SockaServerErrorFrame as n, type SockaServerEventFrame as o, type SockaServerResponseFrame as p, SockaWireError as q, type SockaWireFrame as r, decodeSockaWire as s, defaultReportError as t, defineSocka as u, encodeClientRequest as v, encodeServerError as w, encodeServerEvent as x, encodeServerResponse as y, encodeSockaWire as z };
