locality-idb 1.5.4 β 1.5.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 +27 -10
- package/dist/index.cjs +10 -10
- package/dist/index.d.cts +23 -21
- package/dist/index.d.mts +23 -21
- package/dist/index.iife.js +10 -10
- package/dist/index.mjs +10 -10
- package/package.json +17 -4
package/README.md
CHANGED
|
@@ -26,6 +26,15 @@
|
|
|
26
26
|
|
|
27
27
|
---
|
|
28
28
|
|
|
29
|
+
## Why Not Raw IndexedDB?
|
|
30
|
+
|
|
31
|
+
If you're weighing using `Locality IDB` vs. the raw `IndexedDB` API:
|
|
32
|
+
|
|
33
|
+
- **Type safety**: schema-driven types reduce runtime errors.
|
|
34
|
+
- **Query ergonomics**: SQL-like query builder replaces verbose cursor boilerplate.
|
|
35
|
+
- **Validation built-in**: column type checks and custom validators run automatically.
|
|
36
|
+
- **Consistency**: reusable schema + shared helpers keep data access uniform.
|
|
37
|
+
|
|
29
38
|
## π Table of Contents
|
|
30
39
|
|
|
31
40
|
- [Features](#-features)
|
|
@@ -56,6 +65,7 @@
|
|
|
56
65
|
- [Utility Functions](#utility-functions)
|
|
57
66
|
- [Validation](#validation)
|
|
58
67
|
- [Type System](#-type-system)
|
|
68
|
+
- [FAQ / Common Pitfalls](#-faq--common-pitfalls)
|
|
59
69
|
- [License](#-license)
|
|
60
70
|
|
|
61
71
|
---
|
|
@@ -64,16 +74,16 @@
|
|
|
64
74
|
|
|
65
75
|
- π― **Type-Safe**: Full TypeScript support with automatic type inference
|
|
66
76
|
- π **SQL-like Queries**: Familiar query syntax inspired by Drizzle ORM
|
|
67
|
-
- π **Modern API**: Clean and intuitive interface for IndexedDB operations
|
|
77
|
+
- π **Modern API**: Clean and intuitive interface for `IndexedDB` operations
|
|
68
78
|
- π¦ **Zero Dependencies**: Lightweight with only development dependencies
|
|
69
79
|
- π **Auto-Generation**: Automatic UUID and timestamp generation
|
|
70
80
|
- π¨ **Schema-First**: Define your database schema with a simple, declarative API
|
|
71
81
|
- π οΈ **Rich Column Types**: Support for various data types including custom types
|
|
72
|
-
- β
**Built-in Validation**:
|
|
82
|
+
- β
**Built-in Validation**: Validation for built-in column types during insert and update operations
|
|
73
83
|
- π§ **Custom Validators**: Define custom validation logic for columns to enforce complex rules
|
|
74
|
-
- π **
|
|
84
|
+
- π **Transactions**: Execute multiple operations across tables with automatic rollback on failure
|
|
75
85
|
- π€ **Database Export**: Export database data as JSON for backup, migration, or debugging
|
|
76
|
-
- π₯ **Database Import**: Import exported data with merge
|
|
86
|
+
- π₯ **Database Import**: Import exported data with `'merge'`, `'replace'`, or `'upsert'` modes
|
|
77
87
|
|
|
78
88
|
---
|
|
79
89
|
|
|
@@ -322,9 +332,10 @@ const schema = defineSchema({
|
|
|
322
332
|
> - Auto-generated values can be overridden by providing explicit values during insert.
|
|
323
333
|
> - Use the `default()` modifier to set custom default values instead of auto-generated ones.
|
|
324
334
|
> - Auto-generated values are generated at runtime during insert operations.
|
|
335
|
+
> - `onUpdate()` modifier can be used to auto-update values on update operations (e.g. `updatedAt` timestamp).
|
|
325
336
|
> - Type extensions for `uuid` and `timestamp` are not applicable since they are already typed.
|
|
326
337
|
> - For custom UUID versions, use [`uuid`](https://toolbox.nazmul-nhb.dev/docs/utilities/hash/uuid) utility from [`nhb-toolbox`](https://www.npmjs.com/package/nhb-toolbox).
|
|
327
|
-
> - For custom timestamp formats, use date libraries like [`Chronos`](https://toolbox.nazmul-nhb.dev/docs/classes/Chronos) (from [`nhb-toolbox`](https://www.npmjs.com/package/nhb-toolbox)) or [`date-fns`](https://www.npmjs.com/package/date-fns) to generate ISO 8601 strings.
|
|
338
|
+
> - For custom timestamp formats, use date libraries like [`Chronos`](https://toolbox.nazmul-nhb.dev/docs/classes/Chronos) or [`getTimestamp`](https://toolbox.nazmul-nhb.dev/docs/utilities/date/getTimestamp) (from [`nhb-toolbox`](https://www.npmjs.com/package/nhb-toolbox)); or [`date-fns`](https://www.npmjs.com/package/date-fns) to generate ISO 8601 strings.
|
|
328
339
|
|
|
329
340
|
##### Boolean Types (`bool`, `boolean`)
|
|
330
341
|
|
|
@@ -2214,16 +2225,13 @@ type UserRow = $InferRow<typeof schema.users.columns>;
|
|
|
2214
2225
|
|
|
2215
2226
|
### Branded Types
|
|
2216
2227
|
|
|
2217
|
-
Locality IDB uses branded
|
|
2228
|
+
Locality IDB uses branded type for UUIDv4 for better type safety:
|
|
2218
2229
|
|
|
2219
2230
|
```typescript
|
|
2220
|
-
import type { UUID
|
|
2231
|
+
import type { UUID } from 'locality-idb';
|
|
2221
2232
|
|
|
2222
2233
|
// UUID types are branded with their version
|
|
2223
2234
|
type UserId = UUID<'v4'>; // Branded UUID v4
|
|
2224
|
-
|
|
2225
|
-
// Timestamps are branded ISO 8601 strings
|
|
2226
|
-
type CreatedAt = Timestamp; // Branded timestamp string
|
|
2227
2235
|
```
|
|
2228
2236
|
|
|
2229
2237
|
### Helper Types
|
|
@@ -2325,6 +2333,15 @@ type PagedResult = PageResult<User, null>;
|
|
|
2325
2333
|
|
|
2326
2334
|
---
|
|
2327
2335
|
|
|
2336
|
+
## β FAQ / Common Pitfalls
|
|
2337
|
+
|
|
2338
|
+
- **Schema changes arenβt automatic**: any schema change should bump the database `version` so the upgrade path runs.
|
|
2339
|
+
- **Index queries require indexes**: `where('field', value)` only works for primary keys or fields defined with `.index()` or `.unique()`.
|
|
2340
|
+
- **Predicate filters are in-memory**: `where((row) => ...)` filters client-side after fetching rows, so prefer indexes for large datasets.
|
|
2341
|
+
- **IndexedDB is browser-only**: calls will fail in SSR/Node environments without a shim.
|
|
2342
|
+
|
|
2343
|
+
---
|
|
2344
|
+
|
|
2328
2345
|
## π License
|
|
2329
2346
|
|
|
2330
2347
|
[MIT](LICENSE) Β© [Nazmul Hassan](https://github.com/nazmul-nhb)
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
|
|
3
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
3
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/guards/primitives.js
|
|
4
4
|
function isNumber(value) {
|
|
5
5
|
return typeof value === "number" && Number.isFinite(value);
|
|
6
6
|
}
|
|
@@ -24,7 +24,7 @@ function isNonEmptyString(value) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
//#endregion
|
|
27
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
27
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/guards/non-primitives.js
|
|
28
28
|
function isArray(value) {
|
|
29
29
|
return Array.isArray(value);
|
|
30
30
|
}
|
|
@@ -54,13 +54,13 @@ function isMap(value) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
//#endregion
|
|
57
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
57
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/string/utilities.js
|
|
58
58
|
const extractNumbersFromString = (input) => {
|
|
59
59
|
return (input.match(/\d+/g) || [])?.map(Number);
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
//#endregion
|
|
63
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
63
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/guards/specials.js
|
|
64
64
|
function isEmail$1(value) {
|
|
65
65
|
return isString(value) && /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value);
|
|
66
66
|
}
|
|
@@ -82,7 +82,7 @@ function isNumericString(value) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
//#endregion
|
|
85
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
85
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/array/sort.js
|
|
86
86
|
function naturalSort(a, b, options) {
|
|
87
87
|
const { caseInsensitive = true, localeAware = false } = options || {};
|
|
88
88
|
const _createChunks = (str) => {
|
|
@@ -151,7 +151,7 @@ function sortAnArray(array, options) {
|
|
|
151
151
|
|
|
152
152
|
//#endregion
|
|
153
153
|
//#region src/core.ts
|
|
154
|
-
/** Symbol key for column
|
|
154
|
+
/** Symbol key for column data type */
|
|
155
155
|
const ColumnType = Symbol("ColumnType");
|
|
156
156
|
/** Symbol key for primary key marker */
|
|
157
157
|
const IsPrimaryKey = Symbol("IsPrimaryKey");
|
|
@@ -401,7 +401,7 @@ function uuidV4(uppercase = false) {
|
|
|
401
401
|
* * Get current timestamp in ISO 8601 format
|
|
402
402
|
* @param value Optional date input (string, number, or Date object). Defaults to {@link Date new Date()}
|
|
403
403
|
* @remarks If the provided value is invalid, the current date and time will be used.
|
|
404
|
-
* @
|
|
404
|
+
* @returns Timestamp string in ISO 8601 format
|
|
405
405
|
*/
|
|
406
406
|
function getTimestamp(value) {
|
|
407
407
|
let date = value instanceof Date ? value : new Date(isNonEmptyString(value) ? value.replace(/['"]/g, "") : value ?? Date.now());
|
|
@@ -705,7 +705,7 @@ var SelectQuery = class {
|
|
|
705
705
|
return this;
|
|
706
706
|
}
|
|
707
707
|
/**
|
|
708
|
-
* @instance
|
|
708
|
+
* @instance Order results by specified key and direction
|
|
709
709
|
* @param key Key to order by
|
|
710
710
|
* @param dir Direction: 'asc' | 'desc' (default: 'asc')
|
|
711
711
|
*
|
|
@@ -1395,7 +1395,7 @@ var Locality = class Locality {
|
|
|
1395
1395
|
/**
|
|
1396
1396
|
* @instance Select records from a table.
|
|
1397
1397
|
* @param table Table name.
|
|
1398
|
-
* @returns
|
|
1398
|
+
* @returns Select query builder for the table.
|
|
1399
1399
|
*/
|
|
1400
1400
|
from(table) {
|
|
1401
1401
|
return new SelectQuery(table, () => this.#db, this.#readyPromise);
|
|
@@ -1575,7 +1575,7 @@ var Locality = class Locality {
|
|
|
1575
1575
|
* }),
|
|
1576
1576
|
* });
|
|
1577
1577
|
*
|
|
1578
|
-
* // Export all tables pretty-printed
|
|
1578
|
+
* // Export all tables pretty-printed with default filename
|
|
1579
1579
|
* await db.export();
|
|
1580
1580
|
*
|
|
1581
1581
|
* // Export specific tables pretty-printed with custom filename
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/core.d.ts
|
|
2
|
-
/** Symbol key for column
|
|
2
|
+
/** Symbol key for column data type */
|
|
3
3
|
declare const ColumnType: unique symbol;
|
|
4
4
|
/** Symbol key for primary key marker */
|
|
5
5
|
declare const IsPrimaryKey: unique symbol;
|
|
@@ -162,7 +162,7 @@ declare class SelectQuery<T extends GenericObject, S extends Partial<Record<stri
|
|
|
162
162
|
*/
|
|
163
163
|
where<IdxKey extends $InferPrimaryKey<Tbl['columns']> | $InferIndex<Tbl['columns']>>(indexName: IdxKey, query: IDBKeyRange | T[IdxKey]): this;
|
|
164
164
|
/**
|
|
165
|
-
* @instance
|
|
165
|
+
* @instance Order results by specified key and direction
|
|
166
166
|
* @param key Key to order by
|
|
167
167
|
* @param dir Direction: 'asc' | 'desc' (default: 'asc')
|
|
168
168
|
*
|
|
@@ -300,9 +300,9 @@ type $Brand<B> = {
|
|
|
300
300
|
/**
|
|
301
301
|
* * Creates a branded version of a base type by intersecting it with a unique compile-time marker.
|
|
302
302
|
*
|
|
303
|
-
* @
|
|
304
|
-
* @
|
|
305
|
-
|
|
303
|
+
* @typeParam T - Base type to brand.
|
|
304
|
+
* @typeParam B - Brand identifier used to distinguish this type from structurally similar types.
|
|
305
|
+
*
|
|
306
306
|
* @remarks Useful for preventing accidental mixing of structurally identical types, while keeping the runtime value unchanged.
|
|
307
307
|
*
|
|
308
308
|
* @example
|
|
@@ -313,7 +313,10 @@ type Branded<T, B> = T & $Brand<B>;
|
|
|
313
313
|
/**
|
|
314
314
|
* * Broadens a literal union (typically `string` or `number`) to also accept any other value of the base type, without losing IntelliSense autocomplete for the provided literals.
|
|
315
315
|
*
|
|
316
|
-
*
|
|
316
|
+
* @remarks
|
|
317
|
+
* This is especially useful in API design where you want to provide suggestions for common options but still allow flexibility for custom user-defined values.
|
|
318
|
+
*
|
|
319
|
+
* Technically, this uses intersection with primitive base types (`string & {}` or `number & {}`) to retain IntelliSense while avoiding type narrowing.
|
|
317
320
|
*
|
|
318
321
|
* @example
|
|
319
322
|
* // β
String literal usage
|
|
@@ -332,8 +335,6 @@ type Branded<T, B> = T & $Brand<B>;
|
|
|
332
335
|
* const m2: Mixed = 2; // β
|
|
333
336
|
* const m3: Mixed = 'anything'; // β
|
|
334
337
|
* const m4: Mixed = 123; // β
|
|
335
|
-
*
|
|
336
|
-
* @note Technically, this uses intersection with primitive base types (`string & {}` or `number & {}`) to retain IntelliSense while avoiding type narrowing.
|
|
337
338
|
*/
|
|
338
339
|
type LooseLiteral<T extends string | number> = T | (T extends string ? string & {} : number & {});
|
|
339
340
|
type ForcedAny = any;
|
|
@@ -365,7 +366,7 @@ type $UnionToTuple<T, L = $LastOf<T>> = [T] extends [never] ? [] : [...$UnionToT
|
|
|
365
366
|
* - If `T` is a single type, it produces a one-element tuple `[T]`.
|
|
366
367
|
* - If `T` is `never`, it produces an empty tuple `[]`.
|
|
367
368
|
*
|
|
368
|
-
* @
|
|
369
|
+
* @typeParam T - The type to convert into a tuple.
|
|
369
370
|
* @returns A tuple type containing the elements of `T`.
|
|
370
371
|
*
|
|
371
372
|
* @example
|
|
@@ -383,7 +384,7 @@ type Tuple<T> = [T] extends [never] ? [] : $UnionToTuple<T>;
|
|
|
383
384
|
* - Useful when you want to preserve all possible union members as a tuple literal instead of an array.
|
|
384
385
|
* - For converting any type to tuple use {@link Tuple}.
|
|
385
386
|
*
|
|
386
|
-
* @
|
|
387
|
+
* @typeParam T - An array type whose element type is a union.
|
|
387
388
|
* @returns A tuple type containing each member of the union in order.
|
|
388
389
|
*
|
|
389
390
|
* @example
|
|
@@ -435,7 +436,7 @@ type AdvancedTypes = Array<unknown> | File | FileList | Blob | Date | RegExp | C
|
|
|
435
436
|
/**
|
|
436
437
|
* * Extracts the parameters of the first overload of a function type `T`.
|
|
437
438
|
*
|
|
438
|
-
* @
|
|
439
|
+
* @typeParam T - The function type to extract parameters from.
|
|
439
440
|
*
|
|
440
441
|
* @returns A tuple type representing the parameters of the first overload of `T`.
|
|
441
442
|
*
|
|
@@ -457,8 +458,8 @@ type FirstOverloadParams<T> = T extends ({
|
|
|
457
458
|
/**
|
|
458
459
|
* * Maps all values of object `T` to a fixed type `R`, keeping original keys.
|
|
459
460
|
*
|
|
460
|
-
* @
|
|
461
|
-
* @
|
|
461
|
+
* @typeParam T - The source object type.
|
|
462
|
+
* @typeParam R - The replacement value type.
|
|
462
463
|
*
|
|
463
464
|
* @example
|
|
464
465
|
* type T = { name: string; age: number };
|
|
@@ -488,14 +489,15 @@ type PageResult<T, Selection extends Partial<Record<keyof T, boolean>> | null> =
|
|
|
488
489
|
/** Retrieved items for the current page */items: Selection extends null ? T[] : SelectFields<T, Extract<Selection, object>>[]; /** Cursor key for the next page, if more results are available */
|
|
489
490
|
nextCursor: Maybe<IDBValidKey>;
|
|
490
491
|
};
|
|
491
|
-
/**
|
|
492
|
+
/** Extract only primitive keys from an object, including nested dot-notation keys. */
|
|
492
493
|
type NestedPrimitiveKey<T> = T extends AdvancedTypes ? never : T extends GenericObject ? { [K in keyof T & string]: T[K] extends Function ? never : T[K] extends NormalPrimitive ? K : T[K] extends GenericObject ? `${K}.${NestedPrimitiveKey<T[K]>}` : never }[keyof T & string] : never;
|
|
493
|
-
/**
|
|
494
|
+
/** Generic object but with `any` value */
|
|
494
495
|
type GenericObject = Record<string, any>;
|
|
495
496
|
/**
|
|
496
497
|
* * Forces TypeScript to simplify a complex or inferred type into a more readable flat object.
|
|
497
498
|
*
|
|
498
|
-
*
|
|
499
|
+
* @remarks
|
|
500
|
+
* Useful when working with utility types like `Merge`, `Omit`, etc., that produce deeply nested or unresolved intersections.
|
|
499
501
|
*
|
|
500
502
|
* @example
|
|
501
503
|
* type A = { a: number };
|
|
@@ -574,7 +576,7 @@ type $InferUUID<T extends ColumnDefinition> = { [K in keyof T]: T[K] extends Col
|
|
|
574
576
|
/** Finds the field name with {@link Timestamp} type. */
|
|
575
577
|
type $InferTimestamp<T extends ColumnDefinition> = { [K in keyof T]: T[K] extends Column<infer C, TypeName> ? C extends Timestamp ? K : never : never }[keyof T];
|
|
576
578
|
/** Timestamp string type in ISO 8601 format */
|
|
577
|
-
type Timestamp =
|
|
579
|
+
type Timestamp = `${number}-${number}-${number}T${number}:${number}:${number}.${number}${'Z' | `${'+' | '-'}${number}:${number}`}`;
|
|
578
580
|
/** Sort direction type for ordering queries */
|
|
579
581
|
type SortDirection = 'asc' | 'desc';
|
|
580
582
|
/** Predicate function type for WHERE clauses in queries */
|
|
@@ -696,7 +698,7 @@ declare class Locality<DBName extends string = string, Version extends number =
|
|
|
696
698
|
/**
|
|
697
699
|
* @instance Select records from a table.
|
|
698
700
|
* @param table Table name.
|
|
699
|
-
* @returns
|
|
701
|
+
* @returns Select query builder for the table.
|
|
700
702
|
*/
|
|
701
703
|
from<T extends TName, Row extends $InferRow<Schema[T]['columns']>>(table: T): SelectQuery<Row, null, Schema[T]>;
|
|
702
704
|
/**
|
|
@@ -824,7 +826,7 @@ declare class Locality<DBName extends string = string, Version extends number =
|
|
|
824
826
|
* }),
|
|
825
827
|
* });
|
|
826
828
|
*
|
|
827
|
-
* // Export all tables pretty-printed
|
|
829
|
+
* // Export all tables pretty-printed with default filename
|
|
828
830
|
* await db.export();
|
|
829
831
|
*
|
|
830
832
|
* // Export specific tables pretty-printed with custom filename
|
|
@@ -1162,7 +1164,7 @@ declare const column: {
|
|
|
1162
1164
|
* - This column type is used for storing date and time information in ISO 8601 format.
|
|
1163
1165
|
* - Automatically generates the current timestamp when no value is provided.
|
|
1164
1166
|
*/
|
|
1165
|
-
timestamp: () => Column
|
|
1167
|
+
timestamp: () => Column<`${number}-${number}-${number}T${number}:${number}:${number}.${number}Z` | `${number}-${number}-${number}T${number}:${number}:${number}.${number}+${number}:${number}` | `${number}-${number}-${number}T${number}:${number}:${number}.${number}-${number}:${number}`, "timestamp">;
|
|
1166
1168
|
/**
|
|
1167
1169
|
* Creates an email column.
|
|
1168
1170
|
* @returns A new {@link Column} instance for emails.
|
|
@@ -1372,7 +1374,7 @@ declare function uuidV4(uppercase?: boolean): UUID<'v4'>;
|
|
|
1372
1374
|
* * Get current timestamp in ISO 8601 format
|
|
1373
1375
|
* @param value Optional date input (string, number, or Date object). Defaults to {@link Date new Date()}
|
|
1374
1376
|
* @remarks If the provided value is invalid, the current date and time will be used.
|
|
1375
|
-
* @
|
|
1377
|
+
* @returns Timestamp string in ISO 8601 format
|
|
1376
1378
|
*/
|
|
1377
1379
|
declare function getTimestamp(value?: string | number | Date): Timestamp;
|
|
1378
1380
|
/**
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/core.d.ts
|
|
2
|
-
/** Symbol key for column
|
|
2
|
+
/** Symbol key for column data type */
|
|
3
3
|
declare const ColumnType: unique symbol;
|
|
4
4
|
/** Symbol key for primary key marker */
|
|
5
5
|
declare const IsPrimaryKey: unique symbol;
|
|
@@ -162,7 +162,7 @@ declare class SelectQuery<T extends GenericObject, S extends Partial<Record<stri
|
|
|
162
162
|
*/
|
|
163
163
|
where<IdxKey extends $InferPrimaryKey<Tbl['columns']> | $InferIndex<Tbl['columns']>>(indexName: IdxKey, query: IDBKeyRange | T[IdxKey]): this;
|
|
164
164
|
/**
|
|
165
|
-
* @instance
|
|
165
|
+
* @instance Order results by specified key and direction
|
|
166
166
|
* @param key Key to order by
|
|
167
167
|
* @param dir Direction: 'asc' | 'desc' (default: 'asc')
|
|
168
168
|
*
|
|
@@ -300,9 +300,9 @@ type $Brand<B> = {
|
|
|
300
300
|
/**
|
|
301
301
|
* * Creates a branded version of a base type by intersecting it with a unique compile-time marker.
|
|
302
302
|
*
|
|
303
|
-
* @
|
|
304
|
-
* @
|
|
305
|
-
|
|
303
|
+
* @typeParam T - Base type to brand.
|
|
304
|
+
* @typeParam B - Brand identifier used to distinguish this type from structurally similar types.
|
|
305
|
+
*
|
|
306
306
|
* @remarks Useful for preventing accidental mixing of structurally identical types, while keeping the runtime value unchanged.
|
|
307
307
|
*
|
|
308
308
|
* @example
|
|
@@ -313,7 +313,10 @@ type Branded<T, B> = T & $Brand<B>;
|
|
|
313
313
|
/**
|
|
314
314
|
* * Broadens a literal union (typically `string` or `number`) to also accept any other value of the base type, without losing IntelliSense autocomplete for the provided literals.
|
|
315
315
|
*
|
|
316
|
-
*
|
|
316
|
+
* @remarks
|
|
317
|
+
* This is especially useful in API design where you want to provide suggestions for common options but still allow flexibility for custom user-defined values.
|
|
318
|
+
*
|
|
319
|
+
* Technically, this uses intersection with primitive base types (`string & {}` or `number & {}`) to retain IntelliSense while avoiding type narrowing.
|
|
317
320
|
*
|
|
318
321
|
* @example
|
|
319
322
|
* // β
String literal usage
|
|
@@ -332,8 +335,6 @@ type Branded<T, B> = T & $Brand<B>;
|
|
|
332
335
|
* const m2: Mixed = 2; // β
|
|
333
336
|
* const m3: Mixed = 'anything'; // β
|
|
334
337
|
* const m4: Mixed = 123; // β
|
|
335
|
-
*
|
|
336
|
-
* @note Technically, this uses intersection with primitive base types (`string & {}` or `number & {}`) to retain IntelliSense while avoiding type narrowing.
|
|
337
338
|
*/
|
|
338
339
|
type LooseLiteral<T extends string | number> = T | (T extends string ? string & {} : number & {});
|
|
339
340
|
type ForcedAny = any;
|
|
@@ -365,7 +366,7 @@ type $UnionToTuple<T, L = $LastOf<T>> = [T] extends [never] ? [] : [...$UnionToT
|
|
|
365
366
|
* - If `T` is a single type, it produces a one-element tuple `[T]`.
|
|
366
367
|
* - If `T` is `never`, it produces an empty tuple `[]`.
|
|
367
368
|
*
|
|
368
|
-
* @
|
|
369
|
+
* @typeParam T - The type to convert into a tuple.
|
|
369
370
|
* @returns A tuple type containing the elements of `T`.
|
|
370
371
|
*
|
|
371
372
|
* @example
|
|
@@ -383,7 +384,7 @@ type Tuple<T> = [T] extends [never] ? [] : $UnionToTuple<T>;
|
|
|
383
384
|
* - Useful when you want to preserve all possible union members as a tuple literal instead of an array.
|
|
384
385
|
* - For converting any type to tuple use {@link Tuple}.
|
|
385
386
|
*
|
|
386
|
-
* @
|
|
387
|
+
* @typeParam T - An array type whose element type is a union.
|
|
387
388
|
* @returns A tuple type containing each member of the union in order.
|
|
388
389
|
*
|
|
389
390
|
* @example
|
|
@@ -435,7 +436,7 @@ type AdvancedTypes = Array<unknown> | File | FileList | Blob | Date | RegExp | C
|
|
|
435
436
|
/**
|
|
436
437
|
* * Extracts the parameters of the first overload of a function type `T`.
|
|
437
438
|
*
|
|
438
|
-
* @
|
|
439
|
+
* @typeParam T - The function type to extract parameters from.
|
|
439
440
|
*
|
|
440
441
|
* @returns A tuple type representing the parameters of the first overload of `T`.
|
|
441
442
|
*
|
|
@@ -457,8 +458,8 @@ type FirstOverloadParams<T> = T extends ({
|
|
|
457
458
|
/**
|
|
458
459
|
* * Maps all values of object `T` to a fixed type `R`, keeping original keys.
|
|
459
460
|
*
|
|
460
|
-
* @
|
|
461
|
-
* @
|
|
461
|
+
* @typeParam T - The source object type.
|
|
462
|
+
* @typeParam R - The replacement value type.
|
|
462
463
|
*
|
|
463
464
|
* @example
|
|
464
465
|
* type T = { name: string; age: number };
|
|
@@ -488,14 +489,15 @@ type PageResult<T, Selection extends Partial<Record<keyof T, boolean>> | null> =
|
|
|
488
489
|
/** Retrieved items for the current page */items: Selection extends null ? T[] : SelectFields<T, Extract<Selection, object>>[]; /** Cursor key for the next page, if more results are available */
|
|
489
490
|
nextCursor: Maybe<IDBValidKey>;
|
|
490
491
|
};
|
|
491
|
-
/**
|
|
492
|
+
/** Extract only primitive keys from an object, including nested dot-notation keys. */
|
|
492
493
|
type NestedPrimitiveKey<T> = T extends AdvancedTypes ? never : T extends GenericObject ? { [K in keyof T & string]: T[K] extends Function ? never : T[K] extends NormalPrimitive ? K : T[K] extends GenericObject ? `${K}.${NestedPrimitiveKey<T[K]>}` : never }[keyof T & string] : never;
|
|
493
|
-
/**
|
|
494
|
+
/** Generic object but with `any` value */
|
|
494
495
|
type GenericObject = Record<string, any>;
|
|
495
496
|
/**
|
|
496
497
|
* * Forces TypeScript to simplify a complex or inferred type into a more readable flat object.
|
|
497
498
|
*
|
|
498
|
-
*
|
|
499
|
+
* @remarks
|
|
500
|
+
* Useful when working with utility types like `Merge`, `Omit`, etc., that produce deeply nested or unresolved intersections.
|
|
499
501
|
*
|
|
500
502
|
* @example
|
|
501
503
|
* type A = { a: number };
|
|
@@ -574,7 +576,7 @@ type $InferUUID<T extends ColumnDefinition> = { [K in keyof T]: T[K] extends Col
|
|
|
574
576
|
/** Finds the field name with {@link Timestamp} type. */
|
|
575
577
|
type $InferTimestamp<T extends ColumnDefinition> = { [K in keyof T]: T[K] extends Column<infer C, TypeName> ? C extends Timestamp ? K : never : never }[keyof T];
|
|
576
578
|
/** Timestamp string type in ISO 8601 format */
|
|
577
|
-
type Timestamp =
|
|
579
|
+
type Timestamp = `${number}-${number}-${number}T${number}:${number}:${number}.${number}${'Z' | `${'+' | '-'}${number}:${number}`}`;
|
|
578
580
|
/** Sort direction type for ordering queries */
|
|
579
581
|
type SortDirection = 'asc' | 'desc';
|
|
580
582
|
/** Predicate function type for WHERE clauses in queries */
|
|
@@ -696,7 +698,7 @@ declare class Locality<DBName extends string = string, Version extends number =
|
|
|
696
698
|
/**
|
|
697
699
|
* @instance Select records from a table.
|
|
698
700
|
* @param table Table name.
|
|
699
|
-
* @returns
|
|
701
|
+
* @returns Select query builder for the table.
|
|
700
702
|
*/
|
|
701
703
|
from<T extends TName, Row extends $InferRow<Schema[T]['columns']>>(table: T): SelectQuery<Row, null, Schema[T]>;
|
|
702
704
|
/**
|
|
@@ -824,7 +826,7 @@ declare class Locality<DBName extends string = string, Version extends number =
|
|
|
824
826
|
* }),
|
|
825
827
|
* });
|
|
826
828
|
*
|
|
827
|
-
* // Export all tables pretty-printed
|
|
829
|
+
* // Export all tables pretty-printed with default filename
|
|
828
830
|
* await db.export();
|
|
829
831
|
*
|
|
830
832
|
* // Export specific tables pretty-printed with custom filename
|
|
@@ -1162,7 +1164,7 @@ declare const column: {
|
|
|
1162
1164
|
* - This column type is used for storing date and time information in ISO 8601 format.
|
|
1163
1165
|
* - Automatically generates the current timestamp when no value is provided.
|
|
1164
1166
|
*/
|
|
1165
|
-
timestamp: () => Column
|
|
1167
|
+
timestamp: () => Column<`${number}-${number}-${number}T${number}:${number}:${number}.${number}Z` | `${number}-${number}-${number}T${number}:${number}:${number}.${number}+${number}:${number}` | `${number}-${number}-${number}T${number}:${number}:${number}.${number}-${number}:${number}`, "timestamp">;
|
|
1166
1168
|
/**
|
|
1167
1169
|
* Creates an email column.
|
|
1168
1170
|
* @returns A new {@link Column} instance for emails.
|
|
@@ -1372,7 +1374,7 @@ declare function uuidV4(uppercase?: boolean): UUID<'v4'>;
|
|
|
1372
1374
|
* * Get current timestamp in ISO 8601 format
|
|
1373
1375
|
* @param value Optional date input (string, number, or Date object). Defaults to {@link Date new Date()}
|
|
1374
1376
|
* @remarks If the provided value is invalid, the current date and time will be used.
|
|
1375
|
-
* @
|
|
1377
|
+
* @returns Timestamp string in ISO 8601 format
|
|
1376
1378
|
*/
|
|
1377
1379
|
declare function getTimestamp(value?: string | number | Date): Timestamp;
|
|
1378
1380
|
/**
|
package/dist/index.iife.js
CHANGED
|
@@ -2,7 +2,7 @@ var LocalityIDB = (function(exports) {
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
4
|
|
|
5
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
5
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/guards/primitives.js
|
|
6
6
|
function isNumber(value) {
|
|
7
7
|
return typeof value === "number" && Number.isFinite(value);
|
|
8
8
|
}
|
|
@@ -26,7 +26,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
//#endregion
|
|
29
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
29
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/guards/non-primitives.js
|
|
30
30
|
function isArray(value) {
|
|
31
31
|
return Array.isArray(value);
|
|
32
32
|
}
|
|
@@ -56,13 +56,13 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
//#endregion
|
|
59
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
59
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/string/utilities.js
|
|
60
60
|
const extractNumbersFromString = (input) => {
|
|
61
61
|
return (input.match(/\d+/g) || [])?.map(Number);
|
|
62
62
|
};
|
|
63
63
|
|
|
64
64
|
//#endregion
|
|
65
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
65
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/guards/specials.js
|
|
66
66
|
function isEmail$1(value) {
|
|
67
67
|
return isString(value) && /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value);
|
|
68
68
|
}
|
|
@@ -84,7 +84,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
//#endregion
|
|
87
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
87
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/array/sort.js
|
|
88
88
|
function naturalSort(a, b, options) {
|
|
89
89
|
const { caseInsensitive = true, localeAware = false } = options || {};
|
|
90
90
|
const _createChunks = (str) => {
|
|
@@ -153,7 +153,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
153
153
|
|
|
154
154
|
//#endregion
|
|
155
155
|
//#region src/core.ts
|
|
156
|
-
/** Symbol key for column
|
|
156
|
+
/** Symbol key for column data type */
|
|
157
157
|
const ColumnType = Symbol("ColumnType");
|
|
158
158
|
/** Symbol key for primary key marker */
|
|
159
159
|
const IsPrimaryKey = Symbol("IsPrimaryKey");
|
|
@@ -403,7 +403,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
403
403
|
* * Get current timestamp in ISO 8601 format
|
|
404
404
|
* @param value Optional date input (string, number, or Date object). Defaults to {@link Date new Date()}
|
|
405
405
|
* @remarks If the provided value is invalid, the current date and time will be used.
|
|
406
|
-
* @
|
|
406
|
+
* @returns Timestamp string in ISO 8601 format
|
|
407
407
|
*/
|
|
408
408
|
function getTimestamp(value) {
|
|
409
409
|
let date = value instanceof Date ? value : new Date(isNonEmptyString(value) ? value.replace(/['"]/g, "") : value ?? Date.now());
|
|
@@ -707,7 +707,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
707
707
|
return this;
|
|
708
708
|
}
|
|
709
709
|
/**
|
|
710
|
-
* @instance
|
|
710
|
+
* @instance Order results by specified key and direction
|
|
711
711
|
* @param key Key to order by
|
|
712
712
|
* @param dir Direction: 'asc' | 'desc' (default: 'asc')
|
|
713
713
|
*
|
|
@@ -1397,7 +1397,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1397
1397
|
/**
|
|
1398
1398
|
* @instance Select records from a table.
|
|
1399
1399
|
* @param table Table name.
|
|
1400
|
-
* @returns
|
|
1400
|
+
* @returns Select query builder for the table.
|
|
1401
1401
|
*/
|
|
1402
1402
|
from(table) {
|
|
1403
1403
|
return new SelectQuery(table, () => this.#db, this.#readyPromise);
|
|
@@ -1577,7 +1577,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
|
1577
1577
|
* }),
|
|
1578
1578
|
* });
|
|
1579
1579
|
*
|
|
1580
|
-
* // Export all tables pretty-printed
|
|
1580
|
+
* // Export all tables pretty-printed with default filename
|
|
1581
1581
|
* await db.export();
|
|
1582
1582
|
*
|
|
1583
1583
|
* // Export specific tables pretty-printed with custom filename
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
1
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/guards/primitives.js
|
|
2
2
|
function isNumber(value) {
|
|
3
3
|
return typeof value === "number" && Number.isFinite(value);
|
|
4
4
|
}
|
|
@@ -22,7 +22,7 @@ function isNonEmptyString(value) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
//#endregion
|
|
25
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
25
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/guards/non-primitives.js
|
|
26
26
|
function isArray(value) {
|
|
27
27
|
return Array.isArray(value);
|
|
28
28
|
}
|
|
@@ -52,13 +52,13 @@ function isMap(value) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
//#endregion
|
|
55
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
55
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/string/utilities.js
|
|
56
56
|
const extractNumbersFromString = (input) => {
|
|
57
57
|
return (input.match(/\d+/g) || [])?.map(Number);
|
|
58
58
|
};
|
|
59
59
|
|
|
60
60
|
//#endregion
|
|
61
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
61
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/guards/specials.js
|
|
62
62
|
function isEmail$1(value) {
|
|
63
63
|
return isString(value) && /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(value);
|
|
64
64
|
}
|
|
@@ -80,7 +80,7 @@ function isNumericString(value) {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
//#endregion
|
|
83
|
-
//#region node_modules/.pnpm/nhb-toolbox@4.28.
|
|
83
|
+
//#region node_modules/.pnpm/nhb-toolbox@4.28.80/node_modules/nhb-toolbox/dist/esm/array/sort.js
|
|
84
84
|
function naturalSort(a, b, options) {
|
|
85
85
|
const { caseInsensitive = true, localeAware = false } = options || {};
|
|
86
86
|
const _createChunks = (str) => {
|
|
@@ -149,7 +149,7 @@ function sortAnArray(array, options) {
|
|
|
149
149
|
|
|
150
150
|
//#endregion
|
|
151
151
|
//#region src/core.ts
|
|
152
|
-
/** Symbol key for column
|
|
152
|
+
/** Symbol key for column data type */
|
|
153
153
|
const ColumnType = Symbol("ColumnType");
|
|
154
154
|
/** Symbol key for primary key marker */
|
|
155
155
|
const IsPrimaryKey = Symbol("IsPrimaryKey");
|
|
@@ -399,7 +399,7 @@ function uuidV4(uppercase = false) {
|
|
|
399
399
|
* * Get current timestamp in ISO 8601 format
|
|
400
400
|
* @param value Optional date input (string, number, or Date object). Defaults to {@link Date new Date()}
|
|
401
401
|
* @remarks If the provided value is invalid, the current date and time will be used.
|
|
402
|
-
* @
|
|
402
|
+
* @returns Timestamp string in ISO 8601 format
|
|
403
403
|
*/
|
|
404
404
|
function getTimestamp(value) {
|
|
405
405
|
let date = value instanceof Date ? value : new Date(isNonEmptyString(value) ? value.replace(/['"]/g, "") : value ?? Date.now());
|
|
@@ -703,7 +703,7 @@ var SelectQuery = class {
|
|
|
703
703
|
return this;
|
|
704
704
|
}
|
|
705
705
|
/**
|
|
706
|
-
* @instance
|
|
706
|
+
* @instance Order results by specified key and direction
|
|
707
707
|
* @param key Key to order by
|
|
708
708
|
* @param dir Direction: 'asc' | 'desc' (default: 'asc')
|
|
709
709
|
*
|
|
@@ -1393,7 +1393,7 @@ var Locality = class Locality {
|
|
|
1393
1393
|
/**
|
|
1394
1394
|
* @instance Select records from a table.
|
|
1395
1395
|
* @param table Table name.
|
|
1396
|
-
* @returns
|
|
1396
|
+
* @returns Select query builder for the table.
|
|
1397
1397
|
*/
|
|
1398
1398
|
from(table) {
|
|
1399
1399
|
return new SelectQuery(table, () => this.#db, this.#readyPromise);
|
|
@@ -1573,7 +1573,7 @@ var Locality = class Locality {
|
|
|
1573
1573
|
* }),
|
|
1574
1574
|
* });
|
|
1575
1575
|
*
|
|
1576
|
-
* // Export all tables pretty-printed
|
|
1576
|
+
* // Export all tables pretty-printed with default filename
|
|
1577
1577
|
* await db.export();
|
|
1578
1578
|
*
|
|
1579
1579
|
* // Export specific tables pretty-printed with custom filename
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "locality-idb",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.7",
|
|
4
4
|
"description": "SQL-like query builder for IndexedDB with Drizzle-style API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -47,16 +47,28 @@
|
|
|
47
47
|
"package.json"
|
|
48
48
|
],
|
|
49
49
|
"keywords": [
|
|
50
|
+
"indexeddb",
|
|
50
51
|
"indexed-db",
|
|
52
|
+
"indexeddb-orm",
|
|
53
|
+
"indexed-db-orm",
|
|
51
54
|
"sql",
|
|
52
|
-
"drizzle",
|
|
53
55
|
"db",
|
|
56
|
+
"orm",
|
|
57
|
+
"idb",
|
|
58
|
+
"idb-orm",
|
|
59
|
+
"typescript",
|
|
60
|
+
"typescript-orm",
|
|
61
|
+
"typescript-idb",
|
|
62
|
+
"typescript-indexeddb",
|
|
63
|
+
"typescript-indexed-db",
|
|
64
|
+
"locality-idb",
|
|
54
65
|
"locality",
|
|
66
|
+
"drizzle",
|
|
55
67
|
"database"
|
|
56
68
|
],
|
|
57
69
|
"devDependencies": {
|
|
58
70
|
"@eslint/js": "^9.39.2",
|
|
59
|
-
"@types/node": "^25.2.
|
|
71
|
+
"@types/node": "^25.2.1",
|
|
60
72
|
"@typescript-eslint/eslint-plugin": "^8.54.0",
|
|
61
73
|
"@typescript-eslint/parser": "^8.54.0",
|
|
62
74
|
"eslint": "^9.39.2",
|
|
@@ -64,7 +76,7 @@
|
|
|
64
76
|
"eslint-plugin-prettier": "^5.5.5",
|
|
65
77
|
"globals": "^17.3.0",
|
|
66
78
|
"nhb-scripts": "^1.9.2",
|
|
67
|
-
"nhb-toolbox": "^4.28.
|
|
79
|
+
"nhb-toolbox": "^4.28.80",
|
|
68
80
|
"prettier": "^3.8.1",
|
|
69
81
|
"tsdown": "^0.20.3",
|
|
70
82
|
"typescript": "^5.9.3",
|
|
@@ -76,6 +88,7 @@
|
|
|
76
88
|
"build": "tsdown",
|
|
77
89
|
"dev:pkg": "tsdown --watch",
|
|
78
90
|
"dev": "pnpm --dir demo run dev",
|
|
91
|
+
"install:demo": "pnpm --dir demo install",
|
|
79
92
|
"build:demo": "pnpm --dir demo run build",
|
|
80
93
|
"preview": "pnpm --dir demo run preview",
|
|
81
94
|
"typecheck": "tsc --noEmit",
|