UNPKG

lmdb

Version:

Simple, efficient, scalable, high-performance LMDB interface

1 lines 259 kB
{"version":3,"file":"index.cjs","sources":["../native.js","../util/when.js","../write.js","../util/RangeIterable.js","../keys.js","../read.js","../caching.js","../open.js","../level.js","../index.js","../node-index.js"],"sourcesContent":["import { dirname, join, default as pathModule } from 'path';\nimport { fileURLToPath } from 'url';\nimport loadNAPI from 'node-gyp-build-optional-packages';\nexport let Env,\n\tTxn,\n\tDbi,\n\tCompression,\n\tCursor,\n\tgetAddress,\n\tgetBufferAddress,\n\tcreateBufferForAddress,\n\tclearKeptObjects,\n\tglobalBuffer,\n\tsetGlobalBuffer,\n\tarch,\n\tfs,\n\tos,\n\tonExit,\n\ttmpdir,\n\tlmdbError,\n\tpath,\n\tEventEmitter,\n\torderedBinary,\n\tMsgpackrEncoder,\n\tWeakLRUCache,\n\tsetEnvMap,\n\tgetEnvMap,\n\tgetByBinary,\n\tdetachBuffer,\n\tstartRead,\n\tsetReadCallback,\n\twrite,\n\tposition,\n\titerate,\n\tprefetch,\n\tresetTxn,\n\tgetCurrentValue,\n\tgetCurrentShared,\n\tgetStringByBinary,\n\tgetSharedByBinary,\n\tgetSharedBuffer,\n\tcompress,\n\tdirectWrite,\n\tgetUserSharedBuffer,\n\tnotifyUserCallbacks,\n\tattemptLock,\n\tunlock,\n\tversion;\npath = pathModule;\nlet dirName = dirname(fileURLToPath(import.meta.url)).replace(/dist$/, '');\nexport let nativeAddon = loadNAPI(dirName);\n\nif (process.isBun && false) {\n\tconst { linkSymbols, FFIType } = require('bun:ffi');\n\tlet lmdbLib = linkSymbols({\n\t\tgetByBinary: {\n\t\t\targs: [FFIType.f64, FFIType.u32],\n\t\t\treturns: FFIType.u32,\n\t\t\tptr: nativeAddon.getByBinaryPtr,\n\t\t},\n\t\titerate: {\n\t\t\targs: [FFIType.f64],\n\t\t\treturns: FFIType.i32,\n\t\t\tptr: nativeAddon.iteratePtr,\n\t\t},\n\t\tposition: {\n\t\t\targs: [FFIType.f64, FFIType.u32, FFIType.u32, FFIType.u32, FFIType.f64],\n\t\t\treturns: FFIType.i32,\n\t\t\tptr: nativeAddon.positionPtr,\n\t\t},\n\t\twrite: {\n\t\t\targs: [FFIType.f64, FFIType.f64],\n\t\t\treturns: FFIType.i32,\n\t\t\tptr: nativeAddon.writePtr,\n\t\t},\n\t\tresetTxn: {\n\t\t\targs: [FFIType.f64],\n\t\t\treturns: FFIType.void,\n\t\t\tptr: nativeAddon.resetTxnPtr,\n\t\t},\n\t});\n\tfor (let key in lmdbLib.symbols) {\n\t\tnativeAddon[key] = lmdbLib.symbols[key].native;\n\t}\n}\nsetNativeFunctions(nativeAddon);\n\nexport function setNativeFunctions(externals) {\n\tEnv = externals.Env;\n\tTxn = externals.Txn;\n\tDbi = externals.Dbi;\n\tCompression = externals.Compression;\n\tgetAddress = externals.getAddress;\n\tgetBufferAddress = externals.getBufferAddress;\n\tcreateBufferForAddress = externals.createBufferForAddress;\n\tclearKeptObjects = externals.clearKeptObjects || function () {};\n\tgetByBinary = externals.getByBinary;\n\tdetachBuffer = externals.detachBuffer;\n\tstartRead = externals.startRead;\n\tsetReadCallback = externals.setReadCallback;\n\tsetGlobalBuffer = externals.setGlobalBuffer;\n\tglobalBuffer = externals.globalBuffer;\n\tgetSharedBuffer = externals.getSharedBuffer;\n\tprefetch = externals.prefetch;\n\titerate = externals.iterate;\n\tposition = externals.position;\n\tresetTxn = externals.resetTxn;\n\tdirectWrite = externals.directWrite;\n\tgetUserSharedBuffer = externals.getUserSharedBuffer;\n\tnotifyUserCallbacks = externals.notifyUserCallbacks;\n\tattemptLock = externals.attemptLock;\n\tunlock = externals.unlock;\n\tgetCurrentValue = externals.getCurrentValue;\n\tgetCurrentShared = externals.getCurrentShared;\n\tgetStringByBinary = externals.getStringByBinary;\n\tgetSharedByBinary = externals.getSharedByBinary;\n\twrite = externals.write;\n\tcompress = externals.compress;\n\tCursor = externals.Cursor;\n\tlmdbError = externals.lmdbError;\n\tversion = externals.version;\n\tif (externals.tmpdir) tmpdir = externals.tmpdir;\n}\nexport function setExternals(externals) {\n\tarch = externals.arch;\n\tfs = externals.fs;\n\tEventEmitter = externals.EventEmitter;\n\torderedBinary = externals.orderedBinary;\n\tMsgpackrEncoder = externals.MsgpackrEncoder;\n\tWeakLRUCache = externals.WeakLRUCache;\n\ttmpdir = externals.tmpdir;\n\tos = externals.os;\n\tonExit = externals.onExit;\n}\n","export function when(promise, callback, errback) {\n if (promise && promise.then) {\n return errback ?\n promise.then(callback, errback) :\n promise.then(callback);\n }\n return callback(promise);\n}\n","import {\n\tgetAddress,\n\tgetBufferAddress,\n\twrite,\n\tcompress,\n\tlmdbError,\n} from './native.js';\nimport { when } from './util/when.js';\nvar backpressureArray;\n\nconst WAITING_OPERATION = 0x2000000;\nconst BACKPRESSURE_THRESHOLD = 300000;\nconst TXN_DELIMITER = 0x8000000;\nconst TXN_COMMITTED = 0x10000000;\nconst TXN_FLUSHED = 0x20000000;\nconst TXN_FAILED = 0x40000000;\nexport const FAILED_CONDITION = 0x4000000;\nconst REUSE_BUFFER_MODE = 512;\nconst RESET_BUFFER_MODE = 1024;\nconst NO_RESOLVE = 16;\nconst HAS_TXN = 8;\nconst CONDITIONAL_VERSION_LESS_THAN = 0x800;\nconst CONDITIONAL_ALLOW_NOTFOUND = 0x800;\n\nconst SYNC_PROMISE_SUCCESS = Promise.resolve(true);\nconst SYNC_PROMISE_FAIL = Promise.resolve(false);\nSYNC_PROMISE_SUCCESS.isSync = true;\nSYNC_PROMISE_SUCCESS.result = true;\nSYNC_PROMISE_FAIL.isSync = true;\nSYNC_PROMISE_FAIL.result = false;\nconst PROMISE_SUCCESS = Promise.resolve(true);\nconst arch = process.arch;\nexport const ABORT = 4.452694326329068e-106; // random/unguessable numbers, which work across module/versions and native\nexport const IF_EXISTS = 3.542694326329068e-103;\nconst CALLBACK_THREW = {};\nconst LocalSharedArrayBuffer =\n\ttypeof Deno != 'undefined' || // Deno can't handle SharedArrayBuffer as an FFI\n\t// argument due to https://github.com/denoland/deno/issues/12678\n\ttypeof SharedArrayBuffer == 'undefined' // Sometimes electron doesn't have a SharedArrayBuffer\n\t\t? ArrayBuffer\n\t\t: SharedArrayBuffer;\nconst ByteArray =\n\ttypeof Buffer != 'undefined'\n\t\t? function (buffer) {\n\t\t\t\treturn Buffer.from(buffer);\n\t\t\t}\n\t\t: Uint8Array;\nconst queueTask =\n\ttypeof setImmediate != 'undefined' ? setImmediate : setTimeout; // TODO: Or queueMicrotask?\n//let debugLog = []\nconst WRITE_BUFFER_SIZE = 0x10000;\nvar log = [];\nexport function addWriteMethods(\n\tLMDBStore,\n\t{\n\t\tenv,\n\t\tfixedBuffer,\n\t\tresetReadTxn,\n\t\tuseWritemap,\n\t\tmaxKeySize,\n\t\teventTurnBatching,\n\t\ttxnStartThreshold,\n\t\tbatchStartThreshold,\n\t\toverlappingSync,\n\t\tcommitDelay,\n\t\tseparateFlushed,\n\t\tmaxFlushDelay,\n\t},\n) {\n\t// stands for write instructions\n\tvar dynamicBytes;\n\tfunction allocateInstructionBuffer(lastPosition) {\n\t\t// Must use a shared buffer on older node in order to use Atomics, and it is also more correct since we are\n\t\t// indeed accessing and modifying it from another thread (in C). However, Deno can't handle it for\n\t\t// FFI so aliased above\n\t\tlet buffer = new LocalSharedArrayBuffer(WRITE_BUFFER_SIZE);\n\t\tlet lastBytes = dynamicBytes;\n\t\tdynamicBytes = new ByteArray(buffer);\n\t\tlet uint32 = (dynamicBytes.uint32 = new Uint32Array(\n\t\t\tbuffer,\n\t\t\t0,\n\t\t\tWRITE_BUFFER_SIZE >> 2,\n\t\t));\n\t\tuint32[2] = 0;\n\t\tdynamicBytes.float64 = new Float64Array(buffer, 0, WRITE_BUFFER_SIZE >> 3);\n\t\tbuffer.address = getBufferAddress(dynamicBytes);\n\t\tuint32.address = buffer.address + uint32.byteOffset;\n\t\tdynamicBytes.position = 1; // we start at position 1 to save space for writing the txn id before the txn delimiter\n\t\tif (lastPosition) {\n\t\t\tlastBytes.float64[lastPosition + 1] =\n\t\t\t\tdynamicBytes.uint32.address + (dynamicBytes.position << 3);\n\t\t\tlastBytes.uint32[lastPosition << 1] = 3; // pointer instruction\n\t\t}\n\t\treturn dynamicBytes;\n\t}\n\tvar newBufferThreshold = (WRITE_BUFFER_SIZE - maxKeySize - 64) >> 3; // need to reserve more room if we do inline values\n\tvar outstandingWriteCount = 0;\n\tvar startAddress = 0;\n\tvar writeTxn = null;\n\tvar committed;\n\tvar abortedNonChildTransactionWarn;\n\tvar nextTxnCallbacks = [];\n\tvar commitPromise,\n\t\tflushPromise,\n\t\tflushResolvers = [],\n\t\tbatchFlushResolvers = [];\n\tcommitDelay = commitDelay || 0;\n\teventTurnBatching = eventTurnBatching === false ? false : true;\n\tvar enqueuedCommit;\n\tvar afterCommitCallbacks = [];\n\tvar beforeCommitCallbacks = [];\n\tvar enqueuedEventTurnBatch;\n\tvar batchDepth = 0;\n\tvar lastWritePromise;\n\tvar writeBatchStart,\n\t\toutstandingBatchCount,\n\t\tlastSyncTxnFlush,\n\t\tlastFlushTimeout,\n\t\tlastFlushCallback;\n\tvar hasUnresolvedTxns;\n\ttxnStartThreshold = txnStartThreshold || 5;\n\tbatchStartThreshold = batchStartThreshold || 1000;\n\tmaxFlushDelay = maxFlushDelay || 500;\n\n\tallocateInstructionBuffer();\n\tdynamicBytes.uint32[2] = TXN_DELIMITER | TXN_COMMITTED | TXN_FLUSHED;\n\tvar txnResolution,\n\t\tnextResolution = {\n\t\t\tuint32: dynamicBytes.uint32,\n\t\t\tflagPosition: 2,\n\t\t\tflag: 0,\n\t\t\tvalueBuffer: null,\n\t\t\tnext: null,\n\t\t\tmeta: null,\n\t\t};\n\tvar uncommittedResolution = {\n\t\tuint32: null,\n\t\tflagPosition: 2,\n\t\tflag: 0,\n\t\tvalueBuffer: null,\n\t\tnext: nextResolution,\n\t\tmeta: null,\n\t};\n\tvar unwrittenResolution = nextResolution;\n\tvar lastPromisedResolution = uncommittedResolution;\n\tvar lastQueuedResolution = uncommittedResolution;\n\tfunction writeInstructions(flags, store, key, value, version, ifVersion) {\n\t\tlet writeStatus;\n\t\tlet targetBytes, position, encoder;\n\t\tlet valueSize, valueBuffer, valueBufferStart;\n\t\tif (flags & 2) {\n\t\t\t// encode first in case we have to write a shared structure\n\t\t\tencoder = store.encoder;\n\t\t\tif (value && value['\\x10binary-data\\x02'])\n\t\t\t\tvalueBuffer = value['\\x10binary-data\\x02'];\n\t\t\telse if (encoder) {\n\t\t\t\tif (encoder.copyBuffers)\n\t\t\t\t\t// use this as indicator for support buffer reuse for now\n\t\t\t\t\tvalueBuffer = encoder.encode(\n\t\t\t\t\t\tvalue,\n\t\t\t\t\t\tREUSE_BUFFER_MODE | (writeTxn ? RESET_BUFFER_MODE : 0),\n\t\t\t\t\t);\n\t\t\t\t// in addition, if we are writing sync, after using, we can immediately reset the encoder's position to reuse that space, which can improve performance\n\t\t\t\telse {\n\t\t\t\t\t// various other encoders, including JSON.stringify, that might serialize to a string\n\t\t\t\t\tvalueBuffer = encoder.encode(value);\n\t\t\t\t\tif (typeof valueBuffer == 'string')\n\t\t\t\t\t\tvalueBuffer = Buffer.from(valueBuffer); // TODO: Would be nice to write strings inline in the instructions\n\t\t\t\t}\n\t\t\t} else if (typeof value == 'string') {\n\t\t\t\tvalueBuffer = Buffer.from(value); // TODO: Would be nice to write strings inline in the instructions\n\t\t\t} else if (value instanceof Uint8Array) valueBuffer = value;\n\t\t\telse\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'Invalid value to put in database ' +\n\t\t\t\t\t\tvalue +\n\t\t\t\t\t\t' (' +\n\t\t\t\t\t\ttypeof value +\n\t\t\t\t\t\t'), consider using encoder',\n\t\t\t\t);\n\t\t\tvalueBufferStart = valueBuffer.start;\n\t\t\tif (valueBufferStart > -1)\n\t\t\t\t// if we have buffers with start/end position\n\t\t\t\tvalueSize = valueBuffer.end - valueBufferStart; // size\n\t\t\telse valueSize = valueBuffer.length;\n\t\t\tif (store.dupSort && valueSize > maxKeySize)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'The value is larger than the maximum size (' +\n\t\t\t\t\t\tmaxKeySize +\n\t\t\t\t\t\t') for a value in a dupSort database',\n\t\t\t\t);\n\t\t} else valueSize = 0;\n\t\tif (writeTxn) {\n\t\t\ttargetBytes = fixedBuffer;\n\t\t\tposition = 0;\n\t\t} else {\n\t\t\tif (eventTurnBatching && !enqueuedEventTurnBatch && batchDepth == 0) {\n\t\t\t\tenqueuedEventTurnBatch = queueTask(() => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tfor (let i = 0, l = beforeCommitCallbacks.length; i < l; i++) {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tbeforeCommitCallbacks[i]();\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\tconsole.error('In beforecommit callback', error);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.error(error);\n\t\t\t\t\t}\n\t\t\t\t\tenqueuedEventTurnBatch = null;\n\t\t\t\t\tbatchDepth--;\n\t\t\t\t\tfinishBatch();\n\t\t\t\t\tif (writeBatchStart) writeBatchStart(); // TODO: When we support delay start of batch, optionally don't delay this\n\t\t\t\t});\n\t\t\t\tcommitPromise = null; // reset the commit promise, can't know if it is really a new transaction prior to finishWrite being called\n\t\t\t\tflushPromise = null;\n\t\t\t\twriteBatchStart = writeInstructions(1, store);\n\t\t\t\toutstandingBatchCount = 0;\n\t\t\t\tbatchDepth++;\n\t\t\t}\n\t\t\ttargetBytes = dynamicBytes;\n\t\t\tposition = targetBytes.position;\n\t\t}\n\t\tlet uint32 = targetBytes.uint32,\n\t\t\tfloat64 = targetBytes.float64;\n\t\tlet flagPosition = position << 1; // flagPosition is the 32-bit word starting position\n\n\t\t// don't increment position until we are sure we don't have any key writing errors\n\t\tif (!uint32) {\n\t\t\tthrow new Error('Internal buffers have been corrupted');\n\t\t}\n\t\tuint32[flagPosition + 1] = store.db.dbi;\n\t\tif (flags & 4) {\n\t\t\tlet keyStartPosition = (position << 3) + 12;\n\t\t\tlet endPosition;\n\t\t\ttry {\n\t\t\t\tendPosition = store.writeKey(key, targetBytes, keyStartPosition);\n\t\t\t\tif (!(keyStartPosition < endPosition) && (flags & 0xf) != 12)\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t'Invalid key or zero length key is not allowed in LMDB ' + key,\n\t\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\ttargetBytes.fill(0, keyStartPosition);\n\t\t\t\tif (error.name == 'RangeError')\n\t\t\t\t\terror = new Error(\n\t\t\t\t\t\t'Key size is larger than the maximum key size (' + maxKeySize + ')',\n\t\t\t\t\t);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tlet keySize = endPosition - keyStartPosition;\n\t\t\tif (keySize > maxKeySize) {\n\t\t\t\ttargetBytes.fill(0, keyStartPosition); // restore zeros\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'Key size is larger than the maximum key size (' + maxKeySize + ')',\n\t\t\t\t);\n\t\t\t}\n\t\t\tuint32[flagPosition + 2] = keySize;\n\t\t\tposition = (endPosition + 16) >> 3;\n\t\t\tif (flags & 2) {\n\t\t\t\tlet mustCompress;\n\t\t\t\tif (valueBufferStart > -1) {\n\t\t\t\t\t// if we have buffers with start/end position\n\t\t\t\t\t// record pointer to value buffer\n\t\t\t\t\tfloat64[position] =\n\t\t\t\t\t\t(valueBuffer.address ||\n\t\t\t\t\t\t\t(valueBuffer.address = getAddress(valueBuffer.buffer))) +\n\t\t\t\t\t\tvalueBufferStart;\n\t\t\t\t\tif (store.compression) {\n\t\t\t\t\t\tlet compressionFlagIndex =\n\t\t\t\t\t\t\tvalueBufferStart + (store.compression.startingOffset || 0);\n\t\t\t\t\t\t// this is the compression indicator, so we must compress\n\t\t\t\t\t\tmustCompress =\n\t\t\t\t\t\t\tcompressionFlagIndex < valueBuffer.end &&\n\t\t\t\t\t\t\tvalueBuffer[compressionFlagIndex] >= 250;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tlet valueArrayBuffer = valueBuffer.buffer;\n\t\t\t\t\t// record pointer to value buffer\n\t\t\t\t\tlet address =\n\t\t\t\t\t\t(valueArrayBuffer.address ||\n\t\t\t\t\t\t\t(valueBuffer.length === 0\n\t\t\t\t\t\t\t\t? 0 // externally allocated buffers of zero-length with the same non-null-pointer can crash node, #161\n\t\t\t\t\t\t\t\t: (valueArrayBuffer.address = getAddress(valueArrayBuffer)))) +\n\t\t\t\t\t\tvalueBuffer.byteOffset;\n\t\t\t\t\tif (address <= 0 && valueBuffer.length > 0)\n\t\t\t\t\t\tconsole.error('Supplied buffer had an invalid address', address);\n\t\t\t\t\tfloat64[position] = address;\n\t\t\t\t\tif (store.compression) {\n\t\t\t\t\t\tlet compressionFlagIndex = store.compression.startingOffset || 0;\n\t\t\t\t\t\t// this is the compression indicator, so we must compress\n\t\t\t\t\t\tmustCompress =\n\t\t\t\t\t\t\tcompressionFlagIndex < valueBuffer.length &&\n\t\t\t\t\t\t\tvalueBuffer[compressionFlagIndex] >= 250;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tuint32[(position++ << 1) - 1] = valueSize;\n\t\t\t\tif (\n\t\t\t\t\tstore.compression &&\n\t\t\t\t\t(valueSize >= store.compression.threshold || mustCompress)\n\t\t\t\t) {\n\t\t\t\t\tflags |= 0x100000;\n\t\t\t\t\tfloat64[position] = store.compression.address;\n\t\t\t\t\tif (!writeTxn)\n\t\t\t\t\t\tcompress(env.address, uint32.address + (position << 3), () => {\n\t\t\t\t\t\t\t// this is never actually called in NodeJS, just use to pin the buffer in memory until it is finished\n\t\t\t\t\t\t\t// and is a no-op in Deno\n\t\t\t\t\t\t\tif (!float64) throw new Error('No float64 available');\n\t\t\t\t\t\t});\n\t\t\t\t\tposition++;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (ifVersion !== undefined) {\n\t\t\t\tif (ifVersion === null)\n\t\t\t\t\tflags |= 0x10; // if it does not exist, MDB_NOOVERWRITE\n\t\t\t\telse {\n\t\t\t\t\tflags |= 0x100;\n\t\t\t\t\tfloat64[position++] = ifVersion;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (version !== undefined) {\n\t\t\t\tflags |= 0x200;\n\t\t\t\tfloat64[position++] = version || 0;\n\t\t\t}\n\t\t} else position++;\n\t\ttargetBytes.position = position;\n\t\tif (writeTxn) {\n\t\t\tuint32[0] = flags;\n\t\t\twrite(env.address, uint32.address);\n\t\t\treturn () =>\n\t\t\t\tuint32[0] & FAILED_CONDITION ? SYNC_PROMISE_FAIL : SYNC_PROMISE_SUCCESS;\n\t\t}\n\t\t// if we ever use buffers that haven't been zero'ed, need to clear out the next slot like this:\n\t\t// uint32[position << 1] = 0 // clear out the next slot\n\t\tlet nextUint32;\n\t\tif (position > newBufferThreshold) {\n\t\t\t// make new buffer and make pointer to it\n\t\t\tlet lastPosition = position;\n\t\t\ttargetBytes = allocateInstructionBuffer(position);\n\t\t\tposition = targetBytes.position;\n\t\t\tnextUint32 = targetBytes.uint32;\n\t\t} else nextUint32 = uint32;\n\t\tlet resolution = nextResolution;\n\t\t// create the placeholder next resolution\n\t\tnextResolution = resolution.next = {\n\t\t\t// we try keep resolutions exactly the same object type\n\t\t\tuint32: nextUint32,\n\t\t\tflagPosition: position << 1,\n\t\t\tflag: 0, // TODO: eventually eliminate this, as we can probably signify HAS_TXN/NO_RESOLVE/FAILED_CONDITION in upper bits\n\t\t\tvalueBuffer: fixedBuffer, // these are all just placeholders so that we have the right hidden class initially allocated\n\t\t\tnext: null,\n\t\t\tmeta: null,\n\t\t};\n\t\tlastQueuedResolution = resolution;\n\n\t\tlet writtenBatchDepth = batchDepth;\n\n\t\treturn (callback) => {\n\t\t\tif (writtenBatchDepth) {\n\t\t\t\t// If we are in a batch, the transaction can't close, so we do the faster,\n\t\t\t\t// but non-deterministic updates, knowing that the write thread can\n\t\t\t\t// just poll for the status change if we miss a status update.\n\t\t\t\t// That is, if we are on x64 architecture...\n\t\t\t\tif (arch === 'x64') {\n\t\t\t\t\twriteStatus = uint32[flagPosition];\n\t\t\t\t\tuint32[flagPosition] = flags;\n\t\t\t\t} else {\n\t\t\t\t\t// However, on ARM processors, apparently more radical memory reordering can occur\n\t\t\t\t\t// so we need to use the slower atomic operation to ensure that a memory barrier is set\n\t\t\t\t\t// and that the value pointer is actually written before the flag is updated\n\t\t\t\t\twriteStatus = Atomics.or(uint32, flagPosition, flags);\n\t\t\t\t}\n\t\t\t\tif (writeBatchStart && !writeStatus) {\n\t\t\t\t\toutstandingBatchCount += 1 + (valueSize >> 12);\n\t\t\t\t\tif (outstandingBatchCount > batchStartThreshold) {\n\t\t\t\t\t\toutstandingBatchCount = 0;\n\t\t\t\t\t\twriteBatchStart();\n\t\t\t\t\t\twriteBatchStart = null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} // otherwise the transaction could end at any time and we need to know the\n\t\t\t// deterministically if it is ending, so we can reset the commit promise\n\t\t\t// so we use the slower atomic operation\n\t\t\telse writeStatus = Atomics.or(uint32, flagPosition, flags);\n\n\t\t\toutstandingWriteCount++;\n\t\t\tif (writeStatus & TXN_DELIMITER) {\n\t\t\t\tcommitPromise = null; // TODO: Don't reset these if this comes from the batch start operation on an event turn batch\n\t\t\t\tflushPromise = null;\n\t\t\t\tflushResolvers = [];\n\t\t\t\tqueueCommitResolution(resolution);\n\t\t\t\tif (!startAddress) {\n\t\t\t\t\tstartAddress = uint32.address + (flagPosition << 2);\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!writtenBatchDepth && batchFlushResolvers.length > 0) {\n\t\t\t\tflushResolvers.push(...batchFlushResolvers);\n\t\t\t\tbatchFlushResolvers = [];\n\t\t\t}\n\t\t\tif (!flushPromise && overlappingSync) {\n\t\t\t\tflushPromise = new Promise((resolve) => {\n\t\t\t\t\tif (writtenBatchDepth) {\n\t\t\t\t\t\tbatchFlushResolvers.push(resolve);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tflushResolvers.push(resolve);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (writeStatus & WAITING_OPERATION) {\n\t\t\t\t// write thread is waiting\n\t\t\t\twrite(env.address, 0);\n\t\t\t}\n\t\t\tif (outstandingWriteCount > BACKPRESSURE_THRESHOLD && !writeBatchStart) {\n\t\t\t\tif (!backpressureArray)\n\t\t\t\t\tbackpressureArray = new Int32Array(new SharedArrayBuffer(4), 0, 1);\n\t\t\t\tAtomics.wait(\n\t\t\t\t\tbackpressureArray,\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t\tMath.round(outstandingWriteCount / BACKPRESSURE_THRESHOLD),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (startAddress) {\n\t\t\t\tif (eventTurnBatching)\n\t\t\t\t\tstartWriting(); // start writing immediately because this has already been batched/queued\n\t\t\t\telse if (!enqueuedCommit && txnStartThreshold) {\n\t\t\t\t\tenqueuedCommit =\n\t\t\t\t\t\tcommitDelay == 0 && typeof setImmediate != 'undefined'\n\t\t\t\t\t\t\t? setImmediate(() => startWriting())\n\t\t\t\t\t\t\t: setTimeout(() => startWriting(), commitDelay);\n\t\t\t\t} else if (outstandingWriteCount > txnStartThreshold) startWriting();\n\t\t\t}\n\n\t\t\tif ((outstandingWriteCount & 7) === 0) resolveWrites();\n\n\t\t\tif (store.cache) {\n\t\t\t\tresolution.meta = {\n\t\t\t\t\tkey,\n\t\t\t\t\tstore,\n\t\t\t\t\tvalueSize: valueBuffer ? valueBuffer.length : 0,\n\t\t\t\t};\n\t\t\t}\n\t\t\tresolution.valueBuffer = valueBuffer;\n\n\t\t\tif (callback) {\n\t\t\t\tif (callback === IF_EXISTS) ifVersion = IF_EXISTS;\n\t\t\t\telse {\n\t\t\t\t\tlet meta = resolution.meta || (resolution.meta = {});\n\t\t\t\t\tmeta.reject = callback;\n\t\t\t\t\tmeta.resolve = (value) => callback(null, value);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// if it is not conditional because of ifVersion or has any flags that can make the write conditional\n\t\t\tif (ifVersion === undefined && !(flags & 0x22030)) {\n\t\t\t\tif (writtenBatchDepth > 1) {\n\t\t\t\t\tif (!resolution.flag && !store.cache) resolution.flag = NO_RESOLVE;\n\t\t\t\t\treturn PROMISE_SUCCESS; // or return undefined?\n\t\t\t\t}\n\t\t\t\tif (commitPromise) {\n\t\t\t\t\tif (!resolution.flag) resolution.flag = NO_RESOLVE;\n\t\t\t\t} else {\n\t\t\t\t\tcommitPromise = new Promise((resolve, reject) => {\n\t\t\t\t\t\tlet meta = resolution.meta || (resolution.meta = {});\n\t\t\t\t\t\tmeta.resolve = resolve;\n\t\t\t\t\t\tresolve.unconditional = true;\n\t\t\t\t\t\tmeta.reject = reject;\n\t\t\t\t\t});\n\t\t\t\t\tif (separateFlushed)\n\t\t\t\t\t\tcommitPromise.flushed = overlappingSync\n\t\t\t\t\t\t\t? flushPromise\n\t\t\t\t\t\t\t: commitPromise;\n\t\t\t\t}\n\t\t\t\treturn commitPromise;\n\t\t\t}\n\t\t\tlastWritePromise = new Promise((resolve, reject) => {\n\t\t\t\tlet meta = resolution.meta || (resolution.meta = {});\n\t\t\t\tmeta.resolve = resolve;\n\t\t\t\tmeta.reject = reject;\n\t\t\t});\n\t\t\tif (separateFlushed)\n\t\t\t\tlastWritePromise.flushed = overlappingSync\n\t\t\t\t\t? flushPromise\n\t\t\t\t\t: lastWritePromise;\n\t\t\treturn lastWritePromise;\n\t\t};\n\t}\n\tlet committedFlushResolvers,\n\t\tlastSync = Promise.resolve();\n\tfunction startWriting() {\n\t\tif (enqueuedCommit) {\n\t\t\tclearImmediate(enqueuedCommit);\n\t\t\tenqueuedCommit = null;\n\t\t}\n\t\tlet resolvers = flushResolvers;\n\t\tlet start = Date.now();\n\t\tenv.startWriting(startAddress, (status) => {\n\t\t\tif (dynamicBytes.uint32[dynamicBytes.position << 1] & TXN_DELIMITER)\n\t\t\t\tqueueCommitResolution(nextResolution);\n\n\t\t\tresolveWrites(true);\n\t\t\tswitch (status) {\n\t\t\t\tcase 0:\n\t\t\t\t\tfor (let resolver of resolvers) {\n\t\t\t\t\t\tresolver();\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\thasUnresolvedTxns = false;\n\t\t\t\t\texecuteTxnCallbacks();\n\t\t\t\t\treturn hasUnresolvedTxns;\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlmdbError(status);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.error(error);\n\t\t\t\t\t\tif (commitRejectPromise) {\n\t\t\t\t\t\t\tcommitRejectPromise.reject(error);\n\t\t\t\t\t\t\tcommitRejectPromise = null;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tstartAddress = 0;\n\t}\n\n\tfunction queueCommitResolution(resolution) {\n\t\tif (!(resolution.flag & HAS_TXN)) {\n\t\t\tresolution.flag = HAS_TXN;\n\t\t\tif (txnResolution) {\n\t\t\t\ttxnResolution.nextTxn = resolution;\n\t\t\t\t//outstandingWriteCount = 0\n\t\t\t} else txnResolution = resolution;\n\t\t}\n\t}\n\tvar TXN_DONE = TXN_COMMITTED | TXN_FAILED;\n\tfunction resolveWrites(async) {\n\t\t// clean up finished instructions\n\t\tlet instructionStatus;\n\t\twhile (\n\t\t\t(instructionStatus =\n\t\t\t\tunwrittenResolution.uint32[unwrittenResolution.flagPosition]) &\n\t\t\t0x1000000\n\t\t) {\n\t\t\tif (unwrittenResolution.callbacks) {\n\t\t\t\tnextTxnCallbacks.push(unwrittenResolution.callbacks);\n\t\t\t\tunwrittenResolution.callbacks = null;\n\t\t\t}\n\t\t\toutstandingWriteCount--;\n\t\t\tif (unwrittenResolution.flag !== HAS_TXN) {\n\t\t\t\tif (\n\t\t\t\t\tunwrittenResolution.flag === NO_RESOLVE &&\n\t\t\t\t\t!unwrittenResolution.meta\n\t\t\t\t) {\n\t\t\t\t\t// in this case we can completely remove from the linked list, clearing more memory\n\t\t\t\t\tlastPromisedResolution.next = unwrittenResolution =\n\t\t\t\t\t\tunwrittenResolution.next;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tunwrittenResolution.uint32 = null;\n\t\t\t}\n\t\t\tunwrittenResolution.valueBuffer = null;\n\t\t\tunwrittenResolution.flag = instructionStatus;\n\t\t\tlastPromisedResolution = unwrittenResolution;\n\t\t\tunwrittenResolution = unwrittenResolution.next;\n\t\t}\n\t\twhile (\n\t\t\ttxnResolution &&\n\t\t\t(instructionStatus =\n\t\t\t\ttxnResolution.uint32[txnResolution.flagPosition] & TXN_DONE)\n\t\t) {\n\t\t\tif (instructionStatus & TXN_FAILED) rejectCommit();\n\t\t\telse resolveCommit(async);\n\t\t}\n\t}\n\n\tfunction resolveCommit(async) {\n\t\tafterCommit(txnResolution.uint32[txnResolution.flagPosition - 1]);\n\t\tif (async) resetReadTxn();\n\t\telse queueMicrotask(resetReadTxn); // TODO: only do this if there are actually committed writes?\n\t\tdo {\n\t\t\tif (uncommittedResolution.meta && uncommittedResolution.meta.resolve) {\n\t\t\t\tlet resolve = uncommittedResolution.meta.resolve;\n\t\t\t\tif (\n\t\t\t\t\tuncommittedResolution.flag & FAILED_CONDITION &&\n\t\t\t\t\t!resolve.unconditional\n\t\t\t\t)\n\t\t\t\t\tresolve(false);\n\t\t\t\telse resolve(true);\n\t\t\t}\n\t\t} while (\n\t\t\t(uncommittedResolution = uncommittedResolution.next) &&\n\t\t\tuncommittedResolution != txnResolution\n\t\t);\n\t\ttxnResolution = txnResolution.nextTxn;\n\t}\n\tvar commitRejectPromise;\n\tfunction rejectCommit() {\n\t\tafterCommit();\n\t\tif (!commitRejectPromise) {\n\t\t\tlet rejectFunction;\n\t\t\tcommitRejectPromise = new Promise(\n\t\t\t\t(resolve, reject) => (rejectFunction = reject),\n\t\t\t);\n\t\t\tcommitRejectPromise.reject = rejectFunction;\n\t\t}\n\t\tdo {\n\t\t\tif (uncommittedResolution.meta && uncommittedResolution.meta.reject) {\n\t\t\t\tlet flag = uncommittedResolution.flag & 0xf;\n\t\t\t\tlet error = new Error('Commit failed (see commitError for details)');\n\t\t\t\terror.commitError = commitRejectPromise;\n\t\t\t\tuncommittedResolution.meta.reject(error);\n\t\t\t}\n\t\t} while (\n\t\t\t(uncommittedResolution = uncommittedResolution.next) &&\n\t\t\tuncommittedResolution != txnResolution\n\t\t);\n\t\ttxnResolution = txnResolution.nextTxn;\n\t}\n\tfunction atomicStatus(uint32, flagPosition, newStatus) {\n\t\tif (batchDepth) {\n\t\t\t// if we are in a batch, the transaction can't close, so we do the faster,\n\t\t\t// but non-deterministic updates, knowing that the write thread can\n\t\t\t// just poll for the status change if we miss a status update\n\t\t\tlet writeStatus = uint32[flagPosition];\n\t\t\tuint32[flagPosition] = newStatus;\n\t\t\treturn writeStatus;\n\t\t\t//return Atomics.or(uint32, flagPosition, newStatus)\n\t\t} // otherwise the transaction could end at any time and we need to know the\n\t\t// deterministically if it is ending, so we can reset the commit promise\n\t\t// so we use the slower atomic operation\n\t\telse\n\t\t\ttry {\n\t\t\t\treturn Atomics.or(uint32, flagPosition, newStatus);\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error(error);\n\t\t\t\treturn;\n\t\t\t}\n\t}\n\tfunction afterCommit(txnId) {\n\t\tfor (let i = 0, l = afterCommitCallbacks.length; i < l; i++) {\n\t\t\ttry {\n\t\t\t\tafterCommitCallbacks[i]({\n\t\t\t\t\tnext: uncommittedResolution,\n\t\t\t\t\tlast: txnResolution,\n\t\t\t\t\ttxnId,\n\t\t\t\t});\n\t\t\t} catch (error) {\n\t\t\t\tconsole.error('In aftercommit callback', error);\n\t\t\t}\n\t\t}\n\t}\n\tasync function executeTxnCallbacks() {\n\t\tenv.writeTxn = writeTxn = { write: true };\n\t\tnextTxnCallbacks.isExecuting = true;\n\t\tfor (let i = 0; i < nextTxnCallbacks.length; i++) {\n\t\t\tlet txnCallbacks = nextTxnCallbacks[i];\n\t\t\tfor (let j = 0, l = txnCallbacks.length; j < l; j++) {\n\t\t\t\tlet userTxnCallback = txnCallbacks[j];\n\t\t\t\tlet asChild = userTxnCallback.asChild;\n\t\t\t\tif (asChild) {\n\t\t\t\t\tenv.beginTxn(1); // abortable\n\t\t\t\t\tlet parentTxn = writeTxn;\n\t\t\t\t\tenv.writeTxn = writeTxn = { write: true };\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlet result = userTxnCallback.callback();\n\t\t\t\t\t\tif (result && result.then) {\n\t\t\t\t\t\t\thasUnresolvedTxns = true;\n\t\t\t\t\t\t\tawait result;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (result === ABORT) env.abortTxn();\n\t\t\t\t\t\telse env.commitTxn();\n\t\t\t\t\t\tclearWriteTxn(parentTxn);\n\t\t\t\t\t\ttxnCallbacks[j] = result;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tclearWriteTxn(parentTxn);\n\t\t\t\t\t\tenv.abortTxn();\n\t\t\t\t\t\ttxnError(error, txnCallbacks, j);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tlet result = userTxnCallback();\n\t\t\t\t\t\ttxnCallbacks[j] = result;\n\t\t\t\t\t\tif (result && result.then) {\n\t\t\t\t\t\t\thasUnresolvedTxns = true;\n\t\t\t\t\t\t\tawait result;\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\ttxnError(error, txnCallbacks, j);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tnextTxnCallbacks = [];\n\t\tclearWriteTxn(null);\n\t\tif (hasUnresolvedTxns) {\n\t\t\tenv.resumeWriting();\n\t\t}\n\t\tfunction txnError(error, txnCallbacks, i) {\n\t\t\t(txnCallbacks.errors || (txnCallbacks.errors = []))[i] = error;\n\t\t\ttxnCallbacks[i] = CALLBACK_THREW;\n\t\t}\n\t}\n\tfunction finishBatch() {\n\t\tlet bytes = dynamicBytes;\n\t\tlet uint32 = bytes.uint32;\n\t\tlet nextPosition = bytes.position + 1;\n\t\tlet writeStatus;\n\t\tif (nextPosition > newBufferThreshold) {\n\t\t\tallocateInstructionBuffer(nextPosition);\n\t\t\tnextResolution.flagPosition = dynamicBytes.position << 1;\n\t\t\tnextResolution.uint32 = dynamicBytes.uint32;\n\t\t\twriteStatus = atomicStatus(uint32, bytes.position << 1, 2); // atomically write the end block\n\t\t} else {\n\t\t\tuint32[nextPosition << 1] = 0; // clear out the next slot\n\t\t\twriteStatus = atomicStatus(uint32, bytes.position++ << 1, 2); // atomically write the end block\n\t\t\tnextResolution.flagPosition += 2;\n\t\t}\n\t\tif (writeStatus & WAITING_OPERATION) {\n\t\t\twrite(env.address, 0);\n\t\t}\n\t}\n\tfunction clearWriteTxn(parentTxn) {\n\t\t// TODO: We might actually want to track cursors in a write txn and manually\n\t\t// close them.\n\t\tif (writeTxn && writeTxn.refCount > 0) writeTxn.isDone = true;\n\t\tenv.writeTxn = writeTxn = parentTxn || null;\n\t}\n\tObject.assign(LMDBStore.prototype, {\n\t\tput(key, value, versionOrOptions, ifVersion) {\n\t\t\tlet callback,\n\t\t\t\tflags = 15,\n\t\t\t\ttype = typeof versionOrOptions;\n\t\t\tif (type == 'object' && versionOrOptions) {\n\t\t\t\tif (versionOrOptions.noOverwrite) flags |= 0x10;\n\t\t\t\tif (versionOrOptions.noDupData) flags |= 0x20;\n\t\t\t\tif (versionOrOptions.instructedWrite) flags |= 0x2000;\n\t\t\t\tif (versionOrOptions.append) flags |= 0x20000;\n\t\t\t\tif (versionOrOptions.ifVersion != undefined)\n\t\t\t\t\tifVersion = versionOrOptions.ifVersion;\n\t\t\t\tversionOrOptions = versionOrOptions.version;\n\t\t\t\tif (typeof ifVersion == 'function') callback = ifVersion;\n\t\t\t} else if (type == 'function') {\n\t\t\t\tcallback = versionOrOptions;\n\t\t\t}\n\t\t\treturn writeInstructions(\n\t\t\t\tflags,\n\t\t\t\tthis,\n\t\t\t\tkey,\n\t\t\t\tvalue,\n\t\t\t\tthis.useVersions ? versionOrOptions || 0 : undefined,\n\t\t\t\tifVersion,\n\t\t\t)(callback);\n\t\t},\n\t\tremove(key, ifVersionOrValue, callback) {\n\t\t\tlet flags = 13;\n\t\t\tlet ifVersion, value;\n\t\t\tif (ifVersionOrValue !== undefined) {\n\t\t\t\tif (typeof ifVersionOrValue == 'function') callback = ifVersionOrValue;\n\t\t\t\telse if (ifVersionOrValue === IF_EXISTS && !callback)\n\t\t\t\t\t// we have a handler for IF_EXISTS in the callback handler for remove\n\t\t\t\t\tcallback = ifVersionOrValue;\n\t\t\t\telse if (this.useVersions) ifVersion = ifVersionOrValue;\n\t\t\t\telse {\n\t\t\t\t\tflags = 14;\n\t\t\t\t\tvalue = ifVersionOrValue;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn writeInstructions(\n\t\t\t\tflags,\n\t\t\t\tthis,\n\t\t\t\tkey,\n\t\t\t\tvalue,\n\t\t\t\tundefined,\n\t\t\t\tifVersion,\n\t\t\t)(callback);\n\t\t},\n\t\tdel(key, options, callback) {\n\t\t\treturn this.remove(key, options, callback);\n\t\t},\n\t\tifNoExists(key, callback) {\n\t\t\treturn this.ifVersion(key, null, callback);\n\t\t},\n\t\tifVersion(key, version, callback, options) {\n\t\t\tif (!callback) {\n\t\t\t\treturn new Batch((operations, callback) => {\n\t\t\t\t\tlet promise = this.ifVersion(key, version, operations, options);\n\t\t\t\t\tif (callback) promise.then(callback);\n\t\t\t\t\treturn promise;\n\t\t\t\t});\n\t\t\t}\n\t\t\tif (writeTxn) {\n\t\t\t\tif (version === undefined || this.doesExist(key, version)) {\n\t\t\t\t\tcallback();\n\t\t\t\t\treturn SYNC_PROMISE_SUCCESS;\n\t\t\t\t}\n\t\t\t\treturn SYNC_PROMISE_FAIL;\n\t\t\t}\n\t\t\tlet flags = key === undefined || version === undefined ? 1 : 4;\n\t\t\tif (options?.ifLessThan) flags |= CONDITIONAL_VERSION_LESS_THAN;\n\t\t\tif (options?.allowNotFound) flags |= CONDITIONAL_ALLOW_NOTFOUND;\n\t\t\tlet finishStartWrite = writeInstructions(\n\t\t\t\tflags,\n\t\t\t\tthis,\n\t\t\t\tkey,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tversion,\n\t\t\t);\n\t\t\tlet promise;\n\t\t\tbatchDepth += 2;\n\t\t\tif (batchDepth > 2) promise = finishStartWrite();\n\t\t\telse {\n\t\t\t\twriteBatchStart = () => {\n\t\t\t\t\tpromise = finishStartWrite();\n\t\t\t\t};\n\t\t\t\toutstandingBatchCount = 0;\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tif (typeof callback === 'function') {\n\t\t\t\t\tcallback();\n\t\t\t\t} else {\n\t\t\t\t\tfor (let i = 0, l = callback.length; i < l; i++) {\n\t\t\t\t\t\tlet operation = callback[i];\n\t\t\t\t\t\tthis[operation.type](operation.key, operation.value);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} finally {\n\t\t\t\tif (!promise) {\n\t\t\t\t\tfinishBatch();\n\t\t\t\t\tbatchDepth -= 2;\n\t\t\t\t\tpromise = finishStartWrite(); // finish write once all the operations have been written (and it hasn't been written prematurely)\n\t\t\t\t\twriteBatchStart = null;\n\t\t\t\t} else {\n\t\t\t\t\tbatchDepth -= 2;\n\t\t\t\t\tfinishBatch();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn promise;\n\t\t},\n\t\tbatch(callbackOrOperations) {\n\t\t\treturn this.ifVersion(undefined, undefined, callbackOrOperations);\n\t\t},\n\t\tdrop(callback) {\n\t\t\treturn writeInstructions(\n\t\t\t\t1024 + 12,\n\t\t\t\tthis,\n\t\t\t\tBuffer.from([]),\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t)(callback);\n\t\t},\n\t\tclearAsync(callback) {\n\t\t\tif (this.encoder) {\n\t\t\t\tif (this.encoder.clearSharedData) this.encoder.clearSharedData();\n\t\t\t\telse if (this.encoder.structures) this.encoder.structures = [];\n\t\t\t}\n\t\t\treturn writeInstructions(\n\t\t\t\t12,\n\t\t\t\tthis,\n\t\t\t\tBuffer.from([]),\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t\tundefined,\n\t\t\t)(callback);\n\t\t},\n\t\t_triggerError() {\n\t\t\tfinishBatch();\n\t\t},\n\n\t\tputSync(key, value, versionOrOptions, ifVersion) {\n\t\t\tif (writeTxn)\n\t\t\t\treturn (\n\t\t\t\t\tthis.put(key, value, versionOrOptions, ifVersion) ===\n\t\t\t\t\tSYNC_PROMISE_SUCCESS\n\t\t\t\t);\n\t\t\telse\n\t\t\t\treturn this.transactionSync(\n\t\t\t\t\t() =>\n\t\t\t\t\t\tthis.put(key, value, versionOrOptions, ifVersion) ===\n\t\t\t\t\t\tSYNC_PROMISE_SUCCESS,\n\t\t\t\t\toverlappingSync ? 0x10002 : 2,\n\t\t\t\t); // non-abortable, async flush\n\t\t},\n\t\tremoveSync(key, ifVersionOrValue) {\n\t\t\tif (writeTxn)\n\t\t\t\treturn this.remove(key, ifVersionOrValue) === SYNC_PROMISE_SUCCESS;\n\t\t\telse\n\t\t\t\treturn this.transactionSync(\n\t\t\t\t\t() => this.remove(key, ifVersionOrValue) === SYNC_PROMISE_SUCCESS,\n\t\t\t\t\toverlappingSync ? 0x10002 : 2,\n\t\t\t\t); // non-abortable, async flush\n\t\t},\n\t\ttransaction(callback) {\n\t\t\tif (writeTxn && !nextTxnCallbacks.isExecuting) {\n\t\t\t\t// already nested in a transaction, just execute and return\n\t\t\t\treturn callback();\n\t\t\t}\n\t\t\treturn this.transactionAsync(callback);\n\t\t},\n\t\tchildTransaction(callback) {\n\t\t\tif (useWritemap)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'Child transactions are not supported in writemap mode',\n\t\t\t\t);\n\t\t\tif (writeTxn) {\n\t\t\t\tlet parentTxn = writeTxn;\n\t\t\t\tlet thisTxn = (env.writeTxn = writeTxn = { write: true });\n\t\t\t\tenv.beginTxn(1); // abortable\n\t\t\t\tlet callbackDone, finishTxn;\n\t\t\t\ttry {\n\t\t\t\t\treturn (writeTxn.childResults = when(\n\t\t\t\t\t\tcallback(),\n\t\t\t\t\t\t(finishTxn = (result) => {\n\t\t\t\t\t\t\tif (writeTxn !== thisTxn)\n\t\t\t\t\t\t\t\t// need to wait for child txn to finish asynchronously\n\t\t\t\t\t\t\t\treturn writeTxn.childResults.then(() => finishTxn(result));\n\t\t\t\t\t\t\tcallbackDone = true;\n\t\t\t\t\t\t\tif (result === ABORT) env.abortTxn();\n\t\t\t\t\t\t\telse env.commitTxn();\n\t\t\t\t\t\t\tclearWriteTxn(parentTxn);\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t}),\n\t\t\t\t\t\t(error) => {\n\t\t\t\t\t\t\tenv.abortTxn();\n\t\t\t\t\t\t\tclearWriteTxn(parentTxn);\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t},\n\t\t\t\t\t));\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!callbackDone) env.abortTxn();\n\t\t\t\t\tclearWriteTxn(parentTxn);\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn this.transactionAsync(callback, true);\n\t\t},\n\t\ttransactionAsync(callback, asChild) {\n\t\t\tlet txnIndex;\n\t\t\tlet txnCallbacks;\n\t\t\tif (lastQueuedResolution.callbacks) {\n\t\t\t\ttxnCallbacks = lastQueuedResolution.callbacks;\n\t\t\t\ttxnIndex =\n\t\t\t\t\ttxnCallbacks.push(asChild ? { callback, asChild } : callback) - 1;\n\t\t\t} else if (nextTxnCallbacks.isExecuting) {\n\t\t\t\ttxnCallbacks = [asChild ? { callback, asChild } : callback];\n\t\t\t\ttxnCallbacks.results = commitPromise;\n\t\t\t\tnextTxnCallbacks.push(txnCallbacks);\n\t\t\t\ttxnIndex = 0;\n\t\t\t} else {\n\t\t\t\tif (writeTxn)\n\t\t\t\t\tthrow new Error('Can not enqueue transaction during write txn');\n\t\t\t\tlet finishWrite = writeInstructions(\n\t\t\t\t\t8 | (this.strictAsyncOrder ? 0x100000 : 0),\n\t\t\t\t\tthis,\n\t\t\t\t);\n\t\t\t\ttxnCallbacks = [asChild ? { callback, asChild } : callback];\n\t\t\t\tlastQueuedResolution.callbacks = txnCallbacks;\n\t\t\t\tlastQueuedResolution.id = Math.random();\n\t\t\t\ttxnCallbacks.results = finishWrite();\n\t\t\t\ttxnIndex = 0;\n\t\t\t}\n\t\t\treturn txnCallbacks.results.then((results) => {\n\t\t\t\tlet result = txnCallbacks[txnIndex];\n\t\t\t\tif (result === CALLBACK_THREW) throw txnCallbacks.errors[txnIndex];\n\t\t\t\treturn result;\n\t\t\t});\n\t\t},\n\t\ttransactionSync(callback, flags) {\n\t\t\tif (writeTxn) {\n\t\t\t\tif (!useWritemap && (flags == undefined || flags & 1))\n\t\t\t\t\t// can't use child transactions in write maps\n\t\t\t\t\t// already nested in a transaction, execute as child transaction (if possible) and return\n\t\t\t\t\treturn this.childTransaction(callback);\n\t\t\t\tlet result = callback(); // else just run in current transaction\n\t\t\t\tif (result == ABORT && !abortedNonChildTransactionWarn) {\n\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t'Can not abort a transaction inside another transaction with ' +\n\t\t\t\t\t\t\t(this.cache ? 'caching enabled' : 'useWritemap enabled'),\n\t\t\t\t\t);\n\t\t\t\t\tabortedNonChildTransactionWarn = true;\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\t\t\tlet callbackDone, finishTxn;\n\t\t\tthis.transactions++;\n\t\t\tif (!env.address)\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'The database has been closed and you can not transact on it',\n\t\t\t\t);\n\t\t\tenv.beginTxn(flags == undefined ? 3 : flags);\n\t\t\tlet thisTxn = (writeTxn = env.writeTxn = { write: true });\n\t\t\ttry {\n\t\t\t\tthis.emit('begin-transaction');\n\t\t\t\treturn (writeTxn.childResults = when(\n\t\t\t\t\tcallback(),\n\t\t\t\t\t(finishTxn = (result) => {\n\t\t\t\t\t\tif (writeTxn !== thisTxn)\n\t\t\t\t\t\t\t// need to wait for child txn to finish asynchronously\n\t\t\t\t\t\t\treturn writeTxn.childResults.then(() => finishTxn(result));\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tcallbackDone = true;\n\t\t\t\t\t\t\tif (result === ABORT) env.abortTxn();\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tenv.commitTxn();\n\t\t\t\t\t\t\t\tresetReadTxn();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn result;\n\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\tclearWriteTxn(null);\n\t\t\t\t\t\t}\n\t\t\t\t\t}),\n\t\t\t\t\t(error) => {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tenv.abortTxn();\n\t\t\t\t\t\t} catch (e) {}\n\t\t\t\t\t\tclearWriteTxn(null);\n\t\t\t\t\t\tthrow error;\n\t\t\t\t\t},\n\t\t\t\t));\n\t\t\t} catch (error) {\n\t\t\t\tif (!callbackDone)\n\t\t\t\t\ttry {\n\t\t\t\t\t\tenv.abortTxn();\n\t\t\t\t\t} catch (e) {}\n\t\t\t\tclearWriteTxn(null);\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t},\n\t\tgetWriteTxnId() {\n\t\t\treturn env.getWriteTxnId();\n\t\t},\n\t\ttransactionSyncStart(callback) {\n\t\t\treturn this.transactionSync(callback, 0);\n\t\t},\n\t\t// make the db a thenable/promise-like for when the last commit is committed\n\t\tcommitted: (committed = {\n\t\t\tthen(onfulfilled, onrejected) {\n\t\t\t\tif (commitPromise) return commitPromise.then(onfulfilled, onrejected);\n\t\t\t\tif (lastWritePromise)\n\t\t\t\t\t// always resolve to true\n\t\t\t\t\treturn lastWritePromise.then(() => onfulfilled(true), onrejected);\n\t\t\t\treturn SYNC_PROMISE_SUCCESS.then(onfulfilled, onrejected);\n\t\t\t},\n\t\t}),\n\t\tflushed: {\n\t\t\t// make this a thenable for when the commit is flushed to disk\n\t\t\tthen(onfulfilled, onrejected) {\n\t\t\t\tif (flushPromise) flushPromise.hasCallbacks = true;\n\t\t\t\treturn Promise.all([flushPromise || committed, lastSyncTxnFlush]).then(\n\t\t\t\t\tonfulfilled,\n\t\t\t\t\tonrejected,\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t\t_endWrites(resolvedPromise, resolvedSyncPromise) {\n\t\t\tthis.put =\n\t\t\t\tthis.remove =\n\t\t\t\tthis.del =\n\t\t\t\tthis.batch =\n\t\t\t\tthis.removeSync =\n\t\t\t\tthis.putSync =\n\t\t\t\tthis.transactionAsync =\n\t\t\t\tthis.drop =\n\t\t\t\tthis.clearAsync =\n\t\t\t\t\t() => {\n\t\t\t\t\t\tthrow new Error('Database is closed');\n\t\t\t\t\t};\n\t\t\t// wait for all txns to finish, checking again after the current txn is done\n\t\t\tlet finalPromise = flushPromise || commitPromise || lastWritePromise;\n\t\t\tif (flushPromise) flushPromise.hasCallbacks = true;\n\t\t\tlet finalSyncPromise = lastSyncTxnFlush;\n\t\t\tif (\n\t\t\t\t(finalPromise && resolvedPromise != finalPromise) ||\n\t\t\t\t(finalSyncPromise && resolvedSyncPromise != finalSyncPromise)\n\t\t\t) {\n\t\t\t\treturn Promise.all([finalPromise, finalSyncPromise]).then(\n\t\t\t\t\t() => this._endWrites(finalPromise, finalSyncPromise),\n\t\t\t\t\t() => this._endWrites(finalPromise, finalSyncPromise),\n\t\t\t\t);\n\t\t\t}\n\t\t\tObject.defineProperty(env, 'sync', { value: null });\n\t\t},\n\t\ton(event, callback) {\n\t\t\tif (event == 'beforecommit') {\n\t\t\t\teventTurnBatching = true;\n\t\t\t\tbeforeCommitCallbacks.push(callback);\n\t\t\t} else if (event == 'aftercommit') afterCommitCallbacks.push(callback);\n\t\t\telse if (event == 'committed') {\n\t\t\t\tthis.getUserSharedBuffer('__committed__', new ArrayBuffer(0), {\n\t\t\t\t\tenvKey: true,\n\t\t\t\t\tcallback,\n\t\t\t\t});\n\t\t\t} else super.on(event, callback);\n\t\t},\n\t});\n}\n\nclass Batch extends Array {\n\tconstructor(callback) {\n\t\tsuper();\n\t\tthis.callback = callback;\n\t}\n\tput(key, value) {\n\t\tthis.push({ type: 'put', key, value });\n\t}\n\tdel(key) {\n\t\tthis.push({ type: 'del', key });\n\t}\n\tclear() {\n\t\tthis.length = 0;\n\t}\n\twrite(callback) {\n\t\treturn this.callback(this, callback);\n\t}\n}\nexport function asBinary(buffer) {\n\treturn {\n\t\t['\\x10binary-data\\x02']: buffer,\n\t};\n}\n","export const SKIP = {};\nconst DONE = {\n\tvalue: null,\n\tdone: true,\n};\nconst RETURN_DONE = {\n\t// we allow this one to be mutated\n\tvalue: null,\n\tdone: true,\n};\nif (!Symbol.asyncIterator) {\n\tSymbol.asyncIterator = Symbol.for('Symbol.asyncIterator');\n}\nconst NO_OPTIONS = {};\n\nexport class RangeIterable {\n\tconstructor(sourceArray) {\n\t\tif (sourceArray) {\n\t\t\tthis.iterate = sourceArray[Symbol.iterator].bind(sourceArray);\n\t\t}\n\t}\n\tmap(func) {\n\t\tlet source = this;\n\t\tlet iterable = new RangeIterable();\n\t\titerable.iterate = (options = NO_OPTIONS) => {\n\t\t\tconst { async } = options;\n\t\t\tlet iterator =\n\t\t\t\tsource[async ? Symbol.asyncIterator : Symbol.iterator](options);\n\t\t\tif (!async) source.isSync = true;\n\t\t\tlet i = -1;\n\t\t\treturn {\n\t\t\t\tnext(resolvedResult) {\n\t\t\t\t\tlet result;\n\t\t\t\t\tdo {\n\t\t\t\t\t\tlet iteratorResult;\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tif (resolvedResult) {\n\t\t\t\t\t\t\t\titeratorResult = resolvedResult;\n\t\t\t\t\t\t\t\tresolvedResult = null; // don't go in this branch on next iteration\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\ti++;\n\t\t\t\t\t\t\t\titeratorResult = iterator.next();\n\t\t\t\t\t\t\t\tif (iteratorResult.then) {\n\t\t\t\t\t\t\t\t\tif (!async) {\n\t\t\t\t\t\t\t\t\t\tthis.throw(\n\t\t\t\t\t\t\t\t\t\t\tnew Error(\n\t\t\t\t\t\t\t\t\t\t\t\t'Can not synchronously iterate with promises as iterator results',\n\t\t\t\t\t\t\t\t\t\t\t),\n\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treturn iteratorResult.then(\n\t\t\t\t\t\t\t\t\t\t(iteratorResult) => this.next(iteratorResult),\n\t\t\t\t\t\t\t\t\t\t(error) => {\n\t\t\t\t\t\t\t\t\t\t\treturn this.throw(error);\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (iteratorResult.done === true) {\n\t\t\t\t\t\t\t\tthis.done = true;\n\t\t\t\t\t\t\t\tif (iterable.onDone) iterable.onDone();\n\t\t\t\t\t\t\t\treturn iteratorResult;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\tresult = func.call(source, iteratorResult.value, i);\n\t\t\t\t\t\t\t\tif (result && result.then && async) {\n\t\t\t\t\t\t\t\t\t// if async, wait for promise to resolve before returning iterator result\n\t\t\t\t\t\t\t\t\treturn result.then(\n\t\t\t\t\t\t\t\t\t\t(result) =>\n\t\t\t\t\t\t\t\t\t\t\tresult === SKIP\n\t\t\t\t\t\t\t\t\t\t\t\t? this.next()\n\t\t\t\t\t\t\t\t\t\t\t\t: {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue: result,\n\t\t\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t(error) => {\n\t\t\t\t\t\t\t\t\t\t\tif (options.continueOnRecoverableError)\n\t\t\t\t\t\t\t\t\t\t\t\terror.continueIteration = true;\n\t\t\t\t\t\t\t\t\t\t\treturn this.throw(error);\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t// if the error came from the user function, we can potentially mark it for continuing iteration\n\t\t\t\t\t\t\t\tif (options.continueOnRecoverableError)\n\t\t\t\t\t\t\t\t\terror.continueIteration = true;\n\t\t\t\t\t\t\t\tthrow error; // throw to next catch to handle\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\tif (iterable.handleError) {\n\t\t\t\t\t\t\t\t// if we have handleError, we can use it to further handle errors\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tresult = iterable.handleError(error, i);\n\t\t\t\t\t\t\t\t} catch (error2) {\n\t\t\t\t\t\t\t\t\treturn this.throw(error2);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else return this.throw(error);\n\t\t\t\t\t\t}\n\t\t\t\t\t} while (result === SKIP);\n\t\t\t\t\tif (result === DONE) {\n\t\t\t\t\t\treturn this.return();\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\tvalue: result,\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t\treturn(value) {\n\t\t\t\t\tif (!this.done) {\n\t\t\t\t\t\tRETURN_DONE.value = value;\n\t\t\t\t\t\tthis.done = true;\n\t\t\t\t\t\tif (iterable.onDone) iterable.onDone();\n\t\t\t\t\t\titerator.return();\n\t\t\t\t\t}\n\t\t\t\t\treturn RETURN_DONE;\n\t\t\t\t},\n\t\t\t\tthrow(error) {\n\t\t\t\t\tif (error.continueIteration) {\n\t\t\t\t\t\t// if it's a recoverable error, we can return or throw without closing the iterator\n\t\t\t\t\t\tif (iterable.returnRecoverableErrors)\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t\tvalue: iterable.returnRecoverableErrors(error),\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\t// if this throws, we need to go back to closing the iterator\n\t\t\t\t\t\t\t\tthis.return();\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\tif (options.continueOnRecoverableError) throw error; // throw without closing iterator\n\t\t\t\t\t}\n\t\t\t\t\t// else we are done with the iterator (and can throw)\n\t\t\t\t\tthis.return();\n\t\t\t\t\tthrow error;\n\t\t\t\t},\n\t\t\t};\n\t\t};\n\t\treturn iterable;\n\t}\n\t[Symbol.asyncIterator](options) {\n\t\tif (options) options = { ...options, async: true };\n\t\telse options = { async: true };\n\t\treturn (this.iterator = this.iterate(options));\n\t}\n\t[Symbol.iterator](options) {\n\t\treturn (this.iterator = this.iterate(options));\n\t}\n\tfilter(func) {\n\t\tlet iterable = this.map((element) => {\n\t\t\tlet result = func(element);\n\t\t\t// handle promise\n\t\t\tif (result?.then)\n\t\t\t\treturn result.then((result) => (result ? element : SKIP));\n\t\t\telse return result ? element : SKIP;\n\t\t});\n\t\tlet iterate = it