jexidb 2.1.4 → 2.1.5
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.
- package/README.md +339 -191
- package/dist/Database.cjs +508 -77
- package/package.json +4 -1
- package/src/Database.mjs +242 -41
- package/src/FileHandler.mjs +235 -33
- package/src/Serializer.mjs +65 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jexidb",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "JexiDB is a pure JS NPM library for managing data on disk efficiently, without the need for a server.",
|
|
6
6
|
"main": "./dist/Database.cjs",
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
},
|
|
23
23
|
"author": "EdenwareApps",
|
|
24
24
|
"license": "MIT",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=16.0.0"
|
|
27
|
+
},
|
|
25
28
|
"devDependencies": {
|
|
26
29
|
"@babel/cli": "^7.25.6",
|
|
27
30
|
"@babel/core": "^7.25.2",
|
package/src/Database.mjs
CHANGED
|
@@ -403,7 +403,19 @@ class Database extends EventEmitter {
|
|
|
403
403
|
saveTime: 0,
|
|
404
404
|
loadTime: 0
|
|
405
405
|
}
|
|
406
|
-
|
|
406
|
+
|
|
407
|
+
// Initialize integrity correction tracking
|
|
408
|
+
this.integrityCorrections = {
|
|
409
|
+
indexSync: 0, // index.totalLines vs offsets.length corrections
|
|
410
|
+
indexInconsistency: 0, // Index record count vs offsets mismatch
|
|
411
|
+
writeBufferForced: 0, // WriteBuffer not cleared after save
|
|
412
|
+
indexSaveFailures: 0, // Failed to save index data
|
|
413
|
+
dataIntegrity: 0, // General data integrity issues
|
|
414
|
+
utf8Recovery: 0, // UTF-8 decoding failures recovered
|
|
415
|
+
jsonRecovery: 0 // JSON parsing failures recovered
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
|
|
407
419
|
// Initialize usage stats for QueryManager
|
|
408
420
|
this.usageStats = {
|
|
409
421
|
totalQueries: 0,
|
|
@@ -596,7 +608,9 @@ class Database extends EventEmitter {
|
|
|
596
608
|
}
|
|
597
609
|
}
|
|
598
610
|
if (arrayStringFields.length > 0) {
|
|
599
|
-
|
|
611
|
+
if (this.opts.debugMode) {
|
|
612
|
+
console.warn(`⚠️ Warning: The following array:string indexed fields were not added to term mapping: ${arrayStringFields.join(', ')}. This may impact performance.`)
|
|
613
|
+
}
|
|
600
614
|
}
|
|
601
615
|
}
|
|
602
616
|
|
|
@@ -638,22 +652,27 @@ class Database extends EventEmitter {
|
|
|
638
652
|
}
|
|
639
653
|
|
|
640
654
|
/**
|
|
641
|
-
* Get term mapping fields from indexes (auto-detected)
|
|
655
|
+
* Get term mapping fields from configuration or indexes (auto-detected)
|
|
642
656
|
* @returns {string[]} Array of field names that use term mapping
|
|
643
657
|
*/
|
|
644
658
|
getTermMappingFields() {
|
|
659
|
+
// If termMappingFields is explicitly configured, use it
|
|
660
|
+
if (this.opts.termMappingFields && Array.isArray(this.opts.termMappingFields)) {
|
|
661
|
+
return [...this.opts.termMappingFields]
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Auto-detect fields that benefit from term mapping from indexes
|
|
645
665
|
if (!this.opts.indexes) return []
|
|
646
|
-
|
|
647
|
-
// Auto-detect fields that benefit from term mapping
|
|
666
|
+
|
|
648
667
|
const termMappingFields = []
|
|
649
|
-
|
|
668
|
+
|
|
650
669
|
for (const [field, type] of Object.entries(this.opts.indexes)) {
|
|
651
670
|
// Fields that should use term mapping (only array fields)
|
|
652
671
|
if (type === 'array:string') {
|
|
653
672
|
termMappingFields.push(field)
|
|
654
673
|
}
|
|
655
674
|
}
|
|
656
|
-
|
|
675
|
+
|
|
657
676
|
return termMappingFields
|
|
658
677
|
}
|
|
659
678
|
|
|
@@ -745,7 +764,7 @@ class Database extends EventEmitter {
|
|
|
745
764
|
|
|
746
765
|
// Reset closed state when reinitializing
|
|
747
766
|
this.closed = false
|
|
748
|
-
|
|
767
|
+
|
|
749
768
|
// Initialize managers (protected against double initialization)
|
|
750
769
|
this.initializeManagers()
|
|
751
770
|
|
|
@@ -768,7 +787,19 @@ class Database extends EventEmitter {
|
|
|
768
787
|
await this.load()
|
|
769
788
|
}
|
|
770
789
|
}
|
|
771
|
-
|
|
790
|
+
|
|
791
|
+
// CRITICAL INTEGRITY CHECK: Ensure IndexManager is consistent with loaded offsets
|
|
792
|
+
// This must happen immediately after load() to prevent any subsequent operations from seeing inconsistent state
|
|
793
|
+
if (this.indexManager && this.offsets && this.offsets.length > 0) {
|
|
794
|
+
const currentTotalLines = this.indexManager.totalLines || 0
|
|
795
|
+
if (currentTotalLines !== this.offsets.length) {
|
|
796
|
+
this.indexManager.setTotalLines(this.offsets.length)
|
|
797
|
+
if (this.opts.debugMode) {
|
|
798
|
+
console.log(`🔧 Post-load integrity sync: IndexManager totalLines ${currentTotalLines} → ${this.offsets.length}`)
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
772
803
|
// Manual save is now the default behavior
|
|
773
804
|
|
|
774
805
|
// CRITICAL FIX: Ensure IndexManager totalLines is consistent with offsets
|
|
@@ -936,11 +967,11 @@ class Database extends EventEmitter {
|
|
|
936
967
|
this.offsets = parsedIdxData.offsets
|
|
937
968
|
// CRITICAL FIX: Update IndexManager totalLines to match offsets length
|
|
938
969
|
// This ensures queries and length property work correctly even if offsets are reset later
|
|
939
|
-
if (this.indexManager
|
|
970
|
+
if (this.indexManager) {
|
|
940
971
|
this.indexManager.setTotalLines(this.offsets.length)
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
972
|
+
if (this.opts.debugMode) {
|
|
973
|
+
console.log(`📂 Loaded ${this.offsets.length} offsets from ${idxPath}, synced IndexManager totalLines`)
|
|
974
|
+
}
|
|
944
975
|
}
|
|
945
976
|
}
|
|
946
977
|
|
|
@@ -1738,7 +1769,13 @@ class Database extends EventEmitter {
|
|
|
1738
1769
|
// Check that all indexed records have valid line numbers
|
|
1739
1770
|
const indexedRecordCount = this.indexManager.getIndexedRecordCount?.() || allData.length
|
|
1740
1771
|
if (indexedRecordCount !== this.offsets.length) {
|
|
1741
|
-
|
|
1772
|
+
this.integrityCorrections.indexInconsistency++
|
|
1773
|
+
console.log(`🔧 Auto-corrected index consistency: ${indexedRecordCount} indexed → ${this.offsets.length} offsets`)
|
|
1774
|
+
|
|
1775
|
+
if (this.integrityCorrections.indexInconsistency > 5) {
|
|
1776
|
+
console.warn(`⚠️ Frequent index inconsistencies detected (${this.integrityCorrections.indexInconsistency} times)`)
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1742
1779
|
// Force consistency by setting totalLines to match offsets
|
|
1743
1780
|
this.indexManager.setTotalLines(this.offsets.length)
|
|
1744
1781
|
} else {
|
|
@@ -2292,7 +2329,7 @@ class Database extends EventEmitter {
|
|
|
2292
2329
|
*/
|
|
2293
2330
|
async find(criteria = {}, options = {}) {
|
|
2294
2331
|
this._validateInitialization('find')
|
|
2295
|
-
|
|
2332
|
+
|
|
2296
2333
|
// CRITICAL FIX: Validate state before find operation
|
|
2297
2334
|
this.validateState()
|
|
2298
2335
|
|
|
@@ -2306,38 +2343,33 @@ class Database extends EventEmitter {
|
|
|
2306
2343
|
|
|
2307
2344
|
try {
|
|
2308
2345
|
// INTEGRITY CHECK: Validate data consistency before querying
|
|
2309
|
-
//
|
|
2346
|
+
// This is a safety net for unexpected inconsistencies - should rarely trigger
|
|
2310
2347
|
if (this.indexManager && this.offsets && this.offsets.length > 0) {
|
|
2311
2348
|
const indexTotalLines = this.indexManager.totalLines || 0
|
|
2312
2349
|
const offsetsLength = this.offsets.length
|
|
2313
2350
|
|
|
2314
2351
|
if (indexTotalLines !== offsetsLength) {
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2352
|
+
// This should be extremely rare - indicates a real bug if it happens frequently
|
|
2353
|
+
this.integrityCorrections.dataIntegrity++
|
|
2354
|
+
|
|
2355
|
+
// Only show in debug mode - these corrections indicate real issues
|
|
2318
2356
|
if (this.opts.debugMode) {
|
|
2319
|
-
console.log(`🔧
|
|
2357
|
+
console.log(`🔧 Integrity correction needed: index.totalLines ${indexTotalLines} → ${offsetsLength} (${this.integrityCorrections.dataIntegrity} total)`)
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
// Warn if corrections are becoming frequent (indicates a real problem)
|
|
2361
|
+
if (this.integrityCorrections.dataIntegrity > 5) {
|
|
2362
|
+
console.warn(`⚠️ Frequent integrity corrections (${this.integrityCorrections.dataIntegrity} times) - this indicates a systemic issue`)
|
|
2320
2363
|
}
|
|
2321
2364
|
|
|
2322
|
-
|
|
2323
|
-
|
|
2365
|
+
this.indexManager.setTotalLines(offsetsLength)
|
|
2366
|
+
|
|
2367
|
+
// Try to persist the fix, but don't fail the operation if it doesn't work
|
|
2324
2368
|
try {
|
|
2325
2369
|
await this._saveIndexDataToFile()
|
|
2326
|
-
if (this.opts.debugMode) {
|
|
2327
|
-
console.log(`💾 Saved corrected index data to prevent future inconsistencies`)
|
|
2328
|
-
}
|
|
2329
2370
|
} catch (error) {
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
|
|
2335
|
-
// Verify the fix worked
|
|
2336
|
-
const newIndexTotalLines = this.indexManager.totalLines || 0
|
|
2337
|
-
if (newIndexTotalLines === offsetsLength) {
|
|
2338
|
-
console.log(`✅ Data integrity successfully corrected: index.totalLines=${newIndexTotalLines}, offsets.length=${offsetsLength}`)
|
|
2339
|
-
} else {
|
|
2340
|
-
console.error(`❌ Data integrity correction failed: index.totalLines=${newIndexTotalLines}, offsets.length=${offsetsLength}`)
|
|
2371
|
+
// Just track the failure - don't throw since this is a safety net
|
|
2372
|
+
this.integrityCorrections.indexSaveFailures++
|
|
2341
2373
|
}
|
|
2342
2374
|
}
|
|
2343
2375
|
}
|
|
@@ -4779,6 +4811,78 @@ class Database extends EventEmitter {
|
|
|
4779
4811
|
return this._getWriteBufferBaseLineNumber() + writeBufferIndex
|
|
4780
4812
|
}
|
|
4781
4813
|
|
|
4814
|
+
|
|
4815
|
+
/**
|
|
4816
|
+
* Attempts to recover a corrupted line by cleaning invalid characters and fixing common JSON issues
|
|
4817
|
+
* @param {string} line - The corrupted line to recover
|
|
4818
|
+
* @returns {string|null} - The recovered line or null if recovery is not possible
|
|
4819
|
+
*/
|
|
4820
|
+
_tryRecoverLine(line) {
|
|
4821
|
+
if (!line || typeof line !== 'string') {
|
|
4822
|
+
return null
|
|
4823
|
+
}
|
|
4824
|
+
|
|
4825
|
+
try {
|
|
4826
|
+
// Try parsing as-is first
|
|
4827
|
+
JSON.parse(line)
|
|
4828
|
+
return line // Line is already valid
|
|
4829
|
+
} catch (e) {
|
|
4830
|
+
// Line is corrupted, attempt recovery
|
|
4831
|
+
}
|
|
4832
|
+
|
|
4833
|
+
let recovered = line.trim()
|
|
4834
|
+
|
|
4835
|
+
// Remove invalid control characters (except \n, \r, \t)
|
|
4836
|
+
recovered = recovered.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '')
|
|
4837
|
+
|
|
4838
|
+
// Try to close unclosed strings
|
|
4839
|
+
// Count quotes and ensure they're balanced
|
|
4840
|
+
const quoteCount = (recovered.match(/"/g) || []).length
|
|
4841
|
+
if (quoteCount % 2 !== 0) {
|
|
4842
|
+
// Odd number of quotes - try to close the string
|
|
4843
|
+
const lastQuoteIndex = recovered.lastIndexOf('"')
|
|
4844
|
+
if (lastQuoteIndex > 0) {
|
|
4845
|
+
// Check if we're inside a string (not escaped)
|
|
4846
|
+
const beforeLastQuote = recovered.substring(0, lastQuoteIndex)
|
|
4847
|
+
const escapedQuotes = (beforeLastQuote.match(/\\"/g) || []).length
|
|
4848
|
+
const unescapedQuotes = (beforeLastQuote.match(/"/g) || []).length - escapedQuotes
|
|
4849
|
+
|
|
4850
|
+
if (unescapedQuotes % 2 !== 0) {
|
|
4851
|
+
// We're inside an unclosed string - try to close it
|
|
4852
|
+
recovered = recovered + '"'
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4856
|
+
|
|
4857
|
+
// Try to close unclosed arrays/objects
|
|
4858
|
+
const openBraces = (recovered.match(/\{/g) || []).length
|
|
4859
|
+
const closeBraces = (recovered.match(/\}/g) || []).length
|
|
4860
|
+
const openBrackets = (recovered.match(/\[/g) || []).length
|
|
4861
|
+
const closeBrackets = (recovered.match(/\]/g) || []).length
|
|
4862
|
+
|
|
4863
|
+
// Remove trailing commas before closing braces/brackets
|
|
4864
|
+
recovered = recovered.replace(/,\s*([}\]])/g, '$1')
|
|
4865
|
+
|
|
4866
|
+
// Try to close arrays
|
|
4867
|
+
if (openBrackets > closeBrackets) {
|
|
4868
|
+
recovered = recovered + ']'.repeat(openBrackets - closeBrackets)
|
|
4869
|
+
}
|
|
4870
|
+
|
|
4871
|
+
// Try to close objects
|
|
4872
|
+
if (openBraces > closeBraces) {
|
|
4873
|
+
recovered = recovered + '}'.repeat(openBraces - closeBraces)
|
|
4874
|
+
}
|
|
4875
|
+
|
|
4876
|
+
// Final validation - try to parse
|
|
4877
|
+
try {
|
|
4878
|
+
JSON.parse(recovered)
|
|
4879
|
+
return recovered
|
|
4880
|
+
} catch (e) {
|
|
4881
|
+
// Recovery failed
|
|
4882
|
+
return null
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4885
|
+
|
|
4782
4886
|
async *_streamingRecoveryGenerator(criteria, options, alreadyYielded = 0, map = null, remainingSkipValue = 0) {
|
|
4783
4887
|
if (this._offsetRecoveryInProgress) {
|
|
4784
4888
|
return
|
|
@@ -5004,6 +5108,17 @@ class Database extends EventEmitter {
|
|
|
5004
5108
|
// If no data at all, return empty
|
|
5005
5109
|
if (this.indexOffset === 0 && this.writeBuffer.length === 0) return
|
|
5006
5110
|
|
|
5111
|
+
// CRITICAL FIX: Wait for any ongoing save operations to complete
|
|
5112
|
+
// This prevents reading partially written data
|
|
5113
|
+
if (this.isSaving) {
|
|
5114
|
+
if (this.opts.debugMode) {
|
|
5115
|
+
console.log('🔍 walk(): waiting for save operation to complete')
|
|
5116
|
+
}
|
|
5117
|
+
while (this.isSaving) {
|
|
5118
|
+
await new Promise(resolve => setTimeout(resolve, 10))
|
|
5119
|
+
}
|
|
5120
|
+
}
|
|
5121
|
+
|
|
5007
5122
|
let count = 0
|
|
5008
5123
|
let remainingSkip = options.skip || 0
|
|
5009
5124
|
|
|
@@ -5142,10 +5257,50 @@ class Database extends EventEmitter {
|
|
|
5142
5257
|
} catch (error) {
|
|
5143
5258
|
// CRITICAL FIX: Log deserialization errors instead of silently ignoring them
|
|
5144
5259
|
// This helps identify data corruption issues
|
|
5145
|
-
if (
|
|
5260
|
+
if (this.opts.debugMode) {
|
|
5146
5261
|
console.warn(`⚠️ walk(): Failed to deserialize record at offset ${row.start}: ${error.message}`)
|
|
5147
5262
|
console.warn(`⚠️ walk(): Problematic line (first 200 chars): ${row.line.substring(0, 200)}`)
|
|
5148
5263
|
}
|
|
5264
|
+
|
|
5265
|
+
// CRITICAL FIX: Attempt to recover corrupted line before giving up
|
|
5266
|
+
const recoveredLine = this._tryRecoverLine(row.line)
|
|
5267
|
+
if (recoveredLine) {
|
|
5268
|
+
try {
|
|
5269
|
+
const record = this.serializer.deserialize(recoveredLine)
|
|
5270
|
+
if (record !== null) {
|
|
5271
|
+
this.integrityCorrections.jsonRecovery++
|
|
5272
|
+
console.log(`🔧 Recovered corrupted JSON line (${this.integrityCorrections.jsonRecovery} recoveries)`)
|
|
5273
|
+
|
|
5274
|
+
if (this.integrityCorrections.jsonRecovery > 20) {
|
|
5275
|
+
console.warn(`⚠️ Frequent JSON recovery detected (${this.integrityCorrections.jsonRecovery} times) - may indicate data corruption`)
|
|
5276
|
+
}
|
|
5277
|
+
|
|
5278
|
+
const recordWithTerms = this.restoreTermIdsAfterDeserialization(record)
|
|
5279
|
+
|
|
5280
|
+
if (remainingSkip > 0) {
|
|
5281
|
+
remainingSkip--
|
|
5282
|
+
continue
|
|
5283
|
+
}
|
|
5284
|
+
|
|
5285
|
+
count++
|
|
5286
|
+
if (options.includeOffsets) {
|
|
5287
|
+
yield { entry: recordWithTerms, start: row.start, _: row._ || 0 }
|
|
5288
|
+
} else {
|
|
5289
|
+
if (this.opts.includeLinePosition) {
|
|
5290
|
+
recordWithTerms._ = row._ || 0
|
|
5291
|
+
}
|
|
5292
|
+
yield recordWithTerms
|
|
5293
|
+
}
|
|
5294
|
+
continue // Successfully recovered and yielded
|
|
5295
|
+
}
|
|
5296
|
+
} catch (recoveryError) {
|
|
5297
|
+
// Recovery attempt failed, continue with normal error handling
|
|
5298
|
+
if (this.opts.debugMode) {
|
|
5299
|
+
console.warn(`⚠️ walk(): Line recovery failed: ${recoveryError.message}`)
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
}
|
|
5303
|
+
|
|
5149
5304
|
if (!this._offsetRecoveryInProgress) {
|
|
5150
5305
|
for await (const recoveredEntry of this._streamingRecoveryGenerator(criteria, options, count, map, remainingSkip)) {
|
|
5151
5306
|
yield recoveredEntry
|
|
@@ -5250,10 +5405,50 @@ class Database extends EventEmitter {
|
|
|
5250
5405
|
} catch (error) {
|
|
5251
5406
|
// CRITICAL FIX: Log deserialization errors instead of silently ignoring them
|
|
5252
5407
|
// This helps identify data corruption issues
|
|
5253
|
-
if (
|
|
5408
|
+
if (this.opts.debugMode) {
|
|
5254
5409
|
console.warn(`⚠️ walk(): Failed to deserialize record at offset ${row.start}: ${error.message}`)
|
|
5255
5410
|
console.warn(`⚠️ walk(): Problematic line (first 200 chars): ${row.line.substring(0, 200)}`)
|
|
5256
5411
|
}
|
|
5412
|
+
|
|
5413
|
+
// CRITICAL FIX: Attempt to recover corrupted line before giving up
|
|
5414
|
+
const recoveredLine = this._tryRecoverLine(row.line)
|
|
5415
|
+
if (recoveredLine) {
|
|
5416
|
+
try {
|
|
5417
|
+
const entry = await this.serializer.deserialize(recoveredLine, { compress: this.opts.compress, v8: this.opts.v8 })
|
|
5418
|
+
if (entry !== null) {
|
|
5419
|
+
this.integrityCorrections.jsonRecovery++
|
|
5420
|
+
console.log(`🔧 Recovered corrupted JSON line (${this.integrityCorrections.jsonRecovery} recoveries)`)
|
|
5421
|
+
|
|
5422
|
+
if (this.integrityCorrections.jsonRecovery > 20) {
|
|
5423
|
+
console.warn(`⚠️ Frequent JSON recovery detected (${this.integrityCorrections.jsonRecovery} times) - may indicate data corruption`)
|
|
5424
|
+
}
|
|
5425
|
+
|
|
5426
|
+
const entryWithTerms = this.restoreTermIdsAfterDeserialization(entry)
|
|
5427
|
+
|
|
5428
|
+
if (remainingSkip > 0) {
|
|
5429
|
+
remainingSkip--
|
|
5430
|
+
continue
|
|
5431
|
+
}
|
|
5432
|
+
|
|
5433
|
+
count++
|
|
5434
|
+
if (options.includeOffsets) {
|
|
5435
|
+
yield { entry: entryWithTerms, start: row.start, _: row._ || this.offsets.findIndex(n => n === row.start) }
|
|
5436
|
+
} else {
|
|
5437
|
+
if (this.opts.includeLinePosition) {
|
|
5438
|
+
entryWithTerms._ = row._ || this.offsets.findIndex(n => n === row.start)
|
|
5439
|
+
}
|
|
5440
|
+
yield entryWithTerms
|
|
5441
|
+
}
|
|
5442
|
+
continue // Successfully recovered and yielded
|
|
5443
|
+
}
|
|
5444
|
+
} catch (recoveryError) {
|
|
5445
|
+
// Recovery attempt failed, continue with normal error handling
|
|
5446
|
+
if (this.opts.debugMode) {
|
|
5447
|
+
console.warn(`⚠️ walk(): Line recovery failed: ${recoveryError.message}`)
|
|
5448
|
+
}
|
|
5449
|
+
}
|
|
5450
|
+
}
|
|
5451
|
+
|
|
5257
5452
|
if (!this._offsetRecoveryInProgress) {
|
|
5258
5453
|
for await (const recoveredEntry of this._streamingRecoveryGenerator(criteria, options, count, map, remainingSkip)) {
|
|
5259
5454
|
yield recoveredEntry
|
|
@@ -5497,7 +5692,13 @@ class Database extends EventEmitter {
|
|
|
5497
5692
|
await this.save()
|
|
5498
5693
|
// Ensure writeBuffer is cleared after save
|
|
5499
5694
|
if (this.writeBuffer.length > 0) {
|
|
5500
|
-
|
|
5695
|
+
this.integrityCorrections.writeBufferForced++
|
|
5696
|
+
console.log(`🔧 Forced WriteBuffer clear after save (${this.writeBuffer.length} items remaining)`)
|
|
5697
|
+
|
|
5698
|
+
if (this.integrityCorrections.writeBufferForced > 3) {
|
|
5699
|
+
console.warn(`⚠️ Frequent WriteBuffer clear issues detected (${this.integrityCorrections.writeBufferForced} times)`)
|
|
5700
|
+
}
|
|
5701
|
+
|
|
5501
5702
|
this.writeBuffer = []
|
|
5502
5703
|
this.writeBufferOffsets = []
|
|
5503
5704
|
this.writeBufferSizes = []
|
|
@@ -5622,7 +5823,8 @@ class Database extends EventEmitter {
|
|
|
5622
5823
|
console.log(`💾 Index data saved to ${idxPath}`)
|
|
5623
5824
|
}
|
|
5624
5825
|
} catch (error) {
|
|
5625
|
-
|
|
5826
|
+
this.integrityCorrections.indexSaveFailures++
|
|
5827
|
+
console.warn(`⚠️ Index save failure (${this.integrityCorrections.indexSaveFailures} times): ${error.message}`)
|
|
5626
5828
|
throw error // Re-throw to let caller handle
|
|
5627
5829
|
}
|
|
5628
5830
|
}
|
|
@@ -5698,5 +5900,4 @@ class Database extends EventEmitter {
|
|
|
5698
5900
|
}
|
|
5699
5901
|
|
|
5700
5902
|
export { Database }
|
|
5701
|
-
export default Database
|
|
5702
5903
|
|