import { Authentication } from "./Authentication"; // Base interface for PocketBase records interface BaseRecord { id: string; [key: string]: any; } // Interface for request options interface RequestOptions { fields?: string[]; disableAutoCancellation?: boolean; expand?: string[]; } // Utility function to check if a value is a UTC date string function isUTCDateString(value: any): boolean { if (typeof value !== "string") return false; const isoDateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z$/; return isoDateRegex.test(value); } // Utility function to convert UTC date strings to local time function convertUTCToLocal(data: T): T { if (!data || typeof data !== "object") return data; const converted = { ...data }; for (const [key, value] of Object.entries(converted)) { if (isUTCDateString(value)) { (converted as any)[key] = new Date(value).toISOString(); } else if (Array.isArray(value)) { (converted as any)[key] = value.map((item) => convertUTCToLocal(item)); } else if (typeof value === "object" && value !== null) { (converted as any)[key] = convertUTCToLocal(value); } } return converted; } export class Get { private auth: Authentication; private static instance: Get; private constructor() { this.auth = Authentication.getInstance(); } /** * Get the singleton instance of Get */ public static getInstance(): Get { if (!Get.instance) { Get.instance = new Get(); } return Get.instance; } /** * Convert UTC date strings to local time * @param data The data to convert * @returns The converted data */ public static convertUTCToLocal(data: T): T { return convertUTCToLocal(data); } /** * Check if a value is a UTC date string * @param value The value to check * @returns True if the value is a UTC date string */ public static isUTCDateString(value: any): boolean { return isUTCDateString(value); } /** * Get a single record by ID * @param collectionName The name of the collection * @param recordId The ID of the record to retrieve * @param options Optional request options including fields to select and auto-cancellation control * @returns The requested record */ public async getOne( collectionName: string, recordId: string, options?: RequestOptions, ): Promise { if (!this.auth.isAuthenticated()) { throw new Error("User must be authenticated to retrieve records"); } try { const pb = this.auth.getPocketBase(); const requestOptions = { ...(options?.fields && { fields: options.fields.join(",") }), ...(options?.disableAutoCancellation && { requestKey: null }), }; const result = await pb .collection(collectionName) .getOne(recordId, requestOptions); return convertUTCToLocal(result); } catch (err) { console.error(`Failed to get record from ${collectionName}:`, err); throw err; } } /** * Get multiple records by their IDs * @param collectionName The name of the collection * @param recordIds Array of record IDs to retrieve * @param options Optional request options including fields to select and auto-cancellation control * @returns Array of requested records */ public async getMany( collectionName: string, recordIds: string[], options?: RequestOptions, ): Promise { if (!this.auth.isAuthenticated()) { throw new Error("User must be authenticated to retrieve records"); } try { const pb = this.auth.getPocketBase(); const filter = `id ?~ "${recordIds.join("|")}"`; const requestOptions = { filter, ...(options?.fields && { fields: options.fields.join(",") }), ...(options?.disableAutoCancellation && { requestKey: null }), }; const result = await pb .collection(collectionName) .getFullList(requestOptions); // Sort results to match the order of requested IDs and convert times const recordMap = new Map( result.map((record) => [record.id, convertUTCToLocal(record)]), ); return recordIds.map((id) => recordMap.get(id)).filter(Boolean) as T[]; } catch (err) { console.error(`Failed to get records from ${collectionName}:`, err); throw err; } } /** * Get records with pagination * @param collectionName The name of the collection * @param page Page number (1-based) * @param perPage Number of items per page * @param filter Optional filter string * @param sort Optional sort string * @param options Optional request options including fields to select and auto-cancellation control * @returns Paginated list of records */ public async getList( collectionName: string, page: number = 1, perPage: number = 20, filter?: string, sort?: string, options?: RequestOptions, ): Promise<{ page: number; perPage: number; totalItems: number; totalPages: number; items: T[]; }> { if (!this.auth.isAuthenticated()) { throw new Error("User must be authenticated to retrieve records"); } try { const pb = this.auth.getPocketBase(); const requestOptions = { ...(filter && { filter }), ...(sort && { sort }), ...(options?.fields && { fields: options.fields.join(",") }), ...(options?.disableAutoCancellation && { requestKey: null }), }; const result = await pb .collection(collectionName) .getList(page, perPage, requestOptions); return { page: result.page, perPage: result.perPage, totalItems: result.totalItems, totalPages: result.totalPages, items: result.items.map((item) => convertUTCToLocal(item)), }; } catch (err) { console.error(`Failed to get list from ${collectionName}:`, err); throw err; } } /** * Get all records from a collection * @param collectionName The name of the collection * @param filter Optional filter string * @param sort Optional sort string * @param options Optional request options including fields to select and auto-cancellation control * @returns Array of all matching records */ public async getAll( collectionName: string, filter?: string, sort?: string, options?: RequestOptions, ): Promise { if (!this.auth.isAuthenticated()) { throw new Error("User must be authenticated to retrieve records"); } try { const pb = this.auth.getPocketBase(); const requestOptions = { ...(filter && { filter }), ...(sort && { sort }), ...(options?.fields && { fields: options.fields.join(",") }), ...(options?.expand && { expand: options.expand.join(",") }), ...(options?.disableAutoCancellation && { requestKey: null }), }; const result = await pb .collection(collectionName) .getFullList(requestOptions); return result.map((item) => convertUTCToLocal(item)); } catch (err) { console.error(`Failed to get all records from ${collectionName}:`, err); throw err; } } /** * Get the first record that matches a filter * @param collectionName The name of the collection * @param filter Filter string * @param options Optional request options including fields to select and auto-cancellation control * @returns The first matching record or null if none found */ public async getFirst( collectionName: string, filter: string, options?: RequestOptions, ): Promise { if (!this.auth.isAuthenticated()) { throw new Error("User must be authenticated to retrieve records"); } try { const pb = this.auth.getPocketBase(); const requestOptions = { filter, ...(options?.fields && { fields: options.fields.join(",") }), ...(options?.disableAutoCancellation && { requestKey: null }), sort: "created", perPage: 1, }; const result = await pb .collection(collectionName) .getList(1, 1, requestOptions); return result.items.length > 0 ? convertUTCToLocal(result.items[0]) : null; } catch (err) { console.error(`Failed to get first record from ${collectionName}:`, err); throw err; } } }