@artinet/sdk
Version:
TypeScript SDK for Agentic Communication
182 lines • 7.14 kB
JavaScript
/**
* HTTP JSON-RPC client utilities.
* Handles the common pattern of sending JSON-RPC requests and processing responses.
*/
import { v4 as uuidv4 } from "uuid";
import { SystemError, INTERNAL_ERROR, PARSE_ERROR, } from "../../utils/common/errors.js";
import { parseResponse } from "./parser.js";
import { logError, logWarn } from "../../utils/logging/log.js";
/**
* Creates a JSON-RPC request body with the specified method and parameters.
*, ErrorCodeParseError
* @param method The JSON-RPC method name
* @param params The parameters for the method
* @param requestId Optional request ID (generates a UUID v4 if not provided)
* @returns A properly formatted JSON-RPC request object
*/
export function createJsonRpcRequest(method, params, requestId = uuidv4()) {
return {
jsonrpc: "2.0",
id: requestId,
method,
params,
};
}
/**
* Sends a JSON-RPC request to the specified endpoint.
*
* @param baseUrl The API endpoint URL
* @param method The JSON-RPC method name
* @param params The parameters for the method
* @param headers Custom headers to include in the request
* @param acceptHeader The desired Accept header ('application/json' or 'text/event-stream')
* @returns A Promise resolving to the fetch Response object
* @throws RpcError if there's a network error
*/
export async function sendJsonRpcRequest(baseUrl, method, params, headers = {}, acceptHeader = "application/json") {
const requestBody = createJsonRpcRequest(method, params);
try {
return await fetch(baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: acceptHeader,
...headers,
},
body: JSON.stringify(requestBody),
});
}
catch (networkError) {
logError("SendJsonRpcRequest", "Network error during RPC call:", networkError);
// Wrap network errors into a standard error format
throw INTERNAL_ERROR(networkError);
}
}
/**
* Sends a GET request to the specified endpoint.
* This is used for non-JSON-RPC calls like agent card retrieval.
*
* @param url The endpoint URL
* @param headers Custom headers to include in the request
* @returns A Promise resolving to the fetch Response object
* @throws RpcError if there's a network error
*/
export async function sendGetRequest(url, headers = {}) {
try {
return await fetch(url, {
method: "GET",
headers: {
Accept: "application/json",
...headers,
},
});
}
catch (networkError) {
logError("SendGetRequest", "Network error during GET request:", networkError);
throw INTERNAL_ERROR(networkError);
}
}
/**
* Processes a standard JSON-RPC response (non-streaming).
* Parses the response, validates it, and returns the result payload.
*
* @param response The fetch Response object
* @param expectedMethod Optional method name for logging purposes
* @returns A promise resolving to the result payload
* @throws RpcError if there's an error in the response
*/
export async function handleJsonRpcResponse(response, expectedMethod) {
let responseBody = null;
try {
responseBody = await response.text();
if (!response.ok) {
try {
// Try to parse error as JSON-RPC
parseResponse(responseBody);
// If we get here, it means there was no error in the response
// But the HTTP status was not OK, so we throw a generic error
}
catch (parseError) {
logWarn("handleJsonRpcResponse", "Error parsing JSON-RPC response:", parseError);
}
// Throw a generic HTTP error if we couldn't extract an RPC error
throw new Error(`HTTP error ${response.status}: ${response.statusText}${responseBody ? ` - ${responseBody}` : ""}`);
}
// Parse and validate the response
// If it has an error, parseResponse will throw
// If it doesn't have a result, parseResponse will also throw
const jsonResponse = parseResponse(responseBody);
// At this point, we know we have a valid result
// NonNullable is used in the return type to ensure TypeScript knows this
return jsonResponse.result;
}
catch (error) {
logError("handleJsonRpcResponse", `Error processing response [${expectedMethod}]:`, error);
// Re-throw RpcError instances directly, wrap others
if (error instanceof SystemError) {
throw error;
}
else {
throw INTERNAL_ERROR(error);
}
}
}
/**
* Processes a JSON response from a regular GET request.
* Handles error checking and returns the parsed JSON.
*
* @param response The fetch Response object
* @param endpoint Optional endpoint description for logging purposes
* @returns A promise resolving to the parsed JSON
* @throws RpcError if there's a response error
*/
export async function handleJsonResponse(response, endpoint) {
let responseBody = null;
try {
responseBody = await response.text();
if (!response.ok) {
throw new Error(`HTTP error ${response.status}: ${response.statusText}${responseBody ? ` - ${responseBody}` : ""}`);
}
return JSON.parse(responseBody);
}
catch (error) {
logError("handleJsonResponse", `Error processing response for ${endpoint || "unknown endpoint"}:`, error);
if (error instanceof SystemError) {
throw error;
}
else {
throw PARSE_ERROR(error);
}
}
}
/**
* Sends a JSON-RPC request and processes the response in a single operation.
* This combines sendJsonRpcRequest and handleJsonRpcResponse into one call.
*
* @param baseUrl The API endpoint URL
* @param method The JSON-RPC method name
* @param params The parameters for the method
* @param headers Custom headers to include in the request
* @param acceptHeader The desired Accept header ('application/json' or 'text/event-stream')
* @returns A Promise resolving to the result payload
* @throws RpcError if there's a network error or error in the response
*/
export async function executeJsonRpcRequest(baseUrl, method, params, headers = {}, acceptHeader = "application/json") {
const response = await sendJsonRpcRequest(baseUrl, method, params, headers, acceptHeader);
return handleJsonRpcResponse(response, method);
}
/**
* Sends a GET request and processes the JSON response.
* Helper for non-RPC REST endpoints.
*
* @param url The endpoint URL
* @param headers Custom headers to include in the request
* @param endpoint Optional endpoint description for logging
* @returns A Promise resolving to the parsed JSON
* @throws RpcError if there's a network error or error in the response
*/
export async function executeGetRequest(url, headers = {}, endpoint) {
const response = await sendGetRequest(url, headers);
return handleJsonResponse(response, endpoint);
}
//# sourceMappingURL=rpc-client.js.map