import { uuid } from "@noya-app/noya-utils"; export type HostStorageRequest = | { id: string; operation: "getItem"; key: string; } | { id: string; operation: "setItem"; key: string; value: string; } | { id: string; operation: "removeItem"; key: string; }; export type HostStorageResponse = | { id: string; type: "success"; value?: string | null; } | { id: string; type: "error"; error: string; }; type PendingRequest = { resolve: (value: string | null | undefined) => void; reject: (error: Error) => void; }; const unavailableError = "Host storage is only available when the app is embedded in Noya."; export class HostStorageManager { private sendRequest?: (request: HostStorageRequest) => void; private pendingRequests = new Map(); connect(sendRequest: (request: HostStorageRequest) => void) { this.sendRequest = sendRequest; return () => { if (this.sendRequest !== sendRequest) return; this.sendRequest = undefined; const error = new Error(unavailableError); this.pendingRequests.forEach(({ reject }) => reject(error)); this.pendingRequests.clear(); }; } getItem(key: string): Promise { return this.request({ operation: "getItem", key }) as Promise< string | null >; } setItem(key: string, value: string): Promise { return this.request({ operation: "setItem", key, value }) as Promise; } removeItem(key: string): Promise { return this.request({ operation: "removeItem", key }) as Promise; } handleResponse(response: HostStorageResponse) { const pendingRequest = this.pendingRequests.get(response.id); if (!pendingRequest) return; this.pendingRequests.delete(response.id); if (response.type === "error") { pendingRequest.reject(new Error(response.error)); } else { pendingRequest.resolve(response.value); } } private request( request: | Omit, "id"> | Omit, "id"> | Omit, "id"> ): Promise { if (!this.sendRequest) { return Promise.reject(new Error(unavailableError)); } const id = uuid(); return new Promise((resolve, reject) => { this.pendingRequests.set(id, { resolve, reject }); try { this.sendRequest?.({ ...request, id } as HostStorageRequest); } catch (error) { this.pendingRequests.delete(id); reject(error); } }); } }