@messari/sdk
Version:
Messari SDK provides a type-safe, intuitive interface for accessing Messari's suite of crypto data and AI APIs.
1,022 lines • 45.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessariClient = void 0;
const types_1 = require("../types");
const utils_1 = require("../utils");
const logging_1 = require("../logging");
const error_1 = require("../error");
const base_1 = require("./base");
/**
* MessariClient is the main client class for interacting with the Messari API.
* It provides a comprehensive interface for accessing market data, news, intelligence,
* and AI-powered features through typed methods and robust error handling.
*
* Key features:
* - Full TypeScript support with strongly typed requests and responses
* - Configurable logging and error handling
* - Built-in request timeout and retry logic
* - Pagination helpers for listing endpoints
* - Event system for monitoring requests, responses and errors
* - Connection pooling support via HTTP agent
* - Custom fetch implementation support
*/
class MessariClient extends base_1.MessariClientBase {
constructor(options) {
super();
this.ai = {
createChatCompletion: (params, options) => this.request({
method: types_1.createChatCompletionOpenAI.method,
path: types_1.createChatCompletionOpenAI.path(),
body: (0, utils_1.pick)(params, types_1.createChatCompletionOpenAI.bodyParams),
options,
}),
createChatCompletionStream: (params, options) => this.requestStream({
method: types_1.createChatCompletionOpenAI.method,
path: types_1.createChatCompletionOpenAI.path(),
body: { ...(0, utils_1.pick)(params, types_1.createChatCompletionOpenAI.bodyParams), stream: true },
options,
}),
extractEntities: (params, options) => this.request({
method: types_1.extractEntities.method,
path: types_1.extractEntities.path(),
body: (0, utils_1.pick)(params, types_1.extractEntities.bodyParams),
options,
}),
};
/**
* @deprecated Asset is Work-in-Progress and not production ready
*/
this.asset = {
getAssetsV2: async (params = {}, options) => {
return this.requestWithMetadata({
method: types_1.getAssetsV2.method,
path: types_1.getAssetsV2.path(),
queryParams: (0, utils_1.pick)(params, types_1.getAssetsV2.queryParams),
options,
});
},
getAssetDetails: async (params, options) => {
return this.requestWithMetadata({
method: types_1.getAssetDetails.method,
path: types_1.getAssetDetails.path(),
queryParams: (0, utils_1.pick)(params, types_1.getAssetDetails.queryParams),
options,
});
},
getAssetsTimeseriesCatalog: async (options) => {
return this.requestWithMetadata({
method: types_1.getAssetsTimeseriesCatalog.method,
path: types_1.getAssetsTimeseriesCatalog.path(),
options,
});
},
getAssetsV2ATH: async (params = {}, options) => {
return this.requestWithMetadata({
method: types_1.getAssetsV2ATH.method,
path: types_1.getAssetsV2ATH.path(),
queryParams: (0, utils_1.pick)(params, types_1.getAssetsV2ATH.queryParams),
options,
});
},
getAssetsV2ROI: async (params = {}, options) => {
return this.requestWithMetadata({
method: types_1.getAssetsV2ROI.method,
path: types_1.getAssetsV2ROI.path(),
queryParams: (0, utils_1.pick)(params, types_1.getAssetsV2ROI.queryParams),
options,
});
},
getAssetTimeseries: async (params, options) => {
return this.requestWithMetadata({
method: types_1.getAssetTimeseries.method,
path: types_1.getAssetTimeseries.path(params),
queryParams: (0, utils_1.pick)(params, types_1.getAssetTimeseries.queryParams),
options,
});
},
getAssetTimeseriesWithGranularity: async (params, options) => {
return this.requestWithMetadata({
method: types_1.getAssetTimeseriesWithGranularity.method,
path: types_1.getAssetTimeseriesWithGranularity.path(params),
queryParams: (0, utils_1.pick)(params, types_1.getAssetTimeseriesWithGranularity.queryParams),
options,
});
},
};
/**
* @deprecated Exchanges is Work-in-Progress and not production ready
*/
this.exchanges = {
getExchanges: async (params = {}, options) => {
return this.requestWithMetadata({
method: types_1.getExchanges.method,
path: types_1.getExchanges.path(),
queryParams: (0, utils_1.pick)(params, types_1.getExchanges.queryParams),
options,
});
},
getExchangeById: async (params, options) => {
return this.requestWithMetadata({
method: types_1.getExchange.method,
path: types_1.getExchange.path(params),
options,
});
},
getExchangeMetrics: async (options) => {
return this.requestWithMetadata({
method: types_1.getExchangeMetrics.method,
path: types_1.getExchangeMetrics.path(),
options,
});
},
getExchangeTimeseries: async (params, options) => {
return this.requestWithMetadata({
method: types_1.getExchangeTimeseries.method,
path: types_1.getExchangeTimeseries.path(params),
queryParams: (0, utils_1.pick)(params, types_1.getExchangeTimeseries.queryParams),
options,
});
},
};
this.networks = {
getNetworks: async (params = {}, options) => {
return this.requestWithMetadata({
method: types_1.getNetworks.method,
path: types_1.getNetworks.path(),
queryParams: (0, utils_1.pick)(params, types_1.getNetworks.queryParams),
options,
});
},
getNetworkById: async (params, options) => {
return this.requestWithMetadata({
method: types_1.getNetwork.method,
path: types_1.getNetwork.path(params),
options,
});
},
getNetworkMetrics: async (options) => {
return this.requestWithMetadata({
method: types_1.getNetworkMetrics.method,
path: types_1.getNetworkMetrics.path(),
options,
});
},
getNetworkTimeseries: async (params, options) => {
return this.requestWithMetadata({
method: types_1.getNetworkTimeseries.method,
path: types_1.getNetworkTimeseries.path(params),
queryParams: (0, utils_1.pick)(params, types_1.getNetworkTimeseries.queryParams),
options,
});
},
};
/**
* @deprecated Markets is Work-in-Progress and not production ready
*/
this.markets = {
getMarkets: async (params = {}, options) => {
return this.requestWithMetadata({
method: types_1.getMarkets.method,
path: types_1.getMarkets.path(),
queryParams: (0, utils_1.pick)(params, types_1.getMarkets.queryParams),
options,
});
},
getMarketById: async (params, options) => {
return this.requestWithMetadata({
method: types_1.getMarket.method,
path: types_1.getMarket.path(params),
options,
});
},
getMarketMetrics: async (options) => {
return this.requestWithMetadata({
method: types_1.getMarketMetrics.method,
path: types_1.getMarketMetrics.path(),
options,
});
},
getMarketTimeseries: async (params, options) => {
return this.requestWithMetadata({
method: types_1.getMarketTimeseries.method,
path: types_1.getMarketTimeseries.path(params),
queryParams: (0, utils_1.pick)(params, types_1.getMarketTimeseries.queryParams),
options,
});
},
};
/**
* @deprecated Intel is Work-in-Progress and not production ready
*/
this.intel = {
getAllEvents: async (params = {}, options) => {
const fetchPage = async (p, o) => {
return this.requestWithMetadata({
method: types_1.getAllEvents.method,
path: types_1.getAllEvents.path(),
body: (0, utils_1.pick)(p, types_1.getAllEvents.bodyParams),
options: o,
});
};
const response = await fetchPage(params, options);
return this.paginate(params, fetchPage, response, options);
},
getById: async (params, options) => {
return this.request({
method: types_1.getEventAndHistory.method,
path: types_1.getEventAndHistory.path(params),
options,
});
},
getAllAssets: async (params = {}, options) => {
const fetchPage = async (p, o) => {
return this.requestWithMetadata({
method: types_1.getAllAssets.method,
path: types_1.getAllAssets.path(),
queryParams: (0, utils_1.pick)(p, types_1.getAllAssets.queryParams),
options: o,
});
};
const response = await fetchPage(params, options);
return this.paginate(params, fetchPage, response, options);
},
};
/**
* @deprecated Fundraising is Work-in-Progress and not production ready
*/
this.fundraising = {
getFundingRounds: async (params) => {
return this.requestWithMetadata({
method: types_1.getFundingRounds.method,
path: types_1.getFundingRounds.path(),
queryParams: (0, utils_1.pick)(params, types_1.getFundingRounds.queryParams),
});
},
getFundingRoundsInvestors: async (params) => {
return this.requestWithMetadata({
method: types_1.getFundingRoundsInvestors.method,
path: types_1.getFundingRoundsInvestors.path(),
queryParams: (0, utils_1.pick)(params, types_1.getFundingRoundsInvestors.queryParams),
});
},
getAcquisitionDeals: async (params) => {
return this.requestWithMetadata({
method: types_1.getAcquisitionDeals.method,
path: types_1.getAcquisitionDeals.path(),
queryParams: (0, utils_1.pick)(params, types_1.getAcquisitionDeals.queryParams),
});
},
getOrganizations: async (params) => {
return this.requestWithMetadata({
method: types_1.getOrganizations.method,
path: types_1.getOrganizations.path(),
queryParams: (0, utils_1.pick)(params, types_1.getOrganizations.queryParams),
});
},
getProjects: async (params) => {
return this.requestWithMetadata({
method: types_1.getProjects.method,
path: types_1.getProjects.path(),
queryParams: (0, utils_1.pick)(params, types_1.getProjects.queryParams),
});
},
};
/**
* @deprecated TokenUnlocks is Work-in-Progress and not production ready
*/
this.tokenUnlocks = {
getSupportedAssets: async (params = {}, options) => {
return this.request({
method: types_1.getTokenUnlockSupportedAssets.method,
path: types_1.getTokenUnlockSupportedAssets.path(),
queryParams: (0, utils_1.pick)(params, types_1.getTokenUnlockSupportedAssets.queryParams),
options,
});
},
getAllocations: async (params = {}, options) => {
return this.request({
method: types_1.getTokenUnlockAllocations.method,
path: types_1.getTokenUnlockAllocations.path(),
queryParams: (0, utils_1.pick)(params, types_1.getTokenUnlockAllocations.queryParams),
options,
});
},
getVestingSchedule: async (params, options) => {
return this.request({
method: types_1.getTokenUnlockVestingSchedule.method,
path: types_1.getTokenUnlockVestingSchedule.path(params),
queryParams: (0, utils_1.pick)(params, types_1.getTokenUnlockVestingSchedule.queryParams),
options,
});
},
getUnlocks: async (params, options) => {
return this.request({
method: types_1.getTokenUnlocks.method,
path: types_1.getTokenUnlocks.path(params),
queryParams: (0, utils_1.pick)(params, types_1.getTokenUnlocks.queryParams),
options,
});
},
getEvents: async (params, options) => {
return this.request({
method: types_1.getTokenUnlockEvents.method,
path: types_1.getTokenUnlockEvents.path(params),
queryParams: (0, utils_1.pick)(params, types_1.getTokenUnlockEvents.queryParams),
options,
});
},
};
/**
* @deprecated News is Work-in-Progress and not production ready
*/
this.news = {
getNewsFeedPaginated: async (params, options) => {
const fetchPage = async (p, o) => {
return this.requestWithMetadata({
method: types_1.getNewsFeed.method,
path: types_1.getNewsFeed.path(),
queryParams: (0, utils_1.pick)(p, types_1.getNewsFeed.queryParams),
options: o,
});
};
const initialResponse = await fetchPage(params, options);
return this.paginate(params, fetchPage, initialResponse, options);
},
getNewsFeedAssetsPaginated: async (params, options) => {
const fetchPage = async (p, o) => {
return this.requestWithMetadata({
method: types_1.getNewsFeedAssets.method,
path: types_1.getNewsFeedAssets.path(),
queryParams: (0, utils_1.pick)(p, types_1.getNewsFeedAssets.queryParams),
options: o,
});
};
const initialResponse = await fetchPage(params, options);
return this.paginate(params, fetchPage, initialResponse, options);
},
getNewsSourcesPaginated: async (params, options) => {
const fetchPage = async (p, o) => {
return this.requestWithMetadata({
method: types_1.getNewsSources.method,
path: types_1.getNewsSources.path(),
queryParams: (0, utils_1.pick)(p, types_1.getNewsSources.queryParams),
options: o,
});
};
const initialResponse = await fetchPage(params, options);
return this.paginate(params, fetchPage, initialResponse, options);
},
};
/**
* @deprecated Research is Work-in-Progress and not production ready
*/
this.research = {
getResearchReports: (params, options) => this.request({
method: types_1.getResearchReports.method,
path: types_1.getResearchReports.path(),
queryParams: (0, utils_1.pick)(params, types_1.getResearchReports.queryParams),
options,
}),
getResearchReportById: (params, options) => this.request({
method: types_1.getResearchReportById.method,
path: types_1.getResearchReportById.path(params),
options,
}),
getResearchReportTags: (options) => this.request({
method: types_1.getResearchReportTags.method,
path: types_1.getResearchReportTags.path(),
options,
}),
};
/**
* @deprecated Diligence is Work-in-Progress and not production ready
*/
this.diligence = {
getDiligencePreview: async () => {
return this.request({
method: types_1.getPreviews.method,
path: types_1.getPreviews.path(),
});
},
getDiligenceReport: async (params) => {
return this.request({
method: types_1.getReportByAssetID.method,
path: types_1.getReportByAssetID.path(params),
});
},
};
/**
* User Management API service
* Provides methods for managing user-specific data like watchlists, credits, and permissions
*/
this.userManagement = {
getTeamAllowance: (options) => this.request({
method: types_1.getTeamAllowance.method,
path: types_1.getTeamAllowance.path(),
options,
}),
getPermissions: (options) => this.request({
method: types_1.getPermissions.method,
path: types_1.getPermissions.path(),
options,
}),
listWatchlists: (options) => this.request({
method: types_1.listWatchlists.method,
path: types_1.listWatchlists.path(),
options,
}),
createWatchlist: (params, options) => this.request({
method: types_1.createWatchlist.method,
path: types_1.createWatchlist.path(),
body: (0, utils_1.pick)(params, types_1.createWatchlist.bodyParams),
options,
}),
getWatchlist: (params, options) => this.request({
method: types_1.getWatchlist.method,
path: types_1.getWatchlist.path(params),
options,
}),
updateWatchlist: (params, options) => this.request({
method: types_1.updateWatchlist.method,
path: types_1.updateWatchlist.path({ id: params.id }),
body: (0, utils_1.pick)(params, types_1.updateWatchlist.bodyParams),
options,
}),
deleteWatchlist: (params, options) => this.request({
method: types_1.deleteWatchlist.method,
path: types_1.deleteWatchlist.path(params),
options,
}),
modifyWatchlistAssets: (params, options) => this.request({
method: types_1.modifyWatchlistAssets.method,
path: types_1.modifyWatchlistAssets.path({ id: params.id }),
body: (0, utils_1.pick)(params, types_1.modifyWatchlistAssets.bodyParams),
options,
}),
};
this.apiKey = options.apiKey;
this.baseUrl = options.baseUrl || "https://api.messari.io";
this.timeoutMs = options.timeoutMs || 60000; // 60 seconds
this.fetchFn = options.fetch || fetch;
this.agent = options.agent;
// Handle logger initialization with disableLogging option
this.isLoggingDisabled = !!options.disableLogging;
if (this.isLoggingDisabled) {
this.logger = logging_1.noOpLogger;
}
else {
const baseLogger = options.logger || (0, logging_1.makeConsoleLogger)("messari-client");
this.logger = (0, logging_1.createFilteredLogger)(baseLogger, options.logLevel ?? logging_1.LogLevel.INFO);
}
this.defaultHeaders = {
"Content-Type": "application/json",
"x-messari-api-key": this.apiKey,
...options.defaultHeaders,
};
// Initialize event handlers
this.eventHandlers = new Map();
// Register event handlers from options
if (options.onError) {
this.on("error", options.onError);
}
if (options.onRequest) {
this.on("request", options.onRequest);
}
if (options.onResponse) {
this.on("response", options.onResponse);
}
}
async request({ method, path, body, queryParams = {}, options = {} }) {
this.logger(logging_1.LogLevel.DEBUG, "request start", {
method,
url: `${this.baseUrl}${path}`,
queryParams,
});
this.emit("request", {
method,
path,
queryParams,
});
const queryString = Object.entries(queryParams)
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => {
if (Array.isArray(value)) {
return value.map((item) => `${encodeURIComponent(key)}=${encodeURIComponent(String(item))}`).join("&");
}
return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`;
})
.join("&");
const url = `${this.baseUrl}${path}${queryString ? `?${queryString}` : ""}`;
const headers = {
...this.defaultHeaders,
...options.headers,
};
const timeoutMs = options.timeoutMs || this.timeoutMs;
try {
const response = await error_1.RequestTimeoutError.rejectAfterTimeout(this.fetchFn(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
signal: options.signal,
cache: options.cache,
credentials: options.credentials,
integrity: options.integrity,
keepalive: options.keepalive,
mode: options.mode,
redirect: options.redirect,
referrer: options.referrer,
referrerPolicy: options.referrerPolicy,
// @ts-ignore - Next.js specific options
next: options.next,
// Node.js specific option
agent: this.agent,
}), timeoutMs);
if (!response.ok) {
const errorData = await response.json();
this.logger(logging_1.LogLevel.ERROR, "request error", {
status: response.status,
statusText: response.statusText,
error: errorData,
});
const error = new Error(errorData.error || "An error occurred");
this.emit("error", {
error,
request: {
method,
path,
queryParams,
},
});
throw error;
}
// Check if the response is JSON or text based on Content-Type header
const contentType = response.headers.get("Content-Type");
if (contentType?.toLowerCase().includes("application/json")) {
const jsonResponse = await response.json();
// If response has data field and no error, unwrap it, otherwise use the whole response
const data = jsonResponse.data && !jsonResponse.error ? jsonResponse.data : jsonResponse;
return data;
}
const text = await response.text();
return text;
}
catch (error) {
this.logger(logging_1.LogLevel.ERROR, "request failed", { error });
// Emit error event
this.emit("error", {
error: error,
request: {
method,
path,
queryParams,
},
});
throw error;
}
}
async requestStream({ method, path, body, queryParams = {}, options = {} }) {
this.logger(logging_1.LogLevel.DEBUG, "stream request start", {
method,
url: `${this.baseUrl}${path}`,
queryParams,
});
this.emit("request", {
method,
path,
queryParams,
});
const queryString = Object.entries(queryParams)
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => {
if (Array.isArray(value)) {
return value.map((item) => `${encodeURIComponent(key)}=${encodeURIComponent(String(item))}`).join("&");
}
return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`;
})
.join("&");
const url = `${this.baseUrl}${path}${queryString ? `?${queryString}` : ""}`;
const headers = {
...this.defaultHeaders,
...options.headers,
"Accept": "text/event-stream",
"Cache-Control": "no-cache",
"Connection": "keep-alive",
};
const timeoutMs = options.timeoutMs || this.timeoutMs;
try {
const response = await error_1.RequestTimeoutError.rejectAfterTimeout(this.fetchFn(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
signal: options.signal,
cache: options.cache,
credentials: options.credentials,
integrity: options.integrity,
keepalive: options.keepalive,
mode: options.mode,
redirect: options.redirect,
referrer: options.referrer,
referrerPolicy: options.referrerPolicy,
// @ts-ignore - Next.js specific options
next: options.next,
// Node.js specific option
agent: this.agent,
}), timeoutMs);
if (!response.ok) {
const errorData = await response.json();
this.logger(logging_1.LogLevel.ERROR, "request error", {
status: response.status,
statusText: response.statusText,
error: errorData,
});
const error = new Error(errorData.error || "An error occurred");
this.emit("error", {
error,
request: {
method,
path,
queryParams,
},
});
throw error;
}
// For streaming responses, return a transformed stream that parses the chunks
if (!response.body) {
throw new Error("No reader available for streaming response");
}
let buffer = "";
const decoder = new TextDecoder();
// Create a TransformStream that will parse the raw bytes into the expected type T
const transformer = new TransformStream({
transform: async (chunk, controller) => {
try {
// Decode the chunk and add to buffer
const text = decoder.decode(chunk, { stream: true });
buffer += text;
// Process any complete lines in the buffer
const lines = buffer.split("\n");
// Keep the last potentially incomplete line in the buffer
buffer = lines.pop() || "";
for (const line of lines) {
if (line.startsWith("data: ")) {
const jsonData = line.slice(6).trim(); // Remove 'data: ' prefix
// Skip [DONE] marker
if (jsonData === "[DONE]") {
continue;
}
if (jsonData) {
try {
const parsed = JSON.parse(jsonData);
controller.enqueue(parsed);
}
catch (e) {
this.logger(logging_1.LogLevel.ERROR, "Error parsing JSON from stream", {
error: e,
data: jsonData,
});
}
}
}
else if (line.trim() && !line.startsWith(":")) {
// Try to parse non-empty lines that aren't comments
try {
const parsed = JSON.parse(line);
controller.enqueue(parsed);
}
catch (e) {
// Not JSON, might be part of a multi-line chunk
if (line.trim()) {
this.logger(logging_1.LogLevel.DEBUG, "Non-JSON line in stream", { line });
}
}
}
}
}
catch (error) {
this.logger(logging_1.LogLevel.ERROR, "Error processing stream chunk", { error });
controller.error(error);
}
},
flush: (controller) => {
// Process any remaining data in the buffer
if (buffer.trim()) {
if (buffer.startsWith("data: ")) {
const jsonData = buffer.slice(6).trim();
if (jsonData && jsonData !== "[DONE]") {
try {
const parsed = JSON.parse(jsonData);
controller.enqueue(parsed);
}
catch (e) {
this.logger(logging_1.LogLevel.ERROR, "Error parsing final JSON from stream", {
error: e,
data: jsonData,
});
}
}
}
}
},
});
// Pipe the response body through our transformer
return response.body.pipeThrough(transformer);
}
catch (error) {
this.logger(logging_1.LogLevel.ERROR, "stream request failed", { error });
// Emit error event
this.emit("error", {
error: error,
request: {
method,
path,
queryParams,
},
});
throw error;
}
}
async requestWithMetadata({ method, path, body, queryParams = {}, options = {} }) {
this.logger(logging_1.LogLevel.DEBUG, "request with metadata start", {
method,
url: `${this.baseUrl}${path}`,
queryParams,
});
// Emit request event
this.emit("request", {
method,
path,
queryParams,
});
const queryString = Object.entries(queryParams)
.filter(([_, value]) => value !== undefined)
.map(([key, value]) => {
// Handle array values
if (Array.isArray(value)) {
return value.map((item) => `${encodeURIComponent(key)}=${encodeURIComponent(String(item))}`).join("&");
}
return `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`;
})
.join("&");
const url = `${this.baseUrl}${path}${queryString ? `?${queryString}` : ""}`;
const headers = {
...this.defaultHeaders,
...options.headers,
};
const timeoutMs = options.timeoutMs || this.timeoutMs;
try {
const response = await error_1.RequestTimeoutError.rejectAfterTimeout(this.fetchFn(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
signal: options.signal,
cache: options.cache,
credentials: options.credentials,
integrity: options.integrity,
keepalive: options.keepalive,
mode: options.mode,
redirect: options.redirect,
referrer: options.referrer,
referrerPolicy: options.referrerPolicy,
// @ts-ignore - Next.js specific options
next: options.next,
// Node.js specific option
agent: this.agent,
}), timeoutMs);
if (!response.ok) {
const errorData = await response.json();
this.logger(logging_1.LogLevel.ERROR, "request with metadata error", {
status: response.status,
statusText: response.statusText,
error: errorData,
});
const error = new Error(errorData.error || "An error occurred");
// Emit error event
this.emit("error", {
error,
request: {
method,
path,
queryParams,
},
});
throw error;
}
const responseData = await response.json();
this.logger(logging_1.LogLevel.DEBUG, "request with metadata success", {
responseData,
});
// Emit response event
this.emit("response", {
method,
path,
status: response.status,
data: responseData,
});
// If response has data field, return wrapped format, otherwise treat whole response as data
return responseData.data !== undefined
? {
data: responseData.data,
metadata: responseData.metadata,
}
: {
data: responseData,
metadata: {},
};
}
catch (error) {
this.logger(logging_1.LogLevel.ERROR, "request with metadata failed", { error });
// Emit error event
this.emit("error", {
error: error,
request: {
method,
path,
queryParams,
},
});
throw error;
}
}
paginate(params, fetchPage, response, options) {
// Convert PaginationResult to PaginationMetadata
const metadata = response.metadata
? {
page: response.metadata.page || 1,
limit: response.metadata.limit || 10,
total: response.metadata.total || 0,
totalRows: response.metadata.total || 0,
totalPages: Math.ceil((response.metadata.total || 0) / (response.metadata.limit || 10)),
hasMore: response.metadata.hasMore || false,
}
: {
page: 1,
limit: 10,
total: 0,
totalRows: 0,
totalPages: 0,
hasMore: false,
};
const currentPage = metadata.page;
const hasNextPage = metadata.hasMore || false || currentPage < (metadata.totalPages || 0);
const hasPreviousPage = currentPage > 1;
// This method adds pagination helpers to the response
const createPaginationHelpers = () => {
return {
hasNextPage,
hasPreviousPage,
nextPage: async () => {
if (!hasNextPage) {
return {
data: response.data,
metadata,
...createPaginationHelpers(),
};
}
const nextPage = currentPage + 1;
const nextPageParams = {
...params,
page: nextPage,
};
try {
const nextPageResponse = await fetchPage(nextPageParams, options);
const nextPageMetadata = nextPageResponse.metadata
? {
page: nextPageResponse.metadata.page || nextPage,
limit: nextPageResponse.metadata.limit || metadata.limit,
totalRows: nextPageResponse.metadata.total || metadata.totalRows || 0,
totalPages: Math.ceil((nextPageResponse.metadata.total || metadata.totalRows || 0) / (nextPageResponse.metadata.limit || metadata.limit)),
}
: {
page: nextPage,
limit: metadata.limit,
totalRows: metadata.totalRows || 0,
totalPages: metadata.totalPages || 0,
};
return {
data: nextPageResponse.data,
metadata: nextPageMetadata,
...createPaginationHelpers(),
};
}
catch (error) {
throw new Error(`Error fetching next page: ${error}`);
}
},
previousPage: async () => {
if (!hasPreviousPage) {
return {
data: response.data,
metadata,
...createPaginationHelpers(),
};
}
const prevPage = currentPage - 1;
const prevPageParams = {
...params,
page: prevPage,
};
try {
const prevPageResponse = await fetchPage(prevPageParams, options);
const prevPageMetadata = prevPageResponse.metadata
? {
page: prevPageResponse.metadata.page || prevPage,
limit: prevPageResponse.metadata.limit || metadata.limit,
totalRows: prevPageResponse.metadata.total || metadata.totalRows || 0,
totalPages: Math.ceil((prevPageResponse.metadata.total || metadata.totalRows || 0) / (prevPageResponse.metadata.limit || metadata.limit)),
}
: {
page: prevPage,
limit: metadata.limit,
totalRows: metadata.totalRows || 0,
totalPages: metadata.totalPages || 0,
};
return {
data: prevPageResponse.data,
metadata: prevPageMetadata,
...createPaginationHelpers(),
};
}
catch (error) {
throw new Error(`Error fetching previous page: ${error}`);
}
},
goToPage: async (page) => {
if (page < 1 || (metadata.totalPages && page > metadata.totalPages)) {
throw new Error(`Page ${page} is out of range. Valid range: 1-${metadata.totalPages || "?"}`);
}
const pageParams = {
...params,
page,
};
try {
const pageResponse = await fetchPage(pageParams, options);
const pageMetadata = pageResponse.metadata
? {
page: pageResponse.metadata.page || page,
limit: pageResponse.metadata.limit || metadata.limit,
totalRows: pageResponse.metadata.total || metadata.totalRows || 0,
totalPages: Math.ceil((pageResponse.metadata.total || metadata.totalRows || 0) / (pageResponse.metadata.limit || metadata.limit)),
}
: {
page,
limit: metadata.limit,
totalRows: metadata.totalRows || 0,
totalPages: metadata.totalPages || 0,
};
return {
data: pageResponse.data,
metadata: pageMetadata,
...createPaginationHelpers(),
};
}
catch (error) {
throw new Error(`Error fetching page ${page}: ${error}`);
}
},
getAllPages: async () => {
if (!metadata.totalPages) {
// If we don't know the total pages, just return the current page data
return Array.isArray(response.data) ? response.data : [response.data];
}
const allPages = [];
const totalPages = metadata.totalPages || 1;
// Add current page data
if (Array.isArray(response.data)) {
allPages.push(...response.data);
}
else {
allPages.push(response.data);
}
// Fetch all other pages
const pagePromises = [];
for (let page = 1; page <= totalPages; page++) {
if (page === currentPage)
continue; // Skip current page
const goToPageFn = this.paginate(params, fetchPage, response, options).goToPage;
pagePromises.push(goToPageFn(page)
.then((pageResponse) => {
if (Array.isArray(pageResponse.data)) {
return pageResponse.data;
}
return [pageResponse.data];
})
.catch(() => []));
}
const pageResults = await Promise.all(pagePromises);
for (const pageData of pageResults) {
allPages.push(...pageData);
}
return allPages;
},
};
};
return {
data: response.data,
metadata,
error: response.error,
...createPaginationHelpers(),
};
}
}
exports.MessariClient = MessariClient;
//# sourceMappingURL=client.js.map