@ohos-ports/lmdb 2.5.3 → 2.8.5-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +72 -28
  3. package/SECURITY.md +12 -12
  4. package/bin/download-prebuilds.js +15 -0
  5. package/binding.gyp +115 -111
  6. package/build/Release/lmdb.node +0 -0
  7. package/caching.js +212 -156
  8. package/dependencies/lmdb/libraries/liblmdb/COPYRIGHT +20 -20
  9. package/dependencies/lmdb/libraries/liblmdb/Doxyfile +1631 -1631
  10. package/dependencies/lmdb/libraries/liblmdb/LICENSE +47 -47
  11. package/dependencies/lmdb/libraries/liblmdb/Makefile +136 -136
  12. package/dependencies/lmdb/libraries/liblmdb/chacha8.c +183 -183
  13. package/dependencies/lmdb/libraries/liblmdb/chacha8.h +14 -14
  14. package/dependencies/lmdb/libraries/liblmdb/crypto.c +121 -121
  15. package/dependencies/lmdb/libraries/liblmdb/intro.doc +192 -192
  16. package/dependencies/lmdb/libraries/liblmdb/lmdb.h +28 -3
  17. package/dependencies/lmdb/libraries/liblmdb/mdb.c +157 -31
  18. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.1 +74 -74
  19. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.c +106 -106
  20. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.1 +53 -53
  21. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.c +154 -154
  22. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.1 +94 -94
  23. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.c +333 -333
  24. package/dependencies/lmdb/libraries/liblmdb/mdb_load.1 +97 -97
  25. package/dependencies/lmdb/libraries/liblmdb/mdb_load.c +530 -530
  26. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.1 +83 -83
  27. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.c +276 -276
  28. package/dependencies/lmdb/libraries/liblmdb/midl-sparse.c +452 -0
  29. package/dependencies/lmdb/libraries/liblmdb/midl.c +452 -452
  30. package/dependencies/lmdb/libraries/liblmdb/midl.h +208 -208
  31. package/dependencies/lmdb/libraries/liblmdb/module.c +101 -101
  32. package/dependencies/lmdb/libraries/liblmdb/module.h +16 -16
  33. package/dependencies/lmdb/libraries/liblmdb/mtest.c +178 -178
  34. package/dependencies/lmdb/libraries/liblmdb/mtest2.c +124 -124
  35. package/dependencies/lmdb/libraries/liblmdb/mtest3.c +133 -133
  36. package/dependencies/lmdb/libraries/liblmdb/mtest4.c +168 -168
  37. package/dependencies/lmdb/libraries/liblmdb/mtest5.c +135 -135
  38. package/dependencies/lmdb/libraries/liblmdb/mtest6.c +141 -141
  39. package/dependencies/lmdb/libraries/liblmdb/mtest_enc.c +190 -190
  40. package/dependencies/lmdb/libraries/liblmdb/mtest_enc2.c +189 -189
  41. package/dependencies/lmdb/libraries/liblmdb/mtest_remap.c +177 -177
  42. package/dependencies/lmdb/libraries/liblmdb/sample-bdb.txt +73 -73
  43. package/dependencies/lmdb/libraries/liblmdb/sample-mdb.txt +62 -62
  44. package/dependencies/lmdb/libraries/liblmdb/tooltag +27 -27
  45. package/dependencies/lmdb-data-v1/libraries/liblmdb/CHANGES +266 -0
  46. package/dependencies/lmdb-data-v1/libraries/liblmdb/COPYRIGHT +20 -20
  47. package/dependencies/lmdb-data-v1/libraries/liblmdb/Doxyfile +1631 -1631
  48. package/dependencies/lmdb-data-v1/libraries/liblmdb/LICENSE +47 -47
  49. package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -192
  50. package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1608 -1653
  51. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +10320 -11359
  52. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +55 -61
  53. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +82 -84
  54. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +75 -81
  55. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.c +319 -319
  56. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.1 +84 -84
  57. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.c +496 -492
  58. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +64 -70
  59. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +263 -264
  60. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +359 -421
  61. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +186 -200
  62. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest.c +177 -177
  63. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest2.c +124 -124
  64. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest3.c +133 -133
  65. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest4.c +168 -168
  66. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest5.c +135 -135
  67. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest6.c +141 -141
  68. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-bdb.txt +73 -73
  69. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-mdb.txt +62 -62
  70. package/dependencies/lmdb-data-v1/libraries/liblmdb/tooltag +22 -27
  71. package/dependencies/lz4/LICENSE +11 -11
  72. package/dependencies/lz4/lib/LICENSE +24 -24
  73. package/dependencies/lz4/lib/Makefile +225 -0
  74. package/dependencies/lz4/lib/README.md +169 -137
  75. package/dependencies/lz4/lib/dll/example/Makefile +63 -0
  76. package/dependencies/lz4/lib/dll/example/README.md +69 -69
  77. package/dependencies/lz4/lib/dll/liblz4.def +62 -62
  78. package/dependencies/lz4/lib/liblz4-dll.rc.in +35 -35
  79. package/dependencies/lz4/lib/liblz4.pc.in +14 -14
  80. package/dependencies/lz4/lib/lz4.c +2722 -2495
  81. package/dependencies/lz4/lib/lz4.h +842 -774
  82. package/dependencies/lz4/lib/lz4file.c +311 -0
  83. package/dependencies/lz4/lib/lz4file.h +93 -0
  84. package/dependencies/lz4/lib/lz4frame.c +2078 -1899
  85. package/dependencies/lz4/lib/lz4frame.h +692 -623
  86. package/dependencies/lz4/lib/lz4frame_static.h +47 -47
  87. package/dependencies/lz4/lib/lz4hc.c +1631 -1615
  88. package/dependencies/lz4/lib/lz4hc.h +413 -413
  89. package/dependencies/lz4/lib/xxhash.c +1030 -1030
  90. package/dependencies/lz4/lib/xxhash.h +328 -328
  91. package/dependencies/v8/v8-fast-api-calls-v16.h +790 -790
  92. package/dependencies/v8/v8-fast-api-calls.h +784 -888
  93. package/dist/index.cjs +1050 -584
  94. package/dist/index.cjs.map +1 -1
  95. package/index.d.cts +420 -0
  96. package/index.d.ts +450 -385
  97. package/index.js +38 -37
  98. package/keys.js +116 -102
  99. package/level.js +26 -26
  100. package/native.js +8 -5
  101. package/node-index.js +21 -17
  102. package/open.js +51 -17
  103. package/package.json +63 -21
  104. package/read.js +338 -95
  105. package/src/compression.cpp +12 -9
  106. package/src/cursor.cpp +82 -31
  107. package/src/dbi.cpp +78 -40
  108. package/src/env.cpp +336 -68
  109. package/src/lmdb-js.cpp +64 -64
  110. package/src/lmdb-js.h +83 -49
  111. package/src/misc.cpp +297 -28
  112. package/src/ordered-binary.cpp +46 -46
  113. package/src/txn.cpp +210 -257
  114. package/src/v8-functions.cpp +170 -166
  115. package/src/windows.c +34 -34
  116. package/src/writer.cpp +169 -84
  117. package/util/RangeIterable.js +112 -17
  118. package/util/set-optional-deps.cjs +14 -10
  119. package/util/when.js +8 -8
  120. package/write.js +145 -121
  121. package/dependencies/lmdb-data-v1/libraries/liblmdb/Makefile +0 -118
  122. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.1 +0 -40
  123. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.c +0 -135
  124. package/rollup.config.js +0 -12
package/dist/index.cjs CHANGED
@@ -6,7 +6,6 @@ var module$1 = require('module');
6
6
  var pathModule = require('path');
7
7
  var url = require('url');
8
8
  var loadNAPI = require('node-gyp-build-optional-packages');
9
- var v8 = require('v8');
10
9
  var events = require('events');
11
10
  var os$1 = require('os');
12
11
  var fs$1 = require('fs');
@@ -39,14 +38,12 @@ var loadNAPI__default = /*#__PURE__*/_interopDefaultLegacy(loadNAPI);
39
38
  var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs$1);
40
39
  var orderedBinary__namespace = /*#__PURE__*/_interopNamespace(orderedBinary$1);
41
40
 
42
- let Env, Txn, Dbi, Compression, Cursor, getAddress; exports.clearKeptObjects = void 0; let setGlobalBuffer, arch, fs, os, tmpdir, lmdbError, path, EventEmitter, orderedBinary, MsgpackrEncoder, WeakLRUCache, getByBinary, detachBuffer, write, position, iterate, prefetch, resetTxn, getCurrentValue, getCurrentShared, getStringByBinary, getSharedByBinary, compress;
43
-
41
+ let Env, Txn, Dbi, Compression, Cursor, getAddress, getBufferAddress; exports.clearKeptObjects = void 0; let globalBuffer, setGlobalBuffer, arch, fs, os, tmpdir, lmdbError, path, EventEmitter, orderedBinary, MsgpackrEncoder, WeakLRUCache, getByBinary, startRead, setReadCallback, write, position, iterate, prefetch, resetTxn, getCurrentValue, getStringByBinary, getSharedBuffer, compress;
44
42
  path = pathModule__default["default"];
45
- let dirName = (typeof __dirname == 'string' ? __dirname : // for bun, which doesn't have fileURLToPath
46
- pathModule.dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('index.cjs', document.baseURI).href))))).replace(/dist$/, ''); // for node, which doesn't have __dirname in ESM
43
+ let dirName = pathModule.dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href)))).replace(/dist$/, '');
47
44
  let nativeAddon = loadNAPI__default["default"](dirName);
48
45
 
49
- if (process.isBun) {
46
+ if (process.isBun && false) {
50
47
  const { linkSymbols, FFIType } = require('bun:ffi');
51
48
  let lmdbLib = linkSymbols({
52
49
  getByBinary: {
@@ -87,19 +84,24 @@ function setNativeFunctions(externals) {
87
84
  Dbi = externals.Dbi;
88
85
  Compression = externals.Compression;
89
86
  getAddress = externals.getAddress;
87
+ getBufferAddress = externals.getBufferAddress;
90
88
  externals.createBufferForAddress;
91
89
  exports.clearKeptObjects = externals.clearKeptObjects || function() {};
92
90
  getByBinary = externals.getByBinary;
93
- detachBuffer = externals.detachBuffer;
91
+ externals.detachBuffer;
92
+ startRead = externals.startRead;
93
+ setReadCallback = externals.setReadCallback;
94
94
  setGlobalBuffer = externals.setGlobalBuffer;
95
+ globalBuffer = externals.globalBuffer;
96
+ getSharedBuffer = externals.getSharedBuffer;
95
97
  prefetch = externals.prefetch;
96
98
  iterate = externals.iterate;
97
99
  position = externals.position;
98
100
  resetTxn = externals.resetTxn;
99
101
  getCurrentValue = externals.getCurrentValue;
100
- getCurrentShared = externals.getCurrentShared;
102
+ externals.getCurrentShared;
101
103
  getStringByBinary = externals.getStringByBinary;
102
- getSharedByBinary = externals.getSharedByBinary;
104
+ externals.getSharedByBinary;
103
105
  write = externals.write;
104
106
  compress = externals.compress;
105
107
  Cursor = externals.Cursor;
@@ -119,36 +121,38 @@ function setExternals(externals) {
119
121
  externals.onExit;
120
122
  }
121
123
 
122
- function when(promise, callback, errback) {
123
- if (promise && promise.then) {
124
- return errback ?
125
- promise.then(callback, errback) :
126
- promise.then(callback);
127
- }
128
- return callback(promise);
124
+ function when(promise, callback, errback) {
125
+ if (promise && promise.then) {
126
+ return errback ?
127
+ promise.then(callback, errback) :
128
+ promise.then(callback);
129
+ }
130
+ return callback(promise);
129
131
  }
130
132
 
131
133
  var backpressureArray;
132
134
 
133
135
  const WAITING_OPERATION = 0x2000000;
134
- const BACKPRESSURE_THRESHOLD = 100000;
136
+ const BACKPRESSURE_THRESHOLD = 300000;
135
137
  const TXN_DELIMITER = 0x8000000;
136
138
  const TXN_COMMITTED = 0x10000000;
137
139
  const TXN_FLUSHED = 0x20000000;
138
140
  const TXN_FAILED = 0x40000000;
139
141
  const FAILED_CONDITION = 0x4000000;
140
- const REUSE_BUFFER_MODE = 512;
142
+ const REUSE_BUFFER_MODE$1 = 512;
141
143
  const RESET_BUFFER_MODE = 1024;
142
144
  const NO_RESOLVE = 16;
143
145
  const HAS_TXN = 8;
146
+ const CONDITIONAL_VERSION_LESS_THAN = 0x800;
147
+ const CONDITIONAL_ALLOW_NOTFOUND = 0x800;
144
148
 
145
149
  const SYNC_PROMISE_SUCCESS = Promise.resolve(true);
146
150
  const SYNC_PROMISE_FAIL = Promise.resolve(false);
147
151
  SYNC_PROMISE_SUCCESS.isSync = true;
148
152
  SYNC_PROMISE_FAIL.isSync = true;
149
153
  const PROMISE_SUCCESS = Promise.resolve(true);
150
- const ABORT = {};
151
- const IF_EXISTS = 3.542694326329068e-103;
154
+ const ABORT = 4.452694326329068e-106; // random/unguessable numbers, which work across module/versions and native
155
+ const IF_EXISTS$1 = 3.542694326329068e-103;
152
156
  const CALLBACK_THREW = {};
153
157
  const LocalSharedArrayBuffer = typeof Deno != 'undefined' ? ArrayBuffer : SharedArrayBuffer; // Deno can't handle SharedArrayBuffer as an FFI argument due to https://github.com/denoland/deno/issues/12678
154
158
  const ByteArray = typeof Buffer != 'undefined' ? function(buffer) { return Buffer.from(buffer) } : Uint8Array;
@@ -159,18 +163,23 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
159
163
  eventTurnBatching, txnStartThreshold, batchStartThreshold, overlappingSync, commitDelay, separateFlushed, maxFlushDelay }) {
160
164
  // stands for write instructions
161
165
  var dynamicBytes;
162
- function allocateInstructionBuffer() {
166
+ function allocateInstructionBuffer(lastPosition) {
163
167
  // Must use a shared buffer on older node in order to use Atomics, and it is also more correct since we are
164
168
  // indeed accessing and modifying it from another thread (in C). However, Deno can't handle it for
165
169
  // FFI so aliased above
166
170
  let buffer = new LocalSharedArrayBuffer(WRITE_BUFFER_SIZE);
171
+ let lastBytes = dynamicBytes;
167
172
  dynamicBytes = new ByteArray(buffer);
168
173
  let uint32 = dynamicBytes.uint32 = new Uint32Array(buffer, 0, WRITE_BUFFER_SIZE >> 2);
169
- uint32[0] = 0;
174
+ uint32[2] = 0;
170
175
  dynamicBytes.float64 = new Float64Array(buffer, 0, WRITE_BUFFER_SIZE >> 3);
171
- buffer.address = getAddress(dynamicBytes);
176
+ buffer.address = getBufferAddress(dynamicBytes);
172
177
  uint32.address = buffer.address + uint32.byteOffset;
173
- dynamicBytes.position = 0;
178
+ dynamicBytes.position = 1; // we start at position 1 to save space for writing the txn id before the txn delimiter
179
+ if (lastPosition) {
180
+ lastBytes.float64[lastPosition + 1] = dynamicBytes.uint32.address + (dynamicBytes.position << 3);
181
+ lastBytes.uint32[lastPosition << 1] = 3; // pointer instruction
182
+ }
174
183
  return dynamicBytes;
175
184
  }
176
185
  var newBufferThreshold = (WRITE_BUFFER_SIZE - maxKeySize - 64) >> 3; // need to reserve more room if we do inline values
@@ -180,7 +189,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
180
189
  var committed;
181
190
  var abortedNonChildTransactionWarn;
182
191
  var nextTxnCallbacks = [];
183
- var commitPromise, flushPromise, flushResolvers = [];
192
+ var commitPromise, flushPromise, flushResolvers = [], batchFlushResolvers = [];
184
193
  commitDelay = commitDelay || 0;
185
194
  eventTurnBatching = eventTurnBatching === false ? false : true;
186
195
  var enqueuedCommit;
@@ -189,23 +198,25 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
189
198
  var enqueuedEventTurnBatch;
190
199
  var batchDepth = 0;
191
200
  var lastWritePromise;
192
- var writeBatchStart, outstandingBatchCount, lastSyncTxnFlush, lastFlushTimeout, lastFlushCallback;
201
+ var writeBatchStart, outstandingBatchCount, lastSyncTxnFlush;
202
+ var hasUnresolvedTxns;
193
203
  txnStartThreshold = txnStartThreshold || 5;
194
204
  batchStartThreshold = batchStartThreshold || 1000;
195
205
  maxFlushDelay = maxFlushDelay || 500;
196
206
 
197
207
  allocateInstructionBuffer();
198
- dynamicBytes.uint32[0] = TXN_DELIMITER | TXN_COMMITTED | TXN_FLUSHED;
199
- var txnResolution, lastQueuedResolution, nextResolution = {
200
- uint32: dynamicBytes.uint32, flagPosition: 0, flag: 0, valueBuffer: null, next: null, meta: null };
208
+ dynamicBytes.uint32[2] = TXN_DELIMITER | TXN_COMMITTED | TXN_FLUSHED;
209
+ var txnResolution, nextResolution = {
210
+ uint32: dynamicBytes.uint32, flagPosition: 2, flag: 0, valueBuffer: null, next: null, meta: null };
201
211
  var uncommittedResolution = {
202
- uint32: null, flagPosition: 0, flag: 0, valueBuffer: null, next: nextResolution, meta: null };
212
+ uint32: null, flagPosition: 2, flag: 0, valueBuffer: null, next: nextResolution, meta: null };
203
213
  var unwrittenResolution = nextResolution;
204
214
  var lastPromisedResolution = uncommittedResolution;
215
+ var lastQueuedResolution = uncommittedResolution;
205
216
  function writeInstructions(flags, store, key, value, version, ifVersion) {
206
217
  let writeStatus;
207
218
  let targetBytes, position, encoder;
208
- let valueBuffer, valueSize, valueBufferStart;
219
+ let valueSize, valueBuffer, valueBufferStart;
209
220
  if (flags & 2) {
210
221
  // encode first in case we have to write a shared structure
211
222
  encoder = store.encoder;
@@ -213,7 +224,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
213
224
  valueBuffer = value['\x10binary-data\x02'];
214
225
  else if (encoder) {
215
226
  if (encoder.copyBuffers) // use this as indicator for support buffer reuse for now
216
- valueBuffer = encoder.encode(value, REUSE_BUFFER_MODE | (writeTxn ? RESET_BUFFER_MODE : 0)); // 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
227
+ valueBuffer = encoder.encode(value, REUSE_BUFFER_MODE$1 | (writeTxn ? RESET_BUFFER_MODE : 0)); // 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
217
228
  else { // various other encoders, including JSON.stringify, that might serialize to a string
218
229
  valueBuffer = encoder.encode(value);
219
230
  if (typeof valueBuffer == 'string')
@@ -242,7 +253,11 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
242
253
  enqueuedEventTurnBatch = queueTask(() => {
243
254
  try {
244
255
  for (let i = 0, l = beforeCommitCallbacks.length; i < l; i++) {
245
- beforeCommitCallbacks[i]();
256
+ try {
257
+ beforeCommitCallbacks[i]();
258
+ } catch(error) {
259
+ console.error('In beforecommit callback', error);
260
+ }
246
261
  }
247
262
  } catch(error) {
248
263
  console.error(error);
@@ -276,7 +291,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
276
291
  try {
277
292
  endPosition = store.writeKey(key, targetBytes, keyStartPosition);
278
293
  if (!(keyStartPosition < endPosition) && (flags & 0xf) != 12)
279
- throw new Error('Invalid key or zero length key is not allowed in LMDB')
294
+ throw new Error('Invalid key or zero length key is not allowed in LMDB ' + key)
280
295
  } catch(error) {
281
296
  targetBytes.fill(0, keyStartPosition);
282
297
  if (error.name == 'RangeError')
@@ -295,14 +310,14 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
295
310
  if (valueBufferStart > -1) { // if we have buffers with start/end position
296
311
  // record pointer to value buffer
297
312
  float64[position] = (valueBuffer.address ||
298
- (valueBuffer.address = getAddress(valueBuffer))) + valueBufferStart;
313
+ (valueBuffer.address = getAddress(valueBuffer.buffer))) + valueBufferStart;
299
314
  mustCompress = valueBuffer[valueBufferStart] >= 250; // this is the compression indicator, so we must compress
300
315
  } else {
301
316
  let valueArrayBuffer = valueBuffer.buffer;
302
317
  // record pointer to value buffer
303
318
  let address = (valueArrayBuffer.address ||
304
319
  (valueBuffer.length === 0 ? 0 : // externally allocated buffers of zero-length with the same non-null-pointer can crash node, #161
305
- (valueArrayBuffer.address = (getAddress(valueBuffer) - valueBuffer.byteOffset))))
320
+ valueArrayBuffer.address = getAddress(valueArrayBuffer)))
306
321
  + valueBuffer.byteOffset;
307
322
  if (address <= 0 && valueBuffer.length > 0)
308
323
  console.error('Supplied buffer had an invalid address', address);
@@ -347,12 +362,8 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
347
362
  // uint32[position << 1] = 0 // clear out the next slot
348
363
  let nextUint32;
349
364
  if (position > newBufferThreshold) {
350
- // make new buffer and make pointer to it
351
- let lastPosition = position;
352
- targetBytes = allocateInstructionBuffer();
365
+ targetBytes = allocateInstructionBuffer(position);
353
366
  position = targetBytes.position;
354
- float64[lastPosition + 1] = targetBytes.uint32.address + position;
355
- uint32[lastPosition << 1] = 3; // pointer instruction
356
367
  nextUint32 = targetBytes.uint32;
357
368
  } else
358
369
  nextUint32 = uint32;
@@ -366,6 +377,8 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
366
377
  next: null,
367
378
  meta: null,
368
379
  };
380
+ lastQueuedResolution = resolution;
381
+
369
382
  let writtenBatchDepth = batchDepth;
370
383
 
371
384
  return (callback) => {
@@ -393,13 +406,25 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
393
406
  if (writeStatus & TXN_DELIMITER) {
394
407
  commitPromise = null; // TODO: Don't reset these if this comes from the batch start operation on an event turn batch
395
408
  flushPromise = null;
409
+ flushResolvers = [];
396
410
  queueCommitResolution(resolution);
397
411
  if (!startAddress) {
398
412
  startAddress = uint32.address + (flagPosition << 2);
399
413
  }
400
414
  }
401
- if (!flushPromise && overlappingSync)
402
- flushPromise = new Promise(resolve => flushResolvers.push(resolve));
415
+ if (!writtenBatchDepth && batchFlushResolvers.length > 0) {
416
+ flushResolvers.push(...batchFlushResolvers);
417
+ batchFlushResolvers = [];
418
+ }
419
+ if (!flushPromise && overlappingSync) {
420
+ flushPromise = new Promise(resolve => {
421
+ if (writtenBatchDepth) {
422
+ batchFlushResolvers.push(resolve);
423
+ } else {
424
+ flushResolvers.push(resolve);
425
+ }
426
+ });
427
+ }
403
428
  if (writeStatus & WAITING_OPERATION) { // write thread is waiting
404
429
  write(env.address, 0);
405
430
  }
@@ -428,11 +453,10 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
428
453
  };
429
454
  }
430
455
  resolution.valueBuffer = valueBuffer;
431
- lastQueuedResolution = resolution;
432
456
 
433
457
  if (callback) {
434
- if (callback === IF_EXISTS)
435
- ifVersion = IF_EXISTS;
458
+ if (callback === IF_EXISTS$1)
459
+ ifVersion = IF_EXISTS$1;
436
460
  else {
437
461
  let meta = resolution.meta || (resolution.meta = {});
438
462
  meta.reject = callback;
@@ -471,15 +495,13 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
471
495
  return lastWritePromise;
472
496
  };
473
497
  }
474
- let committedFlushResolvers, lastSync = Promise.resolve();
498
+ Promise.resolve();
475
499
  function startWriting() {
476
500
  if (enqueuedCommit) {
477
501
  clearImmediate(enqueuedCommit);
478
502
  enqueuedCommit = null;
479
503
  }
480
504
  let resolvers = flushResolvers;
481
- flushResolvers = [];
482
- let start = Date.now();
483
505
  env.startWriting(startAddress, (status) => {
484
506
  if (dynamicBytes.uint32[dynamicBytes.position << 1] & TXN_DELIMITER)
485
507
  queueCommitResolution(nextResolution);
@@ -487,52 +509,30 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
487
509
  resolveWrites(true);
488
510
  switch (status) {
489
511
  case 0:
490
- if (resolvers.length > 0) {
491
- let delay = Date.now() - start;
492
- scheduleFlush(resolvers, Math.min((flushPromise && flushPromise.hasCallbacks ? delay >> 1 : delay) + 1, maxFlushDelay));
512
+ for (let resolver of resolvers) {
513
+ resolver();
493
514
  }
515
+ break;
494
516
  case 1:
495
- break;
517
+ break;
496
518
  case 2:
519
+ hasUnresolvedTxns = false;
497
520
  executeTxnCallbacks();
498
- break;
521
+ return hasUnresolvedTxns;
499
522
  default:
500
- console.error(status);
501
- if (commitRejectPromise) {
502
- commitRejectPromise.reject(status);
503
- commitRejectPromise = null;
504
- }
523
+ try {
524
+ lmdbError(status);
525
+ } catch(error) {
526
+ console.error(error);
527
+ if (commitRejectPromise) {
528
+ commitRejectPromise.reject(error);
529
+ commitRejectPromise = null;
530
+ }
531
+ }
505
532
  }
506
533
  });
507
534
  startAddress = 0;
508
535
  }
509
- function scheduleFlush(resolvers, delay) {
510
- if (committedFlushResolvers)
511
- committedFlushResolvers.push(...resolvers);
512
- else {
513
- committedFlushResolvers = resolvers;
514
- lastFlushTimeout = setTimeout(lastFlushCallback = () => {
515
- lastFlushTimeout = null;
516
- lastSync.then(() => {
517
- let resolvers = committedFlushResolvers || [];
518
- committedFlushResolvers = null;
519
- lastSync = new Promise((resolve) => {
520
- env.sync(() => {
521
- for (let i = 0; i < resolvers.length; i++)
522
- resolvers[i]();
523
- resolve();
524
- });
525
- });
526
- });
527
- }, delay || 0);
528
- }
529
- }
530
- function expediteFlush() {
531
- if (lastFlushTimeout) {
532
- clearTimeout(lastFlushTimeout);
533
- lastFlushCallback();
534
- }
535
- }
536
536
 
537
537
  function queueCommitResolution(resolution) {
538
538
  if (!(resolution.flag & HAS_TXN)) {
@@ -557,7 +557,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
557
557
  }
558
558
  outstandingWriteCount--;
559
559
  if (unwrittenResolution.flag !== HAS_TXN) {
560
- if (unwrittenResolution.flag === NO_RESOLVE && !unwrittenResolution.store) {
560
+ if (unwrittenResolution.flag === NO_RESOLVE && !unwrittenResolution.meta) {
561
561
  // in this case we can completely remove from the linked list, clearing more memory
562
562
  lastPromisedResolution.next = unwrittenResolution = unwrittenResolution.next;
563
563
  continue;
@@ -579,7 +579,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
579
579
  }
580
580
 
581
581
  function resolveCommit(async) {
582
- afterCommit();
582
+ afterCommit(txnResolution.uint32[txnResolution.flagPosition - 1]);
583
583
  if (async)
584
584
  resetReadTxn();
585
585
  else
@@ -625,35 +625,38 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
625
625
  } else // otherwise the transaction could end at any time and we need to know the
626
626
  // deterministically if it is ending, so we can reset the commit promise
627
627
  // so we use the slower atomic operation
628
- return Atomics.or(uint32, flagPosition, newStatus);
628
+ try {
629
+ return Atomics.or(uint32, flagPosition, newStatus);
630
+ } catch(error) {
631
+ console.error(error);
632
+ return;
633
+ }
629
634
  }
630
- function afterCommit() {
635
+ function afterCommit(txnId) {
631
636
  for (let i = 0, l = afterCommitCallbacks.length; i < l; i++) {
632
- afterCommitCallbacks[i]({ next: uncommittedResolution, last: unwrittenResolution});
637
+ try {
638
+ afterCommitCallbacks[i]({next: uncommittedResolution, last: txnResolution, txnId});
639
+ } catch(error) {
640
+ console.error('In aftercommit callback', error);
641
+ }
633
642
  }
634
643
  }
635
644
  async function executeTxnCallbacks() {
636
645
  env.writeTxn = writeTxn = { write: true };
637
- //this.emit('begin-transaction');
638
- let promises;
639
- let txnCallbacks;
640
- for (let i = 0, l = nextTxnCallbacks.length; i < l; i++) {
641
- txnCallbacks = nextTxnCallbacks[i];
642
- for (let i = 0, l = txnCallbacks.length; i < l; i++) {
643
- let userTxnCallback = txnCallbacks[i];
646
+ nextTxnCallbacks.isExecuting = true;
647
+ for (let i = 0; i < nextTxnCallbacks.length; i++) {
648
+ let txnCallbacks = nextTxnCallbacks[i];
649
+ for (let j = 0, l = txnCallbacks.length; j < l; j++) {
650
+ let userTxnCallback = txnCallbacks[j];
644
651
  let asChild = userTxnCallback.asChild;
645
652
  if (asChild) {
646
- if (promises) {
647
- // must complete any outstanding transactions before proceeding
648
- await Promise.all(promises);
649
- promises = null;
650
- }
651
653
  env.beginTxn(1); // abortable
652
654
  let parentTxn = writeTxn;
653
655
  env.writeTxn = writeTxn = { write: true };
654
656
  try {
655
657
  let result = userTxnCallback.callback();
656
658
  if (result && result.then) {
659
+ hasUnresolvedTxns = true;
657
660
  await result;
658
661
  }
659
662
  if (result === ABORT)
@@ -661,33 +664,32 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
661
664
  else
662
665
  env.commitTxn();
663
666
  clearWriteTxn(parentTxn);
664
- txnCallbacks[i] = result;
667
+ txnCallbacks[j] = result;
665
668
  } catch(error) {
666
669
  clearWriteTxn(parentTxn);
667
670
  env.abortTxn();
668
- txnError(error, i);
671
+ txnError(error, txnCallbacks, j);
669
672
  }
670
673
  } else {
671
674
  try {
672
675
  let result = userTxnCallback();
673
- txnCallbacks[i] = result;
676
+ txnCallbacks[j] = result;
674
677
  if (result && result.then) {
675
- if (!promises)
676
- promises = [];
677
- promises.push(result.catch(() => {}));
678
+ hasUnresolvedTxns = true;
679
+ await result;
678
680
  }
679
681
  } catch(error) {
680
- txnError(error, i);
682
+ txnError(error, txnCallbacks, j);
681
683
  }
682
684
  }
683
685
  }
684
686
  }
685
687
  nextTxnCallbacks = [];
686
- if (promises) { // finish any outstanding commit functions
687
- await Promise.all(promises);
688
- }
689
688
  clearWriteTxn(null);
690
- function txnError(error, i) {
689
+ if (hasUnresolvedTxns) {
690
+ env.resumeWriting();
691
+ }
692
+ function txnError(error, txnCallbacks, i) {
691
693
  (txnCallbacks.errors || (txnCallbacks.errors = []))[i] = error;
692
694
  txnCallbacks[i] = CALLBACK_THREW;
693
695
  }
@@ -699,18 +701,23 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
699
701
  if (writeStatus & WAITING_OPERATION) {
700
702
  write(env.address, 0);
701
703
  }
704
+ if (dynamicBytes.position > newBufferThreshold) {
705
+ allocateInstructionBuffer(dynamicBytes.position);
706
+ nextResolution.flagPosition = dynamicBytes.position << 1;
707
+ nextResolution.uint32 = dynamicBytes.uint32;
708
+ }
702
709
  }
703
710
  function clearWriteTxn(parentTxn) {
704
711
  // TODO: We might actually want to track cursors in a write txn and manually
705
712
  // close them.
706
- if (writeTxn.cursorCount > 0)
713
+ if (writeTxn && writeTxn.refCount > 0)
707
714
  writeTxn.isDone = true;
708
715
  env.writeTxn = writeTxn = parentTxn || null;
709
716
  }
710
717
  Object.assign(LMDBStore.prototype, {
711
718
  put(key, value, versionOrOptions, ifVersion) {
712
719
  let callback, flags = 15, type = typeof versionOrOptions;
713
- if (type == 'object') {
720
+ if (type == 'object' && versionOrOptions) {
714
721
  if (versionOrOptions.noOverwrite)
715
722
  flags |= 0x10;
716
723
  if (versionOrOptions.noDupData)
@@ -718,7 +725,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
718
725
  if (versionOrOptions.append)
719
726
  flags |= 0x20000;
720
727
  if (versionOrOptions.ifVersion != undefined)
721
- ifVersion = versionsOrOptions.ifVersion;
728
+ ifVersion = versionOrOptions.ifVersion;
722
729
  versionOrOptions = versionOrOptions.version;
723
730
  if (typeof ifVersion == 'function')
724
731
  callback = ifVersion;
@@ -733,7 +740,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
733
740
  if (ifVersionOrValue !== undefined) {
734
741
  if (typeof ifVersionOrValue == 'function')
735
742
  callback = ifVersionOrValue;
736
- else if (ifVersionOrValue === IF_EXISTS && !callback)
743
+ else if (ifVersionOrValue === IF_EXISTS$1 && !callback)
737
744
  // we have a handler for IF_EXISTS in the callback handler for remove
738
745
  callback = ifVersionOrValue;
739
746
  else if (this.useVersions)
@@ -751,11 +758,10 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
751
758
  ifNoExists(key, callback) {
752
759
  return this.ifVersion(key, null, callback);
753
760
  },
754
-
755
- ifVersion(key, version, callback) {
761
+ ifVersion(key, version, callback, options) {
756
762
  if (!callback) {
757
763
  return new Batch((operations, callback) => {
758
- let promise = this.ifVersion(key, version, operations);
764
+ let promise = this.ifVersion(key, version, operations, options);
759
765
  if (callback)
760
766
  promise.then(callback);
761
767
  return promise;
@@ -768,7 +774,12 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
768
774
  }
769
775
  return SYNC_PROMISE_FAIL;
770
776
  }
771
- let finishStartWrite = writeInstructions(key === undefined || version === undefined ? 1 : 4, this, key, undefined, undefined, version);
777
+ let flags = key === undefined || version === undefined ? 1 : 4;
778
+ if (options?.ifLessThan)
779
+ flags |= CONDITIONAL_VERSION_LESS_THAN;
780
+ if (options?.allowNotFound)
781
+ flags |= CONDITIONAL_ALLOW_NOTFOUND;
782
+ let finishStartWrite = writeInstructions(flags, this, key, undefined, undefined, version);
772
783
  let promise;
773
784
  batchDepth += 2;
774
785
  if (batchDepth > 2)
@@ -805,7 +816,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
805
816
  return this.ifVersion(undefined, undefined, callbackOrOperations);
806
817
  },
807
818
  drop(callback) {
808
- return writeInstructions(1024 + 12, this, undefined, undefined, undefined, undefined)(callback);
819
+ return writeInstructions(1024 + 12, this, Buffer.from([]), undefined, undefined, undefined)(callback);
809
820
  },
810
821
  clearAsync(callback) {
811
822
  if (this.encoder) {
@@ -814,7 +825,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
814
825
  else if (this.encoder.structures)
815
826
  this.encoder.structures = [];
816
827
  }
817
- return writeInstructions(12, this, undefined, undefined, undefined, undefined)(callback);
828
+ return writeInstructions(12, this, Buffer.from([]), undefined, undefined, undefined)(callback);
818
829
  },
819
830
  _triggerError() {
820
831
  finishBatch();
@@ -822,20 +833,20 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
822
833
 
823
834
  putSync(key, value, versionOrOptions, ifVersion) {
824
835
  if (writeTxn)
825
- return this.put(key, value, versionOrOptions, ifVersion);
836
+ return this.put(key, value, versionOrOptions, ifVersion) === SYNC_PROMISE_SUCCESS;
826
837
  else
827
838
  return this.transactionSync(() =>
828
- this.put(key, value, versionOrOptions, ifVersion) == SYNC_PROMISE_SUCCESS, overlappingSync? 0x10002 : 2); // non-abortable, async flush
839
+ this.put(key, value, versionOrOptions, ifVersion) === SYNC_PROMISE_SUCCESS, overlappingSync? 0x10002 : 2); // non-abortable, async flush
829
840
  },
830
841
  removeSync(key, ifVersionOrValue) {
831
842
  if (writeTxn)
832
- return this.remove(key, ifVersionOrValue);
843
+ return this.remove(key, ifVersionOrValue) === SYNC_PROMISE_SUCCESS;
833
844
  else
834
845
  return this.transactionSync(() =>
835
- this.remove(key, ifVersionOrValue) == SYNC_PROMISE_SUCCESS, overlappingSync? 0x10002 : 2); // non-abortable, async flush
846
+ this.remove(key, ifVersionOrValue) === SYNC_PROMISE_SUCCESS, overlappingSync? 0x10002 : 2); // non-abortable, async flush
836
847
  },
837
848
  transaction(callback) {
838
- if (writeTxn) {
849
+ if (writeTxn && !nextTxnCallbacks.isExecuting) {
839
850
  // already nested in a transaction, just execute and return
840
851
  return callback();
841
852
  }
@@ -846,11 +857,13 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
846
857
  throw new Error('Child transactions are not supported in writemap mode');
847
858
  if (writeTxn) {
848
859
  let parentTxn = writeTxn;
849
- env.writeTxn = writeTxn = { write: true };
860
+ let thisTxn = env.writeTxn = writeTxn = { write: true };
850
861
  env.beginTxn(1); // abortable
851
- let callbackDone;
862
+ let callbackDone, finishTxn;
852
863
  try {
853
- return when(callback(), (result) => {
864
+ return writeTxn.childResults = when(callback(), finishTxn = (result) => {
865
+ if (writeTxn !== thisTxn) // need to wait for child txn to finish asynchronously
866
+ return writeTxn.childResults.then(() => finishTxn(result));
854
867
  callbackDone = true;
855
868
  if (result === ABORT)
856
869
  env.abortTxn();
@@ -875,14 +888,23 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
875
888
  transactionAsync(callback, asChild) {
876
889
  let txnIndex;
877
890
  let txnCallbacks;
878
- if (!nextResolution.callbacks) {
891
+ if (lastQueuedResolution.callbacks) {
892
+ txnCallbacks = lastQueuedResolution.callbacks;
893
+ txnIndex = txnCallbacks.push(asChild ? { callback, asChild } : callback) - 1;
894
+ } else if (nextTxnCallbacks.isExecuting) {
879
895
  txnCallbacks = [asChild ? { callback, asChild } : callback];
880
- nextResolution.callbacks = txnCallbacks;
881
- txnCallbacks.results = writeInstructions(8 | (this.strictAsyncOrder ? 0x100000 : 0), this)();
896
+ txnCallbacks.results = commitPromise;
897
+ nextTxnCallbacks.push(txnCallbacks);
882
898
  txnIndex = 0;
883
899
  } else {
884
- txnCallbacks = lastQueuedResolution.callbacks;
885
- txnIndex = txnCallbacks.push(asChild ? { callback, asChild } : callback) - 1;
900
+ if (writeTxn)
901
+ throw new Error('Can not enqueue transaction during write txn');
902
+ let finishWrite = writeInstructions(8 | (this.strictAsyncOrder ? 0x100000 : 0), this);
903
+ txnCallbacks = [asChild ? { callback, asChild } : callback];
904
+ lastQueuedResolution.callbacks = txnCallbacks;
905
+ lastQueuedResolution.id = Math.random();
906
+ txnCallbacks.results = finishWrite();
907
+ txnIndex = 0;
886
908
  }
887
909
  return txnCallbacks.results.then((results) => {
888
910
  let result = txnCallbacks[txnIndex];
@@ -893,7 +915,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
893
915
  },
894
916
  transactionSync(callback, flags) {
895
917
  if (writeTxn) {
896
- if (!useWritemap && !this.isCaching) // can't use child transactions in write maps or caching stores
918
+ if (!useWritemap && (flags == undefined || (flags & 1))) // can't use child transactions in write maps
897
919
  // already nested in a transaction, execute as child transaction (if possible) and return
898
920
  return this.childTransaction(callback);
899
921
  let result = callback(); // else just run in current transaction
@@ -903,13 +925,17 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
903
925
  }
904
926
  return result;
905
927
  }
906
- let callbackDone;
928
+ let callbackDone, finishTxn;
929
+ this.transactions++;
930
+ if (!env.address)
931
+ throw new Error('The database has been closed and you can not transact on it');
932
+ env.beginTxn(flags == undefined ? 3 : flags);
933
+ let thisTxn = writeTxn = env.writeTxn = { write: true };
907
934
  try {
908
- this.transactions++;
909
- env.beginTxn(flags == undefined ? 3 : flags);
910
- writeTxn = env.writeTxn = { write: true };
911
935
  this.emit('begin-transaction');
912
- return when(callback(), (result) => {
936
+ return writeTxn.childResults = when(callback(), finishTxn = (result) => {
937
+ if (writeTxn !== thisTxn) // need to wait for child txn to finish asynchronously
938
+ return writeTxn.childResults.then(() => finishTxn(result));
913
939
  try {
914
940
  callbackDone = true;
915
941
  if (result === ABORT)
@@ -917,8 +943,6 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
917
943
  else {
918
944
  env.commitTxn();
919
945
  resetReadTxn();
920
- if ((flags & 0x10000) && overlappingSync) // if it is no-sync in overlapping-sync mode, need to schedule flush for it to be marked as persisted
921
- lastSyncTxnFlush = new Promise(resolve => scheduleFlush([resolve]));
922
946
  }
923
947
  return result;
924
948
  } finally {
@@ -963,11 +987,9 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
963
987
  let finalPromise = flushPromise || commitPromise || lastWritePromise;
964
988
  if (flushPromise)
965
989
  flushPromise.hasCallbacks = true;
966
- if (lastFlushTimeout)
967
- expediteFlush();
968
990
  let finalSyncPromise = lastSyncTxnFlush;
969
991
  if (finalPromise && resolvedPromise != finalPromise ||
970
- finalSyncPromise && resolvedSyncPromise != finalSyncPromise) {
992
+ finalSyncPromise ) {
971
993
  return Promise.all([finalPromise, finalSyncPromise]).then(() => this._endWrites(finalPromise, finalSyncPromise), () => this._endWrites(finalPromise, finalSyncPromise));
972
994
  }
973
995
  Object.defineProperty(env, 'sync', { value: null });
@@ -1008,160 +1030,11 @@ function asBinary(buffer) {
1008
1030
  };
1009
1031
  }
1010
1032
 
1011
- let getLastVersion$1;
1012
- const mapGet = Map.prototype.get;
1013
- const CachingStore = (Store, env) => {
1014
- let childTxnChanges;
1015
- return class extends Store {
1016
- constructor(dbName, options) {
1017
- super(dbName, options);
1018
- if (!env.cacheCommitter) {
1019
- env.cacheCommitter = true;
1020
- this.on('aftercommit', ({ next, last }) => {
1021
- do {
1022
- let meta = next.meta;
1023
- let store = meta && meta.store;
1024
- if (store) {
1025
- if (next.flag & FAILED_CONDITION)
1026
- store.cache.delete(meta.key); // just delete it from the map
1027
- else {
1028
- let expirationPriority = meta.valueSize >> 10;
1029
- let cache = store.cache;
1030
- let entry = mapGet.call(cache, meta.key);
1031
- if (entry)
1032
- cache.used(entry, expirationPriority + 4); // this will enter it into the LRFU (with a little lower priority than a read)
1033
- }
1034
- }
1035
- } while (next != last && (next = next.next))
1036
- });
1037
- }
1038
- this.db.cachingDb = this;
1039
- if (options.cache.clearKeptInterval)
1040
- options.cache.clearKeptObjects = exports.clearKeptObjects;
1041
- this.cache = new WeakLRUCache(options.cache);
1042
- }
1043
- get isCaching() {
1044
- return true
1045
- }
1046
- get(id, cacheMode) {
1047
- let value = this.cache.getValue(id);
1048
- if (value !== undefined)
1049
- return value;
1050
- value = super.get(id);
1051
- if (value && typeof value === 'object' && !cacheMode && typeof id !== 'object') {
1052
- let entry = this.cache.setValue(id, value, this.lastSize >> 10);
1053
- if (this.useVersions) {
1054
- entry.version = getLastVersion$1();
1055
- }
1056
- }
1057
- return value;
1058
- }
1059
- getEntry(id, cacheMode) {
1060
- let entry = this.cache.get(id);
1061
- if (entry)
1062
- return entry;
1063
- let value = super.get(id);
1064
- if (value === undefined)
1065
- return;
1066
- if (value && typeof value === 'object' && !cacheMode && typeof id !== 'object') {
1067
- entry = this.cache.setValue(id, value, this.lastSize >> 10);
1068
- } else {
1069
- entry = { value };
1070
- }
1071
- if (this.useVersions) {
1072
- entry.version = getLastVersion$1();
1073
- }
1074
- return entry;
1075
- }
1076
- putEntry(id, entry, ifVersion) {
1077
- let result = super.put(id, entry.value, entry.version, ifVersion);
1078
- if (typeof id === 'object')
1079
- return result;
1080
- if (result && result.then)
1081
- this.cache.setManually(id, entry); // set manually so we can keep it pinned in memory until it is committed
1082
- else // sync operation, immediately add to cache
1083
- this.cache.set(id, entry);
1084
- }
1085
- put(id, value, version, ifVersion) {
1086
- let result = super.put(id, value, version, ifVersion);
1087
- if (typeof id !== 'object') {
1088
- if (value && value['\x10binary-data\x02']) {
1089
- // don't cache binary data, since it will be decoded on get
1090
- this.cache.delete(id);
1091
- return result;
1092
- }
1093
- // sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed
1094
- let entry = this.cache.setValue(id, value, !result || result.isSync ? 0 : -1);
1095
- if (childTxnChanges)
1096
- childTxnChanges.add(id);
1097
- if (version !== undefined)
1098
- entry.version = typeof version === 'object' ? version.version : version;
1099
- }
1100
- return result;
1101
- }
1102
- putSync(id, value, version, ifVersion) {
1103
- if (id !== 'object') {
1104
- // sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed
1105
- if (value && typeof value === 'object') {
1106
- let entry = this.cache.setValue(id, value);
1107
- if (childTxnChanges)
1108
- childTxnChanges.add(id);
1109
- if (version !== undefined) {
1110
- entry.version = typeof version === 'object' ? version.version : version;
1111
- }
1112
- } else // it is possible that a value used to exist here
1113
- this.cache.delete(id);
1114
- }
1115
- return super.putSync(id, value, version, ifVersion);
1116
- }
1117
- remove(id, ifVersion) {
1118
- this.cache.delete(id);
1119
- return super.remove(id, ifVersion);
1120
- }
1121
- removeSync(id, ifVersion) {
1122
- this.cache.delete(id);
1123
- return super.removeSync(id, ifVersion);
1124
- }
1125
- clearAsync(callback) {
1126
- this.cache.clear();
1127
- return super.clearAsync(callback);
1128
- }
1129
- clearSync() {
1130
- this.cache.clear();
1131
- super.clearSync();
1132
- }
1133
- childTransaction(callback) {
1134
- return super.childTransaction(() => {
1135
- let cache = this.cache;
1136
- let previousChanges = childTxnChanges;
1137
- try {
1138
- childTxnChanges = new Set();
1139
- return when(callback(), (result) => {
1140
- if (result === ABORT)
1141
- return abort();
1142
- childTxnChanges = previousChanges;
1143
- return result;
1144
- }, abort);
1145
- } catch(error) {
1146
- abort(error);
1147
- }
1148
- function abort(error) {
1149
- // if the transaction was aborted, remove all affected entries from cache
1150
- for (let id of childTxnChanges)
1151
- cache.delete(id);
1152
- childTxnChanges = previousChanges;
1153
- if (error)
1154
- throw error;
1155
- }
1156
- });
1157
- }
1158
- };
1159
- };
1160
- function setGetLastVersion(get) {
1161
- getLastVersion$1 = get;
1162
- }
1163
-
1164
1033
  const SKIP = {};
1034
+ const DONE = {
1035
+ value: null,
1036
+ done: true,
1037
+ };
1165
1038
  if (!Symbol.asyncIterator) {
1166
1039
  Symbol.asyncIterator = Symbol.for('Symbol.asyncIterator');
1167
1040
  }
@@ -1174,9 +1047,10 @@ class RangeIterable {
1174
1047
  }
1175
1048
  map(func) {
1176
1049
  let source = this;
1177
- let result = new RangeIterable();
1178
- result.iterate = (async) => {
1050
+ let iterable = new RangeIterable();
1051
+ iterable.iterate = (async) => {
1179
1052
  let iterator = source[Symbol.iterator](async);
1053
+ let i = 0;
1180
1054
  return {
1181
1055
  next(resolvedResult) {
1182
1056
  let result;
@@ -1193,31 +1067,38 @@ class RangeIterable {
1193
1067
  }
1194
1068
  if (iteratorResult.done === true) {
1195
1069
  this.done = true;
1070
+ if (iterable.onDone) iterable.onDone();
1196
1071
  return iteratorResult;
1197
1072
  }
1198
- result = func(iteratorResult.value);
1073
+ result = func(iteratorResult.value, i++);
1199
1074
  if (result && result.then) {
1200
1075
  return result.then(result =>
1201
- result == SKIP ?
1076
+ result === SKIP ?
1202
1077
  this.next() :
1203
1078
  {
1204
1079
  value: result
1205
1080
  });
1206
1081
  }
1207
- } while(result == SKIP)
1082
+ } while(result === SKIP);
1083
+ if (result === DONE) {
1084
+ if (iterable.onDone) iterable.onDone();
1085
+ return result;
1086
+ }
1208
1087
  return {
1209
1088
  value: result
1210
1089
  };
1211
1090
  },
1212
1091
  return() {
1092
+ if (iterable.onDone) iterable.onDone();
1213
1093
  return iterator.return();
1214
1094
  },
1215
1095
  throw() {
1096
+ if (iterable.onDone) iterable.onDone();
1216
1097
  return iterator.throw();
1217
1098
  }
1218
1099
  };
1219
1100
  };
1220
- return result;
1101
+ return iterable;
1221
1102
  }
1222
1103
  [Symbol.asyncIterator]() {
1223
1104
  return this.iterator = this.iterate();
@@ -1226,7 +1107,12 @@ class RangeIterable {
1226
1107
  return this.iterator = this.iterate();
1227
1108
  }
1228
1109
  filter(func) {
1229
- return this.map(element => func(element) ? element : SKIP);
1110
+ return this.map(element => {
1111
+ let result = func(element);
1112
+ // handle promise
1113
+ if (result?.then) return result.then((result) => result ? element : SKIP);
1114
+ else return result ? element : SKIP;
1115
+ });
1230
1116
  }
1231
1117
 
1232
1118
  forEach(callback) {
@@ -1241,27 +1127,103 @@ class RangeIterable {
1241
1127
  concatIterable.iterate = (async) => {
1242
1128
  let iterator = this.iterator = this.iterate();
1243
1129
  let isFirst = true;
1244
- let concatIterator = {
1130
+ function iteratorDone(result) {
1131
+ if (isFirst) {
1132
+ isFirst = false;
1133
+ iterator = secondIterable[Symbol.iterator](async);
1134
+ result = iterator.next();
1135
+ if (concatIterable.onDone) {
1136
+ if (result.then)
1137
+ result.then((result) => {
1138
+ if (result.done()) concatIterable.onDone();
1139
+ });
1140
+ else if (result.done) concatIterable.onDone();
1141
+ }
1142
+ } else {
1143
+ if (concatIterable.onDone) concatIterable.onDone();
1144
+ }
1145
+ return result;
1146
+ }
1147
+ return {
1245
1148
  next() {
1246
1149
  let result = iterator.next();
1247
- if (isFirst && result.done) {
1248
- isFirst = false;
1249
- iterator = secondIterable[Symbol.iterator](async);
1250
- return iterator.next();
1251
- }
1150
+ if (result.then)
1151
+ return result.then((result) => {
1152
+ if (result.done) return iteratorDone(result);
1153
+ return result;
1154
+ });
1155
+ if (result.done) return iteratorDone(result);
1252
1156
  return result;
1253
1157
  },
1254
1158
  return() {
1159
+ if (concatIterable.onDone) concatIterable.onDone();
1255
1160
  return iterator.return();
1256
1161
  },
1257
1162
  throw() {
1163
+ if (concatIterable.onDone) concatIterable.onDone();
1258
1164
  return iterator.throw();
1259
1165
  }
1260
1166
  };
1261
- return concatIterator;
1262
1167
  };
1263
1168
  return concatIterable;
1264
1169
  }
1170
+
1171
+ flatMap(callback) {
1172
+ let mappedIterable = new RangeIterable();
1173
+ mappedIterable.iterate = (async) => {
1174
+ let iterator = this.iterator = this.iterate(async);
1175
+ let currentSubIterator;
1176
+ return {
1177
+ next() {
1178
+ do {
1179
+ if (currentSubIterator) {
1180
+ let result = currentSubIterator.next();
1181
+ if (!result.done) {
1182
+ return result;
1183
+ }
1184
+ }
1185
+ let result = iterator.next();
1186
+ if (result.done) {
1187
+ if (mappedIterable.onDone) mappedIterable.onDone();
1188
+ return result;
1189
+ }
1190
+ let value = callback(result.value);
1191
+ if (Array.isArray(value) || value instanceof RangeIterable)
1192
+ currentSubIterator = value[Symbol.iterator]();
1193
+ else {
1194
+ currentSubIterator = null;
1195
+ return { value };
1196
+ }
1197
+ } while(true);
1198
+ },
1199
+ return() {
1200
+ if (mappedIterable.onDone) mappedIterable.onDone();
1201
+ if (currentSubIterator)
1202
+ currentSubIterator.return();
1203
+ return iterator.return();
1204
+ },
1205
+ throw() {
1206
+ if (mappedIterable.onDone) mappedIterable.onDone();
1207
+ if (currentSubIterator)
1208
+ currentSubIterator.throw();
1209
+ return iterator.throw();
1210
+ }
1211
+ };
1212
+ };
1213
+ return mappedIterable;
1214
+ }
1215
+
1216
+ slice(start, end) {
1217
+ return this.map((element, i) => {
1218
+ if (i < start)
1219
+ return SKIP;
1220
+ if (i >= end) {
1221
+ DONE.value = element;
1222
+ return DONE;
1223
+ }
1224
+ return element;
1225
+ });
1226
+ }
1265
1227
  next() {
1266
1228
  if (!this.iterator)
1267
1229
  this.iterator = this.iterate();
@@ -1271,7 +1233,7 @@ class RangeIterable {
1271
1233
  if (this.asArray && this.asArray.forEach) {
1272
1234
  return this.asArray;
1273
1235
  }
1274
- throw new Error('Can not serialize async iteratables without first calling resolveJSON');
1236
+ throw new Error('Can not serialize async iterables without first calling resolveJSON');
1275
1237
  //return Array.from(this)
1276
1238
  }
1277
1239
  get asArray() {
@@ -1281,6 +1243,7 @@ class RangeIterable {
1281
1243
  let iterator = this.iterate();
1282
1244
  let array = [];
1283
1245
  let iterable = this;
1246
+ Object.defineProperty(array, 'iterable', { value: iterable });
1284
1247
  function next(result) {
1285
1248
  while (result.done !== true) {
1286
1249
  if (result.then) {
@@ -1290,7 +1253,6 @@ class RangeIterable {
1290
1253
  }
1291
1254
  result = iterator.next();
1292
1255
  }
1293
- array.iterable = iterable;
1294
1256
  resolve(iterable._asArray = array);
1295
1257
  }
1296
1258
  next(iterator.next());
@@ -1302,146 +1264,173 @@ class RangeIterable {
1302
1264
  return this.asArray;
1303
1265
  }
1304
1266
  }
1267
+ RangeIterable.prototype.DONE = DONE;
1268
+
1269
+ const REUSE_BUFFER_MODE = 512;
1270
+ const writeUint32Key = (key, target, start) => {
1271
+ (target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).setUint32(start, key, true);
1272
+ return start + 4;
1273
+ };
1274
+ const readUint32Key = (target, start) => {
1275
+ return (target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).getUint32(start, true);
1276
+ };
1277
+ const writeBufferKey = (key, target, start) => {
1278
+ target.set(key, start);
1279
+ return key.length + start;
1280
+ };
1281
+ const Uint8ArraySlice$1 = Uint8Array.prototype.slice;
1282
+ const readBufferKey = (target, start, end) => {
1283
+ return Uint8ArraySlice$1.call(target, start, end);
1284
+ };
1285
+
1286
+ let lastEncodedValue, bytes;
1287
+ function applyKeyHandling(store) {
1288
+ if (store.encoding == 'ordered-binary') {
1289
+ store.encoder = store.decoder = {
1290
+ writeKey: orderedBinary.writeKey,
1291
+ readKey: orderedBinary.readKey,
1292
+ };
1293
+ }
1294
+ if (store.encoder && store.encoder.writeKey && !store.encoder.encode) {
1295
+ store.encoder.encode = function(value, mode) {
1296
+ if (typeof value !== 'object' && value && value === lastEncodedValue) ; else {
1297
+ lastEncodedValue = value;
1298
+ bytes = saveKey(value, this.writeKey, false, store.maxKeySize);
1299
+ }
1300
+ if (bytes.end > 0 && !(REUSE_BUFFER_MODE & mode)) {
1301
+ return bytes.subarray(bytes.start, bytes.end);
1302
+ }
1303
+ return bytes;
1304
+ };
1305
+ store.encoder.copyBuffers = true; // just an indicator for the buffer reuse in write.js
1306
+ }
1307
+ if (store.decoder && store.decoder.readKey && !store.decoder.decode) {
1308
+ store.decoder.decode = function(buffer) { return this.readKey(buffer, 0, buffer.length); };
1309
+ store.decoderCopies = true;
1310
+ }
1311
+ if (store.keyIsUint32 || store.keyEncoding == 'uint32') {
1312
+ store.writeKey = writeUint32Key;
1313
+ store.readKey = readUint32Key;
1314
+ } else if (store.keyIsBuffer || store.keyEncoding == 'binary') {
1315
+ store.writeKey = writeBufferKey;
1316
+ store.readKey = readBufferKey;
1317
+ } else if (store.keyEncoder) {
1318
+ store.writeKey = store.keyEncoder.writeKey;
1319
+ store.readKey = store.keyEncoder.readKey;
1320
+ } else {
1321
+ store.writeKey = orderedBinary.writeKey;
1322
+ store.readKey = orderedBinary.readKey;
1323
+ }
1324
+ }
1305
1325
 
1306
- const writeUint32Key = (key, target, start) => {
1307
- (target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).setUint32(start, key, true);
1308
- return start + 4;
1309
- };
1310
- const readUint32Key = (target, start) => {
1311
- return (target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).getUint32(start, true);
1312
- };
1313
- const writeBufferKey = (key, target, start) => {
1314
- target.set(key, start);
1315
- return key.length + start;
1316
- };
1317
- const Uint8ArraySlice$1 = Uint8Array.prototype.slice;
1318
- const readBufferKey = (target, start, end) => {
1319
- return Uint8ArraySlice$1.call(target, start, end);
1320
- };
1321
-
1322
- function applyKeyHandling(store) {
1323
- if (store.encoding == 'ordered-binary') {
1324
- store.encoder = store.decoder = {
1325
- writeKey: orderedBinary.writeKey,
1326
- readKey: orderedBinary.readKey,
1327
- };
1328
- }
1329
- if (store.encoder && store.encoder.writeKey && !store.encoder.encode) {
1330
- store.encoder.encode = function(value) {
1331
- return saveKey(value, this.writeKey, false, store.maxKeySize);
1332
- };
1333
- }
1334
- if (store.decoder && store.decoder.readKey && !store.decoder.decode) {
1335
- store.decoder.decode = function(buffer) { return this.readKey(buffer, 0, buffer.length); };
1336
- store.decoderCopies = true;
1337
- }
1338
- if (store.keyIsUint32 || store.keyEncoding == 'uint32') {
1339
- store.writeKey = writeUint32Key;
1340
- store.readKey = readUint32Key;
1341
- } else if (store.keyIsBuffer || store.keyEncoding == 'binary') {
1342
- store.writeKey = writeBufferKey;
1343
- store.readKey = readBufferKey;
1344
- } else if (store.keyEncoder) {
1345
- store.writeKey = store.keyEncoder.writeKey;
1346
- store.readKey = store.keyEncoder.readKey;
1347
- } else {
1348
- store.writeKey = orderedBinary.writeKey;
1349
- store.readKey = orderedBinary.readKey;
1350
- }
1351
- }
1352
-
1353
- let saveBuffer, saveDataView = { setFloat64() {}, setUint32() {} }, saveDataAddress;
1354
- let savePosition = 8000;
1355
- let DYNAMIC_KEY_BUFFER_SIZE = 8192;
1356
- function allocateSaveBuffer() {
1357
- saveBuffer = typeof Buffer != 'undefined' ? Buffer.alloc(DYNAMIC_KEY_BUFFER_SIZE) : new Uint8Array(DYNAMIC_KEY_BUFFER_SIZE);
1358
- saveBuffer.buffer.address = getAddress(saveBuffer);
1359
- saveDataAddress = saveBuffer.buffer.address;
1360
- // TODO: Conditionally only do this for key sequences?
1361
- saveDataView.setUint32(savePosition, 0xffffffff);
1362
- saveDataView.setFloat64(savePosition + 4, saveDataAddress, true); // save a pointer from the old buffer to the new address for the sake of the prefetch sequences
1363
- saveBuffer.dataView = saveDataView = new DataView(saveBuffer.buffer, saveBuffer.byteOffset, saveBuffer.byteLength);
1364
- savePosition = 0;
1365
- }
1366
- function saveKey(key, writeKey, saveTo, maxKeySize) {
1367
- if (savePosition > 7800) {
1368
- allocateSaveBuffer();
1369
- }
1370
- let start = savePosition;
1371
- try {
1372
- savePosition = key === undefined ? start + 4 :
1373
- writeKey(key, saveBuffer, start + 4);
1374
- } catch (error) {
1375
- saveBuffer.fill(0, start + 4); // restore zeros
1376
- if (error.name == 'RangeError') {
1377
- if (8180 - start < maxKeySize) {
1378
- allocateSaveBuffer(); // try again:
1379
- return saveKey(key, writeKey, saveTo, maxKeySize);
1380
- }
1381
- throw new Error('Key was too large, max key size is ' + maxKeySize);
1382
- } else
1383
- throw error;
1384
- }
1385
- let length = savePosition - start - 4;
1386
- if (length > maxKeySize) {
1387
- throw new Error('Key of size ' + length + ' was too large, max key size is ' + maxKeySize);
1388
- }
1389
- if (savePosition >= 8160) { // need to reserve enough room at the end for pointers
1390
- savePosition = start; // reset position
1391
- allocateSaveBuffer(); // try again:
1392
- return saveKey(key, writeKey, saveTo, maxKeySize);
1393
- }
1394
- if (saveTo) {
1395
- saveDataView.setUint32(start, length, true); // save the length
1396
- saveTo.saveBuffer = saveBuffer;
1397
- savePosition = (savePosition + 12) & 0xfffffc;
1398
- return start + saveDataAddress;
1399
- } else {
1400
- saveBuffer.start = start + 4;
1401
- saveBuffer.end = savePosition;
1402
- savePosition = (savePosition + 7) & 0xfffff8; // full 64-bit word alignment since these are usually copied
1403
- return saveBuffer;
1404
- }
1326
+ let saveBuffer, saveDataView = { setFloat64() {}, setUint32() {} }, saveDataAddress;
1327
+ let savePosition$1 = 8000;
1328
+ let DYNAMIC_KEY_BUFFER_SIZE$1 = 8192;
1329
+ function allocateSaveBuffer() {
1330
+ saveBuffer = typeof Buffer != 'undefined' ? Buffer.alloc(DYNAMIC_KEY_BUFFER_SIZE$1) : new Uint8Array(DYNAMIC_KEY_BUFFER_SIZE$1);
1331
+ saveBuffer.buffer.address = getAddress(saveBuffer.buffer);
1332
+ saveDataAddress = saveBuffer.buffer.address;
1333
+ // TODO: Conditionally only do this for key sequences?
1334
+ saveDataView.setUint32(savePosition$1, 0xffffffff);
1335
+ saveDataView.setFloat64(savePosition$1 + 4, saveDataAddress, true); // save a pointer from the old buffer to the new address for the sake of the prefetch sequences
1336
+ saveDataView = saveBuffer.dataView || (saveBuffer.dataView = new DataView(saveBuffer.buffer, saveBuffer.byteOffset, saveBuffer.byteLength));
1337
+ savePosition$1 = 0;
1338
+ }
1339
+ function saveKey(key, writeKey, saveTo, maxKeySize, flags) {
1340
+ if (savePosition$1 > 7800) {
1341
+ allocateSaveBuffer();
1342
+ }
1343
+ let start = savePosition$1;
1344
+ try {
1345
+ savePosition$1 = key === undefined ? start + 4 :
1346
+ writeKey(key, saveBuffer, start + 4);
1347
+ } catch (error) {
1348
+ saveBuffer.fill(0, start + 4); // restore zeros
1349
+ if (error.name == 'RangeError') {
1350
+ if (8180 - start < maxKeySize) {
1351
+ allocateSaveBuffer(); // try again:
1352
+ return saveKey(key, writeKey, saveTo, maxKeySize);
1353
+ }
1354
+ throw new Error('Key was too large, max key size is ' + maxKeySize);
1355
+ } else
1356
+ throw error;
1357
+ }
1358
+ let length = savePosition$1 - start - 4;
1359
+ if (length > maxKeySize) {
1360
+ throw new Error('Key of size ' + length + ' was too large, max key size is ' + maxKeySize);
1361
+ }
1362
+ if (savePosition$1 >= 8160) { // need to reserve enough room at the end for pointers
1363
+ savePosition$1 = start; // reset position
1364
+ allocateSaveBuffer(); // try again:
1365
+ return saveKey(key, writeKey, saveTo, maxKeySize);
1366
+ }
1367
+ if (saveTo) {
1368
+ saveDataView.setUint32(start, flags ? length | flags : length, true); // save the length
1369
+ saveTo.saveBuffer = saveBuffer;
1370
+ savePosition$1 = (savePosition$1 + 12) & 0xfffffc;
1371
+ return start + saveDataAddress;
1372
+ } else {
1373
+ saveBuffer.start = start + 4;
1374
+ saveBuffer.end = savePosition$1;
1375
+ savePosition$1 = (savePosition$1 + 7) & 0xfffff8; // full 64-bit word alignment since these are usually copied
1376
+ return saveBuffer;
1377
+ }
1405
1378
  }
1406
1379
 
1380
+ const IF_EXISTS = 3.542694326329068e-103;
1407
1381
  const ITERATOR_DONE = { done: true, value: undefined };
1408
1382
  const Uint8ArraySlice = Uint8Array.prototype.slice;
1409
- const Uint8A = typeof Buffer != 'undefined' ? Buffer.allocUnsafeSlow : Uint8Array;
1410
- let getValueBytes = makeReusableBuffer(0);
1383
+ let getValueBytes = globalBuffer;
1384
+ if (!getValueBytes.maxLength) {
1385
+ getValueBytes.maxLength = getValueBytes.length;
1386
+ getValueBytes.isGlobal = true;
1387
+ Object.defineProperty(getValueBytes, 'length', { value: getValueBytes.length, writable: true, configurable: true });
1388
+ }
1411
1389
  const START_ADDRESS_POSITION = 4064;
1412
1390
  const NEW_BUFFER_THRESHOLD = 0x8000;
1391
+ const SOURCE_SYMBOL = Symbol.for('source');
1392
+ const UNMODIFIED = {};
1393
+ let mmaps = [];
1413
1394
 
1414
1395
  function addReadMethods(LMDBStore, {
1415
- maxKeySize, env, keyBytes, keyBytesView, getLastVersion
1396
+ maxKeySize, env, keyBytes, keyBytesView, getLastVersion, getLastTxnId
1416
1397
  }) {
1417
1398
  let readTxn, readTxnRenewed, asSafeBuffer = false;
1418
1399
  let renewId = 1;
1400
+ let outstandingReads = 0;
1419
1401
  Object.assign(LMDBStore.prototype, {
1420
- getString(id) {
1421
- (env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(this)));
1422
- let string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0));
1402
+ getString(id, options) {
1403
+ let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
1404
+ let string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), txn.address || 0);
1423
1405
  if (typeof string === 'number') { // indicates the buffer wasn't large enough
1424
1406
  this._allocateGetBuffer(string);
1425
1407
  // and then try again
1426
- string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0));
1408
+ string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), txn.address || 0);
1427
1409
  }
1428
1410
  if (string)
1429
1411
  this.lastSize = string.length;
1430
1412
  return string;
1431
1413
  },
1432
- getBinaryFast(id) {
1433
- (env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(this)));
1434
- let rc = this.lastSize = getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0));
1414
+ getBinaryFast(id, options) {
1415
+ let rc;
1416
+ let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
1417
+ rc = this.lastSize = getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), (options && options.ifNotTxnId) || 0, txn.address || 0);
1435
1418
  if (rc < 0) {
1436
1419
  if (rc == -30798) // MDB_NOTFOUND
1437
1420
  return; // undefined
1421
+ if (rc == -30004) // txn id matched
1422
+ return UNMODIFIED;
1438
1423
  if (rc == -30781 /*MDB_BAD_VALSIZE*/ && this.writeKey(id, keyBytes, 0) == 0)
1439
1424
  throw new Error(id === undefined ?
1440
1425
  'A key is required for get, but is undefined' :
1441
1426
  'Zero length key is not allowed in LMDB');
1442
1427
  if (rc == -30000) // int32 overflow, read uint32
1443
1428
  rc = this.lastSize = keyBytesView.getUint32(0, true);
1444
- else
1429
+ else if (rc == -30001) {// shared buffer
1430
+ this.lastSize = keyBytesView.getUint32(0, true);
1431
+ let bufferId = keyBytesView.getUint32(4, true);
1432
+ return getMMapBuffer(bufferId, this.lastSize);
1433
+ } else
1445
1434
  throw lmdbError(rc);
1446
1435
  }
1447
1436
  let compression = this.compression;
@@ -1449,13 +1438,89 @@ function addReadMethods(LMDBStore, {
1449
1438
  if (rc > bytes.maxLength) {
1450
1439
  // this means the target buffer wasn't big enough, so the get failed to copy all the data from the database, need to either grow or use special buffer
1451
1440
  return this._returnLargeBuffer(
1452
- () => getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0)),
1453
- () => getSharedByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0)));
1441
+ () => getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), 0, txn.address || 0));
1454
1442
  }
1455
1443
  bytes.length = this.lastSize;
1456
1444
  return bytes;
1457
1445
  },
1458
- _returnLargeBuffer(getFast, getShared) {
1446
+ getBFAsync(id, options, callback) {
1447
+ let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
1448
+ txn.refCount = (txn.refCount || 0) + 1;
1449
+ outstandingReads++;
1450
+ let address = recordReadInstruction(txn.address, this.db.dbi, id, this.writeKey, maxKeySize, ( rc, bufferId, offset, size ) => {
1451
+ if (rc && rc !== 1)
1452
+ callback(lmdbError(rc));
1453
+ outstandingReads--;
1454
+ let buffer = mmaps[bufferId];
1455
+ if (!buffer) {
1456
+ buffer = mmaps[bufferId] = getSharedBuffer(bufferId, env.address);
1457
+ }
1458
+ //console.log({bufferId, offset, size})
1459
+ if (buffer.isSharedMap) {
1460
+ // using LMDB shared memory
1461
+ // TODO: We may want explicit support for clearing aborting the transaction on the next event turn,
1462
+ // but for now we are relying on the GC to cleanup transaction for larger blocks of memory
1463
+ let bytes = new Uint8Array(buffer, offset, size);
1464
+ bytes.txn = txn;
1465
+ callback(bytes, 0, size);
1466
+ } else {
1467
+ // using copied memory
1468
+ txn.done(); // decrement and possibly abort
1469
+ callback(buffer, offset, size);
1470
+ }
1471
+ });
1472
+ if (address) {
1473
+ startRead(address, () => {
1474
+ resolveReads();
1475
+ });
1476
+ }
1477
+ },
1478
+ getAsync(id, options, callback) {
1479
+ let promise;
1480
+ if (!callback)
1481
+ promise = new Promise(resolve => callback = resolve);
1482
+ this.getBFAsync(id, options, (buffer, offset, size) => {
1483
+ if (this.useVersions) {
1484
+ // TODO: And get the version
1485
+ offset += 8;
1486
+ size -= 8;
1487
+ }
1488
+ let bytes = new Uint8Array(buffer, offset, size);
1489
+ let value;
1490
+ if (this.decoder) {
1491
+ // the decoder potentially uses the data from the buffer in the future and needs a stable buffer
1492
+ value = bytes && this.decoder.decode(bytes);
1493
+ } else if (this.encoding == 'binary') {
1494
+ value = bytes;
1495
+ } else {
1496
+ value = Buffer.prototype.utf8Slice.call(bytes, 0, size);
1497
+ if (this.encoding == 'json' && value)
1498
+ value = JSON.parse(value);
1499
+ }
1500
+ callback(value);
1501
+ });
1502
+ return promise;
1503
+ },
1504
+ retain(data, options) {
1505
+ if (!data)
1506
+ return
1507
+ let source = data[SOURCE_SYMBOL];
1508
+ let buffer = source ? source.bytes : data;
1509
+ if (!buffer.isGlobal && !env.writeTxn) {
1510
+ let txn = options?.transaction || (readTxnRenewed ? readTxn : renewReadTxn(this));
1511
+ buffer.txn = txn;
1512
+ txn.refCount = (txn.refCount || 0) + 1;
1513
+ return data;
1514
+ } else {
1515
+ buffer = Uint8ArraySlice.call(buffer, 0, this.lastSize);
1516
+ if (source) {
1517
+ source.bytes = buffer;
1518
+ return data;
1519
+ } else
1520
+ return buffer;
1521
+ }
1522
+ },
1523
+ _returnLargeBuffer(getFast) {
1459
1524
  let bytes;
1460
1525
  let compression = this.compression;
1461
1526
  if (asSafeBuffer && this.lastSize > NEW_BUFFER_THRESHOLD) {
@@ -1467,7 +1532,7 @@ function addReadMethods(LMDBStore, {
1467
1532
  let dictionary = compression.dictionary || [];
1468
1533
  let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned
1469
1534
  bytes = makeReusableBuffer(this.lastSize);
1470
- compression.setBuffer(bytes, this.lastSize, dictionary, dictLength);
1535
+ compression.setBuffer(bytes.buffer, bytes.byteOffset, this.lastSize, dictionary, dictLength);
1471
1536
  compression.getValueBytes = bytes;
1472
1537
  } else {
1473
1538
  bytesToRestore = getValueBytes;
@@ -1477,7 +1542,7 @@ function addReadMethods(LMDBStore, {
1477
1542
  } finally {
1478
1543
  if (compression) {
1479
1544
  let dictLength = (compression.dictionary.length >> 3) << 3;
1480
- compression.setBuffer(bytesToRestore, bytesToRestore.maxLength, compression.dictionary, dictLength);
1545
+ compression.setBuffer(bytesToRestore.buffer, bytesToRestore.byteOffset, bytesToRestore.maxLength, compression.dictionary, dictLength);
1481
1546
  compression.getValueBytes = bytesToRestore;
1482
1547
  } else {
1483
1548
  setGlobalBuffer(bytesToRestore);
@@ -1486,12 +1551,6 @@ function addReadMethods(LMDBStore, {
1486
1551
  }
1487
1552
  return bytes;
1488
1553
  }
1489
- if (this.lastSize > NEW_BUFFER_THRESHOLD && !compression) {
1490
- // for large binary objects, cheaper to make a buffer that directly points at the shared LMDB memory to avoid copying a large amount of memory, but only for large data since there is significant overhead to instantiating the buffer
1491
- if (globalThis.__lmdb_last_shared__ && detachBuffer) // we have to detach the last one, or else could crash due to two buffers pointing at same location
1492
- detachBuffer(globalThis.__lmdb_last_shared__.buffer);
1493
- return globalThis.__lmdb_last_shared__ = getShared();
1494
- }
1495
1554
  // grow our shared/static buffer to accomodate the size of the data
1496
1555
  bytes = this._allocateGetBuffer(this.lastSize);
1497
1556
  // and try again
@@ -1503,13 +1562,13 @@ function addReadMethods(LMDBStore, {
1503
1562
  let newLength = Math.min(Math.max(lastSize * 2, 0x1000), 0xfffffff8);
1504
1563
  let bytes;
1505
1564
  if (this.compression) {
1506
- let dictionary = this.compression.dictionary || new Uint8A(0);
1565
+ let dictionary = this.compression.dictionary || Buffer.allocUnsafeSlow(0);
1507
1566
  let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned
1508
- bytes = new Uint8A(newLength + dictLength);
1567
+ bytes = Buffer.allocUnsafeSlow(newLength + dictLength);
1509
1568
  bytes.set(dictionary); // copy dictionary into start
1510
1569
  // the section after the dictionary is the target area for get values
1511
1570
  bytes = bytes.subarray(dictLength);
1512
- this.compression.setBuffer(bytes, newLength, dictionary, dictLength);
1571
+ this.compression.setBuffer(bytes.buffer, bytes.byteOffset, newLength, dictionary, dictLength);
1513
1572
  bytes.maxLength = newLength;
1514
1573
  Object.defineProperty(bytes, 'length', { value: newLength, writable: true, configurable: true });
1515
1574
  this.compression.getValueBytes = bytes;
@@ -1517,41 +1576,51 @@ function addReadMethods(LMDBStore, {
1517
1576
  bytes = makeReusableBuffer(newLength);
1518
1577
  setGlobalBuffer(getValueBytes = bytes);
1519
1578
  }
1520
- bytes.isShared = true;
1579
+ bytes.isGlobal = true;
1521
1580
  return bytes;
1522
1581
  },
1523
- getBinary(id) {
1582
+ getBinary(id, options) {
1524
1583
  try {
1525
1584
  asSafeBuffer = true;
1526
- let fastBuffer = this.getBinaryFast(id);
1527
- return fastBuffer && (fastBuffer.isShared ? Uint8ArraySlice.call(fastBuffer, 0, this.lastSize) : fastBuffer);
1585
+ let fastBuffer = this.getBinaryFast(id, options);
1586
+ return fastBuffer && (fastBuffer.isGlobal ? Uint8ArraySlice.call(fastBuffer, 0, this.lastSize) : fastBuffer);
1528
1587
  } finally {
1529
1588
  asSafeBuffer = false;
1530
1589
  }
1531
1590
  },
1532
- get(id) {
1591
+ getSharedBinary(id, options) {
1592
+ let fastBuffer = this.getBinaryFast(id, options);
1593
+ if (fastBuffer) {
1594
+ if (fastBuffer.isGlobal || writeTxn)
1595
+ return Uint8ArraySlice.call(fastBuffer, 0, this.lastSize)
1596
+ fastBuffer.txn = (options && options.transaction);
1597
+ options.transaction.refCount = (options.transaction.refCount || 0) + 1;
1598
+ return fastBuffer;
1599
+ }
1600
+ },
1601
+ get(id, options) {
1533
1602
  if (this.decoderCopies) {
1534
1603
  // the decoder copies any data, so we can use the fast binary retrieval that overwrites the same buffer space
1535
- let bytes = this.getBinaryFast(id);
1536
- return bytes && this.decoder.decode(bytes);
1604
+ let bytes = this.getBinaryFast(id, options);
1605
+ return bytes && (bytes == UNMODIFIED ? UNMODIFIED : this.decoder.decode(bytes, options));
1537
1606
  }
1538
1607
  if (this.encoding == 'binary')
1539
- return this.getBinary(id);
1608
+ return this.getBinary(id, options);
1540
1609
  if (this.decoder) {
1541
1610
  // the decoder potentially uses the data from the buffer in the future and needs a stable buffer
1542
- let bytes = this.getBinary(id);
1543
- return bytes && this.decoder.decode(bytes);
1611
+ let bytes = this.getBinary(id, options);
1612
+ return bytes && (bytes == UNMODIFIED ? UNMODIFIED : this.decoder.decode(bytes));
1544
1613
  }
1545
1614
 
1546
- let result = this.getString(id);
1615
+ let result = this.getString(id, options);
1547
1616
  if (result) {
1548
1617
  if (this.encoding == 'json')
1549
1618
  return JSON.parse(result);
1550
1619
  }
1551
1620
  return result;
1552
1621
  },
1553
- getEntry(id) {
1554
- let value = this.get(id);
1622
+ getEntry(id, options) {
1623
+ let value = this.get(id, options);
1555
1624
  if (value !== undefined) {
1556
1625
  if (this.useVersions)
1557
1626
  return {
@@ -1570,8 +1639,11 @@ function addReadMethods(LMDBStore, {
1570
1639
  resetReadTxn();
1571
1640
  },
1572
1641
  _commitReadTxn() {
1573
- if (readTxn)
1642
+ if (readTxn) {
1643
+ readTxn.isCommitted = true;
1574
1644
  readTxn.commit();
1645
+ }
1646
+ lastReadTxnRef = null;
1575
1647
  readTxnRenewed = null;
1576
1648
  readTxn = null;
1577
1649
  },
@@ -1580,15 +1652,12 @@ function addReadMethods(LMDBStore, {
1580
1652
  renewReadTxn(this);
1581
1653
  },
1582
1654
  doesExist(key, versionOrValue) {
1583
- if (!env.writeTxn)
1584
- readTxnRenewed ? readTxn : renewReadTxn(this);
1585
1655
  if (versionOrValue == null) {
1586
1656
  // undefined means the entry exists, null is used specifically to check for the entry *not* existing
1587
1657
  return (this.getBinaryFast(key) === undefined) == (versionOrValue === null);
1588
1658
  }
1589
1659
  else if (this.useVersions) {
1590
- this.getBinaryFast(key);
1591
- return this.getBinaryFast(key) !== undefined && getLastVersion() === versionOrValue;
1660
+ return this.getBinaryFast(key) !== undefined && (versionOrValue === IF_EXISTS || getLastVersion() === versionOrValue);
1592
1661
  }
1593
1662
  else {
1594
1663
  if (versionOrValue && versionOrValue['\x10binary-data\x02'])
@@ -1654,28 +1723,39 @@ function addReadMethods(LMDBStore, {
1654
1723
  let cursor, cursorRenewId, cursorAddress;
1655
1724
  let txn;
1656
1725
  let flags = (includeValues ? 0x100 : 0) | (reverse ? 0x400 : 0) |
1657
- (valuesForKey ? 0x800 : 0) | (options.exactMatch ? 0x4000 : 0);
1726
+ (valuesForKey ? 0x800 : 0) | (options.exactMatch ? 0x4000 : 0) |
1727
+ (options.inclusiveEnd ? 0x8000 : 0) |
1728
+ (options.exclusiveStart ? 0x10000 : 0);
1658
1729
  let store = this;
1659
1730
  function resetCursor() {
1660
1731
  try {
1661
1732
  if (cursor)
1662
1733
  finishCursor();
1663
- let writeTxn = env.writeTxn;
1664
- if (writeTxn)
1665
- snapshot = false;
1666
- txn = writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(store));
1667
- cursor = !writeTxn && db.availableCursor;
1734
+ let txnAddress;
1735
+ txn = options.transaction;
1736
+ if (txn) {
1737
+ if (txn.isDone) throw new Error('Can not iterate on range with transaction that is already' +
1738
+ ' done');
1739
+ txnAddress = txn.address;
1740
+ cursor = null;
1741
+ } else {
1742
+ let writeTxn = env.writeTxn;
1743
+ if (writeTxn)
1744
+ snapshot = false;
1745
+ txn = env.writeTxn || options.transaction || (readTxnRenewed ? readTxn : renewReadTxn(store));
1746
+ cursor = !writeTxn && db.availableCursor;
1747
+ }
1668
1748
  if (cursor) {
1669
1749
  db.availableCursor = null;
1670
1750
  flags |= 0x2000;
1671
1751
  } else {
1672
- cursor = new Cursor(db);
1752
+ cursor = new Cursor(db, txnAddress || 0);
1673
1753
  }
1674
1754
  cursorAddress = cursor.address;
1675
- txn.cursorCount = (txn.cursorCount || 0) + 1; // track transaction so we always use the same one
1755
+ txn.refCount = (txn.refCount || 0) + 1; // track transaction so we always use the same one
1676
1756
  if (snapshot === false) {
1677
1757
  cursorRenewId = renewId; // use shared read transaction
1678
- txn.renewingCursorCount = (txn.renewingCursorCount || 0) + 1; // need to know how many are renewing cursors
1758
+ txn.renewingRefCount = (txn.renewingRefCount || 0) + 1; // need to know how many are renewing cursors
1679
1759
  }
1680
1760
  } catch(error) {
1681
1761
  if (cursor) {
@@ -1696,6 +1776,9 @@ function addReadMethods(LMDBStore, {
1696
1776
  return count;
1697
1777
  }
1698
1778
  function position$1(offset) {
1779
+ if (!env.address) {
1780
+ throw new Error('Can not iterate on a closed database');
1781
+ }
1699
1782
  let keySize = currentKey === undefined ? 0 : store.writeKey(currentKey, keyBytes, 0);
1700
1783
  let endAddress;
1701
1784
  if (valuesForKey) {
@@ -1723,9 +1806,11 @@ function addReadMethods(LMDBStore, {
1723
1806
  function finishCursor() {
1724
1807
  if (txn.isDone)
1725
1808
  return;
1809
+ if (iterable.onDone)
1810
+ iterable.onDone();
1726
1811
  if (cursorRenewId)
1727
- txn.renewingCursorCount--;
1728
- if (--txn.cursorCount <= 0 && txn.onlyCursor) {
1812
+ txn.renewingRefCount--;
1813
+ if (--txn.refCount <= 0 && txn.notCurrent) {
1729
1814
  cursor.close();
1730
1815
  txn.abort(); // this is no longer main read txn, abort it now that we are done
1731
1816
  txn.isDone = true;
@@ -1752,7 +1837,7 @@ function addReadMethods(LMDBStore, {
1752
1837
  if (keySize <= 0 ||
1753
1838
  (count++ >= limit)) {
1754
1839
  if (count < 0)
1755
- lmdbError(count);
1840
+ lmdbError(count);
1756
1841
  finishCursor();
1757
1842
  return ITERATOR_DONE;
1758
1843
  }
@@ -1767,24 +1852,27 @@ function addReadMethods(LMDBStore, {
1767
1852
  if (includeValues) {
1768
1853
  let value;
1769
1854
  lastSize = keyBytesView.getUint32(0, true);
1770
- let bytes = compression ? compression.getValueBytes : getValueBytes;
1771
- if (lastSize > bytes.maxLength) {
1772
- store.lastSize = lastSize;
1773
- asSafeBuffer = store.encoding == 'binary';
1774
- try {
1775
- bytes = store._returnLargeBuffer(
1776
- () => getCurrentValue(cursorAddress),
1777
- () => getCurrentShared(cursorAddress)
1778
- );
1779
- } finally {
1780
- asSafeBuffer = false;
1781
- }
1782
- } else
1783
- bytes.length = lastSize;
1855
+ let bufferId = keyBytesView.getUint32(4, true);
1856
+ let bytes;
1857
+ if (bufferId) {
1858
+ bytes = getMMapBuffer(bufferId, lastSize);
1859
+ } else {
1860
+ bytes = compression ? compression.getValueBytes : getValueBytes;
1861
+ if (lastSize > bytes.maxLength) {
1862
+ store.lastSize = lastSize;
1863
+ asSafeBuffer = store.encoding == 'binary';
1864
+ try {
1865
+ bytes = store._returnLargeBuffer(() => getCurrentValue(cursorAddress));
1866
+ } finally {
1867
+ asSafeBuffer = false;
1868
+ }
1869
+ } else
1870
+ bytes.length = lastSize;
1871
+ }
1784
1872
  if (store.decoder) {
1785
1873
  value = store.decoder.decode(bytes, lastSize);
1786
1874
  } else if (store.encoding == 'binary')
1787
- value = bytes.isShared ? Uint8ArraySlice.call(bytes, 0, lastSize) : bytes;
1875
+ value = bytes.isGlobal ? Uint8ArraySlice.call(bytes, 0, lastSize) : bytes;
1788
1876
  else {
1789
1877
  value = bytes.toString('utf8', 0, lastSize);
1790
1878
  if (store.encoding == 'json' && value)
@@ -1850,8 +1938,8 @@ function addReadMethods(LMDBStore, {
1850
1938
  });
1851
1939
  return promise;
1852
1940
  },
1853
- getSharedBufferForGet(id) {
1854
- let txn = (env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(this)));
1941
+ getSharedBufferForGet(id, options) {
1942
+ let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
1855
1943
  this.lastSize = this.keyIsCompatibility ? txn.getBinaryShared(id) : this.db.get(this.writeKey(id, keyBytes, 0));
1856
1944
  if (this.lastSize === -30798) { // not found code
1857
1945
  return; //undefined
@@ -1873,7 +1961,13 @@ function addReadMethods(LMDBStore, {
1873
1961
  let bufferHolder = {};
1874
1962
  let lastBuffer;
1875
1963
  for (let key of keys) {
1876
- let position = saveKey(key, this.writeKey, bufferHolder, maxKeySize);
1964
+ let position;
1965
+ if (key && key.key !== undefined && key.value !== undefined) {
1966
+ position = saveKey(key.value, this.writeKey, bufferHolder, maxKeySize, 0x80000000);
1967
+ saveKey(key.key, this.writeKey, bufferHolder, maxKeySize);
1968
+ } else {
1969
+ position = saveKey(key, this.writeKey, bufferHolder, maxKeySize);
1970
+ }
1877
1971
  if (!startPosition)
1878
1972
  startPosition = position;
1879
1973
  if (bufferHolder.saveBuffer != lastBuffer) {
@@ -1883,7 +1977,9 @@ function addReadMethods(LMDBStore, {
1883
1977
  }
1884
1978
  }
1885
1979
  saveKey(undefined, this.writeKey, bufferHolder, maxKeySize);
1980
+ outstandingReads++;
1886
1981
  prefetch(this.dbAddress, startPosition, (error) => {
1982
+ outstandingReads--;
1887
1983
  if (error)
1888
1984
  console.error('Error with prefetch', buffers, bufferHolder); // partly exists to keep the buffers pinned in memory
1889
1985
  else
@@ -1892,27 +1988,44 @@ function addReadMethods(LMDBStore, {
1892
1988
  if (!callback)
1893
1989
  return new Promise(resolve => callback = resolve);
1894
1990
  },
1991
+ useReadTransaction() {
1992
+ let txn = readTxnRenewed ? readTxn : renewReadTxn(this);
1993
+ if (!txn.use) {
1994
+ throw new Error('Can not use read transaction from a closed database');
1995
+ }
1996
+ txn.use();
1997
+ return txn;
1998
+ },
1895
1999
  close(callback) {
1896
2000
  this.status = 'closing';
2001
+ let txnPromise;
1897
2002
  if (this.isRoot) {
1898
- if (readTxn) {
1899
- try {
1900
- readTxn.abort();
1901
- } catch(error) {}
1902
- }
1903
- readTxn = {
1904
- renew() {
2003
+ // if it is root, we need to abort and/or wait for transactions to finish
2004
+ if (readTxn) readTxn.abort();
2005
+ else readTxn = {};
2006
+ readTxn.isDone = true;
2007
+ Object.defineProperty(readTxn,'renew', {
2008
+ value: () => {
1905
2009
  throw new Error('Can not read from a closed database');
1906
- }
1907
- };
2010
+ }, configurable: true
2011
+ });
2012
+ Object.defineProperty(readTxn,'use', {
2013
+ value: () => {
2014
+ throw new Error('Can not read from a closed database');
2015
+ }, configurable: true
2016
+ });
1908
2017
  readTxnRenewed = null;
2018
+ txnPromise = this._endWrites && this._endWrites();
1909
2019
  }
1910
- let txnPromise = this._endWrites();
1911
2020
  const doClose = () => {
1912
- this.db.close();
1913
2021
  if (this.isRoot) {
2022
+ if (outstandingReads > 0) {
2023
+ return new Promise(resolve => setTimeout(() => resolve(doClose()), 1));
2024
+ }
2025
+ env.address = 0;
1914
2026
  env.close();
1915
- }
2027
+ } else
2028
+ this.db.close();
1916
2029
  this.status = 'closed';
1917
2030
  if (callback)
1918
2031
  callback();
@@ -1925,18 +2038,40 @@ function addReadMethods(LMDBStore, {
1925
2038
  }
1926
2039
  },
1927
2040
  getStats() {
1928
- readTxnRenewed ? readTxn : renewReadTxn(this);
1929
- return this.db.stat();
1930
- }
2041
+ env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(this));
2042
+ let dbStats = this.db.stat();
2043
+ dbStats.root = env.stat();
2044
+ Object.assign(dbStats, env.info());
2045
+ dbStats.free = env.freeStat();
2046
+ return dbStats;
2047
+ },
1931
2048
  });
1932
2049
  let get = LMDBStore.prototype.get;
2050
+ let lastReadTxnRef;
2051
+ function getMMapBuffer(bufferId, size) {
2052
+ let buffer = mmaps[bufferId];
2053
+ if (!buffer) {
2054
+ buffer = mmaps[bufferId] = getSharedBuffer(bufferId, env.address);
2055
+ }
2056
+ let offset = keyBytesView.getUint32(8, true);
2057
+ return new Uint8Array(buffer, offset, size);
2058
+ }
1933
2059
  function renewReadTxn(store) {
2060
+ if (!env.address) {
2061
+ throw new Error('Can not renew a transaction from a closed database');
2062
+ }
1934
2063
  if (!readTxn) {
1935
2064
  let retries = 0;
1936
2065
  let waitArray;
1937
2066
  do {
1938
2067
  try {
1939
- readTxn = new Txn(env, 0x20000);
2068
+ let lastReadTxn = lastReadTxnRef && lastReadTxnRef.deref();
2069
+ readTxn = new Txn(env, 0x20000, lastReadTxn && !lastReadTxn.isDone && lastReadTxn);
2070
+ if (readTxn.address == 0) {
2071
+ readTxn = lastReadTxn;
2072
+ if (readTxn.notCurrent)
2073
+ readTxn.notCurrent = false;
2074
+ }
1940
2075
  break;
1941
2076
  } catch (error) {
1942
2077
  if (error.message.includes('temporarily')) {
@@ -1954,15 +2089,20 @@ function addReadMethods(LMDBStore, {
1954
2089
  store.emit('begin-transaction');
1955
2090
  return readTxn;
1956
2091
  }
1957
- function resetReadTxn(hardReset) {
2092
+ function resetReadTxn() {
1958
2093
  renewId++;
1959
2094
  if (readTxnRenewed) {
1960
2095
  readTxnRenewed = null;
1961
- if (readTxn.cursorCount - (readTxn.renewingCursorCount || 0) > 0) {
1962
- readTxn.onlyCursor = true;
2096
+ if (readTxn.refCount - (readTxn.renewingRefCount || 0) > 0) {
2097
+ readTxn.notCurrent = true;
2098
+ lastReadTxnRef = new WeakRef(readTxn);
1963
2099
  readTxn = null;
1964
- } else
2100
+ } else if (readTxn.address && !readTxn.isDone) {
1965
2101
  resetTxn(readTxn.address);
2102
+ } else {
2103
+ console.warn('Attempt to reset an invalid read txn', readTxn);
2104
+ throw new Error('Attempt to reset an invalid read txn');
2105
+ }
1966
2106
  }
1967
2107
  }
1968
2108
  }
@@ -1973,12 +2113,296 @@ function makeReusableBuffer(size) {
1973
2113
  return bytes;
1974
2114
  }
1975
2115
 
2116
+ Txn.prototype.done = function() {
2117
+ this.refCount--;
2118
+ if (this.refCount === 0 && this.notCurrent) {
2119
+ this.abort();
2120
+ this.isDone = true;
2121
+ }
2122
+ };
2123
+ Txn.prototype.use = function() {
2124
+ this.refCount = (this.refCount || 0) + 1;
2125
+ };
2126
+
2127
+
2128
+ let readInstructions, readCallbacks = new Map(), uint32Instructions, instructionsDataView = { setFloat64() {}, setUint32() {} }, instructionsAddress;
2129
+ let savePosition = 8000;
2130
+ let DYNAMIC_KEY_BUFFER_SIZE = 8192;
2131
+ function allocateInstructionsBuffer() {
2132
+ readInstructions = typeof Buffer != 'undefined' ? Buffer.alloc(DYNAMIC_KEY_BUFFER_SIZE) : new Uint8Array(DYNAMIC_KEY_BUFFER_SIZE);
2133
+ uint32Instructions = new Int32Array(readInstructions.buffer, 0, readInstructions.buffer.byteLength >> 2);
2134
+ uint32Instructions[2] = 0xf0000000; // indicates a new read task must be started
2135
+ instructionsAddress = readInstructions.buffer.address = getAddress(readInstructions.buffer);
2136
+ readInstructions.dataView = instructionsDataView = new DataView(readInstructions.buffer, readInstructions.byteOffset, readInstructions.byteLength);
2137
+ savePosition = 0;
2138
+ }
2139
+ function recordReadInstruction(txnAddress, dbi, key, writeKey, maxKeySize, callback) {
2140
+ if (savePosition > 7800) {
2141
+ allocateInstructionsBuffer();
2142
+ }
2143
+ let start = savePosition;
2144
+ let keyPosition = savePosition + 16;
2145
+ try {
2146
+ savePosition = key === undefined ? keyPosition :
2147
+ writeKey(key, readInstructions, keyPosition);
2148
+ } catch (error) {
2149
+ if (error.name == 'RangeError') {
2150
+ if (8180 - start < maxKeySize) {
2151
+ allocateInstructionsBuffer(); // try again:
2152
+ return recordReadInstruction(txnAddress, dbi, key, writeKey, maxKeySize, callback);
2153
+ }
2154
+ throw new Error('Key was too large, max key size is ' + maxKeySize);
2155
+ } else
2156
+ throw error;
2157
+ }
2158
+ let length = savePosition - keyPosition;
2159
+ if (length > maxKeySize) {
2160
+ savePosition = start;
2161
+ throw new Error('Key of size ' + length + ' was too large, max key size is ' + maxKeySize);
2162
+ }
2163
+ uint32Instructions[(start >> 2) + 3] = length; // save the length
2164
+ uint32Instructions[(start >> 2) + 2] = dbi;
2165
+ savePosition = (savePosition + 12) & 0xfffffc;
2166
+ instructionsDataView.setFloat64(start, txnAddress, true);
2167
+ let callbackId = addReadCallback(() => {
2168
+ let position = start >> 2;
2169
+ let rc = thisInstructions[position];
2170
+ callback(rc, thisInstructions[position + 1], thisInstructions[position + 2], thisInstructions[position + 3]);
2171
+ });
2172
+ let thisInstructions = uint32Instructions;
2173
+ //if (start === 0)
2174
+ return startRead(instructionsAddress + start, callbackId, {}, 'read');
2175
+ //else
2176
+ //nextRead(start);
2177
+ }
2178
+ let nextCallbackId = 0;
2179
+ let addReadCallback = globalThis.__lmdb_read_callback;
2180
+ if (!addReadCallback) {
2181
+ addReadCallback = globalThis.__lmdb_read_callback = function(callback) {
2182
+ let callbackId = nextCallbackId++;
2183
+ readCallbacks.set(callbackId, callback);
2184
+ return callbackId;
2185
+ };
2186
+ setReadCallback(function(callbackId) {
2187
+ readCallbacks.get(callbackId)();
2188
+ readCallbacks.delete(callbackId);
2189
+ });
2190
+ }
2191
+
2192
+ let getLastVersion$1, getLastTxnId$1;
2193
+ const mapGet = Map.prototype.get;
2194
+ const CachingStore = (Store, env) => {
2195
+ let childTxnChanges;
2196
+ return class LMDBStore extends Store {
2197
+ constructor(dbName, options) {
2198
+ super(dbName, options);
2199
+ if (!env.cacheCommitter) {
2200
+ env.cacheCommitter = true;
2201
+ this.on('aftercommit', ({ next, last, txnId }) => {
2202
+ do {
2203
+ let meta = next.meta;
2204
+ let store = meta && meta.store;
2205
+ if (store) {
2206
+ if (next.flag & FAILED_CONDITION)
2207
+ store.cache.delete(meta.key); // just delete it from the map
2208
+ else {
2209
+ let expirationPriority = meta.valueSize >> 10;
2210
+ let cache = store.cache;
2211
+ let entry = mapGet.call(cache, meta.key);
2212
+ if (entry) {
2213
+ entry.txnId = txnId;
2214
+ cache.used(entry, expirationPriority + 4); // this will enter it into the LRFU (with a little lower priority than a read)
2215
+ }
2216
+ }
2217
+ }
2218
+ } while (next != last && (next = next.next))
2219
+ });
2220
+ }
2221
+ this.db.cachingDb = this;
2222
+ if (options.cache.clearKeptInterval)
2223
+ options.cache.clearKeptObjects = exports.clearKeptObjects;
2224
+ this.cache = new WeakLRUCache(options.cache);
2225
+ if (options.cache.validated)
2226
+ this.cache.validated = true;
2227
+ }
2228
+ get isCaching() {
2229
+ return true
2230
+ }
2231
+ get(id, options) {
2232
+ let value;
2233
+ if (this.cache.validated) {
2234
+ let entry = this.cache.get(id);
2235
+ if (entry) {
2236
+ let cachedValue = entry.value;
2237
+ if (entry.txnId != null) {
2238
+ value = super.get(id, { ifNotTxnId: entry.txnId, transaction: options && options.transaction });
2239
+ if (value === UNMODIFIED)
2240
+ return cachedValue;
2241
+ } else // with no txn id we do not validate; this is the state of a cached value after a write before it transacts
2242
+ return cachedValue;
2243
+ } else
2244
+ value = super.get(id, options);
2245
+ } else if (options && options.transaction) {
2246
+ return super.get(id, options);
2247
+ } else {
2248
+ value = this.cache.getValue(id);
2249
+ if (value !== undefined) {
2250
+ return value;
2251
+ }
2252
+ value = super.get(id);
2253
+ }
2254
+ if (value && typeof value === 'object' && !options && typeof id !== 'object') {
2255
+ let entry = this.cache.setValue(id, value, this.lastSize >> 10);
2256
+ if (this.useVersions) {
2257
+ entry.version = getLastVersion$1();
2258
+ }
2259
+ if (this.cache.validated)
2260
+ entry.txnId = getLastTxnId$1();
2261
+ }
2262
+ return value;
2263
+ }
2264
+ getEntry(id, options) {
2265
+ let entry, value;
2266
+ if (this.cache.validated) {
2267
+ entry = this.cache.get(id);
2268
+ if (entry) {
2269
+ if (entry.txnId != null) {
2270
+ value = super.get(id, { ifNotTxnId: entry.txnId, transaction: options && options.transaction });
2271
+ if (value === UNMODIFIED)
2272
+ return entry;
2273
+ } else // with no txn id we do not validate; this is the state of a cached value after a write before it transacts
2274
+ return entry;
2275
+ } else
2276
+ value = super.get(id, options);
2277
+ } else if (options && options.transaction) {
2278
+ return super.getEntry(id, options);
2279
+ } else {
2280
+ entry = this.cache.get(id);
2281
+ if (entry !== undefined) {
2282
+ return entry;
2283
+ }
2284
+ value = super.get(id);
2285
+ }
2286
+ if (value === undefined)
2287
+ return;
2288
+ if (value && typeof value === 'object' && !options && typeof id !== 'object') {
2289
+ entry = this.cache.setValue(id, value, this.lastSize >> 10);
2290
+ } else
2291
+ entry = { value };
2292
+ if (this.useVersions)
2293
+ entry.version = getLastVersion$1();
2294
+ if (this.cache.validated)
2295
+ entry.txnId = getLastTxnId$1();
2296
+ return entry;
2297
+ }
2298
+ putEntry(id, entry, ifVersion) {
2299
+ let result = super.put(id, entry.value, entry.version, ifVersion);
2300
+ if (typeof id === 'object')
2301
+ return result;
2302
+ if (result && result.then)
2303
+ this.cache.setManually(id, entry); // set manually so we can keep it pinned in memory until it is committed
2304
+ else // sync operation, immediately add to cache
2305
+ this.cache.set(id, entry);
2306
+ }
2307
+ put(id, value, version, ifVersion) {
2308
+ let result = super.put(id, value, version, ifVersion);
2309
+ if (typeof id !== 'object') {
2310
+ if (value && value['\x10binary-data\x02']) {
2311
+ // don't cache binary data, since it will be decoded on get
2312
+ this.cache.delete(id);
2313
+ return result;
2314
+ }
2315
+ // sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed
2316
+ let entry = this.cache.setValue(id, value, !result || result.isSync ? 0 : -1);
2317
+ if (childTxnChanges)
2318
+ childTxnChanges.add(id);
2319
+ if (version !== undefined)
2320
+ entry.version = typeof version === 'object' ? version.version : version;
2321
+ }
2322
+ return result;
2323
+ }
2324
+ putSync(id, value, version, ifVersion) {
2325
+ if (id !== 'object') {
2326
+ // sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed
2327
+ if (value && typeof value === 'object') {
2328
+ let entry = this.cache.setValue(id, value);
2329
+ if (childTxnChanges)
2330
+ childTxnChanges.add(id);
2331
+ if (version !== undefined) {
2332
+ entry.version = typeof version === 'object' ? version.version : version;
2333
+ }
2334
+ } else // it is possible that a value used to exist here
2335
+ this.cache.delete(id);
2336
+ }
2337
+ return super.putSync(id, value, version, ifVersion);
2338
+ }
2339
+ remove(id, ifVersion) {
2340
+ this.cache.delete(id);
2341
+ return super.remove(id, ifVersion);
2342
+ }
2343
+ removeSync(id, ifVersion) {
2344
+ this.cache.delete(id);
2345
+ return super.removeSync(id, ifVersion);
2346
+ }
2347
+ clearAsync(callback) {
2348
+ this.cache.clear();
2349
+ return super.clearAsync(callback);
2350
+ }
2351
+ clearSync() {
2352
+ this.cache.clear();
2353
+ super.clearSync();
2354
+ }
2355
+ childTransaction(callback) {
2356
+ return super.childTransaction(() => {
2357
+ let cache = this.cache;
2358
+ let previousChanges = childTxnChanges;
2359
+ try {
2360
+ childTxnChanges = new Set();
2361
+ return when(callback(), (result) => {
2362
+ if (result === ABORT)
2363
+ return abort();
2364
+ childTxnChanges = previousChanges;
2365
+ return result;
2366
+ }, abort);
2367
+ } catch(error) {
2368
+ abort(error);
2369
+ }
2370
+ function abort(error) {
2371
+ // if the transaction was aborted, remove all affected entries from cache
2372
+ for (let id of childTxnChanges)
2373
+ cache.delete(id);
2374
+ childTxnChanges = previousChanges;
2375
+ if (error)
2376
+ throw error;
2377
+ else
2378
+ return ABORT;
2379
+ }
2380
+ });
2381
+ }
2382
+ doesExist(key, versionOrValue) {
2383
+ let entry = this.cache.get(key);
2384
+ if (entry) {
2385
+ if (versionOrValue == null) {
2386
+ return versionOrValue !== null;
2387
+ } else if (this.useVersions) {
2388
+ return versionOrValue === IF_EXISTS$1 || entry.version === versionOrValue;
2389
+ }
2390
+ }
2391
+ return super.doesExist(key, versionOrValue);
2392
+ }
2393
+ };
2394
+ };
2395
+ function setGetLastVersion(get, getTxnId) {
2396
+ getLastVersion$1 = get;
2397
+ getLastTxnId$1 = getTxnId;
2398
+ }
2399
+
1976
2400
  let moduleRequire = typeof require == 'function' && require;
1977
2401
  function setRequire(require) {
1978
2402
  moduleRequire = require;
1979
2403
  }
1980
2404
 
1981
- setGetLastVersion(getLastVersion);
2405
+ setGetLastVersion(getLastVersion, getLastTxnId);
1982
2406
  let keyBytes, keyBytesView;
1983
2407
  const { onExit, getEnvsPointer, setEnvsPointer, getEnvFlags, setJSFlags } = nativeAddon;
1984
2408
  if (globalThis.__lmdb_envs__)
@@ -1989,12 +2413,29 @@ else
1989
2413
  // this is hard coded as an upper limit because it is important assumption of the fixed buffers in writing instructions
1990
2414
  // this corresponds to the max key size for 8KB pages
1991
2415
  const MAX_KEY_SIZE = 4026;
2416
+ // this is used as the key size by default because default page size is OS page size, which is usually
2417
+ // 4KB (but is 16KB on M-series MacOS), and this keeps a consistent max key size when no page size specified.
2418
+ const DEFAULT_MAX_KEY_SIZE = 1978;
1992
2419
  const DEFAULT_COMMIT_DELAY = 0;
1993
2420
 
1994
2421
  const allDbs = new Map();
1995
2422
  let defaultCompression;
1996
2423
  let hasRegisteredOnExit;
1997
2424
  function open(path$1, options) {
2425
+ if (nativeAddon.open) {
2426
+ if (nativeAddon.open !== open) {
2427
+ // this is the case when lmdb-js has been opened in both ESM and CJS mode, which means that there are two
2428
+ // separate JS modules, but they are both using the same native module.
2429
+ getLastVersion = nativeAddon.getLastVersion;
2430
+ getLastTxnId = nativeAddon.getLastTxnId;
2431
+ setGetLastVersion(getLastVersion, getLastTxnId);
2432
+ return nativeAddon.open(path$1, options);
2433
+ }
2434
+ } else {
2435
+ nativeAddon.open = open;
2436
+ nativeAddon.getLastVersion = getLastVersion;
2437
+ nativeAddon.getLastTxnId = getLastTxnId;
2438
+ }
1998
2439
  if (!keyBytes) // TODO: Consolidate get buffer and key buffer (don't think we need both)
1999
2440
  allocateFixedBuffer();
2000
2441
  if (typeof path$1 == 'object' && !options) {
@@ -2002,8 +2443,9 @@ function open(path$1, options) {
2002
2443
  path$1 = options.path;
2003
2444
  }
2004
2445
  options = options || {};
2446
+ let noFSAccess = options.noFSAccess; // this can only be configured on open, can't let users change it
2005
2447
  let userOptions = options;
2006
- if (!path$1) {
2448
+ if (path$1 == null) {
2007
2449
  options = Object.assign({
2008
2450
  deleteOnClose: true,
2009
2451
  noSync: true,
@@ -2014,17 +2456,16 @@ function open(path$1, options) {
2014
2456
  let extension = path.extname(path$1);
2015
2457
  let name = path.basename(path$1, extension);
2016
2458
  let is32Bit = arch().endsWith('32');
2017
- let remapChunks = options.remapChunks || (options.mapSize ?
2459
+ let remapChunks = options.remapChunks || options.encryptionKey || (options.mapSize ?
2018
2460
  (is32Bit && options.mapSize > 0x100000000) : // larger than fits in address space, must use dynamic maps
2019
2461
  is32Bit); // without a known map size, we default to being able to handle large data correctly/well*/
2462
+ let userMapSize = options.mapSize;
2020
2463
  options = Object.assign({
2021
- path: path$1,
2022
2464
  noSubdir: Boolean(extension),
2023
2465
  isRoot: true,
2024
2466
  maxDbs: 12,
2025
2467
  remapChunks,
2026
2468
  keyBytes,
2027
- pageSize: 4096,
2028
2469
  overlappingSync: (options.noSync || options.readOnly) ? false : (os != 'win32'),
2029
2470
  // default map size limit of 4 exabytes when using remapChunks, since it is not preallocated and we can
2030
2471
  // make it super huge.
@@ -2032,9 +2473,18 @@ function open(path$1, options) {
2032
2473
  0x20000, // Otherwise we start small with 128KB
2033
2474
  safeRestore: process.env.LMDB_RESTORE == 'safe',
2034
2475
  }, options);
2476
+ options.path = path$1;
2035
2477
  if (options.asyncTransactionOrder == 'strict') {
2036
2478
  options.strictAsyncOrder = true;
2037
2479
  }
2480
+ if (nativeAddon.version.major + nativeAddon.version.minor / 100 + nativeAddon.version.patch / 10000 < 0.0980) {
2481
+ options.overlappingSync = false; // not support on older versions
2482
+ options.trackMetrics = false;
2483
+ options.usePreviousSnapshot = false;
2484
+ options.safeRestore = false;
2485
+ options.remapChunks = false;
2486
+ if (!userMapSize) options.mapSize = 0x40000000; // 1 GB
2487
+ }
2038
2488
 
2039
2489
  if (!exists(options.noSubdir ? path.dirname(path$1) : path$1))
2040
2490
  fs.mkdirSync(options.noSubdir ? path.dirname(path$1) : path$1, { recursive: true }
@@ -2047,7 +2497,7 @@ function open(path$1, options) {
2047
2497
  return defaultCompression;
2048
2498
  compressionOptions = Object.assign({
2049
2499
  threshold: 1000,
2050
- dictionary: fs.readFileSync(new URL('./dict/dict.txt', (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('index.cjs', document.baseURI).href)).replace(/dist[\\\/]index.cjs$/, ''))),
2500
+ dictionary: fs.readFileSync(new URL('./dict/dict.txt', (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href)).replace(/dist[\\\/]index.cjs$/, ''))),
2051
2501
  getValueBytes: makeReusableBuffer(0),
2052
2502
  }, compressionOptions);
2053
2503
  let compression = Object.assign(new Compression(compressionOptions), compressionOptions);
@@ -2070,17 +2520,20 @@ function open(path$1, options) {
2070
2520
  (options.noMemInit ? 0x1000000 : 0) |
2071
2521
  (options.usePreviousSnapshot ? 0x2000000 : 0) |
2072
2522
  (options.remapChunks ? 0x4000000 : 0) |
2073
- (options.safeRestore ? 0x8000000 : 0);
2523
+ (options.safeRestore ? 0x800 : 0) |
2524
+ (options.trackMetrics ? 0x400 : 0);
2074
2525
 
2075
2526
  let env = new Env();
2076
2527
  let jsFlags = (options.overlappingSync ? 0x1000 : 0) |
2077
2528
  (options.separateFlushed ? 1 : 0) |
2078
2529
  (options.deleteOnClose ? 2 : 0);
2079
2530
  let rc = env.open(options, flags, jsFlags);
2531
+ env.path = path$1;
2080
2532
  if (rc)
2081
2533
  lmdbError(rc);
2534
+ delete options.keyBytes; // no longer needed, don't copy to stores
2082
2535
  let maxKeySize = env.getMaxKeySize();
2083
- maxKeySize = Math.min(maxKeySize, MAX_KEY_SIZE);
2536
+ maxKeySize = Math.min(maxKeySize, options.pageSize ? MAX_KEY_SIZE : DEFAULT_MAX_KEY_SIZE);
2084
2537
  flags = getEnvFlags(env.address); // re-retrieve them, they are not necessarily the same if we are connecting to an existing env
2085
2538
  if (flags & 0x1000) {
2086
2539
  if (userOptions.noSync) {
@@ -2134,14 +2587,14 @@ function open(path$1, options) {
2134
2587
  (dbOptions.integerDup ? 0x20 : 0) |
2135
2588
  (dbOptions.reverseDup ? 0x40 : 0) |
2136
2589
  (!options.readOnly && dbOptions.create !== false ? 0x40000 : 0) |
2137
- (dbOptions.useVersions ? 0x1000 : 0);
2590
+ (dbOptions.useVersions ? 0x100 : 0);
2138
2591
  let keyType = (dbOptions.keyIsUint32 || dbOptions.keyEncoding == 'uint32') ? 2 : keyIsBuffer ? 3 : 0;
2139
2592
  if (keyType == 2)
2140
2593
  flags |= 0x08; // integer key
2141
2594
 
2142
2595
  if (options.readOnly) {
2143
2596
  // in read-only mode we use a read-only txn to open the database
2144
- // TODO: LMDB is actually not entire thread-safe when it comes to opening databases with
2597
+ // TODO: LMDB is actually not entirely thread-safe when it comes to opening databases with
2145
2598
  // read-only transactions since there is a race condition on setting the update dbis that
2146
2599
  // occurs outside the lock
2147
2600
  // make sure we are using a fresh read txn, so we don't want to share with a cursor txn
@@ -2186,7 +2639,7 @@ function open(path$1, options) {
2186
2639
  }
2187
2640
  if (Encoder) {
2188
2641
  this.encoder = new Encoder(Object.assign(
2189
- assignConstrainedProperties(['copyBuffers', 'getStructures', 'saveStructures', 'useFloat32', 'useRecords', 'structuredClone', 'variableMapSize', 'useTimestamp32', 'largeBigIntToFloat', 'encodeUndefinedAsNil', 'int64AsNumber', 'onInvalidDate', 'mapsAsObjects', 'useTag259ForMaps', 'pack', 'maxSharedStructures', 'shouldShareStructure'],
2642
+ assignConstrainedProperties(['copyBuffers', 'getStructures', 'saveStructures', 'useFloat32', 'useRecords', 'structuredClone', 'variableMapSize', 'useTimestamp32', 'largeBigIntToFloat', 'encodeUndefinedAsNil', 'int64AsNumber', 'onInvalidDate', 'mapsAsObjects', 'useTag259ForMaps', 'pack', 'maxSharedStructures', 'shouldShareStructure', 'randomAccessStructure', 'freezeData'],
2190
2643
  this.sharedStructuresKey ? this.setupSharedStructures() : {
2191
2644
  copyBuffers: true, // need to copy any embedded buffers that are found since we use unsafe buffers
2192
2645
  }, options, dbOptions), this.encoder));
@@ -2230,8 +2683,11 @@ function open(path$1, options) {
2230
2683
  callback(null, db);
2231
2684
  return db;
2232
2685
  }
2233
- backup(path) {
2234
- return new Promise((resolve, reject) => env.copy(path, false, (error) => {
2686
+ backup(path$1, compact) {
2687
+ if (noFSAccess)
2688
+ return;
2689
+ fs.mkdirSync(path.dirname(path$1), { recursive: true });
2690
+ return new Promise((resolve, reject) => env.copy(path$1, false, (error) => {
2235
2691
  if (error) {
2236
2692
  reject(error);
2237
2693
  } else {
@@ -2343,11 +2799,14 @@ function openAsClass(path, options) {
2343
2799
  function getLastVersion() {
2344
2800
  return keyBytesView.getFloat64(16, true);
2345
2801
  }
2346
-
2347
2802
  function setLastVersion(version) {
2348
2803
  return keyBytesView.setFloat64(16, version, true);
2349
2804
  }
2350
2805
 
2806
+ function getLastTxnId() {
2807
+ return keyBytesView.getUint32(32, true);
2808
+ }
2809
+
2351
2810
  const KEY_BUFFER_SIZE = 4096;
2352
2811
  function allocateFixedBuffer() {
2353
2812
  keyBytes = typeof Buffer != 'undefined' ? Buffer.allocUnsafeSlow(KEY_BUFFER_SIZE) : new Uint8Array(KEY_BUFFER_SIZE);
@@ -2355,7 +2814,7 @@ function allocateFixedBuffer() {
2355
2814
  keyBytesView = keyBytes.dataView || (keyBytes.dataView = new DataView(keyBytes.buffer, 0, KEY_BUFFER_SIZE)); // max key size is actually 4026
2356
2815
  keyBytes.uint32 = new Uint32Array(keyBuffer, 0, KEY_BUFFER_SIZE >> 2);
2357
2816
  keyBytes.float64 = new Float64Array(keyBuffer, 0, KEY_BUFFER_SIZE >> 3);
2358
- keyBytes.uint32.address = keyBytes.address = keyBuffer.address = getAddress(keyBytes);
2817
+ keyBytes.uint32.address = keyBytes.address = keyBuffer.address = getAddress(keyBuffer);
2359
2818
  }
2360
2819
 
2361
2820
  function exists(path) {
@@ -2379,72 +2838,76 @@ function assignConstrainedProperties(allowedProperties, target) {
2379
2838
  return target;
2380
2839
  }
2381
2840
 
2382
- function levelup(store) {
2383
- return Object.assign(Object.create(store), {
2384
- get(key, options, callback) {
2385
- let result = store.get(key);
2386
- if (typeof options == 'function')
2387
- callback = options;
2388
- if (callback) {
2389
- if (result === undefined)
2390
- callback(new NotFoundError());
2391
- else
2392
- callback(null, result);
2393
- } else {
2394
- if (result === undefined)
2395
- return Promise.reject(new NotFoundError());
2396
- else
2397
- return Promise.resolve(result);
2398
- }
2399
- },
2400
- });
2401
- }
2402
- class NotFoundError extends Error {
2403
- constructor(message) {
2404
- super(message);
2405
- this.name = 'NotFoundError';
2406
- this.notFound = true;
2407
- }
2841
+ function levelup(store) {
2842
+ return Object.assign(Object.create(store), {
2843
+ get(key, options, callback) {
2844
+ let result = store.get(key);
2845
+ if (typeof options == 'function')
2846
+ callback = options;
2847
+ if (callback) {
2848
+ if (result === undefined)
2849
+ callback(new NotFoundError());
2850
+ else
2851
+ callback(null, result);
2852
+ } else {
2853
+ if (result === undefined)
2854
+ return Promise.reject(new NotFoundError());
2855
+ else
2856
+ return Promise.resolve(result);
2857
+ }
2858
+ },
2859
+ });
2860
+ }
2861
+ class NotFoundError extends Error {
2862
+ constructor(message) {
2863
+ super(message);
2864
+ this.name = 'NotFoundError';
2865
+ this.notFound = true;
2866
+ }
2408
2867
  }
2409
2868
 
2410
- orderedBinary__namespace.enableNullTermination();
2411
- setExternals({
2412
- arch: os$1.arch, fs: fs__default["default"], tmpdir: os$1.tmpdir, MsgpackrEncoder: msgpackr.Encoder, WeakLRUCache: weakLruCache.WeakLRUCache, orderedBinary: orderedBinary__namespace,
2413
- EventEmitter: events.EventEmitter, os: os$1.platform(), onExit(callback) {
2414
- if (process.getMaxListeners() < process.listenerCount('exit') + 8)
2415
- process.setMaxListeners(process.listenerCount('exit') + 8);
2416
- process.on('exit', callback);
2417
- },
2418
- });
2419
- let { noop } = nativeAddon;
2420
- const TransactionFlags = {
2421
- ABORTABLE: 1,
2422
- SYNCHRONOUS_COMMIT: 2,
2423
- NO_SYNC_FLUSH: 0x10000,
2424
- };
2425
- var index = {
2426
- open, openAsClass, getLastVersion, compareKey: orderedBinary$1.compareKeys, keyValueToBuffer: orderedBinary$1.toBufferKey, bufferToKeyValue: orderedBinary$1.fromBufferKey, ABORT, IF_EXISTS, asBinary, levelup, TransactionFlags
2869
+ orderedBinary__namespace.enableNullTermination();
2870
+ setExternals({
2871
+ arch: os$1.arch, fs: fs__default["default"], tmpdir: os$1.tmpdir, MsgpackrEncoder: msgpackr.Encoder, WeakLRUCache: weakLruCache.WeakLRUCache, orderedBinary: orderedBinary__namespace,
2872
+ EventEmitter: events.EventEmitter, os: os$1.platform(), onExit(callback) {
2873
+ if (process.getMaxListeners() < process.listenerCount('exit') + 8)
2874
+ process.setMaxListeners(process.listenerCount('exit') + 8);
2875
+ process.on('exit', callback);
2876
+ },
2877
+ });
2878
+ let { noop } = nativeAddon;
2879
+ const TransactionFlags = {
2880
+ ABORTABLE: 1,
2881
+ SYNCHRONOUS_COMMIT: 2,
2882
+ NO_SYNC_FLUSH: 0x10000,
2883
+ };
2884
+ var index = {
2885
+ open, openAsClass, getLastVersion, compareKey: orderedBinary$1.compareKeys, keyValueToBuffer: orderedBinary$1.toBufferKey, bufferToKeyValue: orderedBinary$1.fromBufferKey, ABORT, IF_EXISTS: IF_EXISTS$1, asBinary, levelup, TransactionFlags
2427
2886
  };
2428
2887
 
2429
- setRequire(module$1.createRequire((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('index.cjs', document.baseURI).href))));
2888
+ let require$1 = module$1.createRequire((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href)));
2889
+ setRequire(require$1);
2430
2890
  exports.v8AccelerationEnabled = false;
2431
2891
 
2432
2892
  let versions = process.versions;
2433
- let [ majorVersion, minorVersion ] = versions.node.split('.');
2434
-
2435
- if (versions.v8 && +majorVersion == nativeAddon.version.nodeCompiledVersion) {
2436
- let v8Funcs = {};
2437
- let fastApiCalls = (majorVersion == 17 || majorVersion == 18 || majorVersion == 16 && minorVersion > 6) && !process.env.DISABLE_TURBO_CALLS;
2438
- if (fastApiCalls)
2439
- v8.setFlagsFromString('--turbo-fast-api-calls');
2440
- nativeAddon.enableDirectV8(v8Funcs, fastApiCalls);
2441
- Object.assign(nativeAddon, v8Funcs);
2442
- exports.v8AccelerationEnabled = true;
2443
- } else if (majorVersion == 14) {
2444
- // node v14 only has ABI compatibility with node v16 for zero-arg clearKeptObjects
2445
- let v8Funcs = {};
2446
- nativeAddon.enableDirectV8(v8Funcs, false);
2447
- nativeAddon.clearKeptObjects = v8Funcs.clearKeptObjects;
2893
+ if (!versions.deno && !process.isBun) {
2894
+ let [ majorVersion, minorVersion ] = versions.node.split('.');
2895
+ if (versions.v8 && +majorVersion == nativeAddon.version.nodeCompiledVersion) {
2896
+ let v8Funcs = {};
2897
+ let fastApiCalls = (majorVersion == 17 || majorVersion == 18 || majorVersion == 16 && minorVersion > 8) && !process.env.DISABLE_TURBO_CALLS;
2898
+ if (fastApiCalls) {
2899
+ require$1('v8').setFlagsFromString('--turbo-fast-api-calls');
2900
+ }
2901
+ nativeAddon.enableDirectV8(v8Funcs, fastApiCalls);
2902
+ Object.assign(nativeAddon, v8Funcs);
2903
+ exports.v8AccelerationEnabled = true;
2904
+ } else if (majorVersion == 14) {
2905
+ // node v14 only has ABI compatibility with node v16 for zero-arg clearKeptObjects
2906
+ let v8Funcs = {};
2907
+ nativeAddon.enableDirectV8(v8Funcs, false);
2908
+ nativeAddon.clearKeptObjects = v8Funcs.clearKeptObjects;
2909
+ }
2910
+ nativeAddon.enableThreadSafeCalls();
2448
2911
  }
2449
2912
  setNativeFunctions(nativeAddon);
2450
2913
 
@@ -2465,13 +2928,16 @@ Object.defineProperty(exports, 'keyValueToBuffer', {
2465
2928
  get: function () { return orderedBinary$1.toBufferKey; }
2466
2929
  });
2467
2930
  exports.ABORT = ABORT;
2468
- exports.IF_EXISTS = IF_EXISTS;
2931
+ exports.IF_EXISTS = IF_EXISTS$1;
2932
+ exports.SKIP = SKIP;
2469
2933
  exports.TransactionFlags = TransactionFlags;
2470
2934
  exports.allDbs = allDbs;
2471
2935
  exports.asBinary = asBinary;
2472
2936
  exports["default"] = index;
2937
+ exports.getLastTxnId = getLastTxnId;
2473
2938
  exports.getLastVersion = getLastVersion;
2474
2939
  exports.levelup = levelup;
2940
+ exports.nativeAddon = nativeAddon;
2475
2941
  exports.noop = noop;
2476
2942
  exports.open = open;
2477
2943
  exports.openAsClass = openAsClass;