deepbase-sqlite 3.6.5 → 3.6.6

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.6",
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.6"
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
 
@@ -242,6 +248,46 @@ export class SqliteDriver extends DeepBaseDriver {
242
248
  return this._updTxn(keys, func);
243
249
  }
244
250
 
251
+ async first(...args) {
252
+ return this._firstOrLastKey(args, false);
253
+ }
254
+
255
+ async last(...args) {
256
+ return this._firstOrLastKey(args, true);
257
+ }
258
+
259
+ _firstOrLastKey(path, fromEnd) {
260
+ const nestedKey = this._findBoundaryNestedKey(path, fromEnd);
261
+ if (nestedKey !== undefined) {
262
+ return nestedKey;
263
+ }
264
+
265
+ const value = this._getSync(path);
266
+ if (value === null || typeof value !== 'object') {
267
+ return undefined;
268
+ }
269
+
270
+ let keyResult;
271
+ for (const key in value) {
272
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
273
+ if (!fromEnd) return key;
274
+ keyResult = key;
275
+ }
276
+ }
277
+ return keyResult;
278
+ }
279
+
280
+ _findBoundaryNestedKey(path, fromEnd) {
281
+ const likePattern = path.length === 0 ? '%' : this._likePrefix(this._pathToKey(path));
282
+ const row = (fromEnd ? this.getLastChildKeyStmt : this.getFirstChildKeyStmt).get(likePattern);
283
+ if (!row) return undefined;
284
+
285
+ const fullPath = this._keyToPath(row.key);
286
+ if (fullPath.length <= path.length) return undefined;
287
+
288
+ return fullPath[path.length];
289
+ }
290
+
245
291
  _getRootObject() {
246
292
  const rows = this.getAllStmt.all();
247
293
  const result = {};
package/test/test.js CHANGED
@@ -278,6 +278,19 @@ for (const pragma of PRAGMA_MODES) {
278
278
  assert.deepStrictEqual(await db.values('simple'), []);
279
279
  assert.deepStrictEqual(await db.entries('simple'), []);
280
280
  });
281
+
282
+ it('first/last should match keys() boundaries', async function () {
283
+ const keys = await db.keys('users');
284
+ const driver = db.getDriver(0);
285
+ assert.strictEqual(await driver.first('users'), keys[0]);
286
+ assert.strictEqual(await driver.last('users'), keys[keys.length - 1]);
287
+ });
288
+
289
+ it('first/last should return undefined for missing path', async function () {
290
+ const driver = db.getDriver(0);
291
+ assert.strictEqual(await driver.first('missing'), undefined);
292
+ assert.strictEqual(await driver.last('missing'), undefined);
293
+ });
281
294
  });
282
295
 
283
296
  describe('Persistence', function () {