@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.
Files changed (2) hide show
  1. package/index.js +55 -120
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * LacertaDB V5.0.0 - Complete Production Library (Corrected)
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 (CORRECTED)
491
+ // Database-Level Encryption
492
492
  // ========================
493
493
 
494
494
  class SecureDatabaseEncryption {
495
- constructor(config = {}) {
495
+ constructor() {
496
496
  this.masterKey = null;
497
497
  this.salt = null;
498
- // Make iterations configurable with higher default for better security
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 generateSecurePassword(length = 20) {
736
- const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()_+-=[]{}|;:,.<>?';
737
- const randomValues = new Uint8Array(length);
738
- crypto.getRandomValues(randomValues);
739
- return Array.from(randomValues)
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 || 2000000;
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 (CORRECTED)
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] = collectionMetadata.toJSON ?
1986
- collectionMetadata.toJSON() :
1987
- {
1988
- sizeKB: collectionMetadata.sizeKB,
1989
- length: collectionMetadata.length,
1990
- createdAt: collectionMetadata.createdAt,
1991
- modifiedAt: collectionMetadata.modifiedAt,
1992
- documentSizes: collectionMetadata.documentSizes,
1993
- documentModifiedAt: collectionMetadata.documentModifiedAt,
1994
- documentPermanent: collectionMetadata.documentPermanent,
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 (CORRECTED)
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 (CORRECTED)
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, { password: options.password }).catch(() => null);
2734
+ const fullDoc = await this.get(docId);
2781
2735
 
2782
2736
  // Remove from indexes
2783
- if (fullDoc) {
2784
- for (const [indexName, index] of this.indexManager.indexes) {
2785
- const value = this.indexManager.getFieldValue(fullDoc, index.fieldPath);
2786
- if (value !== undefined && value !== null) {
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
- const docIds = await this.indexManager.query(indexName, fieldValue);
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 (CORRECTED)
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(this.database?.encryptionConfig || {});
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('__private_keys__').catch(() => null);
3014
+ let keyStore = await this.getCollection('private_keys').catch(() => null);
3055
3015
  if (!keyStore) {
3056
- keyStore = await this.createCollection('__private_keys__', {
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('__private_keys__');
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
- if (e.code === 'COLLECTION_EXISTS') {
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 (CORRECTED)
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, encryptionConfig = {}) {
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 (!key) continue;
3287
-
3288
- for (const prefix of prefixesToCheck) {
3289
- if (key.startsWith(prefix)) {
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixagram/lacerta-db",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "Lacerta-DB is a Javascript IndexedDB Database for Web Browsers. Simple, Fast, Secure.",
5
5
  "devDependencies": {
6
6
  "@babel/core": "^7.23.6",