/** * Entities Store */ import { ref, computed, readonly } from 'vue' import { defineStore } from 'pinia' import { entityService } from '../services/entityService' import { EntityObject } from '../models' import type { SourceSelector, ListFilter, ListSort, ListRange, DocumentInterface, EntityDeleteResponse, EntityDeltaResponse, } from '../types' export const useEntitiesStore = defineStore('documentsEntitiesStore', () => { // State const _entities = ref>({}) const transceiving = ref(false) // Getters const count = computed(() => Object.keys(_entities.value).length) const has = computed(() => count.value > 0) const entities = computed(() => Object.values(_entities.value)) const entitiesByService = computed(() => { const groups: Record = {} Object.values(_entities.value).forEach((entity) => { const serviceKey = `${entity.provider}:${entity.service}` const serviceEntities = (groups[serviceKey] ??= []) serviceEntities.push(entity) }) return groups }) function identifierKey( provider: string, service: string | number, collection: string | number, identifier: string | number, ): string { return `${provider}:${service}:${collection}:${identifier}` } function entity( provider: string, service: string | number, collection: string | number, identifier: string | number, retrieve: boolean = false, ): EntityObject | null { const key = identifierKey(provider, service, collection, identifier) if (retrieve === true && !_entities.value[key]) { console.debug(`[Documents Manager][Store] - Force fetching entity "${key}"`) fetch(provider, service, collection, [identifier]) } return _entities.value[key] || null } function entitiesForService(provider: string, service: string | number): EntityObject[] { const serviceKeyPrefix = `${provider}:${service}:` return Object.entries(_entities.value) .filter(([key]) => key.startsWith(serviceKeyPrefix)) .map(([_, entityObj]) => entityObj) } function entitiesForCollection(provider: string, service: string | number, collection: string | number): EntityObject[] { const collectionKeyPrefix = `${provider}:${service}:${collection}:` return Object.entries(_entities.value) .filter(([key]) => key.startsWith(collectionKeyPrefix)) .map(([_, entityObj]) => entityObj) } function clearService(provider: string, service: string | number): void { const serviceKeyPrefix = `${provider}:${service}:` Object.keys(_entities.value) .filter((key) => key.startsWith(serviceKeyPrefix)) .forEach((key) => { delete _entities.value[key] }) } function clearCollection(provider: string, service: string | number, collection: string | number): void { const collectionKeyPrefix = `${provider}:${service}:${collection}:` Object.keys(_entities.value) .filter((key) => key.startsWith(collectionKeyPrefix)) .forEach((key) => { delete _entities.value[key] }) } function clearAll(): void { _entities.value = {} } // Actions async function list(sources?: SourceSelector, filter?: ListFilter, sort?: ListSort, range?: ListRange): Promise> { transceiving.value = true try { const response = await entityService.list({ sources, filter, sort, range }) const hydrated: Record = {} Object.entries(response).forEach(([providerId, providerServices]) => { Object.entries(providerServices).forEach(([serviceId, serviceCollections]) => { Object.entries(serviceCollections).forEach(([collectionId, collectionEntities]) => { Object.entries(collectionEntities).forEach(([entityId, entityObj]) => { const key = identifierKey(providerId, serviceId, collectionId, entityId) hydrated[key] = entityObj }) }) }) }) _entities.value = { ..._entities.value, ...hydrated } console.debug('[Documents Manager][Store] - Successfully retrieved', Object.keys(hydrated).length, 'entities') return hydrated } catch (error: any) { console.error('[Documents Manager][Store] - Failed to retrieve entities:', error) throw error } finally { transceiving.value = false } } async function fetch( provider: string, service: string | number, collection: string | number, identifiers: (string | number)[], ): Promise> { transceiving.value = true try { const response = await entityService.fetch({ provider, service, collection, identifiers }) const hydrated: Record = {} Object.entries(response).forEach(([identifier, entityObj]) => { const key = identifierKey(provider, service, collection, identifier) hydrated[key] = entityObj _entities.value[key] = entityObj }) console.debug('[Documents Manager][Store] - Successfully fetched', Object.keys(hydrated).length, 'entities') return hydrated } catch (error: any) { console.error('[Documents Manager][Store] - Failed to fetch entities:', error) throw error } finally { transceiving.value = false } } async function extant(sources: SourceSelector) { transceiving.value = true try { const response = await entityService.extant({ sources }) console.debug('[Documents Manager][Store] - Successfully checked entity availability') return response } catch (error: any) { console.error('[Documents Manager][Store] - Failed to check entity availability:', error) throw error } finally { transceiving.value = false } } async function create( provider: string, service: string | number, collection: string | number, properties: DocumentInterface, options?: Record, ): Promise { transceiving.value = true try { const response = await entityService.create({ provider, service, collection, properties, options }) const key = identifierKey(response.provider, response.service, response.collection, response.identifier) _entities.value[key] = response console.debug('[Documents Manager][Store] - Successfully created entity:', key) return response } catch (error: any) { console.error('[Documents Manager][Store] - Failed to create entity:', error) throw error } finally { transceiving.value = false } } async function update( provider: string, service: string | number, collection: string | number, identifier: string | number, properties: DocumentInterface, ): Promise { transceiving.value = true try { const response = await entityService.update({ provider, service, collection, identifier, properties }) const key = identifierKey(response.provider, response.service, response.collection, response.identifier) _entities.value[key] = response console.debug('[Documents Manager][Store] - Successfully updated entity:', key) return response } catch (error: any) { console.error('[Documents Manager][Store] - Failed to update entity:', error) throw error } finally { transceiving.value = false } } async function remove( provider: string, service: string | number, collection: string | number, identifier: string | number, ): Promise { transceiving.value = true try { const response = await entityService.delete({ provider, service, collection, identifier }) if (response.success) { const key = identifierKey(provider, service, collection, identifier) delete _entities.value[key] } console.debug('[Documents Manager][Store] - Successfully deleted entity:', `${provider}:${service}:${collection}:${identifier}`) return response } catch (error: any) { console.error('[Documents Manager][Store] - Failed to delete entity:', error) throw error } finally { transceiving.value = false } } async function delta(sources: SourceSelector): Promise { transceiving.value = true try { const response = await entityService.delta({ sources }) Object.entries(response).forEach(([provider, providerData]) => { if (providerData === false) return Object.entries(providerData).forEach(([service, serviceData]) => { if (serviceData === false) return Object.entries(serviceData).forEach(([collection, collectionData]) => { if (collectionData === false) return collectionData.deletions.forEach((identifier) => { const key = identifierKey(provider, service, collection, identifier) delete _entities.value[key] }) }) }) }) console.debug('[Documents Manager][Store] - Successfully processed entity delta') return response } catch (error: any) { console.error('[Documents Manager][Store] - Failed to process entity delta:', error) throw error } finally { transceiving.value = false } } async function read( provider: string, service: string | number, collection: string | number, identifier: string | number, ): Promise { transceiving.value = true try { const response = await entityService.read({ provider, service, collection, identifier }) return response.content } catch (error: any) { console.error('[Documents Manager][Store] - Failed to read entity:', error) throw error } finally { transceiving.value = false } } async function write( provider: string, service: string | number, collection: string | number, identifier: string | number, content: string, ): Promise { transceiving.value = true try { const response = await entityService.write({ provider, service, collection, identifier, content, encoding: 'base64' }) return response.bytesWritten } catch (error: any) { console.error('[Documents Manager][Store] - Failed to write entity:', error) throw error } finally { transceiving.value = false } } return { transceiving: readonly(transceiving), count, has, entities, entitiesByService, entity, entitiesForService, entitiesForCollection, clearService, clearCollection, clearAll, list, fetch, extant, create, update, delete: remove, delta, read, write, } })