deepbase-sqlite 3.6.5 → 3.6.7

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
@@ -82,7 +82,7 @@ Efficiently stores nested objects using a key-value schema:
82
82
  - Values are stored as JSON
83
83
  - Fast lookups for both exact keys and partial paths
84
84
 
85
- Each row also stores a monotonic `seq` so reads that rebuild objects use `ORDER BY seq, key`. That matches JavaScript insertion order for sibling keys and keeps `shift()` / `pop()` (via `DeepBase.keys()`) aligned with `JsonDriver`. Existing databases pick up `seq` via `ALTER TABLE` on connect (legacy rows default to `0`, then tie-break by `key`).
85
+ Each row also stores a monotonic `seq` so reads that rebuild objects use `ORDER BY seq, key`. That matches JavaScript insertion order for sibling keys and keeps `shift()` / `pop()` aligned with `JsonDriver`. The driver now exposes `first()` / `last()` using SQL boundary queries in the same order semantics as `keys()`. Existing databases pick up `seq` via `ALTER TABLE` on connect (legacy rows default to `0`, then tie-break by `key`).
86
86
 
87
87
  ### ACID Compliance
88
88
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepbase-sqlite",
3
- "version": "3.6.5",
3
+ "version": "3.6.7",
4
4
  "description": "⚡ DeepBase SQLite - SQLite database driver",
5
5
  "type": "module",
6
6
  "main": "src/index.cjs",
@@ -17,7 +17,7 @@
17
17
  "better-sqlite3": "^11.8.1"
18
18
  },
19
19
  "peerDependencies": {
20
- "deepbase": "^3.6.5"
20
+ "deepbase": "^3.6.7"
21
21
  },
22
22
  "scripts": {
23
23
  "test": "mocha test/test.js"
@@ -112,6 +112,12 @@ export class SqliteDriver extends DeepBaseDriver {
112
112
  this.getKeysLikeStmt = this.db.prepare(
113
113
  "SELECT key, value FROM deepbase WHERE key LIKE ? ESCAPE '!' ORDER BY seq, key",
114
114
  );
115
+ this.getFirstChildKeyStmt = this.db.prepare(
116
+ "SELECT key FROM deepbase WHERE key LIKE ? ESCAPE '!' ORDER BY seq, key LIMIT 1",
117
+ );
118
+ this.getLastChildKeyStmt = this.db.prepare(
119
+ "SELECT key FROM deepbase WHERE key LIKE ? ESCAPE '!' ORDER BY seq DESC, key DESC LIMIT 1",
120
+ );
115
121
  this.delChildrenStmt = this.db.prepare("DELETE FROM deepbase WHERE key LIKE ? ESCAPE '!'");
116
122
  this.hasChildrenStmt = this.db.prepare("SELECT 1 FROM deepbase WHERE key LIKE ? ESCAPE '!' LIMIT 1");
117
123
 
@@ -120,7 +126,8 @@ export class SqliteDriver extends DeepBaseDriver {
120
126
  this.setStmt.run(key, jsonValue);
121
127
  });
122
128
 
123
- this._delTxn = this.db.transaction((key, likePattern) => {
129
+ this._delTxn = this.db.transaction((key, likePattern, keys) => {
130
+ this._expandParentObjects(keys);
124
131
  this.delStmt.run(key);
125
132
  this.delChildrenStmt.run(likePattern);
126
133
  });
@@ -216,7 +223,7 @@ export class SqliteDriver extends DeepBaseDriver {
216
223
  }
217
224
 
218
225
  const key = this._pathToKey(keys);
219
- this._delTxn(key, this._likePrefix(key));
226
+ this._delTxn(key, this._likePrefix(key), keys);
220
227
  }
221
228
 
222
229
  async inc(...args) {
@@ -242,6 +249,46 @@ export class SqliteDriver extends DeepBaseDriver {
242
249
  return this._updTxn(keys, func);
243
250
  }
244
251
 
252
+ async first(...args) {
253
+ return this._firstOrLastKey(args, false);
254
+ }
255
+
256
+ async last(...args) {
257
+ return this._firstOrLastKey(args, true);
258
+ }
259
+
260
+ _firstOrLastKey(path, fromEnd) {
261
+ const nestedKey = this._findBoundaryNestedKey(path, fromEnd);
262
+ if (nestedKey !== undefined) {
263
+ return nestedKey;
264
+ }
265
+
266
+ const value = this._getSync(path);
267
+ if (value === null || typeof value !== 'object') {
268
+ return undefined;
269
+ }
270
+
271
+ let keyResult;
272
+ for (const key in value) {
273
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
274
+ if (!fromEnd) return key;
275
+ keyResult = key;
276
+ }
277
+ }
278
+ return keyResult;
279
+ }
280
+
281
+ _findBoundaryNestedKey(path, fromEnd) {
282
+ const likePattern = path.length === 0 ? '%' : this._likePrefix(this._pathToKey(path));
283
+ const row = (fromEnd ? this.getLastChildKeyStmt : this.getFirstChildKeyStmt).get(likePattern);
284
+ if (!row) return undefined;
285
+
286
+ const fullPath = this._keyToPath(row.key);
287
+ if (fullPath.length <= path.length) return undefined;
288
+
289
+ return fullPath[path.length];
290
+ }
291
+
245
292
  _getRootObject() {
246
293
  const rows = this.getAllStmt.all();
247
294
  const result = {};
package/test/test.js CHANGED
@@ -159,6 +159,15 @@ for (const pragma of PRAGMA_MODES) {
159
159
  assert.deepStrictEqual(await db.get('user'), { name: 'Alice' });
160
160
  });
161
161
 
162
+ it('should delete a deep key from an object value', async function () {
163
+ await db.set('a', {
164
+ b: { c: 1, d: 2 },
165
+ e: 3,
166
+ });
167
+ await db.del('a', 'b', 'c');
168
+ assert.deepStrictEqual(await db.get('a'), { b: { d: 2 }, e: 3 });
169
+ });
170
+
162
171
  it('should delete parent and all children', async function () {
163
172
  await db.set('parent', 'child1', 'value1');
164
173
  await db.set('parent', 'child2', 'value2');
@@ -278,6 +287,19 @@ for (const pragma of PRAGMA_MODES) {
278
287
  assert.deepStrictEqual(await db.values('simple'), []);
279
288
  assert.deepStrictEqual(await db.entries('simple'), []);
280
289
  });
290
+
291
+ it('first/last should match keys() boundaries', async function () {
292
+ const keys = await db.keys('users');
293
+ const driver = db.getDriver(0);
294
+ assert.strictEqual(await driver.first('users'), keys[0]);
295
+ assert.strictEqual(await driver.last('users'), keys[keys.length - 1]);
296
+ });
297
+
298
+ it('first/last should return undefined for missing path', async function () {
299
+ const driver = db.getDriver(0);
300
+ assert.strictEqual(await driver.first('missing'), undefined);
301
+ assert.strictEqual(await driver.last('missing'), undefined);
302
+ });
281
303
  });
282
304
 
283
305
  describe('Persistence', function () {