tskew
Version:
TypeScript interface to Kew Gardens botanical data services
141 lines • 4.61 kB
JavaScript
"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