/** * Nodes Store (thin wrapper over collections/entities stores) */ import { computed, ref, readonly } from 'vue' import { defineStore } from 'pinia' import type { SourceSelector, ListFilter, ListSort, ListRange, CollectionMutableProperties, DocumentInterface, } from '../types' import { CollectionObject, EntityObject } from '../models' import { useCollectionsStore } from './collectionsStore' import { useEntitiesStore } from './entitiesStore' export const ROOT_ID = '00000000-0000-0000-0000-000000000000' type NodeRecord = CollectionObject | EntityObject export const useNodesStore = defineStore('documentsNodesStore', () => { const collectionsStore = useCollectionsStore() const entitiesStore = useEntitiesStore() const error = ref(null) const transceiving = computed(() => collectionsStore.transceiving || entitiesStore.transceiving) const nodeList = computed(() => { return [...collectionsStore.collections, ...entitiesStore.entities] }) const collectionList = computed(() => collectionsStore.collections) const entityList = computed(() => entitiesStore.entities) function toId(value: string | number | null | undefined): string | null { if (value === null || value === undefined || value === '') { return null } return String(value) } function getServiceNodes(providerId: string, serviceId: string | number): NodeRecord[] { return [ ...collectionsStore.collectionsForService(providerId, serviceId), ...entitiesStore.entitiesForService(providerId, serviceId), ] } function getNode(providerId: string, serviceId: string | number, nodeId: string | number): NodeRecord | undefined { const targetId = String(nodeId) return getServiceNodes(providerId, serviceId) .find((node) => String(node.identifier) === targetId) } function isRoot(nodeId: string | number | null | undefined): boolean { const normalized = toId(nodeId) return normalized === null || normalized === ROOT_ID } function getChildren(providerId: string, serviceId: string | number, parentId: string | number | null): NodeRecord[] { const serviceNodes = getServiceNodes(providerId, serviceId) if (isRoot(parentId)) { return serviceNodes.filter((node) => { const parent = toId(node.collection) return parent === null || parent === ROOT_ID }) } const targetParent = String(parentId) return serviceNodes.filter((node) => toId(node.collection) === targetParent) } function getChildCollections(providerId: string, serviceId: string | number, parentId: string | number | null): CollectionObject[] { return getChildren(providerId, serviceId, parentId) .filter((node): node is CollectionObject => node instanceof CollectionObject) } function getChildEntities(providerId: string, serviceId: string | number, parentId: string | number | null): EntityObject[] { return getChildren(providerId, serviceId, parentId) .filter((node): node is EntityObject => node instanceof EntityObject) } function getPath(providerId: string, serviceId: string | number, nodeId: string | number): NodeRecord[] { const path: NodeRecord[] = [] let current = getNode(providerId, serviceId, nodeId) while (current) { path.unshift(current) const parentId = toId(current.collection) if (parentId === null || parentId === ROOT_ID) { break } current = getNode(providerId, serviceId, parentId) } return path } async function fetchCollections( providerId: string, serviceId: string | number, collectionId: string | number | null, filter?: ListFilter, sort?: ListSort, ): Promise { error.value = null try { const sources: SourceSelector = { [providerId]: { [String(serviceId)]: collectionId === null ? true : { [String(collectionId)]: true }, }, } await collectionsStore.list(sources, filter, sort) return collectionsStore.collectionsForService(providerId, serviceId) } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to fetch collections' throw e } } async function fetchEntities( providerId: string, serviceId: string | number, collectionId: string | number | null, filter?: ListFilter, sort?: ListSort, range?: ListRange, ): Promise { error.value = null try { const sources: SourceSelector = { [providerId]: { [String(serviceId)]: collectionId === null ? true : { [String(collectionId)]: true }, }, } await entitiesStore.list(sources, filter, sort, range) return entitiesStore.entitiesForCollection(providerId, serviceId, collectionId) } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to fetch entities' throw e } } async function fetchNodes( providerId: string, serviceId: string | number, parentId: string | number | null = ROOT_ID, filter?: ListFilter, sort?: ListSort, range?: ListRange, ): Promise { error.value = null try { await Promise.all([ fetchCollections(providerId, serviceId, parentId, filter, sort), fetchEntities(providerId, serviceId, parentId, filter, sort, range), ]) return getChildren(providerId, serviceId, parentId) } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to fetch nodes' throw e } } async function createCollection( providerId: string, serviceId: string | number, parentCollectionId: string | number | null, properties: CollectionMutableProperties, ): Promise { error.value = null try { return await collectionsStore.create(providerId, serviceId, parentCollectionId, properties) } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to create collection' throw e } } async function updateCollection( providerId: string, serviceId: string | number, identifier: string | number, properties: CollectionMutableProperties, ): Promise { error.value = null try { return await collectionsStore.update(providerId, serviceId, identifier, properties) } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to update collection' throw e } } async function deleteCollection( providerId: string, serviceId: string | number, identifier: string | number, ): Promise { error.value = null try { const response = await collectionsStore.delete(providerId, serviceId, identifier) return response.success } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to delete collection' throw e } } async function createEntity( providerId: string, serviceId: string | number, collectionId: string | number, properties: DocumentInterface, options?: Record, ): Promise { error.value = null try { return await entitiesStore.create(providerId, serviceId, collectionId, properties, options) } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to create entity' throw e } } async function updateEntity( providerId: string, serviceId: string | number, collectionId: string | number, identifier: string | number, properties: DocumentInterface, ): Promise { error.value = null try { return await entitiesStore.update(providerId, serviceId, collectionId, identifier, properties) } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to update entity' throw e } } async function deleteEntity( providerId: string, serviceId: string | number, collectionId: string | number, identifier: string | number, ): Promise { error.value = null try { const response = await entitiesStore.delete(providerId, serviceId, collectionId, identifier) return response.success } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to delete entity' throw e } } async function readEntity( providerId: string, serviceId: string | number, collectionId: string | number, identifier: string | number, ): Promise { error.value = null try { return await entitiesStore.read(providerId, serviceId, collectionId, identifier) } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to read entity' throw e } } async function writeEntity( providerId: string, serviceId: string | number, collectionId: string | number, identifier: string | number, content: string, ): Promise { error.value = null try { return await entitiesStore.write(providerId, serviceId, collectionId, identifier, content) } catch (e) { error.value = e instanceof Error ? e.message : 'Failed to write entity' throw e } } function clearServiceNodes(providerId: string, serviceId: string | number): void { collectionsStore.clearService(providerId, serviceId) entitiesStore.clearService(providerId, serviceId) } function clearNodes(): void { collectionsStore.clearAll() entitiesStore.clearAll() } return { transceiving: readonly(transceiving), error: readonly(error), ROOT_ID, nodeList, collectionList, entityList, getNode, getServiceNodes, getChildren, getChildCollections, getChildEntities, getPath, isRoot, fetchNodes, fetchCollections, fetchEntities, createCollection, updateCollection, deleteCollection, createEntity, updateEntity, deleteEntity, readEntity, writeEntity, clearServiceNodes, clearNodes, } })