locality-idb 1.0.1 → 1.1.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
@@ -128,7 +128,7 @@ const users = await db.from('users').findAll();
128
128
  const alice = await db
129
129
  .from('users')
130
130
  .where((user) => user.email === 'alice@example.com')
131
- .first();
131
+ .findFirst();
132
132
 
133
133
  // Update data
134
134
  await db
@@ -400,7 +400,7 @@ const topTenUsers = await db
400
400
  const user = await db
401
401
  .from('users')
402
402
  .where((user) => user.email === 'john@example.com')
403
- .first();
403
+ .findFirst();
404
404
  // Returns: User | null
405
405
  ```
406
406
 
@@ -812,12 +812,12 @@ Fetches all matching records.
812
812
  const users = await db.from('users').findAll()
813
813
  ```
814
814
 
815
- ##### `first(): Promise<T | null>`
815
+ ##### `findFirst(): Promise<T | null>`
816
816
 
817
817
  Fetches the first matching record.
818
818
 
819
819
  ```typescript
820
- const user = await db.from('users').first()
820
+ const user = await db.from('users').findFirst()
821
821
  ```
822
822
 
823
823
  ##### `findByPk<Key>(key: Key): Promise<T | null>`
@@ -828,10 +828,10 @@ Finds a single record by its primary key value using IndexedDB's optimized `get(
828
828
 
829
829
  ```typescript
830
830
  const user = await db.from('users').findByPk(1);
831
- const post = await db.from('posts').findByPk('uuid-string');
831
+ const post = await db.from('posts').findByPk('some-uuid-string');
832
832
  ```
833
833
 
834
- ##### `findByIndex<IndexKey>(indexName: IndexKey, query: T[IndexKey] | IDBKeyRange): Promise<T[]>`
834
+ ##### `findByIndex<IdxKey>(indexName: IdxKey, query: T[IdxKey] | IDBKeyRange): Promise<T[]>`
835
835
 
836
836
  Finds records using an indexed field. Only accepts field names that are marked with `.index()` or `.unique()`.
837
837
 
@@ -852,7 +852,7 @@ const adults = await db.from('users').findByIndex('age', IDBKeyRange.bound(18, 6
852
852
  > - Unique columns are automatically indexed.
853
853
  > - Unique indexes are recommended for this method to ensure a single result.
854
854
 
855
- ##### `sortByIndex<IndexKey>(indexName: IndexKey, dir?: 'asc' | 'desc'): SelectQuery`
855
+ ##### `sortByIndex<IdxKey>(indexName: IdxKey, dir?: 'asc' | 'desc'): SelectQuery`
856
856
 
857
857
  Sorts results by an indexed field using IndexedDB cursor iteration (avoiding in-memory sorting).
858
858
 
package/dist/index.cjs CHANGED
@@ -12,6 +12,9 @@ function isInteger(value) {
12
12
  function isBoolean(value) {
13
13
  return typeof value === "boolean";
14
14
  }
15
+ function isUndefined(value) {
16
+ return value === void 0;
17
+ }
15
18
  function isBigInt(value) {
16
19
  return typeof value === "bigint";
17
20
  }
@@ -33,6 +36,9 @@ function isObject(value) {
33
36
  function isNotEmptyObject(value) {
34
37
  return isObject(value) && Object.keys(value)?.length > 0;
35
38
  }
39
+ function isFunction(value) {
40
+ return typeof value === "function";
41
+ }
36
42
  function isDate(value) {
37
43
  return value instanceof Date;
38
44
  }
@@ -456,6 +462,8 @@ var SelectQuery = class {
456
462
  #readyPromise;
457
463
  #dbGetter;
458
464
  #whereCondition;
465
+ #whereIndexName;
466
+ #whereIndexQuery;
459
467
  #orderByKey;
460
468
  #orderByDir = "asc";
461
469
  #limitCount;
@@ -465,6 +473,48 @@ var SelectQuery = class {
465
473
  this.#dbGetter = dbGetter;
466
474
  this.#readyPromise = readyPromise;
467
475
  }
476
+ #createTransaction() {
477
+ return this.#dbGetter().transaction(this.#table, "readonly");
478
+ }
479
+ #getStoreWithTransaction() {
480
+ return this.#createTransaction().objectStore(this.#table);
481
+ }
482
+ /** @internal Check if key is an index on the store for the `#whereIndexName` */
483
+ #isIndexKey(store) {
484
+ return isNonEmptyString(this.#whereIndexName) && store.indexNames.contains(this.#whereIndexName);
485
+ }
486
+ /** @internal Check if key is the primary key on the store for the `#whereIndexName` */
487
+ #isPrimaryKey(store) {
488
+ return isNonEmptyString(this.#whereIndexName) && store.keyPath === this.#whereIndexName;
489
+ }
490
+ #buildIndexedStore(store, reject) {
491
+ const isPK = this.#isPrimaryKey(store);
492
+ const isIndex = this.#isIndexKey(store);
493
+ if (!isPK && !isIndex) {
494
+ reject(/* @__PURE__ */ new RangeError(`Index '${this.#whereIndexName}' does not exist on table '${this.#table}'`));
495
+ return null;
496
+ }
497
+ return isPK ? store : store.index(this.#whereIndexName);
498
+ }
499
+ /** @internal Sort data in memory if needed */
500
+ #sort(data) {
501
+ if (this.#orderByKey) return sortAnArray(data, {
502
+ sortOrder: this.#orderByDir,
503
+ sortByField: this.#orderByKey
504
+ });
505
+ return data;
506
+ }
507
+ /** Projects a row based on selected fields */
508
+ #projectRow(row) {
509
+ if (!isNotEmptyObject(this?.[Selected])) return row;
510
+ const projected = {};
511
+ const selectionEntries = Object.entries(this[Selected]);
512
+ const selectionKeys = new Set(Object.keys(this[Selected]));
513
+ if (selectionEntries.some(([, value]) => value === true)) {
514
+ for (const [key, value] of selectionEntries) if (value === true) projected[key] = row[key];
515
+ } else for (const key of Object.keys(row)) if (!selectionKeys.has(key) || this[Selected][key] !== false) projected[key] = row[key];
516
+ return projected;
517
+ }
468
518
  /**
469
519
  * @instance Select or exclude specific columns
470
520
  * @param cols Columns to select or exclude
@@ -473,18 +523,26 @@ var SelectQuery = class {
473
523
  this[Selected] = cols;
474
524
  return this;
475
525
  }
476
- /**
477
- * @instance Filter rows based on predicate function
478
- * @param predicate Filtering function
479
- */
480
- where(predicate) {
481
- this.#whereCondition = predicate;
526
+ where(predicate, query) {
527
+ if (isFunction(predicate)) {
528
+ this.#whereCondition = predicate;
529
+ this.#whereIndexName = void 0;
530
+ this.#whereIndexQuery = void 0;
531
+ } else if (isNonEmptyString(predicate) && !isUndefined(query)) {
532
+ this.#whereIndexName = predicate;
533
+ this.#whereIndexQuery = query;
534
+ this.#whereCondition = void 0;
535
+ }
482
536
  return this;
483
537
  }
484
538
  /**
485
539
  * @instance Order results by specified key and direction
486
540
  * @param key Key to order by
487
541
  * @param dir Direction: 'asc' | 'desc' (default: 'asc')
542
+ *
543
+ * @remarks
544
+ * - This method performs in-memory sorting.
545
+ * - For optimized sorting using `IndexedDB` indexes, use {@link sortByIndex} instead.
488
546
  */
489
547
  orderBy(key, dir = "asc") {
490
548
  this.#orderByKey = key;
@@ -492,6 +550,22 @@ var SelectQuery = class {
492
550
  return this;
493
551
  }
494
552
  /**
553
+ * @instance Order results by index using optimized `IndexedDB` cursor
554
+ * @param indexName Name of the index to sort by
555
+ * @param dir Direction: 'asc' | 'desc' (default: 'asc')
556
+ *
557
+ * @remarks
558
+ * - This method uses `IndexedDB` indexes for sorting, which is more efficient for large datasets.
559
+ * - Ensure that the specified index exists on the table.
560
+ * - For in-memory sorting, use {@link orderBy} instead.
561
+ */
562
+ sortByIndex(indexName, dir = "asc") {
563
+ this.#orderByKey = indexName;
564
+ this.#orderByDir = dir;
565
+ this.#useIndexCursor = true;
566
+ return this;
567
+ }
568
+ /**
495
569
  * @instance Limit number of results
496
570
  * @param count Maximum number of results to return
497
571
  */
@@ -499,21 +573,23 @@ var SelectQuery = class {
499
573
  this.#limitCount = count;
500
574
  return this;
501
575
  }
502
- /** Projects a row based on selected fields */
503
- #projectRow(row) {
504
- if (!isNotEmptyObject(this?.[Selected])) return row;
505
- const projected = {};
506
- const selectionEntries = Object.entries(this[Selected]);
507
- const selectionKeys = new Set(Object.keys(this[Selected]));
508
- if (selectionEntries.some(([, value]) => value === true)) {
509
- for (const [key, value] of selectionEntries) if (value === true) projected[key] = row[key];
510
- } else for (const key of Object.keys(row)) if (!selectionKeys.has(key) || this[Selected][key] !== false) projected[key] = row[key];
511
- return projected;
512
- }
513
576
  async findAll() {
514
577
  await this.#readyPromise;
515
578
  return new Promise((resolve, reject) => {
516
- const store = this.#dbGetter().transaction(this.#table, "readonly").objectStore(this.#table);
579
+ const store = this.#getStoreWithTransaction();
580
+ if (this.#whereIndexName && !isUndefined(this.#whereIndexQuery)) {
581
+ const source = this.#buildIndexedStore(store, reject);
582
+ if (!source) return;
583
+ const request = source.getAll(this.#whereIndexQuery);
584
+ request.onsuccess = () => {
585
+ let results = request.result;
586
+ results = this.#sort(results);
587
+ if (this.#limitCount) results = results.slice(0, this.#limitCount);
588
+ resolve(results.map((row) => this.#projectRow(row)));
589
+ };
590
+ request.onerror = () => reject(request.error);
591
+ return;
592
+ }
517
593
  if (this.#useIndexCursor && this.#orderByKey && isNonEmptyString(this.#orderByKey) && store.indexNames.contains(this.#orderByKey) && !this.#whereCondition) {
518
594
  const index = store.index(this.#orderByKey);
519
595
  const direction = this.#orderByDir === "desc" ? "prev" : "next";
@@ -546,10 +622,23 @@ var SelectQuery = class {
546
622
  }
547
623
  });
548
624
  }
549
- async first() {
625
+ async findFirst() {
550
626
  await this.#readyPromise;
551
627
  return new Promise((resolve, reject) => {
552
- const request = this.#dbGetter().transaction(this.#table, "readonly").objectStore(this.#table).getAll();
628
+ const store = this.#getStoreWithTransaction();
629
+ if (this.#whereIndexName && !isUndefined(this.#whereIndexQuery)) {
630
+ const source = this.#buildIndexedStore(store, reject);
631
+ if (!source) return;
632
+ const request = source.getAll(this.#whereIndexQuery);
633
+ request.onsuccess = () => {
634
+ const results = request.result;
635
+ if (results.length > 0) resolve(this.#projectRow(results[0]));
636
+ else resolve(null);
637
+ };
638
+ request.onerror = () => reject(request.error);
639
+ return;
640
+ }
641
+ const request = store.getAll();
553
642
  request.onsuccess = () => {
554
643
  let results = request.result;
555
644
  if (this.#whereCondition) results = results.filter(this.#whereCondition);
@@ -562,11 +651,16 @@ var SelectQuery = class {
562
651
  /**
563
652
  * @instance Find record by primary key (optimized `IndexedDB` get)
564
653
  * @param key Primary key value
654
+ *
655
+ * @remarks
656
+ * - This method uses the `IndexedDB` primary key for efficient querying.
657
+ * - Ensure that the specified key exists on the table.
658
+ * - To find by index, use {@link findByIndex} instead.
565
659
  */
566
660
  async findByPk(key) {
567
661
  await this.#readyPromise;
568
662
  return new Promise((resolve, reject) => {
569
- const request = this.#dbGetter().transaction(this.#table, "readonly").objectStore(this.#table).get(key);
663
+ const request = this.#getStoreWithTransaction().get(key);
570
664
  request.onsuccess = () => {
571
665
  const result = request.result;
572
666
  if (!result) {
@@ -583,16 +677,21 @@ var SelectQuery = class {
583
677
  });
584
678
  }
585
679
  /**
586
- * @instance Find records by index (optimized IndexedDB index query)
680
+ * @instance Find records by index (optimized `IndexedDB` index query)
587
681
  * @param indexName Name of the index to query
588
682
  * @param query Key value to search for
683
+ *
684
+ * @remarks
685
+ * - This method uses `IndexedDB` indexes for efficient querying.
686
+ * - Ensure that the specified index exists on the table.
687
+ * - To find by primary key, use {@link findByPk} instead.
589
688
  */
590
689
  async findByIndex(indexName, query) {
591
690
  await this.#readyPromise;
592
691
  return new Promise((resolve, reject) => {
593
- const store = this.#dbGetter().transaction(this.#table, "readonly").objectStore(this.#table);
692
+ const store = this.#getStoreWithTransaction();
594
693
  if (!store.indexNames.contains(indexName)) {
595
- reject(/* @__PURE__ */ new Error(`Index "${indexName}" does not exist on table "${this.#table}"`));
694
+ reject(/* @__PURE__ */ new Error(`Index '${indexName}' does not exist on table '${this.#table}'`));
596
695
  return;
597
696
  }
598
697
  const request = store.index(indexName).getAll(query);
@@ -606,24 +705,31 @@ var SelectQuery = class {
606
705
  request.onerror = () => reject(request.error);
607
706
  });
608
707
  }
609
- /**
610
- * @instance Order results by index using optimized IndexedDB cursor
611
- * @param indexName Name of the index to sort by
612
- * @param dir Direction: 'asc' | 'desc' (default: 'asc')
613
- */
614
- sortByIndex(indexName, dir = "asc") {
615
- this.#orderByKey = indexName;
616
- this.#orderByDir = dir;
617
- this.#useIndexCursor = true;
618
- return this;
619
- }
620
- /** @internal Sort data in memory if needed */
621
- #sort(data) {
622
- if (this.#orderByKey) return sortAnArray(data, {
623
- sortOrder: this.#orderByDir,
624
- sortByField: this.#orderByKey
708
+ /** @instance Count matching records */
709
+ async count() {
710
+ await this.#readyPromise;
711
+ return new Promise((resolve, reject) => {
712
+ const store = this.#getStoreWithTransaction();
713
+ if (this.#whereIndexName && !isUndefined(this.#whereIndexQuery)) {
714
+ const source = this.#buildIndexedStore(store, reject);
715
+ if (!source) return;
716
+ const request = source.count(this.#whereIndexQuery);
717
+ request.onsuccess = () => resolve(request.result);
718
+ request.onerror = () => reject(request.error);
719
+ return;
720
+ }
721
+ if (this.#whereCondition) {
722
+ const request = store.getAll();
723
+ request.onsuccess = () => {
724
+ resolve(request.result.filter(this.#whereCondition).length);
725
+ };
726
+ request.onerror = () => reject(request.error);
727
+ return;
728
+ }
729
+ const request = store.count();
730
+ request.onsuccess = () => resolve(request.result);
731
+ request.onerror = () => reject(request.error);
625
732
  });
626
- return data;
627
733
  }
628
734
  };
629
735
  /** @class Insert query builder. */
package/dist/index.d.cts CHANGED
@@ -329,6 +329,8 @@ type $InferTimestamp<T extends ColumnDefinition> = { [K in keyof T]: T[K] extend
329
329
  type Timestamp = Branded<string, 'Timestamp'>;
330
330
  /** Sort direction type for ordering queries */
331
331
  type SortDirection = 'asc' | 'desc';
332
+ /** Predicate function type for WHERE clauses in queries */
333
+ type WherePredicate<T extends GenericObject> = (row: T) => boolean;
332
334
  /** Creates a type for insert operations with auto-generated fields optional. */
333
335
  type InferInsertType<T extends Table> = Prettify<Omit<$InferRow<T['columns']>, $InferAutoInc<T['columns']> | $InferDefault<T['columns']> | $InferTimestamp<T['columns']> | $InferUUID<T['columns']>> & { [K in $InferAutoInc<T['columns']> | $InferDefault<T['columns']> | $InferTimestamp<T['columns']> | $InferUUID<T['columns']>]?: K extends keyof $InferRow<T['columns']> ? $InferRow<T['columns']>[K] : never }>;
334
336
  /** Creates a type for update operations with all fields optional except primary key. */
@@ -376,13 +378,34 @@ declare class SelectQuery<T extends GenericObject, S extends Partial<Record<stri
376
378
  * @instance Filter rows based on predicate function
377
379
  * @param predicate Filtering function
378
380
  */
379
- where(predicate: (row: T) => boolean): this;
381
+ where(predicate: WherePredicate<T>): this;
382
+ /**
383
+ * @instance Filter rows based on index query
384
+ * @param indexName Name of the index/primary key to query
385
+ * @param query Key value or {@link IDBKeyRange} to search for
386
+ */
387
+ where<IdxKey extends $InferPrimaryKey<Tbl['columns']> | $InferIndex<Tbl['columns']>>(indexName: IdxKey, query: IDBKeyRange | T[keyof T]): this;
380
388
  /**
381
389
  * @instance Order results by specified key and direction
382
390
  * @param key Key to order by
383
391
  * @param dir Direction: 'asc' | 'desc' (default: 'asc')
392
+ *
393
+ * @remarks
394
+ * - This method performs in-memory sorting.
395
+ * - For optimized sorting using `IndexedDB` indexes, use {@link sortByIndex} instead.
384
396
  */
385
397
  orderBy<Key extends NestedPrimitiveKey<T>>(key: Key, dir?: SortDirection): this;
398
+ /**
399
+ * @instance Order results by index using optimized `IndexedDB` cursor
400
+ * @param indexName Name of the index to sort by
401
+ * @param dir Direction: 'asc' | 'desc' (default: 'asc')
402
+ *
403
+ * @remarks
404
+ * - This method uses `IndexedDB` indexes for sorting, which is more efficient for large datasets.
405
+ * - Ensure that the specified index exists on the table.
406
+ * - For in-memory sorting, use {@link orderBy} instead.
407
+ */
408
+ sortByIndex<IdxKey extends $InferIndex<Tbl['columns']> | $InferPrimaryKey<Tbl['columns']>>(indexName: IdxKey, dir?: SortDirection): this;
386
409
  /**
387
410
  * @instance Limit number of results
388
411
  * @param count Maximum number of results to return
@@ -393,26 +416,32 @@ declare class SelectQuery<T extends GenericObject, S extends Partial<Record<stri
393
416
  /** Fetch all matching records with selected fields */
394
417
  findAll<Selection extends Partial<Record<keyof T, boolean>>>(this: SelectQuery<T, Selection>): Promise<SelectFields<T, Selection>[]>;
395
418
  /** Fetch first matching record */
396
- first(this: SelectQuery<T, null>): Promise<T | null>;
419
+ findFirst(this: SelectQuery<T, null>): Promise<T | null>;
397
420
  /** Fetch first matching record with selected fields */
398
- first<Selection extends Partial<Record<keyof T, boolean>>>(this: SelectQuery<T, Selection>): Promise<SelectFields<T, Selection> | null>;
421
+ findFirst<Selection extends Partial<Record<keyof T, boolean>>>(this: SelectQuery<T, Selection>): Promise<SelectFields<T, Selection> | null>;
399
422
  /**
400
423
  * @instance Find record by primary key (optimized `IndexedDB` get)
401
424
  * @param key Primary key value
425
+ *
426
+ * @remarks
427
+ * - This method uses the `IndexedDB` primary key for efficient querying.
428
+ * - Ensure that the specified key exists on the table.
429
+ * - To find by index, use {@link findByIndex} instead.
402
430
  */
403
431
  findByPk(key: $InferPrimaryKey<Tbl['columns']> extends keyof T ? T[$InferPrimaryKey<Tbl['columns']>] : T[keyof T]): Promise<S extends null ? T | null : S extends Partial<Record<keyof T, boolean>> ? SelectFields<T, S> | null : never>;
404
432
  /**
405
- * @instance Find records by index (optimized IndexedDB index query)
433
+ * @instance Find records by index (optimized `IndexedDB` index query)
406
434
  * @param indexName Name of the index to query
407
435
  * @param query Key value to search for
436
+ *
437
+ * @remarks
438
+ * - This method uses `IndexedDB` indexes for efficient querying.
439
+ * - Ensure that the specified index exists on the table.
440
+ * - To find by primary key, use {@link findByPk} instead.
408
441
  */
409
- findByIndex<IndexKey extends $InferIndex<Tbl['columns']> & keyof T & string>(indexName: IndexKey, query: T[IndexKey] | IDBKeyRange): Promise<S extends null ? T[] : S extends Partial<Record<keyof T, boolean>> ? SelectFields<T, S>[] : never>;
410
- /**
411
- * @instance Order results by index using optimized IndexedDB cursor
412
- * @param indexName Name of the index to sort by
413
- * @param dir Direction: 'asc' | 'desc' (default: 'asc')
414
- */
415
- sortByIndex<IndexKey extends ($InferIndex<Tbl['columns']> | $InferPrimaryKey<Tbl['columns']>) & keyof T & string>(indexName: IndexKey, dir?: SortDirection): this;
442
+ findByIndex<IdxKey extends $InferIndex<Tbl['columns']> & keyof T & string>(indexName: IdxKey, query: T[IdxKey] | IDBKeyRange): Promise<S extends null ? T[] : S extends Partial<Record<keyof T, boolean>> ? SelectFields<T, S>[] : never>;
443
+ /** @instance Count matching records */
444
+ count(): Promise<number>;
416
445
  }
417
446
  /** @class Insert query builder. */
418
447
  declare class InsertQuery<Raw extends GenericObject, Inserted extends Raw | Raw[], Data extends GenericObject, Return extends (Inserted extends Array<infer _> ? Data[] : Data)> {
@@ -848,4 +877,4 @@ declare function deleteDB(name: string): Promise<void>;
848
877
  */
849
878
  declare function validateColumnType<T extends TypeName>(type: T, value: unknown): string | null;
850
879
  //#endregion
851
- export { $InferAutoInc, $InferDefault, $InferIndex, $InferOptional, $InferPrimaryKey, $InferRow, $InferTimestamp, $InferUUID, $InferUnique, $UUID, $UUIDVersion, $UnionToIntersection, $ValidateSinglePK, AdvancedTypes, ArrayToTuple, AsyncFunction, BasicPrimitive, Branded, type Column, ColumnDefinition, ColumnRecord, Constructor, DateLike, FirstOverloadParams, GenericFn, GenericObject, IndexConfig, IndexKeyType, InferInsertType, InferSelectType, InferUpdateType, List, Locality, LocalityConfig, LooseLiteral, MapObjectValues, Maybe, NestedPrimitiveKey, NormalPrimitive, Numeric, Prettify, PrimaryKeyType, Primitive, RejectFn, SchemaDefinition, SchemaRecord, SelectFields, SortDirection, StoreConfig, type Table, Timestamp, Tuple, TypeName, UUID, UUIDVersion, UniqueKeyType, ValidatedColumnDefinition, VoidFn, column, defineSchema, deleteDB, getTimestamp, isTimestamp, openDBWithStores, table, uuidV4, validateColumnType };
880
+ export { $InferAutoInc, $InferDefault, $InferIndex, $InferOptional, $InferPrimaryKey, $InferRow, $InferTimestamp, $InferUUID, $InferUnique, $UUID, $UUIDVersion, $UnionToIntersection, $ValidateSinglePK, AdvancedTypes, ArrayToTuple, AsyncFunction, BasicPrimitive, Branded, type Column, ColumnDefinition, ColumnRecord, Constructor, DateLike, FirstOverloadParams, GenericFn, GenericObject, IndexConfig, IndexKeyType, InferInsertType, InferSelectType, InferUpdateType, List, Locality, LocalityConfig, LooseLiteral, MapObjectValues, Maybe, NestedPrimitiveKey, NormalPrimitive, Numeric, Prettify, PrimaryKeyType, Primitive, RejectFn, SchemaDefinition, SchemaRecord, SelectFields, SortDirection, StoreConfig, type Table, Timestamp, Tuple, TypeName, UUID, UUIDVersion, UniqueKeyType, ValidatedColumnDefinition, VoidFn, WherePredicate, column, defineSchema, deleteDB, getTimestamp, isTimestamp, openDBWithStores, table, uuidV4, validateColumnType };
package/dist/index.d.mts CHANGED
@@ -329,6 +329,8 @@ type $InferTimestamp<T extends ColumnDefinition> = { [K in keyof T]: T[K] extend
329
329
  type Timestamp = Branded<string, 'Timestamp'>;
330
330
  /** Sort direction type for ordering queries */
331
331
  type SortDirection = 'asc' | 'desc';
332
+ /** Predicate function type for WHERE clauses in queries */
333
+ type WherePredicate<T extends GenericObject> = (row: T) => boolean;
332
334
  /** Creates a type for insert operations with auto-generated fields optional. */
333
335
  type InferInsertType<T extends Table> = Prettify<Omit<$InferRow<T['columns']>, $InferAutoInc<T['columns']> | $InferDefault<T['columns']> | $InferTimestamp<T['columns']> | $InferUUID<T['columns']>> & { [K in $InferAutoInc<T['columns']> | $InferDefault<T['columns']> | $InferTimestamp<T['columns']> | $InferUUID<T['columns']>]?: K extends keyof $InferRow<T['columns']> ? $InferRow<T['columns']>[K] : never }>;
334
336
  /** Creates a type for update operations with all fields optional except primary key. */
@@ -376,13 +378,34 @@ declare class SelectQuery<T extends GenericObject, S extends Partial<Record<stri
376
378
  * @instance Filter rows based on predicate function
377
379
  * @param predicate Filtering function
378
380
  */
379
- where(predicate: (row: T) => boolean): this;
381
+ where(predicate: WherePredicate<T>): this;
382
+ /**
383
+ * @instance Filter rows based on index query
384
+ * @param indexName Name of the index/primary key to query
385
+ * @param query Key value or {@link IDBKeyRange} to search for
386
+ */
387
+ where<IdxKey extends $InferPrimaryKey<Tbl['columns']> | $InferIndex<Tbl['columns']>>(indexName: IdxKey, query: IDBKeyRange | T[keyof T]): this;
380
388
  /**
381
389
  * @instance Order results by specified key and direction
382
390
  * @param key Key to order by
383
391
  * @param dir Direction: 'asc' | 'desc' (default: 'asc')
392
+ *
393
+ * @remarks
394
+ * - This method performs in-memory sorting.
395
+ * - For optimized sorting using `IndexedDB` indexes, use {@link sortByIndex} instead.
384
396
  */
385
397
  orderBy<Key extends NestedPrimitiveKey<T>>(key: Key, dir?: SortDirection): this;
398
+ /**
399
+ * @instance Order results by index using optimized `IndexedDB` cursor
400
+ * @param indexName Name of the index to sort by
401
+ * @param dir Direction: 'asc' | 'desc' (default: 'asc')
402
+ *
403
+ * @remarks
404
+ * - This method uses `IndexedDB` indexes for sorting, which is more efficient for large datasets.
405
+ * - Ensure that the specified index exists on the table.
406
+ * - For in-memory sorting, use {@link orderBy} instead.
407
+ */
408
+ sortByIndex<IdxKey extends $InferIndex<Tbl['columns']> | $InferPrimaryKey<Tbl['columns']>>(indexName: IdxKey, dir?: SortDirection): this;
386
409
  /**
387
410
  * @instance Limit number of results
388
411
  * @param count Maximum number of results to return
@@ -393,26 +416,32 @@ declare class SelectQuery<T extends GenericObject, S extends Partial<Record<stri
393
416
  /** Fetch all matching records with selected fields */
394
417
  findAll<Selection extends Partial<Record<keyof T, boolean>>>(this: SelectQuery<T, Selection>): Promise<SelectFields<T, Selection>[]>;
395
418
  /** Fetch first matching record */
396
- first(this: SelectQuery<T, null>): Promise<T | null>;
419
+ findFirst(this: SelectQuery<T, null>): Promise<T | null>;
397
420
  /** Fetch first matching record with selected fields */
398
- first<Selection extends Partial<Record<keyof T, boolean>>>(this: SelectQuery<T, Selection>): Promise<SelectFields<T, Selection> | null>;
421
+ findFirst<Selection extends Partial<Record<keyof T, boolean>>>(this: SelectQuery<T, Selection>): Promise<SelectFields<T, Selection> | null>;
399
422
  /**
400
423
  * @instance Find record by primary key (optimized `IndexedDB` get)
401
424
  * @param key Primary key value
425
+ *
426
+ * @remarks
427
+ * - This method uses the `IndexedDB` primary key for efficient querying.
428
+ * - Ensure that the specified key exists on the table.
429
+ * - To find by index, use {@link findByIndex} instead.
402
430
  */
403
431
  findByPk(key: $InferPrimaryKey<Tbl['columns']> extends keyof T ? T[$InferPrimaryKey<Tbl['columns']>] : T[keyof T]): Promise<S extends null ? T | null : S extends Partial<Record<keyof T, boolean>> ? SelectFields<T, S> | null : never>;
404
432
  /**
405
- * @instance Find records by index (optimized IndexedDB index query)
433
+ * @instance Find records by index (optimized `IndexedDB` index query)
406
434
  * @param indexName Name of the index to query
407
435
  * @param query Key value to search for
436
+ *
437
+ * @remarks
438
+ * - This method uses `IndexedDB` indexes for efficient querying.
439
+ * - Ensure that the specified index exists on the table.
440
+ * - To find by primary key, use {@link findByPk} instead.
408
441
  */
409
- findByIndex<IndexKey extends $InferIndex<Tbl['columns']> & keyof T & string>(indexName: IndexKey, query: T[IndexKey] | IDBKeyRange): Promise<S extends null ? T[] : S extends Partial<Record<keyof T, boolean>> ? SelectFields<T, S>[] : never>;
410
- /**
411
- * @instance Order results by index using optimized IndexedDB cursor
412
- * @param indexName Name of the index to sort by
413
- * @param dir Direction: 'asc' | 'desc' (default: 'asc')
414
- */
415
- sortByIndex<IndexKey extends ($InferIndex<Tbl['columns']> | $InferPrimaryKey<Tbl['columns']>) & keyof T & string>(indexName: IndexKey, dir?: SortDirection): this;
442
+ findByIndex<IdxKey extends $InferIndex<Tbl['columns']> & keyof T & string>(indexName: IdxKey, query: T[IdxKey] | IDBKeyRange): Promise<S extends null ? T[] : S extends Partial<Record<keyof T, boolean>> ? SelectFields<T, S>[] : never>;
443
+ /** @instance Count matching records */
444
+ count(): Promise<number>;
416
445
  }
417
446
  /** @class Insert query builder. */
418
447
  declare class InsertQuery<Raw extends GenericObject, Inserted extends Raw | Raw[], Data extends GenericObject, Return extends (Inserted extends Array<infer _> ? Data[] : Data)> {
@@ -848,4 +877,4 @@ declare function deleteDB(name: string): Promise<void>;
848
877
  */
849
878
  declare function validateColumnType<T extends TypeName>(type: T, value: unknown): string | null;
850
879
  //#endregion
851
- export { $InferAutoInc, $InferDefault, $InferIndex, $InferOptional, $InferPrimaryKey, $InferRow, $InferTimestamp, $InferUUID, $InferUnique, $UUID, $UUIDVersion, $UnionToIntersection, $ValidateSinglePK, AdvancedTypes, ArrayToTuple, AsyncFunction, BasicPrimitive, Branded, type Column, ColumnDefinition, ColumnRecord, Constructor, DateLike, FirstOverloadParams, GenericFn, GenericObject, IndexConfig, IndexKeyType, InferInsertType, InferSelectType, InferUpdateType, List, Locality, LocalityConfig, LooseLiteral, MapObjectValues, Maybe, NestedPrimitiveKey, NormalPrimitive, Numeric, Prettify, PrimaryKeyType, Primitive, RejectFn, SchemaDefinition, SchemaRecord, SelectFields, SortDirection, StoreConfig, type Table, Timestamp, Tuple, TypeName, UUID, UUIDVersion, UniqueKeyType, ValidatedColumnDefinition, VoidFn, column, defineSchema, deleteDB, getTimestamp, isTimestamp, openDBWithStores, table, uuidV4, validateColumnType };
880
+ export { $InferAutoInc, $InferDefault, $InferIndex, $InferOptional, $InferPrimaryKey, $InferRow, $InferTimestamp, $InferUUID, $InferUnique, $UUID, $UUIDVersion, $UnionToIntersection, $ValidateSinglePK, AdvancedTypes, ArrayToTuple, AsyncFunction, BasicPrimitive, Branded, type Column, ColumnDefinition, ColumnRecord, Constructor, DateLike, FirstOverloadParams, GenericFn, GenericObject, IndexConfig, IndexKeyType, InferInsertType, InferSelectType, InferUpdateType, List, Locality, LocalityConfig, LooseLiteral, MapObjectValues, Maybe, NestedPrimitiveKey, NormalPrimitive, Numeric, Prettify, PrimaryKeyType, Primitive, RejectFn, SchemaDefinition, SchemaRecord, SelectFields, SortDirection, StoreConfig, type Table, Timestamp, Tuple, TypeName, UUID, UUIDVersion, UniqueKeyType, ValidatedColumnDefinition, VoidFn, WherePredicate, column, defineSchema, deleteDB, getTimestamp, isTimestamp, openDBWithStores, table, uuidV4, validateColumnType };