import LRU from "lru-cache"; import { UpListing, UpObject } from "."; import type { Address, AttributeListingResult, EntityListing, IJob, IValue, ListingResult, PutInput, PutResult, StoreInfo, VaultInfo, } from "./types"; export class UpEndApi { private instanceUrl = ""; private queryOnceLRU = new LRU({ max: 128 }); private inFlightRequests: { [key: string]: Promise | null } = {}; constructor(instanceUrl = "") { this.setInstanceUrl(instanceUrl); } public setInstanceUrl(apiUrl: string) { this.instanceUrl = apiUrl.replace(/\/+$/g, ""); } private get apiUrl() { return this.instanceUrl + "/api"; } public async fetchEntity(address: string): Promise { const entityFetch = await fetch(`${this.apiUrl}/obj/${address}`); const entityResult = (await entityFetch.json()) as EntityListing; const entityListing = new UpListing(entityResult.entries); return entityListing.getObject(address); } public async fetchEntry(address: string) { const response = await fetch(`${this.apiUrl}/raw/${address}`); const data = await response.json(); const listing = new UpListing({ address: data }); return listing.entries[0]; } public async query(query: string): Promise { const cacheResult = this.queryOnceLRU.get(query); if (!cacheResult) { if (!this.inFlightRequests[query]) { console.debug(`Querying: ${query}`); this.inFlightRequests[query] = new Promise((resolve, reject) => { fetch(`${this.apiUrl}/query`, { method: "POST", body: query, keepalive: true, }) .then(async (response) => { resolve(new UpListing(await response.json())); this.inFlightRequests[query] = null; }) .catch((err) => reject(err)); }); } else { console.debug(`Chaining request for ${query}...`); } return await (this.inFlightRequests[query] as Promise); // TODO? } else { console.debug(`Returning cached: ${query}`); return cacheResult; } } public async putEntry(input: PutInput): Promise { const response = await fetch(`${this.apiUrl}/obj`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(input), }); return await response.json(); } public async putEntityAttribute( entity: Address, attribute: string, value: IValue ): Promise
{ const response = await fetch(`${this.apiUrl}/obj/${entity}/${attribute}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(value), }); return await response.json(); } public async uploadFile(file: File): Promise { const formData = new FormData(); formData.append("file", file); const response = await fetch(`${this.apiUrl}/blob`, { method: "PUT", body: formData, }); if (!response.ok) { throw Error(await response.text()); } return await response.json(); } public async deleteEntry(address: Address): Promise { await fetch(`${this.apiUrl}/obj/${address}`, { method: "DELETE" }); } public async getRaw(address: Address, preview = false) { return await fetch( `${this.apiUrl}/${preview ? "thumb" : "raw"}/${address}` ); } public async refreshVault() { return await fetch(`${this.apiUrl}/refresh`, { method: "POST" }); } public async nativeOpen(address: Address) { return fetch(`${this.apiUrl}/raw/${address}?native=1`); } public async fetchRoots(): Promise { const response = await fetch(`${this.apiUrl}/hier_roots`); return await response.json(); } public async fetchJobs(): Promise { const response = await fetch(`${this.apiUrl}/jobs`); return await response.json(); } public async fetchAllAttributes(): Promise { const response = await fetch(`${this.apiUrl}/all/attributes`); return await response.json(); } public async fetchInfo(): Promise { const response = await fetch(`${this.apiUrl}/info`); return await response.json(); } public async fetchStoreInfo(): Promise<{ [key: string]: StoreInfo }> { const response = await fetch(`${this.apiUrl}/store`); return await response.json(); } public async getAddress( input: { attribute: string } | { url: string } | { urlContent: string } ): Promise { let response; if ("attribute" in input) { response = await fetch( `${this.apiUrl}/address?attribute=${input.attribute}` ); } else if ("url" in input) { response = await fetch(`${this.apiUrl}/address?url=${input.url}`); } else if ("urlContent" in input) { response = await fetch( `${this.apiUrl}/address?url_content=${input.urlContent}` ); } else { throw new Error("Input cannot be empty."); } return await response.json(); } }