@@ -20,6 +20,8 @@ import type {
2020 BatchAddResult ,
2121 CatalogueStorageProvider ,
2222 CreateListParams ,
23+ GraphAnnotationStorage ,
24+ GraphSnapshotStorage ,
2325 ListStats ,
2426 ShareAccessResult ,
2527} from './catalogue-storage-provider.js' ;
@@ -33,11 +35,17 @@ export class InMemoryStorageProvider implements CatalogueStorageProvider {
3335 private lists : Map < string , CatalogueList > ;
3436 private entities : Map < string , CatalogueEntity > ;
3537 private shares : Map < string , CatalogueShareRecord > ;
38+ private annotations : Map < string , GraphAnnotationStorage > ;
39+ private snapshots : Map < string , GraphSnapshotStorage > ;
40+ private searchHistory : Map < string , { query : string ; timestamp : Date } > ;
3641
3742 constructor ( ) {
3843 this . lists = new Map ( ) ;
3944 this . entities = new Map ( ) ;
4045 this . shares = new Map ( ) ;
46+ this . annotations = new Map ( ) ;
47+ this . snapshots = new Map ( ) ;
48+ this . searchHistory = new Map ( ) ;
4149 }
4250
4351 /**
@@ -48,6 +56,9 @@ export class InMemoryStorageProvider implements CatalogueStorageProvider {
4856 this . lists . clear ( ) ;
4957 this . entities . clear ( ) ;
5058 this . shares . clear ( ) ;
59+ this . annotations . clear ( ) ;
60+ this . snapshots . clear ( ) ;
61+ this . searchHistory . clear ( ) ;
5162 }
5263
5364 // ========== List Operations ==========
@@ -846,4 +857,226 @@ export class InMemoryStorageProvider implements CatalogueStorageProvider {
846857
847858 return addedIds ;
848859 }
860+
861+ // ========== Annotation Operations ==========
862+
863+ async addAnnotation ( annotation : Omit < GraphAnnotationStorage , 'id' | 'createdAt' | 'updatedAt' > ) : Promise < string > {
864+ const id = crypto . randomUUID ( ) ;
865+ const timestamp = new Date ( ) ;
866+ const storedAnnotation : GraphAnnotationStorage = {
867+ id,
868+ type : annotation . type ,
869+ visible : annotation . visible ?? true ,
870+ x : annotation . x ,
871+ y : annotation . y ,
872+ content : annotation . content ,
873+ width : annotation . width ,
874+ height : annotation . height ,
875+ radius : annotation . radius ,
876+ borderColor : annotation . borderColor ,
877+ fillColor : annotation . fillColor ,
878+ borderWidth : annotation . borderWidth ,
879+ points : annotation . points ,
880+ strokeColor : annotation . strokeColor ,
881+ strokeWidth : annotation . strokeWidth ,
882+ closed : annotation . closed ,
883+ fontSize : annotation . fontSize ,
884+ backgroundColor : annotation . backgroundColor ,
885+ nodeId : annotation . nodeId ,
886+ graphId : annotation . graphId ,
887+ createdAt : timestamp ,
888+ updatedAt : timestamp ,
889+ } ;
890+ this . annotations . set ( id , storedAnnotation ) ;
891+ return id ;
892+ }
893+
894+ async getAnnotations ( graphId ?: string ) : Promise < GraphAnnotationStorage [ ] > {
895+ const allAnnotations = Array . from ( this . annotations . values ( ) ) ;
896+ if ( ! graphId ) {
897+ return allAnnotations ;
898+ }
899+ return allAnnotations . filter ( ann => ann . graphId === graphId ) ;
900+ }
901+
902+ async updateAnnotation ( annotationId : string , updates : {
903+ visible ?: boolean ;
904+ x ?: number ;
905+ y ?: number ;
906+ content ?: string ;
907+ } ) : Promise < void > {
908+ const annotation = this . annotations . get ( annotationId ) ;
909+ if ( ! annotation ) {
910+ throw new Error ( 'Annotation not found' ) ;
911+ }
912+ const updatedAnnotation : GraphAnnotationStorage = {
913+ ...annotation ,
914+ ...updates ,
915+ updatedAt : new Date ( ) ,
916+ } ;
917+ this . annotations . set ( annotationId , updatedAnnotation ) ;
918+ }
919+
920+ async deleteAnnotation ( annotationId : string ) : Promise < void > {
921+ this . annotations . delete ( annotationId ) ;
922+ }
923+
924+ async toggleAnnotationVisibility ( annotationId : string , visible : boolean ) : Promise < void > {
925+ const annotation = this . annotations . get ( annotationId ) ;
926+ if ( ! annotation ) {
927+ throw new Error ( 'Annotation not found' ) ;
928+ }
929+ const updatedAnnotation : GraphAnnotationStorage = {
930+ ...annotation ,
931+ visible,
932+ updatedAt : new Date ( ) ,
933+ } ;
934+ this . annotations . set ( annotationId , updatedAnnotation ) ;
935+ }
936+
937+ async deleteAnnotationsByGraph ( graphId : string ) : Promise < void > {
938+ for ( const [ id , annotation ] of this . annotations . entries ( ) ) {
939+ if ( annotation . graphId === graphId ) {
940+ this . annotations . delete ( id ) ;
941+ }
942+ }
943+ }
944+
945+ // ========== Snapshot Operations ==========
946+
947+ async saveSnapshot ( snapshot : {
948+ name : string ;
949+ nodes : string ;
950+ edges : string ;
951+ zoom : number ;
952+ panX : number ;
953+ panY : number ;
954+ layoutType : string ;
955+ nodePositions ?: string ;
956+ annotations ?: string ;
957+ isAutoSave ?: boolean ;
958+ } ) : Promise < string > {
959+ const id = crypto . randomUUID ( ) ;
960+ const timestamp = new Date ( ) ;
961+ const storedSnapshot : GraphSnapshotStorage = {
962+ id,
963+ name : snapshot . name ,
964+ nodes : snapshot . nodes ,
965+ edges : snapshot . edges ,
966+ zoom : snapshot . zoom ,
967+ panX : snapshot . panX ,
968+ panY : snapshot . panY ,
969+ layoutType : snapshot . layoutType ,
970+ nodePositions : snapshot . nodePositions ,
971+ annotations : snapshot . annotations ,
972+ isAutoSave : snapshot . isAutoSave ?? false ,
973+ createdAt : timestamp ,
974+ updatedAt : timestamp ,
975+ } ;
976+ this . snapshots . set ( id , storedSnapshot ) ;
977+ return id ;
978+ }
979+
980+ async getSnapshots ( ) : Promise < GraphSnapshotStorage [ ] > {
981+ return Array . from ( this . snapshots . values ( ) ) ;
982+ }
983+
984+ async getSnapshot ( snapshotId : string ) : Promise < GraphSnapshotStorage | null > {
985+ return this . snapshots . get ( snapshotId ) ?? null ;
986+ }
987+
988+ async deleteSnapshot ( snapshotId : string ) : Promise < void > {
989+ this . snapshots . delete ( snapshotId ) ;
990+ }
991+
992+ async updateSnapshot ( snapshotId : string , updates : {
993+ name ?: string ;
994+ nodes ?: string ;
995+ edges ?: string ;
996+ zoom ?: number ;
997+ panX ?: number ;
998+ panY ?: number ;
999+ layoutType ?: string ;
1000+ nodePositions ?: string ;
1001+ annotations ?: string ;
1002+ } ) : Promise < void > {
1003+ const snapshot = this . snapshots . get ( snapshotId ) ;
1004+ if ( ! snapshot ) {
1005+ throw new Error ( 'Snapshot not found' ) ;
1006+ }
1007+ const updatedSnapshot : GraphSnapshotStorage = {
1008+ ...snapshot ,
1009+ ...updates ,
1010+ updatedAt : new Date ( ) ,
1011+ } ;
1012+ this . snapshots . set ( snapshotId , updatedSnapshot ) ;
1013+ }
1014+
1015+ async pruneAutoSaveSnapshots ( maxCount : number ) : Promise < void > {
1016+ const autoSnapshots = Array . from ( this . snapshots . values ( ) )
1017+ . filter ( snap => snap . isAutoSave )
1018+ . sort ( ( a , b ) => a . createdAt . getTime ( ) - b . createdAt . getTime ( ) ) ;
1019+
1020+ if ( autoSnapshots . length > maxCount ) {
1021+ const toDelete = autoSnapshots . slice ( 0 , autoSnapshots . length - maxCount ) ;
1022+ for ( const snap of toDelete ) {
1023+ if ( snap . id ) {
1024+ this . snapshots . delete ( snap . id ) ;
1025+ }
1026+ }
1027+ }
1028+ }
1029+
1030+ async addSnapshot ( snapshot : Omit < GraphSnapshotStorage , 'id' | 'createdAt' | 'updatedAt' > ) : Promise < string > {
1031+ const id = crypto . randomUUID ( ) ;
1032+ const timestamp = new Date ( ) ;
1033+ const storedSnapshot : GraphSnapshotStorage = {
1034+ id,
1035+ name : snapshot . name ,
1036+ nodes : snapshot . nodes ,
1037+ edges : snapshot . edges ,
1038+ zoom : snapshot . zoom ,
1039+ panX : snapshot . panX ,
1040+ panY : snapshot . panY ,
1041+ layoutType : snapshot . layoutType ,
1042+ nodePositions : snapshot . nodePositions ,
1043+ annotations : snapshot . annotations ,
1044+ isAutoSave : snapshot . isAutoSave ?? false ,
1045+ createdAt : timestamp ,
1046+ updatedAt : timestamp ,
1047+ } ;
1048+ this . snapshots . set ( id , storedSnapshot ) ;
1049+ return id ;
1050+ }
1051+
1052+ // ========== Search History Operations ==========
1053+
1054+ async addSearchQuery ( query : string , maxHistory : number = 50 ) : Promise < void > {
1055+ const timestamp = new Date ( ) ;
1056+ const id = crypto . randomUUID ( ) ;
1057+ this . searchHistory . set ( id , { query, timestamp } ) ;
1058+
1059+ // Prune old entries if exceeds max
1060+ const entries = Array . from ( this . searchHistory . entries ( ) )
1061+ . sort ( ( [ , a ] , [ , b ] ) => b . timestamp . getTime ( ) - a . timestamp . getTime ( ) ) ;
1062+
1063+ if ( entries . length > maxHistory ) {
1064+ for ( const [ idToDelete ] of entries . slice ( maxHistory ) ) {
1065+ this . searchHistory . delete ( idToDelete ) ;
1066+ }
1067+ }
1068+ }
1069+
1070+ async getSearchHistory ( ) : Promise < Array < { query : string ; timestamp : Date } > > {
1071+ return Array . from ( this . searchHistory . values ( ) )
1072+ . sort ( ( a , b ) => b . timestamp . getTime ( ) - a . timestamp . getTime ( ) ) ;
1073+ }
1074+
1075+ async removeSearchQuery ( queryId : string ) : Promise < void > {
1076+ this . searchHistory . delete ( queryId ) ;
1077+ }
1078+
1079+ async clearSearchHistory ( ) : Promise < void > {
1080+ this . searchHistory . clear ( ) ;
1081+ }
8491082}
0 commit comments