@aashari/mcp-server-atlassian-bitbucket
Version:
Node.js/TypeScript MCP server for Atlassian Bitbucket. Enables AI systems (LLMs) to interact with workspaces, repositories, and pull requests via tools (list, get, comment, search). Connects AI directly to version control workflows through the standard MC
123 lines (122 loc) • 5.33 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PaginationType = void 0;
exports.extractPaginationInfo = extractPaginationInfo;
const logger_util_js_1 = require("./logger.util.js");
/**
* Represents the possible pagination types.
*/
var PaginationType;
(function (PaginationType) {
PaginationType["CURSOR"] = "cursor";
PaginationType["OFFSET"] = "offset";
PaginationType["PAGE"] = "page";
})(PaginationType || (exports.PaginationType = PaginationType = {}));
/**
* Extract pagination information from API response
* @param data The API response containing pagination information
* @param paginationType The type of pagination mechanism used
* @returns Object with nextCursor, hasMore, and count properties
*/
function extractPaginationInfo(data, paginationType) {
if (!data) {
return undefined;
}
let pagination;
const methodLogger = logger_util_js_1.Logger.forContext('utils/pagination.util.ts', 'extractPaginationInfo');
switch (paginationType) {
case PaginationType.PAGE: {
// Bitbucket page-based pagination (page, pagelen, size, next)
if (data.page !== undefined && data.pagelen !== undefined) {
const hasMore = !!data.next;
let nextCursorValue = undefined;
if (hasMore) {
try {
// First attempt to parse the full URL if it looks like one
if (typeof data.next === 'string' &&
data.next.includes('://')) {
const nextUrl = new URL(data.next);
nextCursorValue =
nextUrl.searchParams.get('page') || undefined;
methodLogger.debug(`Successfully extracted page from URL: ${nextCursorValue}`);
}
else if (data.next === 'available') {
// Handle the 'available' placeholder used in some transformedResponses
nextCursorValue = String(Number(data.page) + 1);
methodLogger.debug(`Using calculated next page from 'available': ${nextCursorValue}`);
}
else if (typeof data.next === 'string') {
// Try to use data.next directly if it's not a URL but still a string
nextCursorValue = data.next;
methodLogger.debug(`Using next value directly: ${nextCursorValue}`);
}
}
catch (e) {
// If URL parsing fails, calculate the next page based on current page
nextCursorValue = String(Number(data.page) + 1);
methodLogger.debug(`Calculated next page after URL parsing error: ${nextCursorValue}`);
methodLogger.warn(`Failed to parse next URL: ${data.next}`, e);
}
}
pagination = {
hasMore,
count: data.values?.length ?? 0,
page: data.page,
size: data.pagelen,
total: data.size,
nextCursor: nextCursorValue, // Store next page number as cursor
};
}
break;
}
case PaginationType.OFFSET: {
// Jira offset-based pagination
const countOffset = data.values?.length;
if (data.startAt !== undefined &&
data.maxResults !== undefined &&
data.total !== undefined &&
data.startAt + data.maxResults < data.total) {
pagination = {
hasMore: true,
count: countOffset,
total: data.total,
nextCursor: String(data.startAt + data.maxResults),
};
}
else if (data.nextPage) {
pagination = {
hasMore: true,
count: countOffset,
nextCursor: data.nextPage,
};
}
break;
}
case PaginationType.CURSOR: {
// Confluence cursor-based pagination
const countCursor = data.results?.length;
if (data._links && data._links.next) {
const nextUrl = data._links.next;
const cursorMatch = nextUrl.match(/cursor=([^&]+)/);
if (cursorMatch && cursorMatch[1]) {
pagination = {
hasMore: true,
count: countCursor,
nextCursor: decodeURIComponent(cursorMatch[1]),
};
}
}
break;
}
default:
methodLogger.warn(`Unknown pagination type: ${paginationType}`);
}
// Ensure a default pagination object if none was created but data exists
if (!pagination && (data.results || data.values)) {
pagination = {
hasMore: false,
count: data.results?.length ?? data.values?.length ?? 0,
};
}
return pagination;
}