@push.rocks/smartrequest
Version:
A module for modern HTTP/HTTPS requests with support for form data, file uploads, JSON, binary data, streams, and more.
152 lines • 11.2 kB
JavaScript
import {} from '../../core/index.js';
import { PaginationStrategy, } from '../types/pagination.js';
/**
* Creates a paginated response from a regular response
*/
export async function createPaginatedResponse(response, paginationConfig, queryParams, fetchNextPage) {
// Parse response body first
const body = (await response.json());
// Default to response.body for items if response is JSON
let items = Array.isArray(body)
? body
: body?.items || body?.data || body?.results || [];
let hasNextPage = false;
let nextPageParams = {};
// Determine if there's a next page based on pagination strategy
switch (paginationConfig.strategy) {
case PaginationStrategy.OFFSET: {
const config = paginationConfig;
const currentPage = parseInt(queryParams[config.pageParam || 'page'] ||
String(config.startPage || 1));
const limit = parseInt(queryParams[config.limitParam || 'limit'] ||
String(config.pageSize || 20));
const total = getValueByPath(body, config.totalPath || 'total') || 0;
hasNextPage = currentPage * limit < total;
if (hasNextPage) {
nextPageParams = {
...queryParams,
[config.pageParam || 'page']: String(currentPage + 1),
};
}
break;
}
case PaginationStrategy.CURSOR: {
const config = paginationConfig;
const nextCursor = getValueByPath(body, config.cursorPath || 'nextCursor');
const hasMore = getValueByPath(body, config.hasMorePath || 'hasMore');
hasNextPage = !!nextCursor || !!hasMore;
if (hasNextPage && nextCursor) {
nextPageParams = {
...queryParams,
[config.cursorParam || 'cursor']: nextCursor,
};
}
break;
}
case PaginationStrategy.LINK_HEADER: {
const linkHeader = response.headers['link'] || '';
// Handle both string and string[] types for the link header
const headerValue = Array.isArray(linkHeader)
? linkHeader[0]
: linkHeader;
const links = parseLinkHeader(headerValue);
hasNextPage = !!links.next;
if (hasNextPage && links.next) {
// Extract query parameters from next link URL
const url = new URL(links.next);
nextPageParams = {};
url.searchParams.forEach((value, key) => {
nextPageParams[key] = value;
});
}
break;
}
case PaginationStrategy.CUSTOM: {
const config = paginationConfig;
hasNextPage = config.hasNextPage(response);
if (hasNextPage) {
nextPageParams = config.getNextPageParams(response, queryParams);
}
break;
}
}
// Create a function to fetch the next page
const getNextPage = async () => {
if (!hasNextPage) {
throw new Error('No more pages available');
}
return fetchNextPage(nextPageParams);
};
// Create a function to fetch all remaining pages
const getAllPages = async () => {
const allItems = [...items];
let currentPage = {
items,
hasNextPage,
getNextPage,
getAllPages,
response,
};
while (currentPage.hasNextPage) {
try {
currentPage = await currentPage.getNextPage();
allItems.push(...currentPage.items);
}
catch (error) {
break;
}
}
return allItems;
};
return {
items,
hasNextPage,
getNextPage,
getAllPages,
response,
};
}
/**
* Parse Link header for pagination
* Link: <https://api.example.com/users?page=2>; rel="next", <https://api.example.com/users?page=5>; rel="last"
*/
export function parseLinkHeader(header) {
const links = {};
if (!header) {
return links;
}
// Split parts by comma
const parts = header.split(',');
// Parse each part into a name:value pair
for (const part of parts) {
const section = part.split(';');
if (section.length < 2) {
continue;
}
const url = section[0].replace(/<(.*)>/, '$1').trim();
const name = section[1].replace(/rel="(.*)"/, '$1').trim();
links[name] = url;
}
return links;
}
/**
* Get a nested value from an object using dot notation path
* e.g., getValueByPath(obj, "data.pagination.nextCursor")
*/
export function getValueByPath(obj, path) {
if (!path || !obj) {
return undefined;
}
const keys = path.split('.');
let current = obj;
for (const key of keys) {
if (current === null ||
current === undefined ||
typeof current !== 'object') {
return undefined;
}
current = current[key];
}
return current;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFnaW5hdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL2NsaWVudC9mZWF0dXJlcy9wYWdpbmF0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBcUIsTUFBTSxxQkFBcUIsQ0FBQztBQUV4RCxPQUFPLEVBRUwsa0JBQWtCLEdBRW5CLE1BQU0sd0JBQXdCLENBQUM7QUFFaEM7O0dBRUc7QUFDSCxNQUFNLENBQUMsS0FBSyxVQUFVLHVCQUF1QixDQUMzQyxRQUE0QixFQUM1QixnQkFBbUMsRUFDbkMsV0FBbUMsRUFDbkMsYUFFbUM7SUFFbkMsNEJBQTRCO0lBQzVCLE1BQU0sSUFBSSxHQUFHLENBQUMsTUFBTSxRQUFRLENBQUMsSUFBSSxFQUFFLENBQVEsQ0FBQztJQUU1Qyx5REFBeUQ7SUFDekQsSUFBSSxLQUFLLEdBQVEsS0FBSyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7UUFDbEMsQ0FBQyxDQUFDLElBQUk7UUFDTixDQUFDLENBQUMsSUFBSSxFQUFFLEtBQUssSUFBSSxJQUFJLEVBQUUsSUFBSSxJQUFJLElBQUksRUFBRSxPQUFPLElBQUksRUFBRSxDQUFDO0lBRXJELElBQUksV0FBVyxHQUFHLEtBQUssQ0FBQztJQUN4QixJQUFJLGNBQWMsR0FBMkIsRUFBRSxDQUFDO0lBRWhELGdFQUFnRTtJQUNoRSxRQUFRLGdCQUFnQixDQUFDLFFBQVEsRUFBRSxDQUFDO1FBQ2xDLEtBQUssa0JBQWtCLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztZQUMvQixNQUFNLE1BQU0sR0FBRyxnQkFBZ0IsQ0FBQztZQUNoQyxNQUFNLFdBQVcsR0FBRyxRQUFRLENBQzFCLFdBQVcsQ0FBQyxNQUFNLENBQUMsU0FBUyxJQUFJLE1BQU0sQ0FBQztnQkFDckMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxTQUFTLElBQUksQ0FBQyxDQUFDLENBQ2hDLENBQUM7WUFDRixNQUFNLEtBQUssR0FBRyxRQUFRLENBQ3BCLFdB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