jexidb 2.0.1 → 2.0.3
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/CHANGELOG.md +109 -0
- package/README.md +142 -27
- package/dist/JSONLDatabase.js +240 -101
- package/dist/index.js +13 -4
- package/docs/API.md +390 -0
- package/docs/EXAMPLES.md +177 -0
- package/docs/MIGRATION.md +295 -0
- package/docs/README.md +184 -0
- package/examples/auto-save-example.js +158 -0
- package/examples/cjs-usage.cjs +82 -0
- package/examples/close-vs-delete-example.js +71 -0
- package/examples/esm-usage.js +113 -0
- package/examples/example-columns.idx.jdb +0 -0
- package/examples/example-columns.jdb +9 -0
- package/examples/example-options.idx.jdb +0 -0
- package/examples/example-options.jdb +0 -0
- package/examples/example-users.idx.jdb +0 -0
- package/examples/example-users.jdb +5 -0
- package/examples/simple-test.js +55 -0
- package/package.json +5 -2
- package/src/JSONLDatabase.js +245 -102
- package/src/index.js +7 -3
package/dist/JSONLDatabase.js
CHANGED
|
@@ -33,13 +33,41 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
33
33
|
} else {
|
|
34
34
|
this.filePath = filePath;
|
|
35
35
|
}
|
|
36
|
+
|
|
37
|
+
// Enhanced configuration with intelligent defaults
|
|
36
38
|
this.options = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
// Original options
|
|
40
|
+
batchSize: 50,
|
|
41
|
+
// Reduced from 100 for faster response
|
|
39
42
|
create: true,
|
|
40
43
|
// Create database if it doesn't exist (default: true)
|
|
41
44
|
clear: false,
|
|
42
45
|
// Clear database on load if not empty (default: false)
|
|
46
|
+
|
|
47
|
+
// Auto-save intelligent configuration
|
|
48
|
+
autoSave: true,
|
|
49
|
+
// Enable auto-save by default
|
|
50
|
+
autoSaveThreshold: 50,
|
|
51
|
+
// Flush when buffer reaches 50 records
|
|
52
|
+
autoSaveInterval: 5000,
|
|
53
|
+
// Flush every 5 seconds
|
|
54
|
+
forceSaveOnClose: true,
|
|
55
|
+
// Always save when closing
|
|
56
|
+
|
|
57
|
+
// Performance configuration
|
|
58
|
+
adaptiveBatchSize: true,
|
|
59
|
+
// Adjust batch size based on usage
|
|
60
|
+
minBatchSize: 10,
|
|
61
|
+
// Minimum batch size for flush
|
|
62
|
+
maxBatchSize: 200,
|
|
63
|
+
// Maximum batch size for performance
|
|
64
|
+
|
|
65
|
+
// Memory management
|
|
66
|
+
maxMemoryUsage: 'auto',
|
|
67
|
+
// Calculate automatically or use fixed value
|
|
68
|
+
maxFlushChunkBytes: 8 * 1024 * 1024,
|
|
69
|
+
// 8MB default
|
|
70
|
+
|
|
43
71
|
...options
|
|
44
72
|
};
|
|
45
73
|
|
|
@@ -47,6 +75,11 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
47
75
|
if (this.options.clear === true) {
|
|
48
76
|
this.options.create = true;
|
|
49
77
|
}
|
|
78
|
+
|
|
79
|
+
// Auto-save timer and state
|
|
80
|
+
this.autoSaveTimer = null;
|
|
81
|
+
this.lastFlushTime = null;
|
|
82
|
+
this.lastAutoSaveTime = Date.now();
|
|
50
83
|
this.isInitialized = false;
|
|
51
84
|
this.offsets = [];
|
|
52
85
|
this.indexOffset = 0;
|
|
@@ -278,11 +311,29 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
278
311
|
|
|
279
312
|
// Convert back to Map objects
|
|
280
313
|
for (const [field, indexMap] of Object.entries(savedIndexes)) {
|
|
281
|
-
if
|
|
314
|
+
// Initialize index if it doesn't exist
|
|
315
|
+
if (!this.indexes[field]) {
|
|
282
316
|
this.indexes[field] = new Map();
|
|
283
|
-
|
|
284
|
-
|
|
317
|
+
}
|
|
318
|
+
this.indexes[field] = new Map();
|
|
319
|
+
for (const [value, indices] of Object.entries(indexMap)) {
|
|
320
|
+
// Convert value back to original type based on field configuration
|
|
321
|
+
let convertedValue = value;
|
|
322
|
+
if (this.indexes[field] && this.indexes[field].constructor === Map) {
|
|
323
|
+
// Try to convert based on field type
|
|
324
|
+
if (field === 'id' || field.includes('id') || field.includes('Id')) {
|
|
325
|
+
convertedValue = parseInt(value, 10);
|
|
326
|
+
} else if (typeof value === 'string' && !isNaN(parseFloat(value))) {
|
|
327
|
+
// Try to convert numeric strings back to numbers
|
|
328
|
+
const num = parseFloat(value);
|
|
329
|
+
if (Number.isInteger(num)) {
|
|
330
|
+
convertedValue = parseInt(value, 10);
|
|
331
|
+
} else {
|
|
332
|
+
convertedValue = num;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
285
335
|
}
|
|
336
|
+
this.indexes[field].set(convertedValue, new Set(indices));
|
|
286
337
|
}
|
|
287
338
|
}
|
|
288
339
|
return true;
|
|
@@ -340,7 +391,7 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
340
391
|
}
|
|
341
392
|
}
|
|
342
393
|
|
|
343
|
-
// ORIGINAL STRATEGY: Buffer in memory + batch write
|
|
394
|
+
// ORIGINAL STRATEGY: Buffer in memory + batch write with intelligent auto-save
|
|
344
395
|
async insert(data) {
|
|
345
396
|
if (!this.isInitialized) {
|
|
346
397
|
throw new Error('Database not initialized');
|
|
@@ -363,23 +414,76 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
363
414
|
// Add to index immediately for searchability
|
|
364
415
|
this.addToIndex(record, this.recordCount - 1);
|
|
365
416
|
|
|
366
|
-
//
|
|
367
|
-
if (this.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
417
|
+
// Intelligent auto-save logic
|
|
418
|
+
if (this.options.autoSave) {
|
|
419
|
+
// Auto-save based on threshold
|
|
420
|
+
if (this.insertionBuffer.length >= this.options.autoSaveThreshold) {
|
|
421
|
+
await this.flush();
|
|
422
|
+
this.emit('buffer-full');
|
|
423
|
+
}
|
|
371
424
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
425
|
+
// Auto-save based on time interval
|
|
426
|
+
if (!this.autoSaveTimer) {
|
|
427
|
+
this.autoSaveTimer = setTimeout(async () => {
|
|
428
|
+
if (this.insertionBuffer.length > 0) {
|
|
429
|
+
await this.flush();
|
|
430
|
+
this.emit('auto-save-timer');
|
|
431
|
+
}
|
|
432
|
+
this.autoSaveTimer = null;
|
|
433
|
+
}, this.options.autoSaveInterval);
|
|
434
|
+
}
|
|
435
|
+
} else {
|
|
436
|
+
// Manual mode: flush only when buffer is full
|
|
437
|
+
if (this.insertionBuffer.length >= this.insertionStats.batchSize) {
|
|
438
|
+
await this.flushInsertionBuffer();
|
|
439
|
+
}
|
|
375
440
|
}
|
|
441
|
+
this.shouldSave = true;
|
|
376
442
|
|
|
377
443
|
// Emit insert event
|
|
378
444
|
this.emit('insert', record, this.recordCount - 1);
|
|
379
445
|
return record; // Return immediately (ORIGINAL STRATEGY)
|
|
380
446
|
}
|
|
381
447
|
|
|
382
|
-
//
|
|
448
|
+
// PUBLIC METHOD: Flush buffer to disk
|
|
449
|
+
async flush() {
|
|
450
|
+
if (!this.isInitialized) {
|
|
451
|
+
throw new Error('Database not initialized');
|
|
452
|
+
}
|
|
453
|
+
if (this.insertionBuffer.length > 0) {
|
|
454
|
+
const flushCount = this.insertionBuffer.length;
|
|
455
|
+
await this.flushInsertionBuffer();
|
|
456
|
+
this.lastFlushTime = Date.now();
|
|
457
|
+
this.emit('buffer-flush', flushCount);
|
|
458
|
+
return flushCount;
|
|
459
|
+
}
|
|
460
|
+
return 0;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// PUBLIC METHOD: Force save - always saves regardless of buffer size
|
|
464
|
+
async forceSave() {
|
|
465
|
+
if (!this.isInitialized) {
|
|
466
|
+
throw new Error('Database not initialized');
|
|
467
|
+
}
|
|
468
|
+
await this.flush();
|
|
469
|
+
await this.save();
|
|
470
|
+
this.emit('save-complete');
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// PUBLIC METHOD: Get buffer status information
|
|
474
|
+
getBufferStatus() {
|
|
475
|
+
return {
|
|
476
|
+
pendingCount: this.insertionBuffer.length,
|
|
477
|
+
bufferSize: this.options.batchSize,
|
|
478
|
+
lastFlush: this.lastFlushTime,
|
|
479
|
+
lastAutoSave: this.lastAutoSaveTime,
|
|
480
|
+
shouldFlush: this.insertionBuffer.length >= this.options.autoSaveThreshold,
|
|
481
|
+
autoSaveEnabled: this.options.autoSave,
|
|
482
|
+
autoSaveTimer: this.autoSaveTimer ? 'active' : 'inactive'
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// ULTRA-OPTIMIZED STRATEGY: Bulk flush with minimal I/O (chunked to avoid OOM)
|
|
383
487
|
async flushInsertionBuffer() {
|
|
384
488
|
if (this.insertionBuffer.length === 0) {
|
|
385
489
|
return;
|
|
@@ -395,44 +499,50 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
395
499
|
currentOffset = 0;
|
|
396
500
|
}
|
|
397
501
|
|
|
398
|
-
//
|
|
399
|
-
const
|
|
400
|
-
|
|
502
|
+
// Write in chunks to avoid allocating a huge buffer/string at once
|
|
503
|
+
const maxChunkBytes = this.options.maxFlushChunkBytes || 8 * 1024 * 1024; // 8MB default
|
|
504
|
+
let chunkParts = [];
|
|
505
|
+
let chunkBytes = 0;
|
|
401
506
|
|
|
402
|
-
//
|
|
507
|
+
// We'll push offsets directly to avoid creating a separate large array
|
|
508
|
+
const pendingOffsets = [];
|
|
403
509
|
for (let i = 0; i < this.insertionBuffer.length; i++) {
|
|
404
510
|
const record = this.insertionBuffer[i];
|
|
405
|
-
|
|
406
|
-
// Records are already indexed in insert/insertMany methods
|
|
407
|
-
// No need to index again here
|
|
408
|
-
|
|
409
|
-
// Serialize record (batch operation)
|
|
410
511
|
const line = JSON.stringify(record) + '\n';
|
|
411
|
-
|
|
512
|
+
const lineBytes = Buffer.byteLength(line, 'utf8');
|
|
513
|
+
|
|
514
|
+
// Track offset for this record
|
|
515
|
+
pendingOffsets.push(currentOffset);
|
|
516
|
+
currentOffset += lineBytes;
|
|
517
|
+
|
|
518
|
+
// If one line is larger than chunk size, write the current chunk and then this line alone
|
|
519
|
+
if (lineBytes > maxChunkBytes) {
|
|
520
|
+
if (chunkParts.length > 0) {
|
|
521
|
+
await _fs.promises.appendFile(this.filePath, chunkParts.join(''));
|
|
522
|
+
chunkParts.length = 0;
|
|
523
|
+
chunkBytes = 0;
|
|
524
|
+
}
|
|
525
|
+
await _fs.promises.appendFile(this.filePath, line);
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
412
528
|
|
|
413
|
-
//
|
|
414
|
-
|
|
415
|
-
|
|
529
|
+
// If adding this line would exceed the chunk size, flush current chunk first
|
|
530
|
+
if (chunkBytes + lineBytes > maxChunkBytes) {
|
|
531
|
+
await _fs.promises.appendFile(this.filePath, chunkParts.join(''));
|
|
532
|
+
chunkParts.length = 0;
|
|
533
|
+
chunkBytes = 0;
|
|
534
|
+
}
|
|
535
|
+
chunkParts.push(line);
|
|
536
|
+
chunkBytes += lineBytes;
|
|
537
|
+
}
|
|
538
|
+
if (chunkParts.length > 0) {
|
|
539
|
+
await _fs.promises.appendFile(this.filePath, chunkParts.join(''));
|
|
416
540
|
}
|
|
417
541
|
|
|
418
|
-
//
|
|
419
|
-
|
|
420
|
-
const batchBuffer = Buffer.from(batchString, 'utf8');
|
|
421
|
-
|
|
422
|
-
// Single file write operation
|
|
423
|
-
await _fs.promises.appendFile(this.filePath, batchBuffer);
|
|
424
|
-
|
|
425
|
-
// Batch update offsets
|
|
426
|
-
this.offsets.push(...offsets);
|
|
427
|
-
|
|
428
|
-
// Record count is already updated in insert/insertMany methods
|
|
429
|
-
// No need to update it again here
|
|
430
|
-
|
|
431
|
-
// Clear the insertion buffer
|
|
542
|
+
// Update offsets and clear buffer
|
|
543
|
+
this.offsets.push(...pendingOffsets);
|
|
432
544
|
this.insertionBuffer.length = 0;
|
|
433
|
-
|
|
434
|
-
// Mark that we need to save (offset line will be added by save() method)
|
|
435
|
-
this.shouldSave = true;
|
|
545
|
+
this.shouldSave = true; // Mark that we need to save (offset line will be added by save())
|
|
436
546
|
} catch (error) {
|
|
437
547
|
console.error('Error flushing insertion buffer:', error);
|
|
438
548
|
throw new Error(`Failed to flush insertion buffer: ${error.message}`);
|
|
@@ -459,20 +569,21 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
459
569
|
matchingIndices = this.queryIndex(indexedCriteria);
|
|
460
570
|
}
|
|
461
571
|
|
|
462
|
-
// If no indexed fields
|
|
463
|
-
if (
|
|
572
|
+
// If no indexed fields, start with all records
|
|
573
|
+
if (indexedFields.length === 0) {
|
|
464
574
|
matchingIndices = Array.from({
|
|
465
575
|
length: this.recordCount
|
|
466
576
|
}, (_, i) => i);
|
|
577
|
+
} else if (matchingIndices.length === 0) {
|
|
578
|
+
// If we have indexed fields but no matches, return empty array
|
|
579
|
+
return [];
|
|
467
580
|
}
|
|
468
581
|
if (matchingIndices.length === 0) {
|
|
469
582
|
return [];
|
|
470
583
|
}
|
|
471
584
|
|
|
472
|
-
// Step 2: Collect results from
|
|
585
|
+
// Step 2: Collect results from disk (existing records)
|
|
473
586
|
const results = [];
|
|
474
|
-
|
|
475
|
-
// First, get results from disk (existing records)
|
|
476
587
|
for (const index of matchingIndices) {
|
|
477
588
|
if (index < this.offsets.length) {
|
|
478
589
|
const offset = this.offsets[index];
|
|
@@ -489,54 +600,16 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
489
600
|
}
|
|
490
601
|
}
|
|
491
602
|
|
|
492
|
-
//
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
// Use the same queryIndex logic for buffer records
|
|
496
|
-
for (const [field, fieldCriteria] of Object.entries(indexedFields.reduce((acc, field) => {
|
|
497
|
-
acc[field] = criteria[field];
|
|
498
|
-
return acc;
|
|
499
|
-
}, {}))) {
|
|
500
|
-
const indexMap = this.indexes[field];
|
|
501
|
-
if (indexMap) {
|
|
502
|
-
if (typeof fieldCriteria === 'object' && !Array.isArray(fieldCriteria)) {
|
|
503
|
-
// Handle operators like 'in'
|
|
504
|
-
for (const [operator, operatorValue] of Object.entries(fieldCriteria)) {
|
|
505
|
-
if (operator === 'in' && Array.isArray(operatorValue)) {
|
|
506
|
-
for (const searchValue of operatorValue) {
|
|
507
|
-
const indexSet = indexMap.get(searchValue);
|
|
508
|
-
if (indexSet) {
|
|
509
|
-
for (const index of indexSet) {
|
|
510
|
-
if (index >= this.recordCount - this.insertionBuffer.length) {
|
|
511
|
-
bufferIndices.add(index);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
} else {
|
|
522
|
-
// No indexed fields, include all buffer records
|
|
603
|
+
// Step 3: Add results from buffer (new records) if buffer is not empty
|
|
604
|
+
if (this.insertionBuffer.length > 0) {
|
|
605
|
+
// Check each buffer record against criteria
|
|
523
606
|
for (let i = 0; i < this.insertionBuffer.length; i++) {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
const bufferOffset = bufferIndex - (this.recordCount - this.insertionBuffer.length);
|
|
531
|
-
if (bufferOffset >= 0 && bufferOffset < this.insertionBuffer.length) {
|
|
532
|
-
const record = this.insertionBuffer[bufferOffset];
|
|
533
|
-
|
|
534
|
-
// Check non-indexed fields
|
|
535
|
-
if (nonIndexedFields.length === 0 || this.matchesCriteria(record, nonIndexedFields.reduce((acc, field) => {
|
|
536
|
-
acc[field] = criteria[field];
|
|
537
|
-
return acc;
|
|
538
|
-
}, {}))) {
|
|
539
|
-
results.push(record);
|
|
607
|
+
const record = this.insertionBuffer[i];
|
|
608
|
+
if (record && !record._deleted) {
|
|
609
|
+
// Check if record matches all criteria
|
|
610
|
+
if (this.matchesCriteria(record, criteria)) {
|
|
611
|
+
results.push(record);
|
|
612
|
+
}
|
|
540
613
|
}
|
|
541
614
|
}
|
|
542
615
|
}
|
|
@@ -830,11 +903,22 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
830
903
|
this.shouldSave = false;
|
|
831
904
|
}
|
|
832
905
|
async close() {
|
|
906
|
+
// Clear auto-save timer
|
|
907
|
+
if (this.autoSaveTimer) {
|
|
908
|
+
clearTimeout(this.autoSaveTimer);
|
|
909
|
+
this.autoSaveTimer = null;
|
|
910
|
+
}
|
|
911
|
+
|
|
833
912
|
// Flush any pending inserts first
|
|
834
913
|
if (this.insertionBuffer.length > 0) {
|
|
835
|
-
await this.
|
|
914
|
+
await this.flush();
|
|
836
915
|
}
|
|
837
|
-
|
|
916
|
+
|
|
917
|
+
// Force save on close if enabled
|
|
918
|
+
if (this.options.forceSaveOnClose && this.shouldSave) {
|
|
919
|
+
await this.save();
|
|
920
|
+
this.emit('close-save-complete');
|
|
921
|
+
} else if (this.shouldSave) {
|
|
838
922
|
await this.save();
|
|
839
923
|
}
|
|
840
924
|
if (this.fileHandle) {
|
|
@@ -842,6 +926,7 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
842
926
|
this.fileHandle = null;
|
|
843
927
|
}
|
|
844
928
|
this.isInitialized = false;
|
|
929
|
+
this.emit('close');
|
|
845
930
|
}
|
|
846
931
|
get length() {
|
|
847
932
|
return this.recordCount;
|
|
@@ -857,7 +942,16 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
857
942
|
// No buffer in memory - on-demand reading
|
|
858
943
|
fileHandle: this.fileHandle ? 'open' : 'closed',
|
|
859
944
|
insertionBufferSize: this.insertionBuffer.length,
|
|
860
|
-
batchSize: this.insertionStats.batchSize
|
|
945
|
+
batchSize: this.insertionStats.batchSize,
|
|
946
|
+
// Auto-save information
|
|
947
|
+
autoSave: {
|
|
948
|
+
enabled: this.options.autoSave,
|
|
949
|
+
threshold: this.options.autoSaveThreshold,
|
|
950
|
+
interval: this.options.autoSaveInterval,
|
|
951
|
+
timerActive: this.autoSaveTimer ? true : false,
|
|
952
|
+
lastFlush: this.lastFlushTime,
|
|
953
|
+
lastAutoSave: this.lastAutoSaveTime
|
|
954
|
+
}
|
|
861
955
|
};
|
|
862
956
|
}
|
|
863
957
|
get indexStats() {
|
|
@@ -867,6 +961,33 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
867
961
|
};
|
|
868
962
|
}
|
|
869
963
|
|
|
964
|
+
// PUBLIC METHOD: Configure performance settings
|
|
965
|
+
configurePerformance(settings) {
|
|
966
|
+
if (settings.batchSize !== undefined) {
|
|
967
|
+
this.options.batchSize = Math.max(this.options.minBatchSize, Math.min(this.options.maxBatchSize, settings.batchSize));
|
|
968
|
+
this.insertionStats.batchSize = this.options.batchSize;
|
|
969
|
+
}
|
|
970
|
+
if (settings.autoSaveThreshold !== undefined) {
|
|
971
|
+
this.options.autoSaveThreshold = settings.autoSaveThreshold;
|
|
972
|
+
}
|
|
973
|
+
if (settings.autoSaveInterval !== undefined) {
|
|
974
|
+
this.options.autoSaveInterval = settings.autoSaveInterval;
|
|
975
|
+
}
|
|
976
|
+
this.emit('performance-configured', this.options);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// PUBLIC METHOD: Get performance configuration
|
|
980
|
+
getPerformanceConfig() {
|
|
981
|
+
return {
|
|
982
|
+
batchSize: this.options.batchSize,
|
|
983
|
+
autoSaveThreshold: this.options.autoSaveThreshold,
|
|
984
|
+
autoSaveInterval: this.options.autoSaveInterval,
|
|
985
|
+
adaptiveBatchSize: this.options.adaptiveBatchSize,
|
|
986
|
+
minBatchSize: this.options.minBatchSize,
|
|
987
|
+
maxBatchSize: this.options.maxBatchSize
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
|
|
870
991
|
/**
|
|
871
992
|
* Compatibility method: readColumnIndex - gets unique values from indexed columns only
|
|
872
993
|
* Maintains compatibility with JexiDB v1 code
|
|
@@ -1002,10 +1123,28 @@ class JSONLDatabase extends _events.EventEmitter {
|
|
|
1002
1123
|
}
|
|
1003
1124
|
}
|
|
1004
1125
|
async destroy() {
|
|
1126
|
+
// destroy() agora é equivalente a close() - fecha instância, mantém arquivo
|
|
1005
1127
|
await this.close();
|
|
1006
|
-
await _fs.promises.unlink(this.filePath);
|
|
1007
1128
|
this.emit('destroy');
|
|
1008
1129
|
}
|
|
1130
|
+
async deleteDatabase() {
|
|
1131
|
+
await this.close();
|
|
1132
|
+
await _fs.promises.unlink(this.filePath);
|
|
1133
|
+
|
|
1134
|
+
// Also remove index file if it exists
|
|
1135
|
+
try {
|
|
1136
|
+
const indexPath = this.filePath.replace('.jdb', '.idx.jdb');
|
|
1137
|
+
await _fs.promises.unlink(indexPath);
|
|
1138
|
+
} catch (e) {
|
|
1139
|
+
// Index file might not exist
|
|
1140
|
+
}
|
|
1141
|
+
this.emit('delete-database');
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// Alias for deleteDatabase
|
|
1145
|
+
async removeDatabase() {
|
|
1146
|
+
return this.deleteDatabase();
|
|
1147
|
+
}
|
|
1009
1148
|
async findOne(criteria = {}) {
|
|
1010
1149
|
const results = await this.find(criteria);
|
|
1011
1150
|
return results.length > 0 ? results[0] : null;
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.Database = void 0;
|
|
6
7
|
Object.defineProperty(exports, "FileHandler", {
|
|
7
8
|
enumerable: true,
|
|
8
9
|
get: function () {
|
|
@@ -207,11 +208,15 @@ class JexiDBCompatibility extends _JSONLDatabase.default {
|
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
/**
|
|
210
|
-
*
|
|
211
|
+
* Streaming iterator over records without loading all into memory
|
|
212
|
+
* Supports a limited subset of options: { limit }
|
|
211
213
|
*/
|
|
212
214
|
async *walk(options = {}) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
+
// Delegate to core engine streaming implementation
|
|
216
|
+
// Ensure pending inserts are flushed by calling super.walk
|
|
217
|
+
for await (const record of super.walk({
|
|
218
|
+
limit: options.limit
|
|
219
|
+
})) {
|
|
215
220
|
yield record;
|
|
216
221
|
}
|
|
217
222
|
}
|
|
@@ -289,7 +294,11 @@ class JexiDBCompatibility extends _JSONLDatabase.default {
|
|
|
289
294
|
*/
|
|
290
295
|
|
|
291
296
|
// Export the compatibility wrapper as default
|
|
292
|
-
var _default = exports.default = JexiDBCompatibility; //
|
|
297
|
+
var _default = exports.default = JexiDBCompatibility; // Retrocompat: named export 'Database' (JexiDB v1 used { Database })
|
|
298
|
+
const Database = exports.Database = JexiDBCompatibility;
|
|
299
|
+
|
|
300
|
+
// Export auxiliary classes for advanced use
|
|
301
|
+
|
|
293
302
|
// Export useful constants
|
|
294
303
|
const OPERATORS = exports.OPERATORS = {
|
|
295
304
|
GT: '>',
|