@@ -4,9 +4,9 @@ import { Store, type StoreOptions } from './store.js';
44import { config , type TransactionOptions , type RocksDatabaseConfig } from './load-binding.js' ;
55import * as orderedBinary from 'ordered-binary' ;
66import { Encoder as MsgpackEncoder } from 'msgpackr' ;
7- import type { Key } from './encoding.js' ;
7+ import type { EncoderFunction , Key } from './encoding.js' ;
88
9- interface RocksDatabaseOptions extends StoreOptions {
9+ export interface RocksDatabaseOptions extends StoreOptions {
1010 /**
1111 * The column family name.
1212 *
@@ -183,8 +183,11 @@ export class RocksDatabase extends DBI<DBITransactional> {
183183 * 4. encoding === `ordered-binary`
184184 * 5. encoder.writeKey()
185185 */
186- let EncoderClass = store . encoder ?. Encoder ;
187- if ( store . encoding === false || typeof EncoderClass === 'function' ) {
186+ let EncoderClass : EncoderFunction | undefined = store . encoder ?. Encoder ;
187+ if ( store . encoding === false ) {
188+ store . encoder = null ;
189+ EncoderClass = undefined ;
190+ } else if ( typeof EncoderClass === 'function' ) {
188191 store . encoder = null ;
189192 } else if (
190193 typeof store . encoder ?. encode !== 'function' &&
@@ -206,8 +209,12 @@ export class RocksDatabase extends DBI<DBITransactional> {
206209 } ;
207210 opts . saveStructures = ( structures : any , isCompatible : boolean | ( ( existingStructures : any ) => boolean ) ) => {
208211 this . transactionSync ( ( txn : Transaction ) => {
209- const existingStructuresBuffer = txn . getBinarySync ( sharedStructuresKey ) ;
210- const existingStructures = existingStructuresBuffer && store . decoder ?. decode ? store . decoder . decode ( existingStructuresBuffer ) : undefined ;
212+ // note: we need to get a fresh copy of the shared structures,
213+ // so we don't want to use the transaction's getBinarySync()
214+ const existingStructuresBuffer = this . getBinarySync ( sharedStructuresKey ) ;
215+ const existingStructures = existingStructuresBuffer && store . decoder ?. decode
216+ ? store . decoder . decode ( existingStructuresBuffer )
217+ : undefined ;
211218 if ( typeof isCompatible == 'function' ) {
212219 if ( ! isCompatible ( existingStructures ) ) {
213220 return false ;
@@ -228,7 +235,6 @@ export class RocksDatabase extends DBI<DBITransactional> {
228235 if ( ! store . decoder ) {
229236 store . decoder = store . encoder ;
230237 }
231- store . decoderCopies = ! store . encoder . needsStableBuffer ;
232238 } else if ( store . encoding === 'ordered-binary' ) {
233239 store . encoder = {
234240 readKey : orderedBinary . readKey ,
@@ -246,6 +252,11 @@ export class RocksDatabase extends DBI<DBITransactional> {
246252 return store . encodeBuffer . subarray ( 0 , bytesWritten ) ;
247253 }
248254 } ;
255+ store . encoder . copyBuffers = true ;
256+ }
257+
258+ if ( store . decoder ?. needsStableBuffer !== true ) {
259+ store . decoderCopies = true ;
249260 }
250261
251262 if ( store . decoder ?. readKey && ! store . decoder . decode ) {
@@ -261,11 +272,15 @@ export class RocksDatabase extends DBI<DBITransactional> {
261272 return this ;
262273 }
263274
275+ get path ( ) {
276+ return this . store . path ;
277+ }
278+
264279 /**
265280 * Executes all operations in the callback as a single transaction.
266281 *
267282 * @param callback - A async function that receives the transaction as an argument.
268- * @returns A promise that resolves when the transaction is committed or aborted .
283+ * @returns A promise that resolves the `callback` return value .
269284 *
270285 * @example
271286 * ```ts
@@ -275,7 +290,7 @@ export class RocksDatabase extends DBI<DBITransactional> {
275290 * });
276291 * ```
277292 */
278- async transaction ( callback : ( txn : Transaction ) => Promise < any > , options ?: TransactionOptions ) {
293+ async transaction ( callback : ( txn : Transaction ) => PromiseLike < any > , options ?: TransactionOptions ) {
279294 if ( typeof callback !== 'function' ) {
280295 throw new TypeError ( 'Callback must be a function' ) ;
281296 }
@@ -292,7 +307,24 @@ export class RocksDatabase extends DBI<DBITransactional> {
292307 }
293308 }
294309
295- transactionSync ( callback : ( txn : Transaction ) => void , options ?: TransactionOptions ) {
310+ /**
311+ * Executes all operations in the callback as a single transaction.
312+ *
313+ * @param callback - A function that receives the transaction as an
314+ * argument. If the callback return promise-like value, it is awaited
315+ * before committing the transaction. Otherwise, the callback is treated as
316+ * synchronous.
317+ * @returns The `callback` return value.
318+ *
319+ * @example
320+ * ```ts
321+ * const db = RocksDatabase.open('/path/to/database');
322+ * await db.transaction(async (txn) => {
323+ * await txn.put('key', 'value');
324+ * });
325+ * ```
326+ */
327+ transactionSync < T > ( callback : ( txn : Transaction ) => T | PromiseLike < T > , options ?: TransactionOptions ) : T | PromiseLike < T > {
296328 if ( typeof callback !== 'function' ) {
297329 throw new TypeError ( 'Callback must be a function' ) ;
298330 }
@@ -301,6 +333,20 @@ export class RocksDatabase extends DBI<DBITransactional> {
301333
302334 try {
303335 const result = callback ( txn ) ;
336+ let committed = false ;
337+
338+ // despite being 'sync', we need to support async operations
339+ if ( result && typeof result === 'object' && 'then' in result && typeof result . then === 'function' ) {
340+ return result . then ( ( value ) => {
341+ if ( committed ) {
342+ throw new Error ( 'Transaction already committed' ) ;
343+ }
344+ committed = true ;
345+ txn . commitSync ( ) ;
346+ return value as T ;
347+ } ) ;
348+ }
349+
304350 txn . commitSync ( ) ;
305351 return result ;
306352 } catch ( error ) {
0 commit comments