@pixagram/lacerta-db 0.5.4 → 0.5.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/index.js +55 -120
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* LacertaDB V5.0.0 - Complete Production Library
|
|
2
|
+
* LacertaDB V5.0.0 - Complete Production Library
|
|
3
3
|
* A powerful browser-based document database with encryption, compression, OPFS support,
|
|
4
4
|
* custom indexes, LRU caching, and database-level security for private keys
|
|
5
5
|
* @version 5.0.0
|
|
@@ -488,44 +488,22 @@ class BrowserEncryptionUtility {
|
|
|
488
488
|
}
|
|
489
489
|
|
|
490
490
|
// ========================
|
|
491
|
-
// Database-Level Encryption
|
|
491
|
+
// Database-Level Encryption
|
|
492
492
|
// ========================
|
|
493
493
|
|
|
494
494
|
class SecureDatabaseEncryption {
|
|
495
|
-
constructor(
|
|
495
|
+
constructor() {
|
|
496
496
|
this.masterKey = null;
|
|
497
497
|
this.salt = null;
|
|
498
|
-
|
|
499
|
-
this.iterations = config.iterations || 2000000; // Increased from 1M to 2M
|
|
500
|
-
this.minPasswordLength = config.minPasswordLength || 12; // Minimum password length
|
|
498
|
+
this.iterations = 1000000;
|
|
501
499
|
this.initialized = false;
|
|
502
500
|
}
|
|
503
501
|
|
|
504
|
-
validatePassword(password) {
|
|
505
|
-
if (password.length < this.minPasswordLength) {
|
|
506
|
-
throw new Error(`Password must be at least ${this.minPasswordLength} characters for secure encryption`);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// Check password complexity
|
|
510
|
-
const hasUpperCase = /[A-Z]/.test(password);
|
|
511
|
-
const hasLowerCase = /[a-z]/.test(password);
|
|
512
|
-
const hasNumbers = /\d/.test(password);
|
|
513
|
-
const hasNonalphas = /\W/.test(password);
|
|
514
|
-
const complexity = [hasUpperCase, hasLowerCase, hasNumbers, hasNonalphas].filter(Boolean).length;
|
|
515
|
-
|
|
516
|
-
if (complexity < 3) {
|
|
517
|
-
throw new Error('Password must contain at least 3 of: uppercase, lowercase, numbers, special characters');
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
502
|
async initialize(pin, salt = null) {
|
|
522
503
|
if (this.initialized) {
|
|
523
504
|
throw new Error('Database encryption already initialized');
|
|
524
505
|
}
|
|
525
506
|
|
|
526
|
-
// Validate password strength
|
|
527
|
-
this.validatePassword(pin);
|
|
528
|
-
|
|
529
507
|
this.salt = salt || crypto.getRandomValues(new Uint8Array(32));
|
|
530
508
|
|
|
531
509
|
const encoder = new TextEncoder();
|
|
@@ -732,12 +710,11 @@ class SecureDatabaseEncryption {
|
|
|
732
710
|
return new TextDecoder().decode(decryptedKey);
|
|
733
711
|
}
|
|
734
712
|
|
|
735
|
-
static
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
.map(x => charset[x % charset.length])
|
|
713
|
+
static generateSecurePIN(length = 6) {
|
|
714
|
+
const digits = new Uint8Array(length);
|
|
715
|
+
crypto.getRandomValues(digits);
|
|
716
|
+
return Array.from(digits)
|
|
717
|
+
.map(b => (b % 10).toString())
|
|
741
718
|
.join('');
|
|
742
719
|
}
|
|
743
720
|
|
|
@@ -762,17 +739,12 @@ class SecureDatabaseEncryption {
|
|
|
762
739
|
throw new Error('Database encryption not initialized');
|
|
763
740
|
}
|
|
764
741
|
|
|
765
|
-
// Validate new password
|
|
766
|
-
this.validatePassword(newPin);
|
|
767
|
-
|
|
768
742
|
const currentSalt = this.salt;
|
|
769
|
-
const currentIterations = this.iterations;
|
|
770
743
|
|
|
771
744
|
this.destroy();
|
|
772
745
|
await this.initialize(oldPin, currentSalt);
|
|
773
746
|
|
|
774
747
|
this.destroy();
|
|
775
|
-
this.iterations = currentIterations;
|
|
776
748
|
const newSalt = await this.initialize(newPin);
|
|
777
749
|
|
|
778
750
|
return newSalt;
|
|
@@ -787,8 +759,7 @@ class SecureDatabaseEncryption {
|
|
|
787
759
|
salt: base64.encode(this.salt),
|
|
788
760
|
iterations: this.iterations,
|
|
789
761
|
algorithm: 'AES-GCM-256',
|
|
790
|
-
kdf: 'PBKDF2-SHA-512'
|
|
791
|
-
minPasswordLength: this.minPasswordLength
|
|
762
|
+
kdf: 'PBKDF2-SHA-512'
|
|
792
763
|
};
|
|
793
764
|
}
|
|
794
765
|
|
|
@@ -798,8 +769,7 @@ class SecureDatabaseEncryption {
|
|
|
798
769
|
}
|
|
799
770
|
|
|
800
771
|
this.salt = base64.decode(metadata.salt);
|
|
801
|
-
this.iterations = metadata.iterations ||
|
|
802
|
-
this.minPasswordLength = metadata.minPasswordLength || 12;
|
|
772
|
+
this.iterations = metadata.iterations || 1000000;
|
|
803
773
|
|
|
804
774
|
return true;
|
|
805
775
|
}
|
|
@@ -1830,7 +1800,7 @@ class Document {
|
|
|
1830
1800
|
_created: this._created,
|
|
1831
1801
|
_modified: this._modified,
|
|
1832
1802
|
_permanent: this._permanent,
|
|
1833
|
-
...this.data
|
|
1803
|
+
...(this.data || {}) // Fixed: handle undefined data
|
|
1834
1804
|
};
|
|
1835
1805
|
if (includeAttachments && this._attachments.length > 0) {
|
|
1836
1806
|
output._attachments = this._attachments;
|
|
@@ -1853,7 +1823,7 @@ class Document {
|
|
|
1853
1823
|
}
|
|
1854
1824
|
|
|
1855
1825
|
// ========================
|
|
1856
|
-
// Metadata Classes
|
|
1826
|
+
// Metadata Classes
|
|
1857
1827
|
// ========================
|
|
1858
1828
|
|
|
1859
1829
|
class CollectionMetadata {
|
|
@@ -1921,20 +1891,6 @@ class CollectionMetadata {
|
|
|
1921
1891
|
.slice(0, count)
|
|
1922
1892
|
.map(([docId]) => docId);
|
|
1923
1893
|
}
|
|
1924
|
-
|
|
1925
|
-
toJSON() {
|
|
1926
|
-
return {
|
|
1927
|
-
name: this.name,
|
|
1928
|
-
sizeKB: this.sizeKB,
|
|
1929
|
-
length: this.length,
|
|
1930
|
-
createdAt: this.createdAt,
|
|
1931
|
-
modifiedAt: this.modifiedAt,
|
|
1932
|
-
documentSizes: this.documentSizes,
|
|
1933
|
-
documentModifiedAt: this.documentModifiedAt,
|
|
1934
|
-
documentPermanent: this.documentPermanent,
|
|
1935
|
-
documentAttachments: this.documentAttachments
|
|
1936
|
-
};
|
|
1937
|
-
}
|
|
1938
1894
|
}
|
|
1939
1895
|
|
|
1940
1896
|
class DatabaseMetadata {
|
|
@@ -1982,18 +1938,16 @@ class DatabaseMetadata {
|
|
|
1982
1938
|
}
|
|
1983
1939
|
|
|
1984
1940
|
setCollection(collectionMetadata) {
|
|
1985
|
-
this.collections[collectionMetadata.name] =
|
|
1986
|
-
collectionMetadata.
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
documentAttachments: collectionMetadata.documentAttachments
|
|
1996
|
-
};
|
|
1941
|
+
this.collections[collectionMetadata.name] = {
|
|
1942
|
+
sizeKB: collectionMetadata.sizeKB,
|
|
1943
|
+
length: collectionMetadata.length,
|
|
1944
|
+
createdAt: collectionMetadata.createdAt,
|
|
1945
|
+
modifiedAt: collectionMetadata.modifiedAt,
|
|
1946
|
+
documentSizes: collectionMetadata.documentSizes,
|
|
1947
|
+
documentModifiedAt: collectionMetadata.documentModifiedAt,
|
|
1948
|
+
documentPermanent: collectionMetadata.documentPermanent,
|
|
1949
|
+
documentAttachments: collectionMetadata.documentAttachments
|
|
1950
|
+
};
|
|
1997
1951
|
this.recalculate();
|
|
1998
1952
|
this.save();
|
|
1999
1953
|
}
|
|
@@ -2146,16 +2100,16 @@ class QuickStore {
|
|
|
2146
2100
|
}
|
|
2147
2101
|
|
|
2148
2102
|
clear() {
|
|
2149
|
-
const index = this._readIndex();
|
|
2103
|
+
const index = this._readIndex(); // Fixed: was this.readIndex()
|
|
2150
2104
|
for (const docId of index) {
|
|
2151
|
-
localStorage.removeItem(`${this.keyPrefix}data_${docId}`);
|
|
2105
|
+
localStorage.removeItem(`${this.keyPrefix}data_${docId}`); // Fixed: added underscore
|
|
2152
2106
|
}
|
|
2153
2107
|
localStorage.removeItem(this.indexKey);
|
|
2154
2108
|
}
|
|
2155
2109
|
}
|
|
2156
2110
|
|
|
2157
2111
|
// ========================
|
|
2158
|
-
// Query Engine
|
|
2112
|
+
// Query Engine
|
|
2159
2113
|
// ========================
|
|
2160
2114
|
|
|
2161
2115
|
class QueryEngine {
|
|
@@ -2539,7 +2493,7 @@ class PerformanceMonitor {
|
|
|
2539
2493
|
}
|
|
2540
2494
|
|
|
2541
2495
|
// ========================
|
|
2542
|
-
// Collection Class
|
|
2496
|
+
// Collection Class
|
|
2543
2497
|
// ========================
|
|
2544
2498
|
|
|
2545
2499
|
class Collection {
|
|
@@ -2777,15 +2731,13 @@ class Collection {
|
|
|
2777
2731
|
}
|
|
2778
2732
|
|
|
2779
2733
|
// Get full document for index cleanup
|
|
2780
|
-
const fullDoc = await this.get(docId
|
|
2734
|
+
const fullDoc = await this.get(docId);
|
|
2781
2735
|
|
|
2782
2736
|
// Remove from indexes
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
await this.indexManager.updateIndex(indexName, docId, value, undefined);
|
|
2788
|
-
}
|
|
2737
|
+
for (const [indexName, index] of this.indexManager.indexes) {
|
|
2738
|
+
const value = this.indexManager.getFieldValue(fullDoc, index.fieldPath);
|
|
2739
|
+
if (value !== undefined && value !== null) {
|
|
2740
|
+
await this.indexManager.updateIndex(indexName, docId, value, undefined);
|
|
2789
2741
|
}
|
|
2790
2742
|
}
|
|
2791
2743
|
|
|
@@ -2822,7 +2774,15 @@ class Collection {
|
|
|
2822
2774
|
for (const [indexName, index] of this.indexManager.indexes) {
|
|
2823
2775
|
const fieldValue = filter[index.fieldPath];
|
|
2824
2776
|
if (fieldValue !== undefined) {
|
|
2825
|
-
|
|
2777
|
+
// Fixed: Handle query operators properly
|
|
2778
|
+
let queryOptions;
|
|
2779
|
+
if (typeof fieldValue === 'object' && fieldValue !== null && !Array.isArray(fieldValue)) {
|
|
2780
|
+
queryOptions = fieldValue; // e.g., { $gte: 25 }
|
|
2781
|
+
} else {
|
|
2782
|
+
queryOptions = { $eq: fieldValue }; // e.g., simple value
|
|
2783
|
+
}
|
|
2784
|
+
|
|
2785
|
+
const docIds = await this.indexManager.query(indexName, queryOptions);
|
|
2826
2786
|
results = await Promise.all(
|
|
2827
2787
|
docIds.map(id => this.get(id).catch(() => null))
|
|
2828
2788
|
);
|
|
@@ -2965,7 +2925,7 @@ class Collection {
|
|
|
2965
2925
|
}
|
|
2966
2926
|
|
|
2967
2927
|
// ========================
|
|
2968
|
-
// Database Class
|
|
2928
|
+
// Database Class
|
|
2969
2929
|
// ========================
|
|
2970
2930
|
|
|
2971
2931
|
class Database {
|
|
@@ -2996,7 +2956,7 @@ class Database {
|
|
|
2996
2956
|
}
|
|
2997
2957
|
|
|
2998
2958
|
async initializeEncryption(pin, salt = null) {
|
|
2999
|
-
this.encryption = new SecureDatabaseEncryption(
|
|
2959
|
+
this.encryption = new SecureDatabaseEncryption();
|
|
3000
2960
|
|
|
3001
2961
|
// Check if we have existing salt in metadata
|
|
3002
2962
|
const encMetaKey = `lacertadb_${this.name}_encryption`;
|
|
@@ -3051,9 +3011,9 @@ class Database {
|
|
|
3051
3011
|
);
|
|
3052
3012
|
|
|
3053
3013
|
// Store in a special collection
|
|
3054
|
-
let keyStore = await this.getCollection('
|
|
3014
|
+
let keyStore = await this.getCollection('private_keys').catch(() => null);
|
|
3055
3015
|
if (!keyStore) {
|
|
3056
|
-
keyStore = await this.createCollection('
|
|
3016
|
+
keyStore = await this.createCollection('private_keys', {
|
|
3057
3017
|
encrypted: false // Already encrypted at database level
|
|
3058
3018
|
});
|
|
3059
3019
|
}
|
|
@@ -3075,7 +3035,7 @@ class Database {
|
|
|
3075
3035
|
throw new Error('Database must be encrypted to retrieve private keys');
|
|
3076
3036
|
}
|
|
3077
3037
|
|
|
3078
|
-
const keyStore = await this.getCollection('
|
|
3038
|
+
const keyStore = await this.getCollection('private_keys');
|
|
3079
3039
|
const doc = await keyStore.get(keyName);
|
|
3080
3040
|
|
|
3081
3041
|
if (!doc) {
|
|
@@ -3202,11 +3162,7 @@ class Database {
|
|
|
3202
3162
|
try {
|
|
3203
3163
|
collection = await this.createCollection(collName);
|
|
3204
3164
|
} catch (e) {
|
|
3205
|
-
|
|
3206
|
-
collection = await this.getCollection(collName);
|
|
3207
|
-
} else {
|
|
3208
|
-
throw e;
|
|
3209
|
-
}
|
|
3165
|
+
collection = await this.getCollection(collName);
|
|
3210
3166
|
}
|
|
3211
3167
|
await collection.batchAdd(docs);
|
|
3212
3168
|
}
|
|
@@ -3236,10 +3192,10 @@ class Database {
|
|
|
3236
3192
|
}
|
|
3237
3193
|
|
|
3238
3194
|
// ========================
|
|
3239
|
-
// Main LacertaDB Class
|
|
3195
|
+
// Main LacertaDB Class
|
|
3240
3196
|
// ========================
|
|
3241
3197
|
|
|
3242
|
-
class LacertaDB {
|
|
3198
|
+
export class LacertaDB {
|
|
3243
3199
|
constructor() {
|
|
3244
3200
|
this.databases = new Map();
|
|
3245
3201
|
this.performanceMonitor = new PerformanceMonitor();
|
|
@@ -3254,12 +3210,8 @@ class LacertaDB {
|
|
|
3254
3210
|
return this.databases.get(name);
|
|
3255
3211
|
}
|
|
3256
3212
|
|
|
3257
|
-
async getSecureDatabase(name, pin, salt = null
|
|
3258
|
-
return this.getDatabase(name, {
|
|
3259
|
-
pin,
|
|
3260
|
-
salt,
|
|
3261
|
-
encryptionConfig
|
|
3262
|
-
});
|
|
3213
|
+
async getSecureDatabase(name, pin, salt = null) {
|
|
3214
|
+
return this.getDatabase(name, { pin, salt });
|
|
3263
3215
|
}
|
|
3264
3216
|
|
|
3265
3217
|
async dropDatabase(name) {
|
|
@@ -3279,32 +3231,15 @@ class LacertaDB {
|
|
|
3279
3231
|
|
|
3280
3232
|
listDatabases() {
|
|
3281
3233
|
const dbNames = new Set();
|
|
3282
|
-
const prefixesToCheck = ['lacertadb_'];
|
|
3283
|
-
|
|
3284
3234
|
for (let i = 0; i < localStorage.length; i++) {
|
|
3285
3235
|
const key = localStorage.key(i);
|
|
3286
|
-
if (
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
// Extract database name - everything between prefix and next underscore
|
|
3291
|
-
const afterPrefix = key.substring(prefix.length);
|
|
3292
|
-
const suffixes = ['_metadata', '_settings', '_version', '_encryption', '_quickstore', '_indexes'];
|
|
3293
|
-
|
|
3294
|
-
for (const suffix of suffixes) {
|
|
3295
|
-
const suffixIndex = afterPrefix.lastIndexOf(suffix);
|
|
3296
|
-
if (suffixIndex > 0) {
|
|
3297
|
-
const dbName = afterPrefix.substring(0, suffixIndex);
|
|
3298
|
-
if (dbName) {
|
|
3299
|
-
dbNames.add(dbName);
|
|
3300
|
-
}
|
|
3301
|
-
break;
|
|
3302
|
-
}
|
|
3303
|
-
}
|
|
3236
|
+
if (key && key.startsWith('lacertadb_')) { // Fixed: was 'lacertadb'
|
|
3237
|
+
const parts = key.split('_'); // Fixed: was split('')
|
|
3238
|
+
if (parts.length >= 2) {
|
|
3239
|
+
dbNames.add(parts[1]);
|
|
3304
3240
|
}
|
|
3305
3241
|
}
|
|
3306
3242
|
}
|
|
3307
|
-
|
|
3308
3243
|
return [...dbNames];
|
|
3309
3244
|
}
|
|
3310
3245
|
|