@pixagram/lacerta-db 0.7.3 → 0.8.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.
package/index.js CHANGED
@@ -1,13 +1,32 @@
1
1
  /**
2
- * LacertaDB V0.7.3 - Production Library with QuickStore
3
- * Added: QuickStore feature, private property/method conventions
4
- * @version 0.7.3
2
+ * LacertaDB V0.8.0 - Production Library with QuickStore
3
+ *
4
+ * A high-performance, browser-based document database with support for:
5
+ * - IndexedDB storage with connection pooling
6
+ * - Multiple caching strategies (LRU, LFU, TTL)
7
+ * - Full-text search and geospatial indexing
8
+ * - Document encryption and compression
9
+ * - Binary attachments via OPFS (Origin Private File System)
10
+ * - MongoDB-like query syntax and aggregation pipeline
11
+ * - Schema migrations and versioning
12
+ * - QuickStore for fast localStorage-based operations
13
+ *
14
+ * @module LacertaDB
15
+ * @version 0.8.0
5
16
  * @license MIT
17
+ * @author Pixagram SA
6
18
  */
7
19
 
8
20
  'use strict';
9
21
 
10
- // Add polyfill at the top of the file:
22
+ // ========================
23
+ // Polyfills
24
+ // ========================
25
+
26
+ /**
27
+ * Polyfill for requestIdleCallback if not available in the browser.
28
+ * Falls back to setTimeout with 0ms delay for environments that don't support it.
29
+ */
11
30
  if (!window.requestIdleCallback) {
12
31
  window.requestIdleCallback = function(callback) {
13
32
  return setTimeout(callback, 0);
@@ -15,29 +34,72 @@ if (!window.requestIdleCallback) {
15
34
  window.cancelIdleCallback = clearTimeout;
16
35
  }
17
36
 
18
- // Dependencies - for browser environments using a bundler (e.g., Webpack, Vite)
37
+ // ========================
38
+ // Dependencies
39
+ // ========================
40
+
41
+ /**
42
+ * Import external serialization and encoding utilities.
43
+ * TurboSerial provides efficient binary serialization with compression and deduplication.
44
+ * TurboBase64 provides fast base64 encoding/decoding.
45
+ */
19
46
  import TurboSerial from "@pixagram/turboserial";
20
47
  import TurboBase64 from "@pixagram/turbobase64";
21
48
 
49
+ /**
50
+ * Global serializer instance configured for optimal performance.
51
+ * @type {TurboSerial}
52
+ */
22
53
  const serializer = new TurboSerial({
23
- compression: true,
24
- deduplication: true,
25
- simdOptimization: true,
26
- detectCircular: true
54
+ compression: true, // Enable built-in compression
55
+ deduplication: true, // Deduplicate repeated values
56
+ simdOptimization: true, // Use SIMD instructions when available
57
+ detectCircular: true // Handle circular references
27
58
  });
59
+
60
+ /**
61
+ * Global Base64 encoder/decoder instance.
62
+ * @type {TurboBase64}
63
+ */
28
64
  const base64 = new TurboBase64();
29
65
 
30
66
  // ========================
31
67
  // Quick Store (localStorage based)
32
68
  // ========================
33
69
 
70
+ /**
71
+ * QuickStore provides fast, synchronous access to small amounts of data using localStorage.
72
+ * Ideal for frequently accessed metadata, user preferences, or small documents
73
+ * where the overhead of IndexedDB is not justified.
74
+ *
75
+ * Data is serialized using TurboSerial and encoded as Base64 for localStorage storage.
76
+ *
77
+ * @class QuickStore
78
+ * @example
79
+ * const store = new QuickStore('myDatabase');
80
+ * store.add('user1', { name: 'John', age: 30 });
81
+ * const user = store.get('user1');
82
+ * console.log(user.name); // 'John'
83
+ */
34
84
  class QuickStore {
85
+ /**
86
+ * Creates a new QuickStore instance.
87
+ * @param {string} dbName - The database name used to namespace localStorage keys
88
+ */
35
89
  constructor(dbName) {
90
+ /** @private @type {string} Database name */
36
91
  this._dbName = dbName;
92
+ /** @private @type {string} Prefix for all localStorage keys */
37
93
  this._keyPrefix = `lacertadb_${dbName}_quickstore_`;
94
+ /** @private @type {string} Key for storing the document index */
38
95
  this._indexKey = `${this._keyPrefix}index`;
39
96
  }
40
97
 
98
+ /**
99
+ * Reads the index of all stored document IDs from localStorage.
100
+ * @private
101
+ * @returns {Array<string>} Array of document IDs
102
+ */
41
103
  _readIndex() {
42
104
  const indexStr = localStorage.getItem(this._indexKey);
43
105
  if (!indexStr) return [];
@@ -49,12 +111,24 @@ class QuickStore {
49
111
  }
50
112
  }
51
113
 
114
+ /**
115
+ * Writes the document ID index to localStorage.
116
+ * @private
117
+ * @param {Array<string>} index - Array of document IDs to store
118
+ */
52
119
  _writeIndex(index) {
53
120
  const serializedIndex = serializer.serialize(index);
54
121
  const encodedIndex = base64.encode(serializedIndex);
55
122
  localStorage.setItem(this._indexKey, encodedIndex);
56
123
  }
57
124
 
125
+ /**
126
+ * Adds or replaces a document in the QuickStore.
127
+ * @param {string} docId - Unique identifier for the document
128
+ * @param {Object} data - Document data to store
129
+ * @returns {boolean} True if successful, false otherwise
130
+ * @throws {LacertaDBError} If localStorage quota is exceeded
131
+ */
58
132
  add(docId, data) {
59
133
  const key = `${this._keyPrefix}data_${docId}`;
60
134
  try {
@@ -76,6 +150,11 @@ class QuickStore {
76
150
  }
77
151
  }
78
152
 
153
+ /**
154
+ * Retrieves a document from the QuickStore.
155
+ * @param {string} docId - Document ID to retrieve
156
+ * @returns {Object|null} The document data, or null if not found
157
+ */
79
158
  get(docId) {
80
159
  const key = `${this._keyPrefix}data_${docId}`;
81
160
  const stored = localStorage.getItem(key);
@@ -90,10 +169,20 @@ class QuickStore {
90
169
  return null;
91
170
  }
92
171
 
172
+ /**
173
+ * Updates an existing document (alias for add).
174
+ * @param {string} docId - Document ID to update
175
+ * @param {Object} data - New document data
176
+ * @returns {boolean} True if successful
177
+ */
93
178
  update(docId, data) {
94
179
  return this.add(docId, data);
95
180
  }
96
181
 
182
+ /**
183
+ * Deletes a document from the QuickStore.
184
+ * @param {string} docId - Document ID to delete
185
+ */
97
186
  delete(docId) {
98
187
  const key = `${this._keyPrefix}data_${docId}`;
99
188
  localStorage.removeItem(key);
@@ -106,6 +195,10 @@ class QuickStore {
106
195
  }
107
196
  }
108
197
 
198
+ /**
199
+ * Retrieves all documents from the QuickStore.
200
+ * @returns {Array<Object>} Array of all documents with their _id fields
201
+ */
109
202
  getAll() {
110
203
  const index = this._readIndex();
111
204
  return index.map(docId => {
@@ -114,6 +207,12 @@ class QuickStore {
114
207
  }).filter(Boolean);
115
208
  }
116
209
 
210
+ /**
211
+ * Queries documents using MongoDB-like filter syntax.
212
+ * Uses the global queryEngine for evaluation.
213
+ * @param {Object} [filter={}] - Query filter object
214
+ * @returns {Array<Object>} Array of matching documents
215
+ */
117
216
  query(filter = {}) {
118
217
  const allDocs = this.getAll();
119
218
  if (Object.keys(filter).length === 0) return allDocs;
@@ -122,6 +221,9 @@ class QuickStore {
122
221
  return allDocs.filter(doc => queryEngine.evaluate(doc, filter));
123
222
  }
124
223
 
224
+ /**
225
+ * Clears all documents from the QuickStore.
226
+ */
125
227
  clear() {
126
228
  const index = this._readIndex();
127
229
  for (const docId of index) {
@@ -130,6 +232,10 @@ class QuickStore {
130
232
  localStorage.removeItem(this._indexKey);
131
233
  }
132
234
 
235
+ /**
236
+ * Gets the number of documents in the QuickStore.
237
+ * @type {number}
238
+ */
133
239
  get size() {
134
240
  return this._readIndex().length;
135
241
  }
@@ -139,12 +245,34 @@ class QuickStore {
139
245
  // Global IndexedDB Connection Pool
140
246
  // ========================
141
247
 
248
+ /**
249
+ * Manages a pool of IndexedDB connections to optimize database access.
250
+ * Connections are reused across multiple operations and reference-counted
251
+ * to ensure proper cleanup.
252
+ *
253
+ * @class IndexedDBConnectionPool
254
+ */
142
255
  class IndexedDBConnectionPool {
256
+ /**
257
+ * Creates a new connection pool instance.
258
+ */
143
259
  constructor() {
260
+ /** @private @type {Map<string, IDBDatabase>} Active database connections */
144
261
  this._connections = new Map();
262
+ /** @private @type {Map<string, number>} Reference counts for each connection */
145
263
  this._refCounts = new Map();
146
264
  }
147
265
 
266
+ /**
267
+ * Gets or creates a connection to an IndexedDB database.
268
+ * If a connection already exists, increments its reference count.
269
+ *
270
+ * @param {string} dbName - Database name
271
+ * @param {number} [version=1] - Database version
272
+ * @param {Function} [upgradeCallback] - Callback for onupgradeneeded event
273
+ * @returns {Promise<IDBDatabase>} The database connection
274
+ * @throws {LacertaDBError} If database cannot be opened
275
+ */
148
276
  async getConnection(dbName, version = 1, upgradeCallback) {
149
277
  const key = `${dbName}_v${version}`;
150
278
 
@@ -171,6 +299,13 @@ class IndexedDBConnectionPool {
171
299
  return db;
172
300
  }
173
301
 
302
+ /**
303
+ * Releases a database connection. If reference count reaches zero,
304
+ * the connection is closed.
305
+ *
306
+ * @param {string} dbName - Database name
307
+ * @param {number} [version=1] - Database version
308
+ */
174
309
  releaseConnection(dbName, version = 1) {
175
310
  const key = `${dbName}_v${version}`;
176
311
  const refCount = this._refCounts.get(key) || 0;
@@ -188,6 +323,10 @@ class IndexedDBConnectionPool {
188
323
  }
189
324
  }
190
325
 
326
+ /**
327
+ * Closes all connections in the pool.
328
+ * Should be called during application cleanup.
329
+ */
191
330
  closeAll() {
192
331
  for (const db of this._connections.values()) {
193
332
  db.close();
@@ -197,18 +336,34 @@ class IndexedDBConnectionPool {
197
336
  }
198
337
  }
199
338
 
339
+ /** @type {IndexedDBConnectionPool} Global connection pool instance */
200
340
  const connectionPool = new IndexedDBConnectionPool();
201
341
 
202
342
  // ========================
203
343
  // Async Mutex for managing concurrent operations
204
344
  // ========================
205
345
 
346
+ /**
347
+ * Provides mutual exclusion for asynchronous operations.
348
+ * Ensures that only one async operation can access a critical section at a time.
349
+ *
350
+ * @class AsyncMutex
351
+ */
206
352
  class AsyncMutex {
353
+ /**
354
+ * Creates a new mutex instance.
355
+ */
207
356
  constructor() {
357
+ /** @private @type {Array<Function>} Queue of waiting operations */
208
358
  this._queue = [];
359
+ /** @private @type {boolean} Whether the mutex is currently locked */
209
360
  this._locked = false;
210
361
  }
211
362
 
363
+ /**
364
+ * Acquires the mutex lock. Returns a release function when the lock is acquired.
365
+ * @returns {Promise<Function>} A function to release the lock
366
+ */
212
367
  acquire() {
213
368
  return new Promise(resolve => {
214
369
  this._queue.push(resolve);
@@ -216,11 +371,21 @@ class AsyncMutex {
216
371
  });
217
372
  }
218
373
 
374
+ /**
375
+ * Releases the mutex lock.
376
+ */
219
377
  release() {
220
378
  this._locked = false;
221
379
  this._dispatch();
222
380
  }
223
381
 
382
+ /**
383
+ * Runs a callback with exclusive access to the mutex.
384
+ * Automatically acquires and releases the lock.
385
+ *
386
+ * @param {Function} callback - Async function to run exclusively
387
+ * @returns {Promise<*>} Result of the callback
388
+ */
224
389
  async runExclusive(callback) {
225
390
  const release = await this.acquire();
226
391
  try {
@@ -230,6 +395,10 @@ class AsyncMutex {
230
395
  }
231
396
  }
232
397
 
398
+ /**
399
+ * Dispatches the next waiting operation if the mutex is unlocked.
400
+ * @private
401
+ */
233
402
  _dispatch() {
234
403
  if (this._locked || this._queue.length === 0) {
235
404
  return;
@@ -244,12 +413,29 @@ class AsyncMutex {
244
413
  // Custom error class for LacertaDB
245
414
  // ========================
246
415
 
416
+ /**
417
+ * Custom error class for LacertaDB operations.
418
+ * Includes error codes for programmatic error handling.
419
+ *
420
+ * @class LacertaDBError
421
+ * @extends Error
422
+ */
247
423
  class LacertaDBError extends Error {
424
+ /**
425
+ * Creates a new LacertaDBError.
426
+ * @param {string} message - Human-readable error message
427
+ * @param {string} code - Machine-readable error code
428
+ * @param {Error} [originalError] - Original error that caused this error
429
+ */
248
430
  constructor(message, code, originalError) {
249
431
  super(message);
432
+ /** @type {string} Error name identifier */
250
433
  this.name = 'LacertaDBError';
434
+ /** @type {string} Machine-readable error code */
251
435
  this.code = code;
436
+ /** @type {Error|null} Original error if this is a wrapped error */
252
437
  this.originalError = originalError || null;
438
+ /** @type {string} ISO timestamp when the error occurred */
253
439
  this.timestamp = new Date().toISOString();
254
440
  }
255
441
  }
@@ -258,15 +444,38 @@ class LacertaDBError extends Error {
258
444
  // LRU Cache Implementation
259
445
  // ========================
260
446
 
447
+ /**
448
+ * Least Recently Used (LRU) cache implementation.
449
+ * Evicts the least recently accessed items when the cache reaches its maximum size.
450
+ * Optionally supports TTL (time-to-live) for cache entries.
451
+ *
452
+ * @class LRUCache
453
+ */
261
454
  class LRUCache {
455
+ /**
456
+ * Creates a new LRU cache.
457
+ * @param {number} [maxSize=100] - Maximum number of items to store
458
+ * @param {number|null} [ttl=null] - Time-to-live in milliseconds (null for no expiration)
459
+ */
262
460
  constructor(maxSize = 100, ttl = null) {
461
+ /** @private @type {number} Maximum cache size */
263
462
  this._maxSize = maxSize;
463
+ /** @private @type {number|null} TTL in milliseconds */
264
464
  this._ttl = ttl;
465
+ /** @private @type {Map<string, *>} Cache storage */
265
466
  this._cache = new Map();
467
+ /** @private @type {Array<string>} Access order tracking (most recent at end) */
266
468
  this._accessOrder = [];
469
+ /** @private @type {Map<string, number>} Timestamps for TTL */
267
470
  this._timestamps = new Map();
268
471
  }
269
472
 
473
+ /**
474
+ * Retrieves a value from the cache.
475
+ * Updates the access order and checks TTL expiration.
476
+ * @param {string} key - Cache key
477
+ * @returns {*|null} Cached value or null if not found/expired
478
+ */
270
479
  get(key) {
271
480
  if (!this._cache.has(key)) {
272
481
  return null;
@@ -289,6 +498,12 @@ class LRUCache {
289
498
  return this._cache.get(key);
290
499
  }
291
500
 
501
+ /**
502
+ * Stores a value in the cache.
503
+ * Evicts the oldest items if the cache exceeds maxSize.
504
+ * @param {string} key - Cache key
505
+ * @param {*} value - Value to store
506
+ */
292
507
  set(key, value) {
293
508
  if (this._cache.has(key)) {
294
509
  const index = this._accessOrder.indexOf(key);
@@ -308,6 +523,11 @@ class LRUCache {
308
523
  }
309
524
  }
310
525
 
526
+ /**
527
+ * Deletes an item from the cache.
528
+ * @param {string} key - Cache key to delete
529
+ * @returns {boolean} True if item was deleted
530
+ */
311
531
  delete(key) {
312
532
  const index = this._accessOrder.indexOf(key);
313
533
  if (index > -1) {
@@ -317,12 +537,20 @@ class LRUCache {
317
537
  return this._cache.delete(key);
318
538
  }
319
539
 
540
+ /**
541
+ * Clears all items from the cache.
542
+ */
320
543
  clear() {
321
544
  this._cache.clear();
322
545
  this._accessOrder = [];
323
546
  this._timestamps.clear();
324
547
  }
325
548
 
549
+ /**
550
+ * Checks if a key exists in the cache (respects TTL).
551
+ * @param {string} key - Cache key
552
+ * @returns {boolean} True if key exists and is not expired
553
+ */
326
554
  has(key) {
327
555
  if (this._ttl && this._cache.has(key)) {
328
556
  const timestamp = this._timestamps.get(key);
@@ -334,13 +562,30 @@ class LRUCache {
334
562
  return this._cache.has(key);
335
563
  }
336
564
 
565
+ /**
566
+ * Gets the current number of items in the cache.
567
+ * @type {number}
568
+ */
337
569
  get size() {
338
570
  return this._cache.size;
339
571
  }
340
572
  }
341
573
 
342
- // LFU (Least Frequently Used) Cache
574
+ // ========================
575
+ // LFU Cache Implementation
576
+ // ========================
577
+
578
+ /**
579
+ * Least Frequently Used (LFU) cache implementation.
580
+ * Evicts the least frequently accessed items when the cache reaches its maximum size.
581
+ * @class LFUCache
582
+ */
343
583
  class LFUCache {
584
+ /**
585
+ * Creates a new LFU cache.
586
+ * @param {number} [maxSize=100] - Maximum number of items to store
587
+ * @param {number|null} [ttl=null] - Time-to-live in milliseconds
588
+ */
344
589
  constructor(maxSize = 100, ttl = null) {
345
590
  this._maxSize = maxSize;
346
591
  this._ttl = ttl;
@@ -349,6 +594,11 @@ class LFUCache {
349
594
  this._timestamps = new Map();
350
595
  }
351
596
 
597
+ /**
598
+ * Retrieves a value from the cache and increments its frequency.
599
+ * @param {string} key - Cache key
600
+ * @returns {*|null} Cached value or null if not found/expired
601
+ */
352
602
  get(key) {
353
603
  if (!this._cache.has(key)) {
354
604
  return null;
@@ -366,6 +616,11 @@ class LFUCache {
366
616
  return this._cache.get(key);
367
617
  }
368
618
 
619
+ /**
620
+ * Stores a value in the cache.
621
+ * @param {string} key - Cache key
622
+ * @param {*} value - Value to store
623
+ */
369
624
  set(key, value) {
370
625
  if (this._cache.has(key)) {
371
626
  this._cache.set(key, value);
@@ -391,39 +646,73 @@ class LFUCache {
391
646
  }
392
647
  }
393
648
 
649
+ /**
650
+ * Deletes an item from the cache.
651
+ * @param {string} key - Cache key to delete
652
+ * @returns {boolean} True if item was deleted
653
+ */
394
654
  delete(key) {
395
655
  this._frequencies.delete(key);
396
656
  this._timestamps.delete(key);
397
657
  return this._cache.delete(key);
398
658
  }
399
659
 
660
+ /** Clears all items from the cache. */
400
661
  clear() {
401
662
  this._cache.clear();
402
663
  this._frequencies.clear();
403
664
  this._timestamps.clear();
404
665
  }
405
666
 
667
+ /**
668
+ * Checks if a key exists in the cache.
669
+ * @param {string} key - Cache key
670
+ * @returns {boolean} True if key exists
671
+ */
406
672
  has(key) {
407
673
  return this._cache.has(key);
408
674
  }
409
675
 
676
+ /** @type {number} Current number of items in the cache */
410
677
  get size() {
411
678
  return this._cache.size;
412
679
  }
413
680
  }
414
681
 
415
- // TTL (Time To Live) Only Cache
682
+ // ========================
683
+ // TTL Cache Implementation
684
+ // ========================
685
+
686
+ /**
687
+ * Time-To-Live (TTL) only cache implementation.
688
+ * Items automatically expire after a specified duration.
689
+ * @class TTLCache
690
+ */
416
691
  class TTLCache {
692
+ /**
693
+ * Creates a new TTL cache.
694
+ * @param {number} [ttl=60000] - Time-to-live in milliseconds (default 1 minute)
695
+ */
417
696
  constructor(ttl = 60000) {
418
697
  this._ttl = ttl;
419
698
  this._cache = new Map();
420
699
  this._timers = new Map();
421
700
  }
422
701
 
702
+ /**
703
+ * Retrieves a value from the cache.
704
+ * @param {string} key - Cache key
705
+ * @returns {*|null} Cached value or null if not found
706
+ */
423
707
  get(key) {
424
708
  return this._cache.get(key) || null;
425
709
  }
426
710
 
711
+ /**
712
+ * Stores a value in the cache with automatic expiration.
713
+ * @param {string} key - Cache key
714
+ * @param {*} value - Value to store
715
+ */
427
716
  set(key, value) {
428
717
  if (this._timers.has(key)) {
429
718
  clearTimeout(this._timers.get(key));
@@ -437,6 +726,11 @@ class TTLCache {
437
726
  this._timers.set(key, timer);
438
727
  }
439
728
 
729
+ /**
730
+ * Deletes an item from the cache.
731
+ * @param {string} key - Cache key to delete
732
+ * @returns {boolean} True if item was deleted
733
+ */
440
734
  delete(key) {
441
735
  if (this._timers.has(key)) {
442
736
  clearTimeout(this._timers.get(key));
@@ -445,6 +739,7 @@ class TTLCache {
445
739
  return this._cache.delete(key);
446
740
  }
447
741
 
742
+ /** Clears all items and timers from the cache. */
448
743
  clear() {
449
744
  for (const timer of this._timers.values()) {
450
745
  clearTimeout(timer);
@@ -453,15 +748,21 @@ class TTLCache {
453
748
  this._cache.clear();
454
749
  }
455
750
 
751
+ /**
752
+ * Checks if a key exists in the cache.
753
+ * @param {string} key - Cache key
754
+ * @returns {boolean} True if key exists
755
+ */
456
756
  has(key) {
457
757
  return this._cache.has(key);
458
758
  }
459
759
 
760
+ /** @type {number} Current number of items in the cache */
460
761
  get size() {
461
762
  return this._cache.size;
462
763
  }
463
764
 
464
- // Add a destroy method:
765
+ /** Destroys the cache, clearing all timers and data. */
465
766
  destroy() {
466
767
  for (const timer of this._timers.values()) {
467
768
  clearTimeout(timer);
@@ -475,7 +776,20 @@ class TTLCache {
475
776
  // Cache Strategy System
476
777
  // ========================
477
778
 
779
+ /**
780
+ * Unified cache strategy manager that supports multiple caching algorithms.
781
+ * Provides a consistent interface regardless of the underlying strategy.
782
+ * @class CacheStrategy
783
+ */
478
784
  class CacheStrategy {
785
+ /**
786
+ * Creates a new cache strategy.
787
+ * @param {Object} [config={}] - Configuration options
788
+ * @param {string} [config.type='lru'] - Cache type: 'lru', 'lfu', 'ttl', or 'none'
789
+ * @param {number} [config.maxSize=100] - Maximum cache size
790
+ * @param {number|null} [config.ttl=null] - Time-to-live in milliseconds
791
+ * @param {boolean} [config.enabled=true] - Whether caching is enabled
792
+ */
479
793
  constructor(config = {}) {
480
794
  this._type = config.type || 'lru';
481
795
  this._maxSize = config.maxSize || 100;
@@ -484,7 +798,7 @@ class CacheStrategy {
484
798
  this._cache = null;
485
799
  }
486
800
 
487
- // Lazy initialization with getter
801
+ /** Gets the cache instance, creating it lazily if needed. */
488
802
  get cache() {
489
803
  if (!this._cache) {
490
804
  this._cache = this._createCache();
@@ -492,6 +806,7 @@ class CacheStrategy {
492
806
  return this._cache;
493
807
  }
494
808
 
809
+ /** @private Creates the appropriate cache instance based on config type. */
495
810
  _createCache() {
496
811
  switch (this._type) {
497
812
  case 'lru':
@@ -507,31 +822,51 @@ class CacheStrategy {
507
822
  }
508
823
  }
509
824
 
825
+ /**
826
+ * Retrieves a value from the cache.
827
+ * @param {string} key - Cache key
828
+ * @returns {*|null} Cached value or null
829
+ */
510
830
  get(key) {
511
831
  if (!this._enabled || !this.cache) return null;
512
832
  return this.cache.get(key);
513
833
  }
514
834
 
835
+ /**
836
+ * Stores a value in the cache.
837
+ * @param {string} key - Cache key
838
+ * @param {*} value - Value to store
839
+ */
515
840
  set(key, value) {
516
841
  if (!this._enabled || !this.cache) return;
517
842
  this.cache.set(key, value);
518
843
  }
519
844
 
845
+ /**
846
+ * Deletes an item from the cache.
847
+ * @param {string} key - Cache key to delete
848
+ */
520
849
  delete(key) {
521
850
  if (!this._enabled || !this.cache) return;
522
851
  this.cache.delete(key);
523
852
  }
524
853
 
854
+ /** Clears all items from the cache. */
525
855
  clear() {
526
856
  if (!this._enabled || !this.cache) return;
527
857
  this.cache.clear();
528
858
  }
529
859
 
860
+ /**
861
+ * Updates the cache strategy configuration.
862
+ * @param {Object} newConfig - New configuration options
863
+ */
530
864
  updateStrategy(newConfig) {
531
865
  Object.assign(this, newConfig);
532
866
  this._cache = null; // Reset cache for lazy reinitialization
533
867
  }
534
868
 
869
+ /** Destroys the cache and releases resources. */
535
870
  destroy() {
536
871
  if (this._cache && this._cache.destroy) {
537
872
  this._cache.destroy();
@@ -543,10 +878,21 @@ class CacheStrategy {
543
878
  }
544
879
 
545
880
  // ========================
546
- // Compression Utility (Fixed)
881
+ // Compression Utility
547
882
  // ========================
548
883
 
884
+ /**
885
+ * Browser-based compression utility using the Compression Streams API.
886
+ * Provides deflate compression and decompression for data storage optimization.
887
+ * @class BrowserCompressionUtility
888
+ */
549
889
  class BrowserCompressionUtility {
890
+ /**
891
+ * Compresses data using the deflate algorithm.
892
+ * @param {Uint8Array} input - Data to compress
893
+ * @returns {Promise<Uint8Array>} Compressed data
894
+ * @throws {TypeError} If input is not a Uint8Array
895
+ */
550
896
  async compress(input) {
551
897
  if (!(input instanceof Uint8Array)) {
552
898
  throw new TypeError('Input must be Uint8Array');
@@ -562,6 +908,11 @@ class BrowserCompressionUtility {
562
908
  }
563
909
  }
564
910
 
911
+ /**
912
+ * Decompresses deflate-compressed data.
913
+ * @param {Uint8Array} compressedData - Compressed data to decompress
914
+ * @returns {Promise<Uint8Array>} Decompressed data
915
+ */
565
916
  async decompress(compressedData) {
566
917
  if (!(compressedData instanceof Uint8Array)) {
567
918
  throw new TypeError('Input must be Uint8Array');
@@ -577,6 +928,11 @@ class BrowserCompressionUtility {
577
928
  }
578
929
  }
579
930
 
931
+ /**
932
+ * Synchronous compression (pass-through, no actual compression).
933
+ * @param {Uint8Array} input - Data to compress
934
+ * @returns {Uint8Array} Original data (uncompressed)
935
+ */
580
936
  compressSync(input) {
581
937
  if (!(input instanceof Uint8Array)) {
582
938
  throw new TypeError('Input must be Uint8Array');
@@ -584,6 +940,11 @@ class BrowserCompressionUtility {
584
940
  return input;
585
941
  }
586
942
 
943
+ /**
944
+ * Synchronous decompression (pass-through).
945
+ * @param {Uint8Array} compressedData - Data to decompress
946
+ * @returns {Uint8Array} Original data
947
+ */
587
948
  decompressSync(compressedData) {
588
949
  if (!(compressedData instanceof Uint8Array)) {
589
950
  throw new TypeError('Input must be Uint8Array');
@@ -596,7 +957,18 @@ class BrowserCompressionUtility {
596
957
  // Browser Encryption Utility
597
958
  // ========================
598
959
 
960
+ /**
961
+ * Utility for encrypting and decrypting data using Web Crypto API.
962
+ * Uses AES-GCM for authenticated encryption with PBKDF2 for key derivation.
963
+ * @class BrowserEncryptionUtility
964
+ */
599
965
  class BrowserEncryptionUtility {
966
+ /**
967
+ * Encrypts data using AES-256-GCM with PBKDF2 key derivation.
968
+ * @param {Uint8Array} data - Data to encrypt
969
+ * @param {string} password - Password for encryption
970
+ * @returns {Promise<Uint8Array>} Encrypted data (salt + IV + ciphertext)
971
+ */
600
972
  async encrypt(data, password) {
601
973
  const encoder = new TextEncoder();
602
974
  const salt = crypto.getRandomValues(new Uint8Array(16));
@@ -638,6 +1010,12 @@ class BrowserEncryptionUtility {
638
1010
  return result;
639
1011
  }
640
1012
 
1013
+ /**
1014
+ * Decrypts AES-256-GCM encrypted data.
1015
+ * @param {Uint8Array} encryptedData - Encrypted data (salt + IV + ciphertext)
1016
+ * @param {string} password - Password for decryption
1017
+ * @returns {Promise<Uint8Array>} Decrypted data
1018
+ */
641
1019
  async decrypt(encryptedData, password) {
642
1020
  const encoder = new TextEncoder();
643
1021
  const salt = encryptedData.slice(0, 16);
@@ -680,7 +1058,21 @@ class BrowserEncryptionUtility {
680
1058
  // Database-Level Encryption
681
1059
  // ========================
682
1060
 
1061
+ /**
1062
+ * Advanced encryption system for database-level security.
1063
+ * Provides PIN-based encryption with HMAC verification for data integrity.
1064
+ * Suitable for securing entire databases or sensitive documents like private keys.
1065
+ * @class SecureDatabaseEncryption
1066
+ */
683
1067
  class SecureDatabaseEncryption {
1068
+ /**
1069
+ * Creates a new secure encryption instance.
1070
+ * @param {Object} [config={}] - Encryption configuration
1071
+ * @param {number} [config.iterations=100000] - PBKDF2 iteration count
1072
+ * @param {string} [config.hashAlgorithm='SHA-256'] - Hash algorithm for key derivation
1073
+ * @param {number} [config.keyLength=256] - AES key length in bits
1074
+ * @param {number} [config.saltLength=32] - Salt length in bytes
1075
+ */
684
1076
  constructor(config = {}) {
685
1077
  this._masterKey = null;
686
1078
  this._salt = null;
@@ -693,10 +1085,17 @@ class SecureDatabaseEncryption {
693
1085
  this._hmacKey = null;
694
1086
  }
695
1087
 
1088
+ /** @type {boolean} Whether encryption has been initialized */
696
1089
  get initialized() {
697
1090
  return this._initialized;
698
1091
  }
699
1092
 
1093
+ /**
1094
+ * Initializes encryption with a PIN code.
1095
+ * @param {string} pin - PIN code (typically 4-8 digits)
1096
+ * @param {Uint8Array|null} [salt=null] - Salt for key derivation (generated if null)
1097
+ * @returns {Promise<string>} Base64-encoded salt
1098
+ */
700
1099
  async initialize(pin, salt = null) {
701
1100
  if (this._initialized) {
702
1101
  throw new Error('Database encryption already initialized');
@@ -752,6 +1151,11 @@ class SecureDatabaseEncryption {
752
1151
  return base64.encode(this._salt);
753
1152
  }
754
1153
 
1154
+ /**
1155
+ * Encrypts data with AES-GCM and adds HMAC for integrity.
1156
+ * @param {string|Object|Uint8Array} data - Data to encrypt
1157
+ * @returns {Promise<Uint8Array>} Encrypted data with IV and HMAC
1158
+ */
755
1159
  async encrypt(data) {
756
1160
  if (!this._initialized) {
757
1161
  throw new Error('Database encryption not initialized');
@@ -794,6 +1198,12 @@ class SecureDatabaseEncryption {
794
1198
  return result;
795
1199
  }
796
1200
 
1201
+ /**
1202
+ * Decrypts data and verifies HMAC integrity.
1203
+ * @param {Uint8Array} encryptedPackage - Encrypted data (IV + ciphertext + HMAC)
1204
+ * @returns {Promise<Uint8Array>} Decrypted data
1205
+ * @throws {Error} If HMAC verification fails
1206
+ */
797
1207
  async decrypt(encryptedPackage) {
798
1208
  if (!this._initialized) {
799
1209
  throw new Error('Database encryption not initialized');
@@ -831,6 +1241,12 @@ class SecureDatabaseEncryption {
831
1241
  return new Uint8Array(decryptedData);
832
1242
  }
833
1243
 
1244
+ /**
1245
+ * Encrypts a private key with additional authentication data.
1246
+ * @param {string|Uint8Array|Object} privateKey - Key to encrypt
1247
+ * @param {string} [additionalAuth=''] - Additional authentication data
1248
+ * @returns {Promise<string>} Base64-encoded encrypted key
1249
+ */
834
1250
  async encryptPrivateKey(privateKey, additionalAuth = '') {
835
1251
  if (!this._initialized) {
836
1252
  throw new Error('Database encryption not initialized');
@@ -874,6 +1290,12 @@ class SecureDatabaseEncryption {
874
1290
  return base64.encode(result);
875
1291
  }
876
1292
 
1293
+ /**
1294
+ * Decrypts a private key, verifying additional authentication data.
1295
+ * @param {string} encryptedKeyString - Base64-encoded encrypted key
1296
+ * @param {string} [additionalAuth=''] - Additional authentication data to verify
1297
+ * @returns {Promise<string>} Decrypted private key
1298
+ */
877
1299
  async decryptPrivateKey(encryptedKeyString, additionalAuth = '') {
878
1300
  if (!this._initialized) {
879
1301
  throw new Error('Database encryption not initialized');
@@ -908,6 +1330,12 @@ class SecureDatabaseEncryption {
908
1330
  return new TextDecoder().decode(decryptedKey);
909
1331
  }
910
1332
 
1333
+ /**
1334
+ * Generates a cryptographically secure random PIN.
1335
+ * @static
1336
+ * @param {number} [length=6] - PIN length (number of digits)
1337
+ * @returns {string} Random PIN
1338
+ */
911
1339
  static generateSecurePIN(length = 6) {
912
1340
  const digits = new Uint8Array(length);
913
1341
  crypto.getRandomValues(digits);
@@ -916,6 +1344,7 @@ class SecureDatabaseEncryption {
916
1344
  .join('');
917
1345
  }
918
1346
 
1347
+ /** Destroys the encryption instance and clears all keys from memory. */
919
1348
  destroy() {
920
1349
  this._masterKey = null;
921
1350
  this._encKey = null;
@@ -924,6 +1353,7 @@ class SecureDatabaseEncryption {
924
1353
  this._initialized = false;
925
1354
  }
926
1355
 
1356
+ /** @private Compares two Uint8Arrays for equality. */
927
1357
  _arrayEquals(a, b) {
928
1358
  if (a.length !== b.length) return false;
929
1359
  for (let i = 0; i < a.length; i++) {
@@ -932,6 +1362,12 @@ class SecureDatabaseEncryption {
932
1362
  return true;
933
1363
  }
934
1364
 
1365
+ /**
1366
+ * Changes the encryption PIN.
1367
+ * @param {string} oldPin - Current PIN
1368
+ * @param {string} newPin - New PIN to set
1369
+ * @returns {Promise<string>} Base64-encoded new salt
1370
+ */
935
1371
  async changePin(oldPin, newPin) {
936
1372
  if (!this._initialized) {
937
1373
  throw new Error('Database encryption not initialized');
@@ -956,6 +1392,10 @@ class SecureDatabaseEncryption {
956
1392
  return newSalt;
957
1393
  }
958
1394
 
1395
+ /**
1396
+ * Exports encryption metadata for persistence (does NOT include keys).
1397
+ * @returns {Object} Encryption metadata
1398
+ */
959
1399
  exportMetadata() {
960
1400
  if (!this._salt) {
961
1401
  throw new Error('No encryption metadata to export');
@@ -972,6 +1412,11 @@ class SecureDatabaseEncryption {
972
1412
  };
973
1413
  }
974
1414
 
1415
+ /**
1416
+ * Imports encryption metadata from persistence.
1417
+ * @param {Object} metadata - Encryption metadata
1418
+ * @returns {boolean} True if successful
1419
+ */
975
1420
  importMetadata(metadata) {
976
1421
  if (!metadata.salt) {
977
1422
  throw new Error('Invalid encryption metadata');
@@ -991,7 +1436,17 @@ class SecureDatabaseEncryption {
991
1436
  // B-Tree Index Implementation with Self-Healing
992
1437
  // ========================
993
1438
 
1439
+ /**
1440
+ * Node in a B-Tree index structure.
1441
+ * @class BTreeNode
1442
+ * @private
1443
+ */
994
1444
  class BTreeNode {
1445
+ /**
1446
+ * Creates a new B-Tree node.
1447
+ * @param {number} order - B-Tree order (determines branching factor)
1448
+ * @param {boolean} leaf - Whether this is a leaf node
1449
+ */
995
1450
  constructor(order, leaf) {
996
1451
  this.keys = new Array(2 * order - 1);
997
1452
  this.values = new Array(2 * order - 1);
@@ -1001,6 +1456,7 @@ class BTreeNode {
1001
1456
  this.order = order;
1002
1457
  }
1003
1458
 
1459
+ /** Searches for a key in this node and its subtree. */
1004
1460
  search(key) {
1005
1461
  let i = 0;
1006
1462
  while (i < this.n && key > this.keys[i]) {
@@ -1018,6 +1474,7 @@ class BTreeNode {
1018
1474
  return this.children[i] ? this.children[i].search(key) : null;
1019
1475
  }
1020
1476
 
1477
+ /** Performs a range search within this node and its subtree. */
1021
1478
  rangeSearch(min, max, results) {
1022
1479
  let i = 0;
1023
1480
 
@@ -1040,6 +1497,7 @@ class BTreeNode {
1040
1497
  }
1041
1498
  }
1042
1499
 
1500
+ /** Inserts a key-value pair into a non-full node. */
1043
1501
  insertNonFull(key, value) {
1044
1502
  let i = this.n - 1;
1045
1503
 
@@ -1086,6 +1544,7 @@ class BTreeNode {
1086
1544
  }
1087
1545
  }
1088
1546
 
1547
+ /** Splits a full child node. */
1089
1548
  splitChild(i, y) {
1090
1549
  const z = new BTreeNode(this.order, y.leaf);
1091
1550
  z.n = this.order - 1;
@@ -1119,6 +1578,7 @@ class BTreeNode {
1119
1578
  this.n++;
1120
1579
  }
1121
1580
 
1581
+ /** Removes a value from the key's value set. */
1122
1582
  remove(key, value) {
1123
1583
  let i = 0;
1124
1584
  while (i < this.n && key > this.keys[i]) {
@@ -1141,6 +1601,7 @@ class BTreeNode {
1141
1601
  }
1142
1602
  }
1143
1603
 
1604
+ /** Verifies and repairs the node structure (self-healing). */
1144
1605
  verify() {
1145
1606
  const issues = [];
1146
1607
 
@@ -1170,7 +1631,16 @@ class BTreeNode {
1170
1631
  }
1171
1632
  }
1172
1633
 
1634
+ /**
1635
+ * B-Tree index for efficient ordered data access and range queries.
1636
+ * Supports multiple document IDs per key and includes self-healing capabilities.
1637
+ * @class BTreeIndex
1638
+ */
1173
1639
  class BTreeIndex {
1640
+ /**
1641
+ * Creates a new B-Tree index.
1642
+ * @param {number} [order=4] - B-Tree order (minimum degree)
1643
+ */
1174
1644
  constructor(order = 4) {
1175
1645
  this._root = null;
1176
1646
  this._order = order;
@@ -1179,6 +1649,11 @@ class BTreeIndex {
1179
1649
  this._verificationInterval = 60000;
1180
1650
  }
1181
1651
 
1652
+ /**
1653
+ * Inserts a key-value pair into the index.
1654
+ * @param {*} key - Index key
1655
+ * @param {string} value - Document ID
1656
+ */
1182
1657
  insert(key, value) {
1183
1658
  if (Date.now() - this._lastVerification > this._verificationInterval) {
1184
1659
  this.verify();
@@ -1207,17 +1682,33 @@ class BTreeIndex {
1207
1682
  this._size++;
1208
1683
  }
1209
1684
 
1685
+ /**
1686
+ * Finds all document IDs associated with a key.
1687
+ * @param {*} key - Key to search for
1688
+ * @returns {Array<string>} Array of document IDs
1689
+ */
1210
1690
  find(key) {
1211
1691
  if (!this._root) return [];
1212
1692
  const values = this._root.search(key);
1213
1693
  return values ? Array.from(values) : [];
1214
1694
  }
1215
1695
 
1696
+ /**
1697
+ * Checks if a key exists in the index.
1698
+ * @param {*} key - Key to check
1699
+ * @returns {boolean} True if key exists
1700
+ */
1216
1701
  contains(key) {
1217
1702
  if (!this._root) return false;
1218
1703
  return this._root.search(key) !== null;
1219
1704
  }
1220
1705
 
1706
+ /**
1707
+ * Finds all document IDs within a key range.
1708
+ * @param {*} min - Minimum key (inclusive)
1709
+ * @param {*} max - Maximum key (inclusive)
1710
+ * @returns {Array<string>} Array of document IDs
1711
+ */
1221
1712
  range(min, max) {
1222
1713
  if (!this._root) return [];
1223
1714
  const results = [];
@@ -1225,6 +1716,7 @@ class BTreeIndex {
1225
1716
  return results;
1226
1717
  }
1227
1718
 
1719
+ /** Finds all document IDs with keys >= min. */
1228
1720
  rangeFrom(min) {
1229
1721
  if (!this._root) return [];
1230
1722
  const results = [];
@@ -1232,6 +1724,7 @@ class BTreeIndex {
1232
1724
  return results;
1233
1725
  }
1234
1726
 
1727
+ /** Finds all document IDs with keys <= max. */
1235
1728
  rangeTo(max) {
1236
1729
  if (!this._root) return [];
1237
1730
  const results = [];
@@ -1239,6 +1732,7 @@ class BTreeIndex {
1239
1732
  return results;
1240
1733
  }
1241
1734
 
1735
+ /** Removes a document ID from a key's value set. */
1242
1736
  remove(key, value) {
1243
1737
  if (!this._root) return;
1244
1738
  this._root.remove(key, value);
@@ -1250,6 +1744,7 @@ class BTreeIndex {
1250
1744
  this._size--;
1251
1745
  }
1252
1746
 
1747
+ /** Verifies index integrity and performs self-healing repairs. */
1253
1748
  verify() {
1254
1749
  this._lastVerification = Date.now();
1255
1750
  if (!this._root) return { healthy: true, issues: [] };
@@ -1267,18 +1762,34 @@ class BTreeIndex {
1267
1762
  };
1268
1763
  }
1269
1764
 
1765
+ /** @type {number} Total number of entries in the index */
1270
1766
  get size() {
1271
1767
  return this._size;
1272
1768
  }
1273
1769
  }
1274
1770
 
1275
- // Text Index for full-text search
1771
+ // ========================
1772
+ // Text Index for Full-Text Search
1773
+ // ========================
1774
+
1775
+ /**
1776
+ * Inverted index for full-text search capabilities.
1777
+ * Tokenizes text and builds an index for fast keyword lookups.
1778
+ * @class TextIndex
1779
+ */
1276
1780
  class TextIndex {
1277
1781
  constructor() {
1782
+ /** @private Token to document IDs mapping */
1278
1783
  this._invertedIndex = new Map();
1784
+ /** @private Document ID to text mapping */
1279
1785
  this._documentTexts = new Map();
1280
1786
  }
1281
1787
 
1788
+ /**
1789
+ * Adds a document to the text index.
1790
+ * @param {string} text - Document text content
1791
+ * @param {string} docId - Document ID
1792
+ */
1282
1793
  addDocument(text, docId) {
1283
1794
  if (typeof text !== 'string') return;
1284
1795
 
@@ -1293,6 +1804,7 @@ class TextIndex {
1293
1804
  }
1294
1805
  }
1295
1806
 
1807
+ /** Removes a document from the text index. */
1296
1808
  removeDocument(docId) {
1297
1809
  const text = this._documentTexts.get(docId);
1298
1810
  if (!text) return;
@@ -1311,11 +1823,17 @@ class TextIndex {
1311
1823
  this._documentTexts.delete(docId);
1312
1824
  }
1313
1825
 
1826
+ /** Updates a document's text in the index. */
1314
1827
  updateDocument(docId, newText) {
1315
1828
  this.removeDocument(docId);
1316
1829
  this.addDocument(newText, docId);
1317
1830
  }
1318
1831
 
1832
+ /**
1833
+ * Searches for documents containing all query terms.
1834
+ * @param {string} query - Search query
1835
+ * @returns {Array<string>} Array of matching document IDs
1836
+ */
1319
1837
  search(query) {
1320
1838
  const tokens = this._tokenize(query);
1321
1839
  if (tokens.length === 0) return [];
@@ -1337,6 +1855,7 @@ class TextIndex {
1337
1855
  return results ? Array.from(results) : [];
1338
1856
  }
1339
1857
 
1858
+ /** @private Tokenizes text into searchable terms. */
1340
1859
  _tokenize(text) {
1341
1860
  return text.toLowerCase()
1342
1861
  .replace(/[^\w\s]/g, ' ')
@@ -1344,17 +1863,32 @@ class TextIndex {
1344
1863
  .filter(token => token.length > 2);
1345
1864
  }
1346
1865
 
1866
+ /** @type {number} Number of indexed documents */
1347
1867
  get size() {
1348
1868
  return this._documentTexts.size;
1349
1869
  }
1350
1870
  }
1351
1871
 
1352
- // Geo Index for spatial queries
1872
+ // ========================
1873
+ // Geo Index for Spatial Queries
1874
+ // ========================
1875
+
1876
+ /**
1877
+ * Geospatial index for location-based queries.
1878
+ * Supports proximity searches and bounding box queries.
1879
+ * @class GeoIndex
1880
+ */
1353
1881
  class GeoIndex {
1354
1882
  constructor() {
1883
+ /** @private Document ID to coordinates mapping */
1355
1884
  this._points = new Map();
1356
1885
  }
1357
1886
 
1887
+ /**
1888
+ * Adds a geographic point to the index.
1889
+ * @param {Object} coords - Coordinates object with lat/lng properties
1890
+ * @param {string} docId - Document ID
1891
+ */
1358
1892
  addPoint(coords, docId) {
1359
1893
  if (!coords || typeof coords.lat !== 'number' || typeof coords.lng !== 'number') {
1360
1894
  return;
@@ -1362,14 +1896,22 @@ class GeoIndex {
1362
1896
  this._points.set(docId, coords);
1363
1897
  }
1364
1898
 
1899
+ /** Removes a point from the index. */
1365
1900
  removePoint(docId) {
1366
1901
  this._points.delete(docId);
1367
1902
  }
1368
1903
 
1904
+ /** Updates a point's coordinates. */
1369
1905
  updatePoint(docId, newCoords) {
1370
1906
  this._points.set(docId, newCoords);
1371
1907
  }
1372
1908
 
1909
+ /**
1910
+ * Finds all points within a distance from a center point.
1911
+ * @param {Object} center - Center coordinates
1912
+ * @param {number} maxDistance - Maximum distance in kilometers
1913
+ * @returns {Array<string>} Document IDs sorted by distance
1914
+ */
1373
1915
  findNear(center, maxDistance) {
1374
1916
  const results = [];
1375
1917
 
@@ -1384,6 +1926,11 @@ class GeoIndex {
1384
1926
  .map(r => r.docId);
1385
1927
  }
1386
1928
 
1929
+ /**
1930
+ * Finds all points within a bounding box.
1931
+ * @param {Object} bounds - Bounding box with minLat, maxLat, minLng, maxLng
1932
+ * @returns {Array<string>} Document IDs within bounds
1933
+ */
1387
1934
  findWithin(bounds) {
1388
1935
  const results = [];
1389
1936
 
@@ -1396,8 +1943,9 @@ class GeoIndex {
1396
1943
  return results;
1397
1944
  }
1398
1945
 
1946
+ /** @private Calculates distance using the Haversine formula. */
1399
1947
  _haversine(coord1, coord2) {
1400
- const R = 6371;
1948
+ const R = 6371; // Earth's radius in km
1401
1949
  const dLat = this._toRad(coord2.lat - coord1.lat);
1402
1950
  const dLng = this._toRad(coord2.lng - coord1.lng);
1403
1951
 
@@ -1409,24 +1957,25 @@ class GeoIndex {
1409
1957
  return R * c;
1410
1958
  }
1411
1959
 
1960
+ /** @private Converts degrees to radians. */
1412
1961
  _toRad(deg) {
1413
1962
  return deg * (Math.PI / 180);
1414
1963
  }
1415
1964
 
1965
+ /** @private Checks if coordinates are within bounds. */
1416
1966
  _isWithinBounds(coords, bounds) {
1417
1967
  return coords.lat >= bounds.minLat && coords.lat <= bounds.maxLat &&
1418
1968
  coords.lng >= bounds.minLng && coords.lng <= bounds.maxLng;
1419
1969
  }
1420
1970
 
1971
+ /** @type {number} Number of indexed points */
1421
1972
  get size() {
1422
1973
  return this._points.size;
1423
1974
  }
1424
1975
  }
1425
-
1426
1976
  // ========================
1427
- // Index Manager (Optimized)
1977
+ // B-Tree Index Implementation with Self-Healing
1428
1978
  // ========================
1429
-
1430
1979
  class IndexManager {
1431
1980
  constructor(collection) {
1432
1981
  this._collection = collection;
@@ -3459,7 +4008,7 @@ class Database {
3459
4008
 
3460
4009
  async export(format = 'json', password = null) {
3461
4010
  const data = {
3462
- version: '0.7.0',
4011
+ version: '0.8.0',
3463
4012
  database: this.name,
3464
4013
  timestamp: Date.now(),
3465
4014
  collections: {}
@@ -3626,7 +4175,7 @@ class LacertaDB {
3626
4175
 
3627
4176
  async createBackup(password = null) {
3628
4177
  const backup = {
3629
- version: '0.7.0',
4178
+ version: '0.8.0',
3630
4179
  timestamp: Date.now(),
3631
4180
  databases: {}
3632
4181
  };