@@ -132,83 +132,26 @@ export class IndexedDBEventRepository implements IEventRepository {
132132 }
133133 }
134134
135- async getNextSequence (
136- publicKey : Proto . PublicKey ,
137- collection : number ,
138- identity : string ,
139- ) : Promise < bigint > {
135+ async getByEventKey ( key : Proto . EventKey ) : Promise < Proto . SignedEvent | null > {
136+ if ( ! key . signedBy ) return null ;
140137 try {
141- const pubKeyHex = bytesToHex ( Proto . PublicKey . toBinary ( publicKey ) ) ;
142- console . log ( pubKeyHex ) ;
143-
144138 const transaction = this . database . createTransaction (
145139 IndexedDBEventRepository . STORE_NAME ,
146140 'readonly' ,
147141 ) ;
148142 const store = transaction . objectStore (
149143 IndexedDBEventRepository . STORE_NAME ,
150144 ) ;
151-
152- const results = await IndexedDBDatabase . requestAsPromise <
153- PersistedEvent [ ]
154- > ( store . getAll ( ) ) ;
155-
156- let maxSeq = 0n ;
157- for ( const row of results ) {
158- if (
159- row . publicKey === pubKeyHex &&
160- row . collection === collection &&
161- row . identity === identity
162- ) {
163- const seq = BigInt ( row . sequence ) ;
164- if ( seq > maxSeq ) maxSeq = seq ;
165- }
166- }
167-
168- return maxSeq + 1n ;
145+ const pkHex = bytesToHex ( Proto . PublicKey . toBinary ( key . signedBy ) ) ;
146+ const result = await IndexedDBDatabase . requestAsPromise <
147+ PersistedEvent | undefined
148+ > ( store . get ( [ key . identity , pkHex , key . collection , Number ( key . sequence ) ] ) ) ;
149+ return result ? this . toSignedEvent ( result ) : null ;
169150 } catch ( error ) {
170- throw new DatabaseError ( 'Failed to get next sequence : ' , error ) ;
151+ throw new DatabaseError ( 'Failed to get event by key : ' , error ) ;
171152 }
172153 }
173154
174- async getEventsByIdentity (
175- publicKey : Proto . PublicKey ,
176- identity : string ,
177- ) : Promise < Proto . SignedEvent [ ] > {
178- try {
179- const pubKeyHex = bytesToHex ( Proto . PublicKey . toBinary ( publicKey ) ) ;
180-
181- const transaction = this . database . createTransaction (
182- IndexedDBEventRepository . STORE_NAME ,
183- 'readonly' ,
184- ) ;
185- const store = transaction . objectStore (
186- IndexedDBEventRepository . STORE_NAME ,
187- ) ;
188-
189- const results = await IndexedDBDatabase . requestAsPromise <
190- PersistedEvent [ ]
191- > ( store . getAll ( ) ) ;
192-
193- return results
194- . filter (
195- ( row ) => row . publicKey === pubKeyHex && row . identity === identity ,
196- )
197- . sort ( ( a , b ) => a . sequence - b . sequence )
198- . map ( ( row ) => this . toSignedEvent ( row ) ) ;
199- } catch ( error ) {
200- throw new DatabaseError ( 'Failed to get events by identity: ' , error ) ;
201- }
202- }
203-
204- async getLatestEvent (
205- publicKey : Proto . PublicKey ,
206- identity : string ,
207- ) : Promise < Proto . SignedEvent | null > {
208- const events = await this . getEventsByIdentity ( publicKey , identity ) ;
209- return events . length > 0 ? events [ events . length - 1 ] : null ;
210- }
211-
212155 async getBatch (
213156 batchSize : number ,
214157 offset ?: number ,
@@ -222,7 +165,14 @@ export class IndexedDBEventRepository implements IEventRepository {
222165 return { events, offset : start + events . length } ;
223166 }
224167
225- async getHeadsByIdentity ( identity : string ) : Promise < Proto . SignedEvent [ ] > {
168+ async getByIdentity (
169+ identity : string ,
170+ options ?: {
171+ signer ?: Proto . PublicKey ;
172+ collection ?: number ;
173+ headsOnly ?: boolean ;
174+ } ,
175+ ) : Promise < Proto . SignedEvent [ ] > {
226176 try {
227177 const transaction = this . database . createTransaction (
228178 IndexedDBEventRepository . STORE_NAME ,
@@ -233,34 +183,48 @@ export class IndexedDBEventRepository implements IEventRepository {
233183 ) ;
234184
235185 // Compound keyPath: [identity, publicKey, collection, sequence].
236- // Reverse cursor hits the max-sequence entry for each
237- // (publicKey, collection) group first. After reading a head,
238- // skip the rest of the group by continuing to
239- // [identity, publicKey, collection] (without sequence) — this
240- // compares less than any key with a sequence component, so the
241- // reverse cursor jumps to the previous group's max.
242- const range = IDBKeyRange . bound ( [ identity ] , [ identity , '\uffff' ] ) ;
243- const heads : PersistedEvent [ ] = [ ] ;
186+ // Build the longest prefix of fixed components so the cursor scans
187+ // only matching rows.
188+ const prefix : ( string | number ) [ ] = [ identity ] ;
189+ if ( options ?. signer ) {
190+ prefix . push ( bytesToHex ( Proto . PublicKey . toBinary ( options . signer ) ) ) ;
191+ if ( options . collection !== undefined ) prefix . push ( options . collection ) ;
192+ }
193+ const range = IDBKeyRange . bound ( prefix , [ ...prefix , [ ] ] ) ;
194+ const headsOnly = options ?. headsOnly ?? false ;
195+ const collectionFilter = options ?. collection ;
196+
197+ // headsOnly: walk in reverse; first hit per (signer, collection) is
198+ // its max-sequence entry, then skip the rest of the group.
199+ // Otherwise: walk forward and collect everything matching.
200+ const direction : IDBCursorDirection = headsOnly ? 'prev' : 'next' ;
201+ const rows : PersistedEvent [ ] = [ ] ;
244202
245203 await new Promise < void > ( ( resolve , reject ) => {
246- const request = store . openCursor ( range , 'prev' ) ;
204+ const request = store . openCursor ( range , direction ) ;
247205 request . onerror = ( ) => reject ( request . error ) ;
248206 request . onsuccess = ( ) => {
249207 const cursor = request . result ;
250- if ( ! cursor ) {
251- resolve ( ) ;
252- return ;
253- }
208+ if ( ! cursor ) return resolve ( ) ;
254209 const row = cursor . value as PersistedEvent ;
255- heads . push ( row ) ;
256- // Skip to the previous group's max entry.
257- cursor . continue ( [ identity , row . publicKey , row . collection ] ) ;
210+ if (
211+ collectionFilter === undefined ||
212+ row . collection === collectionFilter
213+ ) {
214+ rows . push ( row ) ;
215+ }
216+ if ( headsOnly ) {
217+ // Skip the rest of this (signer, collection) group.
218+ cursor . continue ( [ identity , row . publicKey , row . collection ] ) ;
219+ } else {
220+ cursor . continue ( ) ;
221+ }
258222 } ;
259223 } ) ;
260224
261- return heads . map ( ( row ) => this . toSignedEvent ( row ) ) ;
225+ return rows . map ( ( row ) => this . toSignedEvent ( row ) ) ;
262226 } catch ( error ) {
263- throw new DatabaseError ( 'Failed to get heads by identity: ' , error ) ;
227+ throw new DatabaseError ( 'Failed to get events by identity: ' , error ) ;
264228 }
265229 }
266230}
0 commit comments