import { isArray, isNonEmptyObject } from '@sindresorhus/is'; import { HttpBase, type InternalJsonUnsafeOptions } from './http.ts'; import type { HttpMethod, HttpOptions, HttpResponse } from './types.ts'; const MAX_LIMIT = 100; const MAX_PAGES = 100; let baseUrl: string; export function setBaseUrl(url: string): void { baseUrl = url; } export interface BitbucketServerHttpOptions extends HttpOptions { paginate?: boolean; limit?: number; maxPages?: number; } interface PagedResult { nextPageStart?: number; values: T[]; } export class BitbucketServerHttp extends HttpBase { protected override get baseUrl(): string | undefined { return baseUrl; } constructor(type = 'bitbucket-server', options?: BitbucketServerHttpOptions) { super(type, options); } protected override extraOptions(): readonly string[] { return super .extraOptions() .concat([ 'paginate', 'limit', 'maxPages', ] as (keyof BitbucketServerHttpOptions)[]); } protected override async requestJsonUnsafe( method: HttpMethod, options: InternalJsonUnsafeOptions, ): Promise> { const resolvedUrl = this.resolveUrl(options.url, options.httpOptions); const opts = { ...options, url: resolvedUrl }; opts.httpOptions ??= {}; opts.httpOptions.headers ??= {}; opts.httpOptions.headers['X-Atlassian-Token'] = 'no-check'; const paginate = opts.httpOptions.paginate; if (paginate) { const limit = opts.httpOptions.limit ?? MAX_LIMIT; resolvedUrl.searchParams.set('limit', limit.toString()); } const result = await super.requestJsonUnsafe>( method, opts, ); if (paginate && isPagedResult(result.body)) { // v8 ignore else -- TODO: add test #40625 if (opts.httpOptions) { delete opts.httpOptions.cacheProvider; opts.httpOptions.memCache = false; } const collectedValues = [...result.body.values]; let nextPageStart = result.body.nextPageStart; let maxPages = opts.httpOptions.maxPages ?? MAX_PAGES; while (nextPageStart && --maxPages > 0) { resolvedUrl.searchParams.set('start', nextPageStart.toString()); const nextResult = await super.requestJsonUnsafe>( method, opts, ); collectedValues.push(...nextResult.body.values); nextPageStart = nextResult.body.nextPageStart; } return { ...result, body: collectedValues as T }; } return result as HttpResponse; } } function isPagedResult(obj: unknown): obj is PagedResult { return isNonEmptyObject(obj) && isArray(obj.values); }