Source: util/format.js

/**
 * Generates a stringified JSON-RPC request object with appended delimiter.
 *
 * @function formatRequest
 * @memberof Utils.format
 * @param {object} request
 * @param {string} request.method
 * @param {array|object} request.params
 * @param {string|number} request.id
 * @param {string|number} request.version
 * @param {string} request.delimiter
 */
const formatRequest = ({
  method, params, id, version, delimiter
}) => {
  if (typeof method !== "string") {
    throw new TypeError(`${method} must be a string`);
  }

  const request = {
    method
  };

  // assume 2.0 request unless otherwise specified
  if (!version || version !== 1) {
    request.jsonrpc = "2.0";
  }

  if (params && typeof params !== "object" && !Array.isArray(params)) {
    throw new TypeError(`${params} must be an object or array`);
  } else if (params) {
    request.params = params;
  }

  // assume notification otherwise
  if (typeof id !== "undefined") {
    request.id = id;
  }

  return JSON.stringify(request) + delimiter;
};

/**
 * Generates a stringified JSON-RPC response object with appended delimiter.
 *
 * @function formatResponse
 * @memberof Utils.format
 * @param {object} response
 * @param {string} response.method
 * @param {string|number} response.id
 * @param {string|number} response.jsonrpc
 * @param {string} response.delimiter
 * @param response.result
 */
const formatResponse = ({
  jsonrpc, id, method, result, params, delimiter
}) => {
  if (params && result) {
    throw new Error("Cannot send response with both params and result");
  }

  if (method && id) {
    throw new Error("Cannot send response with both a method and non-null id");
  }

  if (method && typeof method !== "string") {
    throw new TypeError("Method must be a string");
  }

  if (params && typeof params !== "object" && !Array.isArray(params)) {
    throw new TypeError("Params must be an object or array");
  }

  const response = {};

  if (typeof result !== "undefined") {
    response.result = result;
  }

  if (params) {
    response.params = params;
  }

  if (!jsonrpc || jsonrpc === 1) {
    // 1.0 response
    response.error = null;
    // 1.0 notification
    if (!id) {
      response.id = null;
    }
  } else {
    // assume 2.0 response, dont include null error and include jsonrpc version
    response.jsonrpc = "2.0";
  }

  if (method) {
    response.method = method;
  }

  if (id) {
    response.id = id;
  }

  return JSON.stringify(response) + delimiter;
};

/**
 * Generates a stringified JSON-RPC error object with appended delimiter.
 *
 * @function formatError
 * @memberof Utils.format
 * @param {object} error
 * @param {string} error.message
 * @param {array|object} error.code
 * @param {string|number} error.id
 * @param {string|number} error.jsonrpc
 * @param {string} error.delimiter
 * @param {string|object|array} error.data
 */
const formatError = ({
  jsonrpc, id, code, message, data, delimiter
}) => {
  if (!message) {
    throw new Error("Must include message in error response");
  }
  // we're going to assume a 2.0 response if the version isnt explicitly 1
  const response = jsonrpc && jsonrpc !== 1
    ? {
      jsonrpc: "2.0",
      error: { code, message },
      id
    }
    : {
      result: null,
      error: { code, message },
      id
    };

  if (data) {
    response.error.data = data;
  }
  return JSON.stringify(response) + delimiter;
};

/**
 * @static
 *
 */
module.exports = {
  formatRequest,
  formatResponse,
  formatError
};