44 type Change ,
55 FeatureChange ,
66 type SerializedChange ,
7+ checkRegistry ,
78 isFeatureChange ,
89} from '@apollo-annotation/common'
910import type {
@@ -45,17 +46,28 @@ export class LocalDriver extends BackendDriver {
4546 const regions = await this . getRegions ( assemblyName )
4647 const refNames = regions . map ( ( r ) => r . refName )
4748 const db = await openDb ( assemblyName , refNames )
48- const storeName = `features-${ refName } `
49+ const featureStoreName = `features-${ refName } `
50+ const checkStoreName = `checkresults-${ refName } `
4951 const features : AnnotationFeatureSnapshot [ ] = [ ]
50- for await ( const cursor of db
51- . transaction ( storeName )
52- . store . index ( 'min' )
52+ const checkResults : CheckResultSnapshot [ ] = [ ]
53+ const tx = db . transaction ( [ featureStoreName , checkStoreName ] )
54+ for await ( const cursor of tx
55+ . objectStore ( featureStoreName )
56+ . index ( 'min' )
5357 . iterate ( IDBKeyRange . upperBound ( end , true ) ) ) {
5458 if ( ( cursor . value as { max : number } ) . max > start ) {
5559 features . push ( cursor . value as AnnotationFeatureSnapshot )
5660 }
5761 }
58- return [ features , [ ] ]
62+ for await ( const cursor of tx
63+ . objectStore ( checkStoreName )
64+ . index ( 'min' )
65+ . iterate ( IDBKeyRange . upperBound ( end , true ) ) ) {
66+ if ( ( cursor . value as { end : number } ) . end > start ) {
67+ checkResults . push ( cursor . value as CheckResultSnapshot )
68+ }
69+ }
70+ return [ features , checkResults ]
5971 }
6072
6173 async getSequence ( region : Region ) : Promise < { seq : string ; refSeq : string } > {
@@ -150,10 +162,14 @@ export class LocalDriver extends BackendDriver {
150162 const regions = await this . getRegions ( assembly )
151163 const refNames = regions . map ( ( r ) => r . refName )
152164 const db = await openDb ( assembly , refNames )
153- const storeNames = refNames . map ( ( r ) => `features-${ r } ` )
165+ const storeNames = refNames . flatMap ( ( r ) => [
166+ `features-${ r } ` ,
167+ `checkresults-${ r } ` ,
168+ ] )
154169 storeNames . push ( 'changes' )
155170 const tx = db . transaction ( storeNames , 'readwrite' )
156171 const topLevelFeatures = new Set < AnnotationFeature > ( )
172+ const deletedFeatureIds : { refSeq : string ; featureId : string } [ ] = [ ]
157173 if ( isDeleteFeatureChange ( change ) ) {
158174 for ( const c of change . changes ) {
159175 if ( c . parentFeatureId ) {
@@ -162,8 +178,9 @@ export class LocalDriver extends BackendDriver {
162178 topLevelFeatures . add ( feature . topLevelFeature )
163179 }
164180 } else {
165- const { refSeq } = c . deletedFeature
181+ const { refSeq, _id } = c . deletedFeature
166182 void tx . objectStore ( `features-${ refSeq } ` ) . delete ( c . deletedFeature . _id )
183+ deletedFeatureIds . push ( { refSeq, featureId : _id } )
167184 }
168185 }
169186 } else {
@@ -180,10 +197,82 @@ export class LocalDriver extends BackendDriver {
180197 . objectStore ( `features-${ feature . refSeq } ` )
181198 . put ( snapshot , feature . _id )
182199 }
200+ // Delete old check results for deleted features
201+ for ( const { featureId, refSeq } of deletedFeatureIds ) {
202+ const checkStore = tx . objectStore ( `checkresults-${ refSeq } ` )
203+ for await ( const cursor of checkStore
204+ . index ( 'featureId' )
205+ . iterate ( featureId ) ) {
206+ this . clientStore . deleteCheckResult (
207+ ( cursor . value as CheckResultSnapshot ) . _id ,
208+ )
209+ void cursor . delete ( )
210+ }
211+ }
212+ // Delete old check results for modified features
213+ for ( const feature of topLevelFeatures ) {
214+ const checkStore = tx . objectStore ( `checkresults-${ feature . refSeq } ` )
215+ for await ( const cursor of checkStore
216+ . index ( 'featureId' )
217+ . iterate ( feature . _id ) ) {
218+ this . clientStore . deleteCheckResult (
219+ ( cursor . value as CheckResultSnapshot ) . _id ,
220+ )
221+ void cursor . delete ( )
222+ }
223+ }
183224 void tx
184225 . objectStore ( 'changes' )
185226 . put ( { ...change . toJSON ( ) , createdAt : new Date ( ) } )
186227 await tx . done
228+
229+ // Run checks on modified features. Collect all results first since checks
230+ // are async (need sequence data) and would cause the transaction to auto-commit.
231+ if ( topLevelFeatures . size > 0 ) {
232+ const checks = [ ...checkRegistry . getChecks ( ) . values ( ) ]
233+ const allResults : {
234+ refSeq : string
235+ result : CheckResultSnapshot
236+ topLevelFeatureId : string
237+ } [ ] = [ ]
238+ for ( const feature of topLevelFeatures ) {
239+ const snapshot = getSnapshot < AnnotationFeatureSnapshot > ( feature )
240+ const getSequence = async ( start : number , end : number ) => {
241+ const result = await this . getSequence ( {
242+ assemblyName : assembly ,
243+ refName : feature . refSeq ,
244+ start,
245+ end,
246+ } )
247+ return result . seq
248+ }
249+ for ( const check of checks ) {
250+ const results = await check . checkFeature ( snapshot , getSequence )
251+ for ( const result of results ) {
252+ allResults . push ( {
253+ refSeq : feature . refSeq ,
254+ result,
255+ topLevelFeatureId : feature . _id ,
256+ } )
257+ }
258+ }
259+ }
260+ if ( allResults . length > 0 ) {
261+ const checkStoreNames = refNames . map ( ( r ) => `checkresults-${ r } ` )
262+ const checkTx = db . transaction ( checkStoreNames , 'readwrite' )
263+ for ( const { refSeq, result, topLevelFeatureId } of allResults ) {
264+ void checkTx
265+ . objectStore ( `checkresults-${ refSeq } ` )
266+ . put ( { ...result , featureId : topLevelFeatureId } )
267+ }
268+ await checkTx . done
269+ // Add new check results to client store
270+ for ( const { result } of allResults ) {
271+ this . clientStore . addCheckResult ( result )
272+ }
273+ }
274+ }
275+
187276 return new ValidationResultSet ( )
188277 }
189278
@@ -194,6 +283,21 @@ export class LocalDriver extends BackendDriver {
194283 return [ ]
195284 }
196285
286+ async getCheckResults ( assemblyName : string ) : Promise < CheckResultSnapshot [ ] > {
287+ const regions = await this . getRegions ( assemblyName )
288+ const refNames = regions . map ( ( r ) => r . refName )
289+ const db = await openDb ( assemblyName , refNames )
290+ const checkResults : CheckResultSnapshot [ ] = [ ]
291+ const storeNames = refNames . map ( ( r ) => `checkresults-${ r } ` )
292+ const tx = db . transaction ( storeNames )
293+ for ( const storeName of storeNames ) {
294+ for await ( const cursor of tx . objectStore ( storeName ) . iterate ( ) ) {
295+ checkResults . push ( cursor . value as CheckResultSnapshot )
296+ }
297+ }
298+ return checkResults
299+ }
300+
197301 async getChanges ( assemblyName : string ) : Promise < ChangeDocument [ ] > {
198302 const regions = await this . getRegions ( assemblyName )
199303 const refNames = regions . map ( ( r ) => r . refName )
0 commit comments