/** * Entities Store */ import { ref, computed, readonly } from 'vue' import { defineStore } from 'pinia' import { entityService } from '../services' import { EntityObject } from '../models' import type { CollectionIdentifier, EntityIdentifier, ListFilter, ListRange, ListSort, } from '../types/common' import type { EntityPropertiesInterface } from '@/types/entity' export const useEntitiesStore = defineStore('chronoEntitiesStore', () => { // State const _entities = ref>({}) const transceiving = ref(false) /** * Get count of entities in store */ const count = computed(() => Object.keys(_entities.value).length) /** * Check if any entities are present in store */ const has = computed(() => count.value > 0) /** * Get all entities present in store */ const entities = computed(() => Object.values(_entities.value)) /** * Get a specific entity from store, with optional retrieval */ function entity(target: EntityIdentifier, retrieve: boolean = false): EntityObject | null { if (retrieve === true && !_entities.value[target]) { console.debug(`[Chrono Manager][Store] - Force fetching entity "${target}"`) fetch([target]) } return _entities.value[target] || null } /** * Get all entities for a specific collection */ function entitiesForCollection(target: CollectionIdentifier, retrieve: boolean = false): EntityObject[] { const collectionEntities = Object.entries(_entities.value) .filter(([key]) => key.startsWith(`${target}:`)) .map(([_, entity]) => entity) if (retrieve === true && collectionEntities.length === 0) { console.debug(`[Chrono Manager][Store] - Force fetching entities for collection "${target}"`) list([target]) } return collectionEntities } /** * Retrieve all or specific entities, optionally filtered by source collection identifiers */ async function list(sources: CollectionIdentifier[], filter?: ListFilter, sort?: ListSort, range?: ListRange): Promise> { transceiving.value = true try { const entities: Record = {} await entityService.listStream({ sources, filter, sort, range }, (entity: EntityObject) => { _entities.value[entity.identifier] = entity entities[entity.identifier] = entity }) console.debug('[Chrono Manager][Store] - Successfully retrieved', Object.keys(entities).length, 'entities') return entities } catch (error: any) { console.error('[Chrono Manager][Store] - Failed to retrieve entities:', error) throw error } finally { transceiving.value = false } } /** * Retrieve specific entities by their identifiers */ async function fetch(targets: EntityIdentifier[]): Promise> { transceiving.value = true try { const response = await entityService.fetch({ targets }) const entities: Record = {} Object.entries(response).forEach(([identifier, entity]) => { entities[identifier] = entity _entities.value[identifier] = entity }) console.debug('[Chrono Manager][Store] - Successfully fetched', Object.keys(entities).length, 'entities') return entities } catch (error: any) { console.error('[Chrono Manager][Store] - Failed to fetch entities:', error) throw error } finally { transceiving.value = false } } /** * Retrieve entity availability status for a given set of entity identifiers */ async function extant(targets: EntityIdentifier[]) { transceiving.value = true try { const response = await entityService.extant({ targets }) console.debug('[Chrono Manager][Store] - Successfully checked entity availability') return response } catch (error: any) { console.error('[Chrono Manager][Store] - Failed to check entity availability:', error) throw error } finally { transceiving.value = false } } /** * Retrieve delta changes for entities */ async function delta(targets: (CollectionIdentifier | EntityIdentifier)[]) { transceiving.value = true try { const response = await entityService.delta({ targets }) Object.entries(response).forEach(([, providerData]) => { if (providerData === false) return Object.entries(providerData).forEach(([, serviceData]) => { if (serviceData === false) return Object.entries(serviceData).forEach(([, collectionData]) => { if (collectionData === false) return if (collectionData.deletions && collectionData.deletions.length > 0) { collectionData.deletions.forEach((identifier) => { delete _entities.value[identifier] }) } }) }) }) console.debug('[Chrono Manager][Store] - Successfully processed delta changes') return response } catch (error: any) { console.error('[Chrono Manager][Store] - Failed to process delta:', error) throw error } finally { transceiving.value = false } } /** * Create a new empty entity object */ function fresh(): EntityObject { return new EntityObject() } /** * Create a new entity with given collection identifier and properties */ async function create(target: CollectionIdentifier, properties: EntityPropertiesInterface): Promise { transceiving.value = true try { const response = await entityService.create({ target, properties }) _entities.value[response.identifier] = response console.debug('[Chrono Manager][Store] - Successfully created entity:', response.identifier) return response } catch (error: any) { console.error('[Chrono Manager][Store] - Failed to create entity:', error) throw error } finally { transceiving.value = false } } /** * Update an existing entity with given entity identifier and properties */ async function update(target: EntityIdentifier, properties: EntityPropertiesInterface): Promise { transceiving.value = true try { const response = await entityService.update({ target, properties }) _entities.value[response.identifier] = response console.debug('[Chrono Manager][Store] - Successfully updated entity:', response.identifier) return response } catch (error: any) { console.error('[Chrono Manager][Store] - Failed to update entity:', error) throw error } finally { transceiving.value = false } } /** * Delete entities by their identifiers. */ async function remove(targets: EntityIdentifier[]): Promise<{ successes: EntityIdentifier[], failures: EntityIdentifier[] }> { transceiving.value = true try { const response = await entityService.delete({ targets }) const successes: EntityIdentifier[] = [] const failures: EntityIdentifier[] = [] Object.entries(response).forEach(([targetIdentifier, result]) => { const originalIdentifier = targetIdentifier as EntityIdentifier if (!result.disposition || result.disposition === 'error') { console.warn(`[Chrono Manager][Store] - Entity delete on "${originalIdentifier}" returned an error: ${result.error})`) failures.push(originalIdentifier) return } if (result.disposition !== 'moved' && result.disposition !== 'deleted') { console.warn(`[Chrono Manager][Store] - Entity delete on "${originalIdentifier}" returned invalid disposition: ${result.disposition})`) failures.push(originalIdentifier) return } const cachedEntity = _entities.value[originalIdentifier] if (result.disposition === 'moved' && cachedEntity && result.mutation) { const movedEntity = cachedEntity.clone().fromJson({ ...cachedEntity.toJson(), collection: result.destination, identifier: result.mutation, }) _entities.value[result.mutation] = movedEntity } delete _entities.value[originalIdentifier] successes.push(originalIdentifier) }) console.debug('[Chrono Manager][Store] - Successfully deleted', successes.length, 'entities') return { successes, failures } } catch (error: any) { console.error('[Chrono Manager][Store] - Failed to delete entities:', error) throw error } finally { transceiving.value = false } } /** * Move entities to another collection. */ async function move(target: CollectionIdentifier, sources: EntityIdentifier[]): Promise<{ successes: EntityIdentifier[], failures: EntityIdentifier[] }> { transceiving.value = true try { const response = await entityService.move({ target, sources }) const successes: EntityIdentifier[] = [] const failures: EntityIdentifier[] = [] Object.entries(response).forEach(([sourceIdentifier, result]) => { const originalIdentifier = sourceIdentifier as EntityIdentifier if (!result.disposition || result.disposition === 'error') { console.warn(`[Chrono Manager][Store] - Entity move on "${originalIdentifier}" returned an error: ${result.error})`) failures.push(originalIdentifier) return } if (result.disposition !== 'moved') { console.warn(`[Chrono Manager][Store] - Entity move on "${originalIdentifier}" returned invalid disposition: ${result.disposition})`) failures.push(originalIdentifier) return } const cachedEntity = _entities.value[originalIdentifier] if (cachedEntity && result.mutation) { const movedEntity = cachedEntity.clone().fromJson({ ...cachedEntity.toJson(), collection: result.destination, identifier: result.mutation, }) _entities.value[result.mutation] = movedEntity delete _entities.value[originalIdentifier] } successes.push(originalIdentifier) }) console.debug('[Chrono Manager][Store] - Successfully moved', successes.length, 'entities') return { successes, failures } } catch (error: any) { console.error('[Chrono Manager][Store] - Failed to move entities:', error) throw error } finally { transceiving.value = false } } return { transceiving: readonly(transceiving), count, has, entities, entitiesForCollection, entity, list, fetch, extant, create, update, delete: remove, delta, fresh, move, } })