export interface DocumentDetailResult { document: Record paragraphs: Record[] } const MAX_ENTRIES = 25 function cacheKey(mainCollection: string, docId: string): string { return `${mainCollection}:${docId}` } /** * Fetch de "documento principal + sus párrafos" (join de Typesense), con * caché de sesión compartida entre SearchPanel.vue y usePublicationFetch.ts * — antes cada uno hacía la misma query por separado sin cachear nada, así * que reabrir el mismo documento en la sesión repetía la ida y vuelta de red. * Tope de ~25 entradas (documentos completos con cuerpo/párrafos pesan más * que la metadata de useDocMetaCache) con eviction LRU simple. */ export function useDocumentDetailFetch() { const cache = useState>('doc-detail-cache', () => new Map()) function getCached(mainCollection: string, docId: string): DocumentDetailResult | undefined { const key = cacheKey(mainCollection, docId) const entry = cache.value.get(key) if (entry) { cache.value.delete(key) cache.value.set(key, entry) } return entry } function setCached(mainCollection: string, docId: string, detail: DocumentDetailResult) { const key = cacheKey(mainCollection, docId) cache.value.delete(key) cache.value.set(key, detail) if (cache.value.size > MAX_ENTRIES) { const oldestKey = cache.value.keys().next().value if (oldestKey !== undefined) cache.value.delete(oldestKey) } } async function fetchDocumentDetail( documentsApi: ReturnType['documentsApi'], mainCollection: string, paragraphsCollection: string, docId: string, signal?: AbortSignal ): Promise { const cached = getCached(mainCollection, docId) if (cached) return cached const res = await documentsApi.multiSearch({ multiSearchParameters: {}, multiSearchSearchesParameter: { searches: [{ collection: mainCollection, q: '*', queryBy: 'title', filterBy: `id:=${docId} && $${paragraphsCollection}(id: *)`, includeFields: `*, $${paragraphsCollection}(*)`, useCache: true, cacheTtl: 3600 }] } }, { signal }) const hit = (res?.results?.[0] as { hits?: Array<{ document: Record }> })?.hits?.[0] if (!hit) return null const docRaw = { ...hit.document } const raw = docRaw[paragraphsCollection] const paragraphs = (Array.isArray(raw) ? raw : (raw ? [raw] : [])) as Record[] delete docRaw[paragraphsCollection] const detail: DocumentDetailResult = { document: docRaw, paragraphs } setCached(mainCollection, docId, detail) return detail } return { fetchDocumentDetail } }