deepbase-json 3.10.0 → 3.11.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/README.md CHANGED
@@ -244,6 +244,12 @@ new DeepBase(drivers, options)
244
244
  - `await db.last(...path)` - Get last key at path (same order as `keys()`)
245
245
  - `await db.values(...path)` - Get values at path
246
246
  - `await db.entries(...path)` - Get entries at path
247
+ - `db.query(...path)` - Chain a query over the object at path (`where`, `orderBy`, `skip`, `take`, `select`) and run it with `toArray()`, `first()`, `count()` or `any()`
248
+
249
+ `query()` reads the object at the path with `get()` and evaluates the chain in
250
+ memory, so it walks the object already read: v1 pushes no filters to the driver
251
+ and uses no indexes. Terminals resolve to `{ id, value }` records, where `id` is
252
+ the property name and `value` the stored value.
247
253
 
248
254
  ### Migration Methods
249
255
 
@@ -484,58 +490,63 @@ await db.set("a", "b", "circular", "self", await db.get("a", "b"));
484
490
 
485
491
  ## 🔒 Secure Storage with Encryption
486
492
 
487
- You can create encrypted storage by extending DeepBase with custom serialization:
493
+ Use the built-in plugin to encrypt every value with AES-256-GCM before it
494
+ reaches the driver. With `JsonDriver`, values stay encrypted both on disk and
495
+ in its internal memory cache; no custom serialization or memory hooks are needed.
488
496
 
489
497
  ```javascript
490
- import CryptoJS from 'crypto-js';
491
498
  import DeepBase from 'deepbase';
499
+ import { encryptedValues } from 'deepbase/plugins/encryption';
492
500
  import { JsonDriver } from 'deepbase-json';
493
501
 
494
- class DeepbaseSecure extends DeepBase {
495
- constructor(opts) {
496
- const encryptionKey = opts.encryptionKey;
497
- delete opts.encryptionKey;
498
-
499
- // Create JSON driver with encryption
500
- const driver = new JsonDriver({
501
- ...opts,
502
- stringify: (obj) => {
503
- const iv = CryptoJS.lib.WordArray.random(128 / 8);
504
- const encrypted = CryptoJS.AES.encrypt(
505
- JSON.stringify(obj),
506
- encryptionKey,
507
- { iv }
508
- );
509
- return iv.toString(CryptoJS.enc.Hex) + ':' + encrypted.toString();
510
- },
511
- parse: (encryptedData) => {
512
- const [ivHex, encrypted] = encryptedData.split(':');
513
- const iv = CryptoJS.enc.Hex.parse(ivHex);
514
- const bytes = CryptoJS.AES.decrypt(encrypted, encryptionKey, { iv });
515
- return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
516
- }
517
- });
518
-
519
- super(driver);
520
- }
502
+ const encodedKey = process.env.DEEPBASE_ENCRYPTION_KEY;
503
+ if (!encodedKey) {
504
+ throw new Error('DEEPBASE_ENCRYPTION_KEY is required');
521
505
  }
522
506
 
523
- // Create an encrypted database
524
- const secureDB = new DeepbaseSecure({
507
+ const encryptionKey = Buffer.from(encodedKey, 'base64');
508
+ const encryption = encryptedValues({
509
+ activeKeyId: 'primary',
510
+ keys: { primary: encryptionKey }
511
+ });
512
+
513
+ const driver = new JsonDriver({
525
514
  path: '/var/lib/myapp/data',
526
- name: 'secure_db',
527
- encryptionKey: 'your-secret-key-here'
515
+ name: 'secure_db'
528
516
  });
517
+ const secureDB = new DeepBase(driver).use(encryption);
518
+
519
+ try {
520
+ await secureDB.set('config', {
521
+ service: 'my-app',
522
+ accessToken: 'example-token',
523
+ retries: 0
524
+ });
525
+
526
+ await secureDB.inc('config', 'retries', 1);
527
+ const config = await secureDB.get('config'); // Decrypted for the application
528
+ } finally {
529
+ await secureDB.dispose({ clearMemory: true, releaseInstance: true });
530
+ encryptionKey.fill(0);
531
+ }
532
+ ```
529
533
 
530
- await secureDB.connect();
534
+ Provide a base64-encoded, randomly generated 32-byte key through your
535
+ application's secret manager or environment. The application reads it explicitly;
536
+ the plugin requires a 32-byte `Buffer` or `Uint8Array` and never supplies a
537
+ default key. Keep the key available under the same `keyId` to reopen the database.
531
538
 
532
- // Use it like a regular DeepBase instance
533
- await secureDB.set("users", "admin", { password: "secret123" });
534
- const admin = await secureDB.get("users", "admin");
535
- console.log(admin); // { password: 'secret123' }
539
+ Object keys, array lengths, and empty containers remain visible. Plaintext
540
+ exists while your application supplies or reads values, including inside
541
+ `upd()` callbacks; the plugin does not cache decrypted values. Disposal clears
542
+ the JSON driver's cache and the plugin's internal key copies.
536
543
 
537
- // But the file on disk is encrypted!
538
- ```
544
+ Authentication failures, malformed envelopes, and unknown key IDs throw.
545
+ Existing plaintext data is encrypted when rewritten through the plugin;
546
+ registering it does not migrate existing data automatically.
547
+
548
+ See the [encryption plugin documentation](../core/docs/plugins/encryption.md)
549
+ for additional keys and key rotation.
539
550
 
540
551
  ## 🛠️ Creating Custom Drivers
541
552
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepbase-json",
3
- "version": "3.10.0",
3
+ "version": "3.11.0",
4
4
  "description": "⚡ DeepBase JSON - filesystem driver",
5
5
  "type": "module",
6
6
  "main": "src/index.cjs",
@@ -18,10 +18,7 @@
18
18
  "steno": "^0.4.4"
19
19
  },
20
20
  "peerDependencies": {
21
- "deepbase": "^3.10.0"
22
- },
23
- "scripts": {
24
- "test": "mocha test/test.js"
21
+ "deepbase": "^3.11.0"
25
22
  },
26
23
  "devDependencies": {
27
24
  "mocha": "^10.8.2"
@@ -45,5 +42,8 @@
45
42
  "bugs": {
46
43
  "url": "https://github.com/clasen/DeepBase/issues"
47
44
  },
48
- "homepage": "https://github.com/clasen/DeepBase/tree/main/packages/driver-json"
49
- }
45
+ "homepage": "https://github.com/clasen/DeepBase/tree/main/packages/driver-json",
46
+ "scripts": {
47
+ "test": "mocha test/test.js"
48
+ }
49
+ }
package/src/JsonDriver.js CHANGED
@@ -1,4 +1,4 @@
1
- import { DeepBaseDriver } from 'deepbase';
1
+ import { DeepBaseDriver, evaluateQuery, removeKey } from 'deepbase';
2
2
  import steno from 'steno';
3
3
  import fs from 'fs';
4
4
  import * as pathModule from 'path';
@@ -107,6 +107,11 @@ export class JsonDriver extends DeepBaseDriver {
107
107
  const value = this._getRecursive(this.obj, args.slice());
108
108
  return this._decodeFromMemory(value, args);
109
109
  }
110
+
111
+ async query(path, steps) {
112
+ this._assertUsable();
113
+ return evaluateQuery(await this.get(...path), steps, { path });
114
+ }
110
115
 
111
116
  async set(...args) {
112
117
  return this._queueOperation(async () => {
@@ -137,8 +142,7 @@ export class JsonDriver extends DeepBaseDriver {
137
142
  const key = keys.pop();
138
143
  const parentObj = this._getRecursive(this.obj, keys.slice());
139
144
 
140
- if (parentObj && parentObj.hasOwnProperty(key)) {
141
- delete parentObj[key];
145
+ if (removeKey(parentObj, key)) {
142
146
  return this._saveToFile();
143
147
  }
144
148
  });
package/test/test.js CHANGED
@@ -4,6 +4,8 @@ import path from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { fork } from 'child_process';
6
6
  import { DeepBase } from '../../core/src/index.js';
7
+ import { arrayScenarios } from '../../core/test/array-scenario.js';
8
+ import { seedQueryFixture, queryScenarios } from '../../core/test/query-scenario.js';
7
9
  import { JsonDriver } from '../src/JsonDriver.js';
8
10
  import { SqliteDriver } from '../../driver-sqlite/src/SqliteDriver.js';
9
11
 
@@ -905,6 +907,26 @@ describe('JsonDriver', function() {
905
907
  JsonDriver._instances = {};
906
908
  });
907
909
  });
910
+
911
+ describe('query()', function() {
912
+ beforeEach(async function() {
913
+ await seedQueryFixture(db);
914
+ });
915
+
916
+ for (const scenario of queryScenarios) {
917
+ it(scenario.title, async function() {
918
+ await scenario.run(db);
919
+ });
920
+ }
921
+ });
922
+
923
+ describe('array operations', function() {
924
+ for (const scenario of arrayScenarios) {
925
+ it(scenario.title, async function() {
926
+ await scenario.run(db);
927
+ });
928
+ }
929
+ });
908
930
  });
909
931
 
910
932
  describe('Multi-Driver: JsonDriver + SqliteDriver (add + pop + shift)', function() {