UNPKG

tskew

Version:

TypeScript interface to Kew Gardens botanical data services

141 lines 4.61 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.SearchResult = void 0; class SearchResult { constructor(api, query = {}, filters = null, defaultLimit) { this.results = null; this.resultIndex = 0; this.cursor = "*"; this.perPage = 10; this.defaultLimit = 10; this.api = api; this.query = query; this.filters = filters; this.defaultLimit = defaultLimit; } formatQuery() { if (typeof this.query === "string") { return this.query; } // Convert dict query to key:value format for modern APIs const terms = []; for (const [key, value] of Object.entries(this.query)) { if (value !== undefined && value !== null) { // For genus/species queries, use key:value format if (key === "genus" || key === "species" || key === "family") { terms.push(`${key}:${value}`); } else { terms.push(`${key}:"${value}"`); } } } return terms.join(" "); } formatFilters() { if (!this.filters) return ""; if (Array.isArray(this.filters)) { return this.filters.map((f) => String(f)).join(","); } return String(this.filters); } buildParams() { const params = { perPage: this.perPage, }; const queryStr = this.formatQuery(); if (queryStr) { params.q = queryStr; } const filtersStr = this.formatFilters(); if (filtersStr) { params.f = filtersStr; } if (this.cursor && this.cursor !== "*") { params.cursor = this.cursor; } return params; } async runQuery(limit) { if (this.results !== null) return; this.results = []; this.cursor = "*"; const effectiveLimit = limit || this.defaultLimit; let requestCount = 0; const maxRequests = 50; // Safety limit for Cloudflare/CDN issues const seenCursors = new Set(); // eslint-disable-next-line no-constant-condition while (true) { requestCount++; // Safety break for too many requests (Cloudflare/CDN issues) if (requestCount > maxRequests) { break; } const params = this.buildParams(); const response = await this.api.get("search", params); if (response.results && Array.isArray(response.results)) { // Stop if no results returned if (response.results.length === 0) { break; } // Check for cursor issues before adding results if (response.cursor) { // Multiple protections against infinite loops (CDN/Cloudflare issues) if (response.cursor === this.cursor || seenCursors.has(response.cursor)) { break; } } this.results.push(...response.results); // Stop if we have enough results if (effectiveLimit && this.results.length >= effectiveLimit) { this.results = this.results.slice(0, effectiveLimit); break; } if (response.cursor) { seenCursors.add(this.cursor); this.cursor = response.cursor; } else { break; } } else { break; } } } *[Symbol.iterator]() { if (this.results === null) { throw new Error("Must call runQuery() first or use async methods"); } for (const result of this.results) { yield result; } } async *[Symbol.asyncIterator]() { await this.runQuery(); for (const result of this.results) { yield result; } } async all() { await this.runQuery(); return [...this.results]; } async first() { await this.runQuery(1); return this.results[0]; } async size() { await this.runQuery(); return this.results.length; } async take(n) { await this.runQuery(n); return this.results.slice(0, n); } } exports.SearchResult = SearchResult; //# sourceMappingURL=search-result.js.map