/** * Storage Operations with Rollback Support * * Provides transactional operations for all storage adapters. * Each operation can be executed and rolled back atomically. * * Supports: * - Both storage adapters (FileSystem, Memory) * - Nouns (entities) and Verbs (relationships) * - Metadata and vector data */ import type { StorageAdapter, HNSWNoun, HNSWVerb, NounMetadata, VerbMetadata } from '../../coreTypes.js' import type { Operation, RollbackAction } from '../types.js' /** * Save noun metadata with rollback support * * Rollback strategy: * - If metadata existed: Restore previous metadata * - If metadata was new: Delete metadata */ export class SaveNounMetadataOperation implements Operation { readonly name = 'SaveNounMetadata' constructor( private readonly storage: StorageAdapter, private readonly id: string, private readonly metadata: NounMetadata, private readonly isNew: boolean = false ) {} async execute(): Promise { // Skip read for new entities — nothing to rollback to (saves 1 storage round-trip) const previousMetadata = this.isNew ? null : await this.storage.getNounMetadata(this.id) // Save new metadata await this.storage.saveNounMetadata(this.id, this.metadata) // Return rollback action return async () => { if (previousMetadata) { // Restore previous metadata await this.storage.saveNounMetadata(this.id, previousMetadata) } else { // Delete newly created metadata await this.storage.deleteNounMetadata(this.id) } } } } /** * Save noun (vector data) with rollback support * * Rollback strategy: * - If noun existed: Restore previous noun * - If noun was new: Delete noun (if deleteNoun exists on adapter) * * Note: Not all adapters implement deleteNoun - this is acceptable * because orphaned vector data without metadata is invisible to queries */ export class SaveNounOperation implements Operation { readonly name = 'SaveNoun' constructor( private readonly storage: StorageAdapter, private readonly noun: HNSWNoun, private readonly isNew: boolean = false ) {} async execute(): Promise { // Skip read for new entities — nothing to rollback to (saves 1 storage round-trip) const previousNoun = this.isNew ? null : await this.storage.getNoun(this.noun.id) // Save new noun await this.storage.saveNoun(this.noun) // Return rollback action return async () => { if (previousNoun) { // Restore previous noun (extract just vector data) const nounData: HNSWNoun = { id: previousNoun.id, vector: previousNoun.vector, connections: previousNoun.connections || new Map(), level: previousNoun.level || 0 } await this.storage.saveNoun(nounData) } else { // Delete newly created noun (if adapter supports it) // Note: Not all adapters implement deleteNoun // This is acceptable - metadata deletion makes entity invisible if ('deleteNoun' in this.storage && typeof this.storage.deleteNoun === 'function') { await this.storage.deleteNoun(this.noun.id) } } } } } /** * Delete noun metadata with rollback support * * Rollback strategy: * - Restore deleted metadata */ export class DeleteNounMetadataOperation implements Operation { readonly name = 'DeleteNounMetadata' constructor( private readonly storage: StorageAdapter, private readonly id: string ) {} async execute(): Promise { // Get metadata before deletion (for rollback) const previousMetadata = await this.storage.getNounMetadata(this.id) if (!previousMetadata) { // Nothing to delete - no rollback needed return async () => {} } // Delete metadata await this.storage.deleteNounMetadata(this.id) // Return rollback action return async () => { // Restore deleted metadata await this.storage.saveNounMetadata(this.id, previousMetadata) } } } /** * Save verb metadata with rollback support * * Rollback strategy: * - If metadata existed: Restore previous metadata * - If metadata was new: Delete metadata */ export class SaveVerbMetadataOperation implements Operation { readonly name = 'SaveVerbMetadata' constructor( private readonly storage: StorageAdapter, private readonly id: string, private readonly metadata: VerbMetadata ) {} async execute(): Promise { // Get existing metadata (for rollback) const previousMetadata = await this.storage.getVerbMetadata(this.id) // Save new metadata await this.storage.saveVerbMetadata(this.id, this.metadata) // Return rollback action return async () => { if (previousMetadata) { // Restore previous metadata await this.storage.saveVerbMetadata(this.id, previousMetadata) } else { // Delete newly created verb (metadata + vector) // Note: StorageAdapter has deleteVerb but not deleteVerbMetadata await this.storage.deleteVerb(this.id) } } } } /** * Save verb (vector data) with rollback support * * Rollback strategy: * - If verb existed: Restore previous verb * - If verb was new: Delete verb (if deleteVerb exists on adapter) */ export class SaveVerbOperation implements Operation { readonly name = 'SaveVerb' constructor( private readonly storage: StorageAdapter, private readonly verb: HNSWVerb ) {} async execute(): Promise { // Get existing verb (for rollback) const previousVerb = await this.storage.getVerb(this.verb.id) // Save new verb await this.storage.saveVerb(this.verb) // Return rollback action return async () => { if (previousVerb) { // Restore previous verb (extract just vector data) const verbData: HNSWVerb = { id: previousVerb.id, sourceId: previousVerb.sourceId, targetId: previousVerb.targetId, verb: previousVerb.verb, vector: previousVerb.vector, connections: previousVerb.connections || new Map() } await this.storage.saveVerb(verbData) } else { // Delete newly created verb (if adapter supports it) if ('deleteVerb' in this.storage && typeof this.storage.deleteVerb === 'function') { await this.storage.deleteVerb(this.verb.id) } } } } } /** * Delete verb metadata with rollback support * * Rollback strategy: * - Restore deleted metadata */ export class DeleteVerbMetadataOperation implements Operation { readonly name = 'DeleteVerbMetadata' constructor( private readonly storage: StorageAdapter, private readonly id: string ) {} async execute(): Promise { // Get metadata before deletion (for rollback) const previousMetadata = await this.storage.getVerbMetadata(this.id) if (!previousMetadata) { // Nothing to delete - no rollback needed return async () => {} } // Delete verb (metadata + vector) // Note: StorageAdapter has deleteVerb but not deleteVerbMetadata await this.storage.deleteVerb(this.id) // Return rollback action return async () => { // Restore deleted metadata await this.storage.saveVerbMetadata(this.id, previousMetadata) } } } /** * Update noun metadata with rollback support * * Rollback strategy: * - Restore previous metadata * * Note: This is a convenience operation that wraps SaveNounMetadataOperation * with explicit "update" semantics */ export class UpdateNounMetadataOperation implements Operation { readonly name = 'UpdateNounMetadata' private saveOperation: SaveNounMetadataOperation constructor( storage: StorageAdapter, id: string, metadata: NounMetadata ) { this.saveOperation = new SaveNounMetadataOperation(storage, id, metadata) } async execute(): Promise { return await this.saveOperation.execute() } } /** * Update verb metadata with rollback support * * Rollback strategy: * - Restore previous metadata */ export class UpdateVerbMetadataOperation implements Operation { readonly name = 'UpdateVerbMetadata' private saveOperation: SaveVerbMetadataOperation constructor( storage: StorageAdapter, id: string, metadata: VerbMetadata ) { this.saveOperation = new SaveVerbMetadataOperation(storage, id, metadata) } async execute(): Promise { return await this.saveOperation.execute() } }