locality-idb 0.5.1 → 0.6.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
@@ -1,5 +1,7 @@
1
1
  # Locality IDB
2
2
 
3
+ > **SQL**-like query builder for [**IndexedDB**](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Using_IndexedDB) with [**Drizzle**](https://github.com/drizzle-team/drizzle-orm)-style API
4
+
3
5
  <!-- markdownlint-disable-file MD024 -->
4
6
 
5
7
  <div align="center">
@@ -11,8 +13,6 @@
11
13
  <!-- ![bundle](https://img.shields.io/bundlephobia/minzip/locality-idb) -->
12
14
  ![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue)
13
15
 
14
- ## SQL-like query builder for IndexedDB with Drizzle-style API
15
-
16
16
  [Documentation](#-api-reference) • [Examples](#-usage) • [Contributing](#-contributing)
17
17
 
18
18
  </div>
@@ -21,7 +21,7 @@
21
21
 
22
22
  ## ⚠️ Beta Status
23
23
 
24
- >This package is currently in **beta development**. The API is subject to change. Use in production at your own risk.
24
+ >This development of the package is currently in **beta stage**. The API is subject to change. Use in production at your own risk.
25
25
 
26
26
  ---
27
27
 
@@ -496,7 +496,7 @@ const query = db.delete('users');
496
496
 
497
497
  ### Schema Functions
498
498
 
499
- #### `defineSchema<T>(schema: T): Schema<T>`
499
+ #### `defineSchema<Schema extends ColumnRecord, Keys extends keyof Schema>(schema: Schema): SchemaRecord<Schema, Keys>`
500
500
 
501
501
  Defines a database schema from an object mapping table names to column definitions.
502
502
 
package/dist/index.cjs CHANGED
@@ -9,12 +9,12 @@ function isString(value) {
9
9
  function isInteger(value) {
10
10
  return isNumber(value) && Number.isInteger(value);
11
11
  }
12
- function isPositiveInteger(value) {
13
- return isInteger(value) && value > 0;
14
- }
15
12
  function isBoolean(value) {
16
13
  return typeof value === "boolean";
17
14
  }
15
+ function isBigInt(value) {
16
+ return typeof value === "bigint";
17
+ }
18
18
  function isNonEmptyString(value) {
19
19
  return isString(value) && value?.length > 0;
20
20
  }
@@ -33,9 +33,35 @@ function isObject(value) {
33
33
  function isNotEmptyObject(value) {
34
34
  return isObject(value) && Object.keys(value)?.length > 0;
35
35
  }
36
+ function isDate(value) {
37
+ return value instanceof Date;
38
+ }
36
39
  function isArrayOfType(value, typeCheck) {
37
40
  return isArray(value) && value?.every(typeCheck);
38
41
  }
42
+ function isSet(value) {
43
+ return value instanceof Set;
44
+ }
45
+ function isMap(value) {
46
+ return value instanceof Map;
47
+ }
48
+
49
+ //#endregion
50
+ //#region node_modules/.pnpm/nhb-toolbox@4.28.66/node_modules/nhb-toolbox/dist/esm/string/utilities.js
51
+ const extractNumbersFromString = (input) => {
52
+ return (input.match(/\d+/g) || [])?.map(Number);
53
+ };
54
+
55
+ //#endregion
56
+ //#region node_modules/.pnpm/nhb-toolbox@4.28.66/node_modules/nhb-toolbox/dist/esm/guards/specials.js
57
+ function isUUID(value) {
58
+ const h = "[0-9a-f]";
59
+ const expr = new RegExp(`^${h}{8}-${h}{4}-[1-8]${h}{3}-[89ab]${h}{3}-${h}{12}$`, "i");
60
+ return isString(value) && expr.test(value);
61
+ }
62
+ function isNumericString(value) {
63
+ return isString(value) && value?.trim() !== "" && Number.isFinite(Number(value));
64
+ }
39
65
 
40
66
  //#endregion
41
67
  //#region node_modules/.pnpm/nhb-toolbox@4.28.66/node_modules/nhb-toolbox/dist/esm/array/sort.js
@@ -145,7 +171,7 @@ var Column = class {
145
171
  "integer",
146
172
  "float",
147
173
  "number"
148
- ].includes(colType.toLowerCase())) throw new Error(`auto() can only be used with integer columns, got: ${colType}`);
174
+ ].includes(colType)) throw new Error(`auto() can only be used with integer columns, got: ${colType}`);
149
175
  this[IsAutoInc] = true;
150
176
  return this;
151
177
  }
@@ -178,7 +204,7 @@ var Table = class {
178
204
  //#endregion
179
205
  //#region src/factory.ts
180
206
  /**
181
- * * Opens an `IndexedDB` database with the specified stores.
207
+ * * Opens an `IndexedDB` database instance with the specified stores.
182
208
  * @param name Database name
183
209
  * @param stores Array of store configurations
184
210
  * @param version Database version (default is `1`)
@@ -245,6 +271,122 @@ function getTimestamp(value) {
245
271
  if (isNaN(date.getTime())) date = /* @__PURE__ */ new Date();
246
272
  return date.toISOString();
247
273
  }
274
+ /**
275
+ * * Check if a value is a valid Timestamp string in ISO 8601 format
276
+ * @param value The value to check
277
+ * @returns `true` if the value is a valid Timestamp, otherwise `false`
278
+ */
279
+ function isTimestamp(value) {
280
+ return isNonEmptyString(value) && value.match(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/) !== null;
281
+ }
282
+
283
+ //#endregion
284
+ //#region src/validators.ts
285
+ /**
286
+ * * Validate if a value matches the specified column data type
287
+ * @param type The column data type
288
+ * @param value The value to validate
289
+ * @returns `null` if valid, otherwise an error message string
290
+ */
291
+ function validateColumnType(type, value) {
292
+ const strVal = JSON.stringify(value);
293
+ switch (type) {
294
+ case "int":
295
+ if (isInteger(type)) return null;
296
+ return `'${strVal}' is not an integer`;
297
+ case "float":
298
+ case "number":
299
+ if (isNumber(value)) return null;
300
+ return `'${strVal}' is not a ${type === "float" ? "float " : ""}number`;
301
+ case "numeric":
302
+ if (isNumericString(value) || isNumber(value)) return null;
303
+ return `'${strVal}' is not a numeric value`;
304
+ case "bigint":
305
+ if (isBigInt(value)) return null;
306
+ return `'${strVal}' is not a bigint`;
307
+ case "text":
308
+ case "string":
309
+ if (isString(value)) return null;
310
+ return `'${strVal}' is not a ${type === "text" ? "text " : ""}string`;
311
+ case "timestamp":
312
+ if (isTimestamp(value)) return null;
313
+ return `'${strVal}' is not a timestamp string`;
314
+ case "uuid":
315
+ if (isUUID(value)) return null;
316
+ return `'${strVal}' is not a UUID string`;
317
+ case "bool":
318
+ case "boolean":
319
+ if (isBoolean(value)) return null;
320
+ return `'${strVal}' is not a boolean`;
321
+ case "array":
322
+ if (isArray(value)) return null;
323
+ return `'${strVal}' is not an array`;
324
+ case "list":
325
+ if (isArray(value)) return null;
326
+ return `'${strVal}' is not a list`;
327
+ case "tuple":
328
+ if (isArray(value)) return null;
329
+ return `'${strVal}' is not a tuple`;
330
+ case "set":
331
+ if (isSet(value)) return null;
332
+ return `'${strVal}' is not a set`;
333
+ case "object":
334
+ if (isObject(value)) return null;
335
+ return `'${strVal}' is not an object`;
336
+ case "date":
337
+ if (isDate(value)) return null;
338
+ return `'${strVal}' is not a Date object`;
339
+ case "map":
340
+ if (isMap(value)) return null;
341
+ return `'${strVal}' is not a Map object`;
342
+ case "custom": return null;
343
+ default:
344
+ if (type.includes("varchar(")) {
345
+ if (isString(value)) {
346
+ const length = extractNumbersFromString(type)[0];
347
+ if (isNumber(length) && value.length <= length) return null;
348
+ return `'${strVal}' does not satisfy the constraint: varchar length ${length}`;
349
+ }
350
+ return `'${strVal}' is not a varchar string`;
351
+ }
352
+ if (type.includes("char(")) {
353
+ if (isString(value)) {
354
+ const length = extractNumbersFromString(type)[0];
355
+ if (isNumber(length) && value.length === length) return null;
356
+ return `'${strVal}' does not satisfy the constraint: char length ${length}`;
357
+ }
358
+ return `'${strVal}' is not a char string`;
359
+ }
360
+ return null;
361
+ }
362
+ }
363
+ /**
364
+ * * Validate and prepare data for insertion or update based on column definitions
365
+ *
366
+ * @param data The data object to validate and prepare
367
+ * @param columns The column definitions
368
+ * @param keyPath The key path of the primary key column (if any)
369
+ * @param forUpdate Whether the operation is an update (default: `false`)
370
+ *
371
+ * @returns The validated and prepared data object
372
+ * @throws {TypeError} If any value does not match the expected column type
373
+ */
374
+ function validateAndPrepareData(data, columns, keyPath, forUpdate = false) {
375
+ const prepared = { ...data };
376
+ if (columns) Object.entries(columns).forEach((entry) => {
377
+ const [fieldName, column] = entry;
378
+ const defaultValue = column[DefaultValue];
379
+ if (!(fieldName in prepared) && defaultValue !== void 0 && !forUpdate) prepared[fieldName] = defaultValue;
380
+ const columnType = column[ColumnType];
381
+ if (columnType === "uuid" && !(fieldName in prepared) && !forUpdate) prepared[fieldName] = uuidV4();
382
+ if (columnType === "timestamp" && !(fieldName in prepared) && !forUpdate) prepared[fieldName] = getTimestamp();
383
+ if (fieldName !== keyPath) {
384
+ const errorMsg = validateColumnType(columnType, prepared[fieldName]);
385
+ if (errorMsg) throw new TypeError(errorMsg);
386
+ }
387
+ });
388
+ return prepared;
389
+ }
248
390
 
249
391
  //#endregion
250
392
  //#region src/query.ts
@@ -351,11 +493,13 @@ var InsertQuery = class {
351
493
  #readyPromise;
352
494
  #dataToInsert = [];
353
495
  #columns;
354
- constructor(table, dbGetter, readyPromise, columns) {
496
+ #keyPath;
497
+ constructor(table, dbGetter, readyPromise, columns, keyPath) {
355
498
  this.#table = table;
356
499
  this.#dbGetter = dbGetter;
357
500
  this.#readyPromise = readyPromise;
358
501
  this.#columns = columns;
502
+ this.#keyPath = keyPath;
359
503
  }
360
504
  /**
361
505
  * @instance Sets the data to be inserted
@@ -378,15 +522,7 @@ var InsertQuery = class {
378
522
  const insertedDocs = [];
379
523
  const promises = this.#dataToInsert.map((data) => {
380
524
  return new Promise((res, rej) => {
381
- const updated = { ...data };
382
- if (this.#columns) Object.entries(this.#columns).forEach(([fieldName, column]) => {
383
- const defaultValue = column[DefaultValue];
384
- if (!(fieldName in updated) && defaultValue !== void 0) updated[fieldName] = defaultValue;
385
- const columnType = column[ColumnType];
386
- if (columnType === "uuid" && !(fieldName in updated)) updated[fieldName] = uuidV4();
387
- if (columnType === "timestamp" && !(fieldName in updated)) updated[fieldName] = getTimestamp();
388
- });
389
- const request = store.add(updated);
525
+ const request = store.add(validateAndPrepareData(data, this.#columns, this.#keyPath));
390
526
  request.onsuccess = () => {
391
527
  const key = request.result;
392
528
  const getRequest = store.get(key);
@@ -411,10 +547,14 @@ var UpdateQuery = class {
411
547
  #readyPromise;
412
548
  #dataToUpdate;
413
549
  #whereCondition;
414
- constructor(table, dbGetter, readyPromise) {
550
+ #columns;
551
+ #keyPath;
552
+ constructor(table, dbGetter, readyPromise, columns, keyPath) {
415
553
  this.#table = table;
416
554
  this.#dbGetter = dbGetter;
417
555
  this.#readyPromise = readyPromise;
556
+ this.#columns = columns;
557
+ this.#keyPath = keyPath;
418
558
  }
419
559
  /**
420
560
  * @instance Sets the data to be updated
@@ -448,10 +588,10 @@ var UpdateQuery = class {
448
588
  if (this.#whereCondition) rows = rows.filter(this.#whereCondition);
449
589
  const updatePromises = rows.map((row) => {
450
590
  return new Promise((res, rej) => {
451
- const updatedRow = {
591
+ const updatedRow = validateAndPrepareData({
452
592
  ...row,
453
593
  ...this.#dataToUpdate
454
- };
594
+ }, this.#columns, this.#keyPath, true);
455
595
  const putRequest = store.put(updatedRow);
456
596
  putRequest.onsuccess = () => {
457
597
  updateCount++;
@@ -561,17 +701,22 @@ var DeleteQuery = class {
561
701
  var Locality = class {
562
702
  #name;
563
703
  #schema;
564
- #version;
704
+ #keyPath;
705
+ version;
565
706
  #db;
707
+ #version;
566
708
  #readyPromise;
567
709
  constructor(config) {
568
710
  this.#name = config.dbName;
569
711
  this.#schema = config.schema;
570
- this.#version = config.version;
571
712
  const store = this.#buildStoresConfig();
572
- this.#readyPromise = openDBWithStores(this.#name, store, this.#version).then((db) => {
713
+ this.#keyPath = store.find((s) => s.autoIncrement)?.keyPath;
714
+ this.#readyPromise = openDBWithStores(this.#name, store, config.version).then((db) => {
573
715
  this.#db = db;
716
+ }).finally(() => {
717
+ this.#version = this.#db?.version;
574
718
  });
719
+ this.version = this.#version ?? config.version;
575
720
  }
576
721
  /** Build store configurations from schema. */
577
722
  #buildStoresConfig() {
@@ -602,14 +747,14 @@ var Locality = class {
602
747
  * @param table Table name.
603
748
  */
604
749
  insert(table) {
605
- return new InsertQuery(table, () => this.#db, this.#readyPromise, this.#schema[table].columns);
750
+ return new InsertQuery(table, () => this.#db, this.#readyPromise, this.#schema[table].columns, this.#keyPath);
606
751
  }
607
752
  /**
608
753
  * @instance Update records in a table.
609
754
  * @param table Table name.
610
755
  */
611
756
  update(table) {
612
- return new UpdateQuery(table, () => this.#db, this.#readyPromise);
757
+ return new UpdateQuery(table, () => this.#db, this.#readyPromise, this.#schema[table].columns, this.#keyPath);
613
758
  }
614
759
  /**
615
760
  * @instance Delete records from a table.
@@ -716,11 +861,12 @@ const column = {
716
861
  int: () => new Column("int"),
717
862
  float: () => new Column("float"),
718
863
  number: () => new Column("number"),
864
+ numeric: () => new Column("numeric"),
719
865
  bigint: () => new Column("bigint"),
720
866
  text: () => new Column("text"),
721
867
  string: () => new Column("string"),
722
- char: (l) => new Column(`char(${isPositiveInteger(l) ? l : "0"})`),
723
- varchar: (l) => new Column(`varchar(${isPositiveInteger(l) ? l : "0"})`),
868
+ char: (length = 8) => new Column(`char(${length})`),
869
+ varchar: (length = 32) => new Column(`varchar(${length})`),
724
870
  uuid: () => new Column("uuid"),
725
871
  timestamp: () => new Column("timestamp"),
726
872
  bool: () => new Column("bool"),
@@ -739,6 +885,8 @@ exports.Locality = Locality;
739
885
  exports.column = column;
740
886
  exports.defineSchema = defineSchema;
741
887
  exports.getTimestamp = getTimestamp;
888
+ exports.isTimestamp = isTimestamp;
742
889
  exports.openDBWithStores = openDBWithStores;
743
890
  exports.table = table;
744
- exports.uuidV4 = uuidV4;
891
+ exports.uuidV4 = uuidV4;
892
+ exports.validateColumnType = validateColumnType;
package/dist/index.d.cts CHANGED
@@ -16,16 +16,16 @@ declare const IsUnique: unique symbol;
16
16
  /** Symbol key for default value */
17
17
  declare const DefaultValue: unique symbol;
18
18
  /** @class Represents a column definition. */
19
- declare class Column<T = any> {
19
+ declare class Column<T = any, TName extends TypeName = TypeName> {
20
20
  [$ColumnType]: T;
21
- [ColumnType]: string;
21
+ [ColumnType]: TName;
22
22
  [IsPrimaryKey]?: boolean;
23
23
  [IsAutoInc]?: boolean;
24
24
  [IsOptional]?: boolean;
25
25
  [IsIndexed]?: boolean;
26
26
  [IsUnique]?: boolean;
27
27
  [DefaultValue]?: T;
28
- constructor(type: string);
28
+ constructor(type: TName);
29
29
  /** @instance Marks column as primary key */
30
30
  pk(): this & {
31
31
  [IsPrimaryKey]: true;
@@ -103,6 +103,8 @@ type Branded<T, B> = T & $Brand<B>;
103
103
  * @note Technically, this uses intersection with primitive base types (`string & {}` or `number & {}`) to retain IntelliSense while avoiding type narrowing.
104
104
  */
105
105
  type LooseLiteral<T extends string | number> = T | (T extends string ? string & {} : number & {});
106
+ /** Union of `number` and numeric string */
107
+ type Numeric = number | `${number}`;
106
108
  /**
107
109
  * * A readonly array of elements of type `T`.
108
110
  *
@@ -188,6 +190,28 @@ interface DateLike {
188
190
  }
189
191
  /** Advanced types to exclude from counting as object key */
190
192
  type AdvancedTypes = Array<unknown> | File | FileList | Blob | Date | RegExp | Constructor | DateLike | WeakMap<WeakKey, unknown> | WeakSet<WeakKey> | Map<unknown, unknown> | Set<unknown> | Function | GenericFn | VoidFn | AsyncFunction<unknown> | Promise<unknown> | Error | EvalError | RangeError | ReferenceError | SyntaxError | TypeError | URIError | bigint | symbol;
193
+ /**
194
+ * * Extracts the parameters of the first overload of a function type `T`.
195
+ *
196
+ * @template T - The function type to extract parameters from.
197
+ *
198
+ * @returns A tuple type representing the parameters of the first overload of `T`.
199
+ *
200
+ * @example
201
+ * type Fn = {
202
+ * (a: number, b: string): void;
203
+ * (x: boolean): void;
204
+ * };
205
+ *
206
+ * type Params = FirstOverloadParams<Fn>; // [a: number, b: string]
207
+ */
208
+ type FirstOverloadParams<T> = T extends ({
209
+ (a1: infer P1, ...args: infer P2): any;
210
+ (...args: any[]): any;
211
+ }) ? [P1, ...P2] : T extends ({
212
+ (...args: infer P): any;
213
+ (...args: any[]): any;
214
+ }) ? P : T extends ((...args: infer P) => any) ? P : never;
191
215
  /**
192
216
  * * Maps all values of object `T` to a fixed type `R`, keeping original keys.
193
217
  *
@@ -235,8 +259,6 @@ type $UUIDVersion = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8;
235
259
  type UUIDVersion = `v${$UUIDVersion}`;
236
260
  /** General 5 parts UUID string as {@link Branded} type */
237
261
  type UUID<V extends UUIDVersion> = Branded<$UUID, V>;
238
- /** Schema definition type */
239
- type SchemaDefinition<T extends ColumnDefinition = ColumnDefinition> = Record<string, Table<T>>;
240
262
  /** Locality database configuration type */
241
263
  type LocalityConfig<DB extends string, V extends number, S extends SchemaDefinition> = {
242
264
  /** Database name */dbName: DB; /** Database version */
@@ -245,6 +267,12 @@ type LocalityConfig<DB extends string, V extends number, S extends SchemaDefinit
245
267
  };
246
268
  /** Column definition type */
247
269
  type ColumnDefinition<T = any> = Record<string, Column<T>>;
270
+ /** Record of column definitions */
271
+ type ColumnRecord = Record<string, ColumnDefinition>;
272
+ /** Schema record type mapping table names to {@link Table} instances */
273
+ type SchemaRecord<T extends ColumnRecord, Keys extends keyof T> = { [K in Keys]: Table<T[K]> };
274
+ /** Schema definition type */
275
+ type SchemaDefinition<T extends ColumnDefinition = ColumnDefinition> = Record<string, Table<T>>;
248
276
  /** Helper to reliably extract the generic type parameter from a Column using a symbol property. */
249
277
  type ExtractColumnType<C> = C extends {
250
278
  [$ColumnType]: infer U;
@@ -283,10 +311,12 @@ type InferInsertType<T extends Table> = Prettify<Omit<$InferRow<T['columns']>, $
283
311
  type InferUpdateType<T extends Table> = Prettify<Partial<Omit<$InferRow<T['columns']>, $InferPkField<T['columns']>>>>;
284
312
  /** Creates a type for select operations. */
285
313
  type InferSelectType<S extends Table> = Prettify<S extends infer T ? T extends Table<infer C> ? $InferRow<C> : never : never>;
314
+ /** Column type strings used in {@link Column} definitions */
315
+ type TypeName = LooseLiteral<'int' | 'float' | 'number' | 'numeric' | 'bigint' | 'text' | 'string' | 'uuid' | 'timestamp' | 'bool' | 'date' | 'object' | 'array' | 'list' | 'tuple' | 'set' | 'map' | 'custom'>;
286
316
  /** Store configuration type for {@link IndexedDB} */
287
317
  type StoreConfig = {
288
318
  /** Store name */name: string; /** Primary key path(s) */
289
- keyPath?: string | string[]; /** Whether the primary key is auto-incrementing */
319
+ keyPath?: string; /** Whether the primary key is auto-incrementing */
290
320
  autoIncrement?: boolean;
291
321
  };
292
322
  //#endregion
@@ -336,7 +366,7 @@ declare class SelectQuery<T extends GenericObject, S = null> {
336
366
  declare class InsertQuery<Raw extends GenericObject, Inserted, Data extends GenericObject, Return extends (Inserted extends Array<infer _> ? Data[] : Data)> {
337
367
  #private;
338
368
  [IsArray]: boolean;
339
- constructor(table: string, dbGetter: () => IDBDatabase, readyPromise: Promise<void>, columns?: ColumnDefinition);
369
+ constructor(table: string, dbGetter: () => IDBDatabase, readyPromise: Promise<void>, columns?: ColumnDefinition, keyPath?: string);
340
370
  /**
341
371
  * @instance Sets the data to be inserted
342
372
  * @param data Data object or array of data objects to insert
@@ -351,7 +381,7 @@ declare class InsertQuery<Raw extends GenericObject, Inserted, Data extends Gene
351
381
  /** @class Update query builder. */
352
382
  declare class UpdateQuery<T extends GenericObject, S extends Table> {
353
383
  #private;
354
- constructor(table: string, dbGetter: () => IDBDatabase, readyPromise: Promise<void>);
384
+ constructor(table: string, dbGetter: () => IDBDatabase, readyPromise: Promise<void>, columns?: ColumnDefinition, keyPath?: string);
355
385
  /**
356
386
  * @instance Sets the data to be updated
357
387
  * @param values Values to update
@@ -425,6 +455,7 @@ declare class DeleteQuery<T extends GenericObject, Key extends keyof T> {
425
455
  */
426
456
  declare class Locality<DBName extends string = string, Version extends number = 1, Schema extends SchemaDefinition = SchemaDefinition> {
427
457
  #private;
458
+ readonly version: Version;
428
459
  constructor(config: LocalityConfig<DBName, Version, Schema>);
429
460
  /** @instance Waits for database initialization to complete. */
430
461
  ready(): Promise<void>;
@@ -453,7 +484,7 @@ declare class Locality<DBName extends string = string, Version extends number =
453
484
  //#endregion
454
485
  //#region src/factory.d.ts
455
486
  /**
456
- * * Opens an `IndexedDB` database with the specified stores.
487
+ * * Opens an `IndexedDB` database instance with the specified stores.
457
488
  * @param name Database name
458
489
  * @param stores Array of store configurations
459
490
  * @param version Database version (default is `1`)
@@ -494,7 +525,7 @@ declare function openDBWithStores(name: string, stores: StoreConfig[], version?:
494
525
  * type InsertPost = InferInsertType<typeof schema.posts>;
495
526
  * type UpdatePost = InferUpdateType<typeof schema.posts>;
496
527
  */
497
- declare function defineSchema<T extends Record<string, ColumnDefinition>, Keys extends keyof T>(schema: T): { [K in Keys]: Table<T[K]> };
528
+ declare function defineSchema<Schema extends ColumnRecord, Keys extends keyof Schema>(schema: Schema): SchemaRecord<Schema, Keys>;
498
529
  /**
499
530
  * * Factory function to create a new {@link Table} instance.
500
531
  * @param name The name of the table.
@@ -550,51 +581,57 @@ declare const column: {
550
581
  * @returns A new {@link Column} instance for integers.
551
582
  * @remarks column type is typically used for whole numbers.
552
583
  */
553
- readonly int: () => Column<number>;
584
+ readonly int: () => Column<number, "int">;
554
585
  /**
555
586
  * Creates a float column.
556
587
  * @returns A new {@link Column} instance for floating-point numbers.
557
588
  * @remarks This column type is specifically for decimal numbers.
558
589
  */
559
- readonly float: () => Column<number>;
590
+ readonly float: () => Column<number, "float">;
560
591
  /**
561
592
  * Creates a number column.
562
593
  * @returns A new {@link Column} instance for numbers.
563
594
  * @remarks This column type is used for both whole & floating-point numbers.
564
595
  */
565
- readonly number: () => Column<number>;
596
+ readonly number: () => Column<number, "number">;
597
+ /**
598
+ * Creates a numeric (number or numeric string) column.
599
+ * @returns A new {@link Column} instance for numeric.
600
+ * @remarks This column type is used for both whole & floating-point numbers or numeric strings.
601
+ */
602
+ readonly numeric: () => Column<Numeric, "numeric">;
566
603
  /**
567
604
  * Creates a bigint column.
568
605
  * @returns A new {@link Column} instance for bigints.
569
606
  * @remarks This column type is used for large integers beyond the safe integer limit of JavaScript.
570
607
  */
571
- readonly bigint: () => Column<bigint>;
608
+ readonly bigint: () => Column<bigint, "bigint">;
572
609
  /**
573
610
  * Creates a text column.
574
611
  * @returns A new {@link Column} instance for text.
575
612
  * @remarks This column type is used for large strings of text.
576
613
  */
577
- readonly text: () => Column<string>;
614
+ readonly text: () => Column<string, "text">;
578
615
  /**
579
616
  * Creates a string column.
580
617
  * @returns A new {@link Column} instance for strings.
581
618
  * @remarks This column type is used for general string data.
582
619
  */
583
- readonly string: () => Column<string>;
620
+ readonly string: () => Column<string, "string">;
584
621
  /**
585
622
  * Creates a char column with optional length.
586
- * @param l Optional length of the char column.
623
+ * @param length Optional length of the char column. Defaults to `8`.
587
624
  * @returns A new {@link Column} instance for char.
588
625
  * @remarks This column type is used for fixed-length strings.
589
626
  */
590
- readonly char: (l?: number) => Column<string>;
627
+ readonly char: <L extends number = 8>(length?: L) => Column<string, `char(${L})`>;
591
628
  /**
592
629
  * Creates a varchar column with optional length.
593
- * @param l Optional length of the varchar column.
630
+ * @param length Optional length of the varchar column. Defaults to `32`.
594
631
  * @returns A new {@link Column} instance for varchar.
595
632
  * @remarks This column type is used for variable-length strings.
596
633
  */
597
- readonly varchar: (l?: number) => Column<string>;
634
+ readonly varchar: <L extends number = 32>(length?: L) => Column<string, `varchar(${L})`>;
598
635
  /**
599
636
  * Creates a UUID column.
600
637
  * @returns A new {@link Column} instance for UUIDs.
@@ -603,7 +640,7 @@ declare const column: {
603
640
  * - UUIDs are typically used as unique identifiers.
604
641
  * - Automatically genrates UUID v4 values when no value is provided.
605
642
  */
606
- readonly uuid: () => Column<`${string}-${string}-${string}-${string}-${string}`>;
643
+ readonly uuid: () => Column<`${string}-${string}-${string}-${string}-${string}`, "uuid">;
607
644
  /**
608
645
  * Creates a timestamp column.
609
646
  * @returns A new {@link Column} instance for timestamps.
@@ -611,55 +648,55 @@ declare const column: {
611
648
  * - This column type is used for storing date and time information in ISO 8601 format.
612
649
  * - Automatically generates the current timestamp when no value is provided.
613
650
  */
614
- readonly timestamp: () => Column<Timestamp>;
651
+ readonly timestamp: () => Column<Timestamp, "timestamp">;
615
652
  /**
616
653
  * Creates a boolean column.
617
654
  * @returns A new {@link Column} instance for booleans.
618
655
  * @remarks This column type is used for true/false values.
619
656
  */
620
- readonly bool: () => Column<boolean>;
657
+ readonly bool: () => Column<boolean, "bool">;
621
658
  /**
622
659
  * Creates a date column.
623
660
  * @returns A new {@link Column} instance for dates.
624
661
  * @remarks This column type is used for storing date values.
625
662
  */
626
- readonly date: () => Column<Date>;
663
+ readonly date: () => Column<Date, "date">;
627
664
  /**
628
665
  * Creates an object column.
629
666
  * @returns A new {@link Column} instance for objects.
630
667
  * @remarks This column type is used for storing generic objects with string keys.
631
668
  */
632
- readonly object: <Obj extends GenericObject>() => Column<Obj>;
669
+ readonly object: <Obj extends GenericObject>() => Column<Obj, "object">;
633
670
  /**
634
671
  * Creates an array column.
635
672
  * @returns A new {@link Column} instance for arrays.
636
673
  * @remarks This column type is used for storing arrays of any type.
637
674
  */
638
- readonly array: <T = any>() => Column<T[]>;
675
+ readonly array: <T = any>() => Column<T[], "array">;
639
676
  /**
640
677
  * Creates a list column.
641
678
  * @returns A new {@link Column} instance for lists.
642
679
  * @remarks This column type is used for storing lists of any type.
643
680
  */
644
- readonly list: <T = any>() => Column<List<T>>;
681
+ readonly list: <T = any>() => Column<List<T>, "list">;
645
682
  /**
646
683
  * Creates a tuple column.
647
684
  * @returns A new {@link Column} instance for tuples.
648
685
  * @remarks This column type is used for storing fixed-size arrays (tuples) of any type.
649
686
  */
650
- readonly tuple: <T = any>() => Column<Tuple<T>>;
687
+ readonly tuple: <T = any>() => Column<Tuple<T>, "tuple">;
651
688
  /**
652
689
  * Creates a set column.
653
690
  * @returns A new {@link Column} instance for sets.
654
691
  * @remarks This column type is used for storing unique collections of any type.
655
692
  */
656
- readonly set: <T = any>() => Column<Set<T>>;
693
+ readonly set: <T = any>() => Column<Set<T>, "set">;
657
694
  /**
658
695
  * Creates a map column.
659
696
  * @returns A new {@link Column} instance for maps.
660
697
  * @remarks This column type is used for storing key-value pairs of any type.
661
698
  */
662
- readonly map: <K = any, V = any>() => Column<Map<K, V>>;
699
+ readonly map: <K = any, V = any>() => Column<Map<K, V>, "map">;
663
700
  /**
664
701
  * Creates a custom column.
665
702
  * @returns A new {@link Column} instance for custom data types.
@@ -668,7 +705,7 @@ declare const column: {
668
705
  * - You can specify the type when creating the column.
669
706
  * - No built-in serialization/deserialization is provided; you must handle it yourself.
670
707
  */
671
- readonly custom: <T = any>() => Column<T>;
708
+ readonly custom: <T = any>() => Column<T, "custom">;
672
709
  };
673
710
  //#endregion
674
711
  //#region src/utils.d.ts
@@ -685,5 +722,20 @@ declare function uuidV4(uppercase?: boolean): UUID<'v4'>;
685
722
  * @return Timestamp string in ISO 8601 format
686
723
  */
687
724
  declare function getTimestamp(value?: string | number | Date): Timestamp;
725
+ /**
726
+ * * Check if a value is a valid Timestamp string in ISO 8601 format
727
+ * @param value The value to check
728
+ * @returns `true` if the value is a valid Timestamp, otherwise `false`
729
+ */
730
+ declare function isTimestamp(value: unknown): value is Timestamp;
731
+ //#endregion
732
+ //#region src/validators.d.ts
733
+ /**
734
+ * * Validate if a value matches the specified column data type
735
+ * @param type The column data type
736
+ * @param value The value to validate
737
+ * @returns `null` if valid, otherwise an error message string
738
+ */
739
+ declare function validateColumnType<T extends TypeName>(type: T, value: unknown): string | null;
688
740
  //#endregion
689
- export { $InferAutoInc, $InferDefault, $InferOptional, $InferPkField, $InferRow, $InferTimestamp, $InferUUID, $UUID, $UUIDVersion, $UnionToIntersection, AdvancedTypes, ArrayToTuple, AsyncFunction, BasicPrimitive, Branded, type Column, ColumnDefinition, Constructor, DateLike, GenericFn, GenericObject, InferInsertType, InferSelectType, InferUpdateType, List, Locality, LocalityConfig, LooseLiteral, MapObjectValues, NestedPrimitiveKey, NormalPrimitive, Prettify, Primitive, SchemaDefinition, SelectFields, SortDirection, StoreConfig, type Table, Timestamp, Tuple, UUID, UUIDVersion, VoidFn, column, defineSchema, getTimestamp, openDBWithStores, table, uuidV4 };
741
+ export { $InferAutoInc, $InferDefault, $InferOptional, $InferPkField, $InferRow, $InferTimestamp, $InferUUID, $UUID, $UUIDVersion, $UnionToIntersection, AdvancedTypes, ArrayToTuple, AsyncFunction, BasicPrimitive, Branded, type Column, ColumnDefinition, ColumnRecord, Constructor, DateLike, FirstOverloadParams, GenericFn, GenericObject, InferInsertType, InferSelectType, InferUpdateType, List, Locality, LocalityConfig, LooseLiteral, MapObjectValues, NestedPrimitiveKey, NormalPrimitive, Numeric, Prettify, Primitive, SchemaDefinition, SchemaRecord, SelectFields, SortDirection, StoreConfig, type Table, Timestamp, Tuple, TypeName, UUID, UUIDVersion, VoidFn, column, defineSchema, getTimestamp, isTimestamp, openDBWithStores, table, uuidV4, validateColumnType };