"use strict";
/**
 * JSON-RPC 2.0 envelope parsing + error helpers for the MCP Streamable HTTP
 * transport (#614). The MCP wire format is one JSON-RPC message per HTTP body
 * (no batches in the 2025-06-18 revision — spec §transports).
 *
 * Spec: https://www.jsonrpc.org/specification
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.ERROR_CODES = exports.JSONRPC_VERSION = void 0;
exports.parseMessage = parseMessage;
exports.isClientFireAndForget = isClientFireAndForget;
exports.buildSuccess = buildSuccess;
exports.buildError = buildError;
exports.JSONRPC_VERSION = '2.0';
/** Standard JSON-RPC 2.0 error codes (spec §5.1). */
exports.ERROR_CODES = {
    PARSE_ERROR: -32700,
    INVALID_REQUEST: -32600,
    METHOD_NOT_FOUND: -32601,
    INVALID_PARAMS: -32602,
    INTERNAL_ERROR: -32603,
};
/**
 * Validate a JSON-RPC envelope. Accepts either a raw UTF-8 JSON string
 * (e.g., from a streamed request body) or an already-parsed value (e.g.,
 * Fastify hands us a parsed object via its preParsing pipeline). Returns
 * either a typed message or a structured parse error that the caller maps
 * to a JSON-RPC error response. Never throws.
 */
function parseMessage(body) {
    let parsed;
    if (typeof body === 'string') {
        try {
            parsed = JSON.parse(body);
        }
        catch (err) {
            const fail = { ok: false, code: exports.ERROR_CODES.PARSE_ERROR, reason: err.message };
            return fail;
        }
    }
    else {
        parsed = body;
    }
    if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
        // Batch requests (arrays) are not used in the MCP Streamable HTTP wire
        // format; rejecting them keeps the transport unambiguous.
        const fail = { ok: false, code: exports.ERROR_CODES.INVALID_REQUEST, reason: 'expected a JSON object' };
        return fail;
    }
    const obj = parsed;
    if (obj.jsonrpc !== exports.JSONRPC_VERSION) {
        const fail = {
            ok: false,
            code: exports.ERROR_CODES.INVALID_REQUEST,
            reason: `jsonrpc must be "${exports.JSONRPC_VERSION}"`,
        };
        return fail;
    }
    const hasMethod = typeof obj.method === 'string';
    const hasResultOrError = 'result' in obj || 'error' in obj;
    if (!hasMethod && !hasResultOrError) {
        const fail = {
            ok: false,
            code: exports.ERROR_CODES.INVALID_REQUEST,
            reason: 'missing method, result, or error field',
        };
        return fail;
    }
    const success = { ok: true, message: parsed };
    return success;
}
/**
 * True if the message is a "fire-and-forget" frame from the client's
 * perspective — a notification (no `id`) or a response to a server-initiated
 * request (has `result` or `error`). Per MCP §transports point 4, both yield
 * HTTP 202 with no body.
 */
function isClientFireAndForget(message) {
    const hasId = 'id' in message;
    const hasResultOrError = 'result' in message || 'error' in message;
    return !hasId || hasResultOrError;
}
function buildSuccess(id, result) {
    return { jsonrpc: exports.JSONRPC_VERSION, id, result };
}
function buildError(id, code, message, data) {
    const error = { code, message };
    if (data !== undefined)
        error.data = data;
    return { jsonrpc: exports.JSONRPC_VERSION, id, error };
}
//# sourceMappingURL=jsonrpc.js.map