@type32/tauri-sqlite-orm 0.2.15 → 0.4.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.
Files changed (43) hide show
  1. package/dist/aggregates.d.ts +12 -0
  2. package/dist/aggregates.js +9 -0
  3. package/dist/builders/delete.d.ts +23 -0
  4. package/dist/builders/delete.js +73 -0
  5. package/dist/builders/index.d.ts +7 -0
  6. package/dist/builders/index.js +7 -0
  7. package/dist/builders/insert.d.ts +31 -0
  8. package/dist/builders/insert.js +141 -0
  9. package/dist/builders/query-base.d.ts +1 -0
  10. package/dist/builders/query-base.js +1 -0
  11. package/dist/builders/relations.d.ts +11 -0
  12. package/dist/builders/relations.js +1 -0
  13. package/dist/builders/select.d.ts +54 -0
  14. package/dist/builders/select.js +427 -0
  15. package/dist/builders/update.d.ts +30 -0
  16. package/dist/builders/update.js +124 -0
  17. package/dist/builders/with.d.ts +17 -0
  18. package/dist/builders/with.js +34 -0
  19. package/dist/column-helpers.d.ts +22 -0
  20. package/dist/column-helpers.js +17 -0
  21. package/dist/dialect.d.ts +21 -0
  22. package/dist/dialect.js +67 -0
  23. package/dist/errors.d.ts +30 -0
  24. package/dist/errors.js +66 -0
  25. package/dist/index.d.mts +150 -4
  26. package/dist/index.d.ts +150 -4
  27. package/dist/index.js +270 -108
  28. package/dist/index.mjs +263 -104
  29. package/dist/operators.d.ts +30 -0
  30. package/dist/operators.js +84 -0
  31. package/dist/orm.d.ts +180 -0
  32. package/dist/orm.js +556 -0
  33. package/dist/relational-types.d.ts +87 -0
  34. package/dist/relational-types.js +1 -0
  35. package/dist/relations-v2.d.ts +77 -0
  36. package/dist/relations-v2.js +157 -0
  37. package/dist/serialization.d.ts +14 -0
  38. package/dist/serialization.js +135 -0
  39. package/dist/subquery.d.ts +5 -0
  40. package/dist/subquery.js +6 -0
  41. package/dist/types.d.ts +58 -0
  42. package/dist/types.js +1 -0
  43. package/package.json +2 -1
@@ -0,0 +1,21 @@
1
+ import { DatabaseIntrospector, Dialect, DialectAdapter, Driver, Kysely, QueryCompiler } from 'kysely';
2
+ /**
3
+ * Minimal interface that any SQLite-over-Tauri (or compatible mock) database must satisfy.
4
+ * Using this instead of the concrete `@tauri-apps/plugin-sql` type keeps the dialect
5
+ * portable and testable outside a Tauri runtime.
6
+ */
7
+ export interface DatabaseLike {
8
+ select<T>(query: string, params?: any[]): Promise<T>;
9
+ execute(query: string, params?: any[]): Promise<{
10
+ lastInsertId: number;
11
+ rowsAffected: number;
12
+ }>;
13
+ }
14
+ export declare class TauriDialect implements Dialect {
15
+ private readonly db;
16
+ constructor(db: DatabaseLike);
17
+ createAdapter(): DialectAdapter;
18
+ createDriver(): Driver;
19
+ createIntrospector(db: Kysely<any>): DatabaseIntrospector;
20
+ createQueryCompiler(): QueryCompiler;
21
+ }
@@ -0,0 +1,67 @@
1
+ import { CompiledQuery, SqliteAdapter, SqliteIntrospector, SqliteQueryCompiler, } from 'kysely';
2
+ class TauriConnection {
3
+ db;
4
+ constructor(db) {
5
+ this.db = db;
6
+ }
7
+ async executeQuery(compiledQuery) {
8
+ const { sql, parameters } = compiledQuery;
9
+ const params = parameters;
10
+ const trimmed = sql.trimStart();
11
+ const isSelect = /^\s*(SELECT|WITH|PRAGMA)/i.test(trimmed);
12
+ const hasReturning = /\bRETURNING\b/i.test(sql);
13
+ // INSERT/UPDATE/DELETE with RETURNING returns rows; use select() not execute()
14
+ if (isSelect || hasReturning) {
15
+ const rows = await this.db.select(sql, params);
16
+ return { rows };
17
+ }
18
+ const result = await this.db.execute(sql, params);
19
+ return {
20
+ rows: [],
21
+ insertId: BigInt(Math.round(result.lastInsertId ?? 0)),
22
+ numAffectedRows: BigInt(result.rowsAffected ?? 0),
23
+ };
24
+ }
25
+ async *streamQuery(_compiledQuery) {
26
+ throw new Error('Streaming queries are not supported by @tauri-apps/plugin-sql');
27
+ }
28
+ }
29
+ class TauriDriver {
30
+ db;
31
+ constructor(db) {
32
+ this.db = db;
33
+ }
34
+ async init() { }
35
+ async acquireConnection() {
36
+ return new TauriConnection(this.db);
37
+ }
38
+ async beginTransaction(conn, _settings) {
39
+ await conn.executeQuery(CompiledQuery.raw('BEGIN'));
40
+ }
41
+ async commitTransaction(conn) {
42
+ await conn.executeQuery(CompiledQuery.raw('COMMIT'));
43
+ }
44
+ async rollbackTransaction(conn) {
45
+ await conn.executeQuery(CompiledQuery.raw('ROLLBACK'));
46
+ }
47
+ async releaseConnection(_conn) { }
48
+ async destroy() { }
49
+ }
50
+ export class TauriDialect {
51
+ db;
52
+ constructor(db) {
53
+ this.db = db;
54
+ }
55
+ createAdapter() {
56
+ return new SqliteAdapter();
57
+ }
58
+ createDriver() {
59
+ return new TauriDriver(this.db);
60
+ }
61
+ createIntrospector(db) {
62
+ return new SqliteIntrospector(db);
63
+ }
64
+ createQueryCompiler() {
65
+ return new SqliteQueryCompiler();
66
+ }
67
+ }
@@ -0,0 +1,30 @@
1
+ export declare class TauriORMError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class QueryBuilderError extends TauriORMError {
5
+ constructor(message: string);
6
+ }
7
+ export declare class MissingWhereClauseError extends QueryBuilderError {
8
+ constructor(operation: 'UPDATE' | 'DELETE', tableName: string);
9
+ }
10
+ export declare class ValidationError extends TauriORMError {
11
+ constructor(message: string);
12
+ }
13
+ export declare class InsertValidationError extends ValidationError {
14
+ constructor(message: string);
15
+ }
16
+ export declare class UpdateValidationError extends ValidationError {
17
+ constructor(message: string);
18
+ }
19
+ export declare class MigrationError extends TauriORMError {
20
+ constructor(message: string);
21
+ }
22
+ export declare class RelationError extends TauriORMError {
23
+ constructor(message: string);
24
+ }
25
+ export declare class ColumnNotFoundError extends TauriORMError {
26
+ constructor(columnName: string, tableName: string);
27
+ }
28
+ export declare class TableNotFoundError extends TauriORMError {
29
+ constructor(tableName: string);
30
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,66 @@
1
+ // Custom error classes for better error handling
2
+ export class TauriORMError extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = 'TauriORMError';
6
+ // Maintains proper stack trace for where our error was thrown (only available on V8)
7
+ if (Error.captureStackTrace) {
8
+ Error.captureStackTrace(this, this.constructor);
9
+ }
10
+ }
11
+ }
12
+ export class QueryBuilderError extends TauriORMError {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = 'QueryBuilderError';
16
+ }
17
+ }
18
+ export class MissingWhereClauseError extends QueryBuilderError {
19
+ constructor(operation, tableName) {
20
+ super(`${operation} operation on table "${tableName}" requires a WHERE clause to prevent accidental data loss. ` +
21
+ `Use .where() to specify conditions, or use .allowGlobalOperation() to explicitly allow operations without WHERE.`);
22
+ this.name = 'MissingWhereClauseError';
23
+ }
24
+ }
25
+ export class ValidationError extends TauriORMError {
26
+ constructor(message) {
27
+ super(message);
28
+ this.name = 'ValidationError';
29
+ }
30
+ }
31
+ export class InsertValidationError extends ValidationError {
32
+ constructor(message) {
33
+ super(message);
34
+ this.name = 'InsertValidationError';
35
+ }
36
+ }
37
+ export class UpdateValidationError extends ValidationError {
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = 'UpdateValidationError';
41
+ }
42
+ }
43
+ export class MigrationError extends TauriORMError {
44
+ constructor(message) {
45
+ super(message);
46
+ this.name = 'MigrationError';
47
+ }
48
+ }
49
+ export class RelationError extends TauriORMError {
50
+ constructor(message) {
51
+ super(message);
52
+ this.name = 'RelationError';
53
+ }
54
+ }
55
+ export class ColumnNotFoundError extends TauriORMError {
56
+ constructor(columnName, tableName) {
57
+ super(`Column "${columnName}" does not exist on table "${tableName}"`);
58
+ this.name = 'ColumnNotFoundError';
59
+ }
60
+ }
61
+ export class TableNotFoundError extends TauriORMError {
62
+ constructor(tableName) {
63
+ super(`Table "${tableName}" not found in schema`);
64
+ this.name = 'TableNotFoundError';
65
+ }
66
+ }
package/dist/index.d.mts CHANGED
@@ -56,8 +56,28 @@ type RelationType = 'one' | 'many';
56
56
  interface RelationConfig {
57
57
  type: RelationType;
58
58
  foreignTable: AnyTable;
59
+ /** For one: local FK columns. For many (v2): foreign table's FK columns. */
59
60
  fields?: AnySQLiteColumn[];
61
+ /** For one: foreign PK columns. For many (v2): parent table's PK columns. */
60
62
  references?: AnySQLiteColumn[];
63
+ /** For many-to-many via through(): junction table between parent and foreign */
64
+ junctionTable?: AnyTable;
65
+ /** Parent column -> junction column (parent PK = junction FK to parent) */
66
+ fromJunction?: {
67
+ column: AnySQLiteColumn;
68
+ junctionColumn: AnySQLiteColumn;
69
+ };
70
+ /** Junction column -> foreign column (junction FK to foreign = foreign PK) */
71
+ toJunction?: {
72
+ junctionColumn: AnySQLiteColumn;
73
+ column: AnySQLiteColumn;
74
+ };
75
+ /** When false, one relation is required (type-level: T not T | null) */
76
+ optional?: boolean;
77
+ /** Alias for the relation (e.g. for self-referential disambiguation) */
78
+ alias?: string;
79
+ /** Predefined filter for many relations: (alias) => Condition. Receives the joined table alias. */
80
+ where?: (alias: string) => unknown;
61
81
  }
62
82
 
63
83
  type Condition = Expression<SqlBool>;
@@ -233,6 +253,8 @@ type RelationsBuilder = {
233
253
  one: <U extends AnyTable>(table: U, config?: {
234
254
  fields: AnySQLiteColumn[];
235
255
  references: AnySQLiteColumn[];
256
+ optional?: boolean;
257
+ alias?: string;
236
258
  }) => OneRelation<U>;
237
259
  many: <U extends AnyTable>(table: U) => ManyRelation<U>;
238
260
  };
@@ -361,14 +383,53 @@ declare class OneRelation<T extends AnyTable = AnyTable> extends Relation<T> {
361
383
  config?: {
362
384
  fields: AnySQLiteColumn[];
363
385
  references: AnySQLiteColumn[];
386
+ optional?: boolean;
387
+ alias?: string;
364
388
  } | undefined;
365
389
  constructor(foreignTable: T, config?: {
366
390
  fields: AnySQLiteColumn[];
367
391
  references: AnySQLiteColumn[];
392
+ optional?: boolean;
393
+ alias?: string;
368
394
  } | undefined);
369
395
  }
370
396
  declare class ManyRelation<T extends AnyTable = AnyTable> extends Relation<T> {
371
- constructor(foreignTable: T);
397
+ config?: {
398
+ from?: AnySQLiteColumn[];
399
+ to?: AnySQLiteColumn[];
400
+ through?: {
401
+ junctionTable: AnyTable;
402
+ fromRef: {
403
+ column: AnySQLiteColumn;
404
+ junctionColumn: AnySQLiteColumn;
405
+ };
406
+ toRef: {
407
+ column: AnySQLiteColumn;
408
+ junctionColumn: AnySQLiteColumn;
409
+ };
410
+ };
411
+ optional?: boolean;
412
+ alias?: string;
413
+ where?: (alias: string) => unknown;
414
+ } | undefined;
415
+ constructor(foreignTable: T, config?: {
416
+ from?: AnySQLiteColumn[];
417
+ to?: AnySQLiteColumn[];
418
+ through?: {
419
+ junctionTable: AnyTable;
420
+ fromRef: {
421
+ column: AnySQLiteColumn;
422
+ junctionColumn: AnySQLiteColumn;
423
+ };
424
+ toRef: {
425
+ column: AnySQLiteColumn;
426
+ junctionColumn: AnySQLiteColumn;
427
+ };
428
+ };
429
+ optional?: boolean;
430
+ alias?: string;
431
+ where?: (alias: string) => unknown;
432
+ } | undefined);
372
433
  }
373
434
  declare const relations: <T extends AnyTable, R extends Record<string, Relation>>(table: T, relationsCallback: (helpers: RelationsBuilder) => R) => R;
374
435
  declare const getTableColumns: <T extends AnyTable>(table: T) => Record<string, AnySQLiteColumn>;
@@ -411,9 +472,18 @@ declare const enumType: <TName extends string, TValues extends readonly [string,
411
472
 
412
473
  /** Maps relations() return type to typed relation configs with foreign table preserved */
413
474
  type InferRelationsMap<R extends Record<string, OneRelation | ManyRelation>> = {
414
- [K in keyof R]: R[K] extends OneRelation<infer T> ? {
475
+ [K in keyof R]: R[K] extends OneRelation<infer T> ? R[K] extends {
476
+ config?: {
477
+ optional?: infer O;
478
+ };
479
+ } ? {
480
+ type: 'one';
481
+ foreignTable: T;
482
+ optional: O;
483
+ } : {
415
484
  type: 'one';
416
485
  foreignTable: T;
486
+ optional?: true;
417
487
  } : R[K] extends ManyRelation<infer T> ? {
418
488
  type: 'many';
419
489
  foreignTable: T;
@@ -440,7 +510,8 @@ type InferRelationFields<TTable extends AnyTable, TRelationsMap extends Record<s
440
510
  [K in keyof TWith & keyof TRelationsMap]: TWith[K] extends false | undefined ? never : TRelationsMap[K] extends {
441
511
  type: 'one';
442
512
  foreignTable: infer T;
443
- } ? T extends AnyTable ? InferSelectModel<T> & InferNestedFields<T, TWith[K], TAllRelations> : never : TRelationsMap[K] extends {
513
+ optional?: infer O;
514
+ } ? T extends AnyTable ? ([O] extends [false] ? InferSelectModel<T> & InferNestedFields<T, TWith[K], TAllRelations> : (InferSelectModel<T> & InferNestedFields<T, TWith[K], TAllRelations>) | null) : never : TRelationsMap[K] extends {
444
515
  type: 'many';
445
516
  foreignTable: infer T;
446
517
  } ? T extends AnyTable ? (InferSelectModel<T> & InferNestedFields<T, TWith[K], TAllRelations>)[] : never : never;
@@ -484,6 +555,81 @@ type InferRelationalSelectModel<TTable extends AnyTable, TRelations extends Reco
484
555
  [K in TTable['_']['name']]: TRelations;
485
556
  }> = InferSelectModel<TTable> & InferRelationFields<TTable, InferRelationsMap<TRelations>, TAllRelations, TWith>;
486
557
 
558
+ /**
559
+ * Relations v2 API - Drizzle-style defineRelations with from/to and many-without-one.
560
+ * Use defineRelations() for a single place to define all relations, or defineRelationsPart() to split into parts.
561
+ */
562
+
563
+ /** Options for one() relation - from/to replace fields/references */
564
+ interface OneRelationOptions {
565
+ from: AnySQLiteColumn | AnySQLiteColumn[];
566
+ to: AnySQLiteColumn | AnySQLiteColumn[];
567
+ optional?: boolean;
568
+ alias?: string;
569
+ }
570
+ /** Reference for through() - column with junction column for many-to-many */
571
+ interface ThroughRef {
572
+ column: AnySQLiteColumn;
573
+ junctionColumn: AnySQLiteColumn;
574
+ junctionTable: AnyTable;
575
+ }
576
+ /** Create a through reference for many-to-many: through(column, junctionColumn, junctionTable) */
577
+ declare function through(column: AnySQLiteColumn, junctionColumn: AnySQLiteColumn, junctionTable: AnyTable): ThroughRef;
578
+ /** Options for many() relation - optional explicit from/to for many-without-one, or through() for many-to-many */
579
+ interface ManyRelationOptions {
580
+ from?: AnySQLiteColumn | AnySQLiteColumn[] | ThroughRef;
581
+ to?: AnySQLiteColumn | AnySQLiteColumn[] | ThroughRef;
582
+ optional?: boolean;
583
+ alias?: string;
584
+ /** Predefined filter: (alias) => Condition. Applied to the joined relation table. */
585
+ where?: (alias: string) => Condition;
586
+ }
587
+ /** Extract table keys from schema (values that extend AnyTable) */
588
+ type ExtractTableKeys<T> = {
589
+ [K in keyof T]: T[K] extends AnyTable ? K : never;
590
+ }[keyof T];
591
+ type ExtractTables<T extends Record<string, unknown>> = Pick<T, Extract<ExtractTableKeys<T>, keyof T>> extends infer R ? R extends Record<string, AnyTable> ? R : Record<string, AnyTable> : Record<string, AnyTable>;
592
+ /** Build the r object passed to defineRelations callback - all properties required to avoid TS2722 */
593
+ type BuildR<Tables extends Record<string, AnyTable>> = {
594
+ [K in keyof Tables]: Tables[K]['_']['columns'];
595
+ } & {
596
+ one: {
597
+ [K in keyof Tables]: (opts: OneRelationOptions) => OneRelation<Tables[K]>;
598
+ };
599
+ many: {
600
+ [K in keyof Tables]: (opts?: ManyRelationOptions) => ManyRelation<Tables[K]>;
601
+ };
602
+ };
603
+ type DefineRelationsCallback<Tables extends Record<string, AnyTable>> = (r: BuildR<Tables>) => {
604
+ [K in keyof Tables]?: Record<string, OneRelation | ManyRelation>;
605
+ };
606
+ /**
607
+ * Define all relations for your schema in one place (v2 API).
608
+ * Uses from/to instead of fields/references, and supports many-without-one.
609
+ *
610
+ * @example
611
+ * ```ts
612
+ * import * as schema from './schema'
613
+ * import { defineRelations } from '@type32/tauri-sqlite-orm'
614
+ *
615
+ * export const relations = defineRelations(schema, (r) => ({
616
+ * users: {
617
+ * posts: r.many.posts({ from: r.users.id, to: r.posts.userId }),
618
+ * },
619
+ * posts: {
620
+ * user: r.one.users({ from: r.posts.userId, to: r.users.id }),
621
+ * postTags: r.many.postTags({ from: r.posts.id, to: r.postTags.postId }),
622
+ * },
623
+ * }))
624
+ * ```
625
+ */
626
+ declare function defineRelations<TSchema extends Record<string, unknown>>(schema: TSchema, callback: DefineRelationsCallback<ExtractTables<TSchema>>): Record<string, Record<string, OneRelation | ManyRelation>>;
627
+ /**
628
+ * Define a part of relations - merge multiple parts when passing to TauriORM.
629
+ * Useful for splitting large schema definitions.
630
+ */
631
+ declare function defineRelationsPart<TSchema extends Record<string, unknown>>(schema: TSchema, callback: DefineRelationsCallback<Record<string, AnyTable>>): Record<string, Record<string, OneRelation | ManyRelation>>;
632
+
487
633
  declare class TauriORMError extends Error {
488
634
  constructor(message: string);
489
635
  }
@@ -515,4 +661,4 @@ declare class TableNotFoundError extends TauriORMError {
515
661
  constructor(tableName: string);
516
662
  }
517
663
 
518
- export { type AnySQLiteColumn, type AnyTable, type ColumnDataType, ColumnNotFoundError, type ColumnOptions, type ColumnValueTypes, type Condition, type DatabaseLike, DeleteQueryBuilder, type ExtractColumnType, type InferInsertModel, type InferRelationalSelectModel, type InferRelationsMap, type InferSelectModel, InsertQueryBuilder, InsertValidationError, ManyRelation, MigrationError, MissingWhereClauseError, type Mode, OneRelation, QueryBuilderError, Relation, type RelationConfig, RelationError, type RelationType, type RelationsBuilder, type SQLAggregate, type SQLCondition, type SQLSubquery, SQLiteColumn, SelectQueryBuilder, Table, TableNotFoundError, TauriDialect, TauriORM, TauriORMError, UpdateQueryBuilder, UpdateValidationError, ValidationError, WithQueryBuilder, alias, and, as, asc, avg, between, blob, boolean, contains, count, countDistinct, desc, endsWith, enumType, eq, eqSubquery, exists, getTableColumns, groupConcat, gt, gtSubquery, gte, gteSubquery, ilike, inArray, integer, isNotNull, isNull, like, lt, ltSubquery, lte, lteSubquery, max, min, ne, neSubquery, not, notExists, notIn, numeric, or, real, relations, scalarSubquery, sqliteTable, startsWith, subquery, sum, text };
664
+ export { type AnySQLiteColumn, type AnyTable, type BuildR, type ColumnDataType, ColumnNotFoundError, type ColumnOptions, type ColumnValueTypes, type Condition, type DatabaseLike, type DefineRelationsCallback, DeleteQueryBuilder, type ExtractColumnType, type InferInsertModel, type InferRelationalSelectModel, type InferRelationsMap, type InferSelectModel, InsertQueryBuilder, InsertValidationError, ManyRelation, type ManyRelationOptions, MigrationError, MissingWhereClauseError, type Mode, OneRelation, type OneRelationOptions, QueryBuilderError, Relation, type RelationConfig, RelationError, type RelationType, type RelationsBuilder, type SQLAggregate, type SQLCondition, type SQLSubquery, SQLiteColumn, SelectQueryBuilder, Table, TableNotFoundError, TauriDialect, TauriORM, TauriORMError, type ThroughRef, UpdateQueryBuilder, UpdateValidationError, ValidationError, WithQueryBuilder, alias, and, as, asc, avg, between, blob, boolean, contains, count, countDistinct, defineRelations, defineRelationsPart, desc, endsWith, enumType, eq, eqSubquery, exists, getTableColumns, groupConcat, gt, gtSubquery, gte, gteSubquery, ilike, inArray, integer, isNotNull, isNull, like, lt, ltSubquery, lte, lteSubquery, max, min, ne, neSubquery, not, notExists, notIn, numeric, or, real, relations, scalarSubquery, sqliteTable, startsWith, subquery, sum, text, through };
package/dist/index.d.ts CHANGED
@@ -56,8 +56,28 @@ type RelationType = 'one' | 'many';
56
56
  interface RelationConfig {
57
57
  type: RelationType;
58
58
  foreignTable: AnyTable;
59
+ /** For one: local FK columns. For many (v2): foreign table's FK columns. */
59
60
  fields?: AnySQLiteColumn[];
61
+ /** For one: foreign PK columns. For many (v2): parent table's PK columns. */
60
62
  references?: AnySQLiteColumn[];
63
+ /** For many-to-many via through(): junction table between parent and foreign */
64
+ junctionTable?: AnyTable;
65
+ /** Parent column -> junction column (parent PK = junction FK to parent) */
66
+ fromJunction?: {
67
+ column: AnySQLiteColumn;
68
+ junctionColumn: AnySQLiteColumn;
69
+ };
70
+ /** Junction column -> foreign column (junction FK to foreign = foreign PK) */
71
+ toJunction?: {
72
+ junctionColumn: AnySQLiteColumn;
73
+ column: AnySQLiteColumn;
74
+ };
75
+ /** When false, one relation is required (type-level: T not T | null) */
76
+ optional?: boolean;
77
+ /** Alias for the relation (e.g. for self-referential disambiguation) */
78
+ alias?: string;
79
+ /** Predefined filter for many relations: (alias) => Condition. Receives the joined table alias. */
80
+ where?: (alias: string) => unknown;
61
81
  }
62
82
 
63
83
  type Condition = Expression<SqlBool>;
@@ -233,6 +253,8 @@ type RelationsBuilder = {
233
253
  one: <U extends AnyTable>(table: U, config?: {
234
254
  fields: AnySQLiteColumn[];
235
255
  references: AnySQLiteColumn[];
256
+ optional?: boolean;
257
+ alias?: string;
236
258
  }) => OneRelation<U>;
237
259
  many: <U extends AnyTable>(table: U) => ManyRelation<U>;
238
260
  };
@@ -361,14 +383,53 @@ declare class OneRelation<T extends AnyTable = AnyTable> extends Relation<T> {
361
383
  config?: {
362
384
  fields: AnySQLiteColumn[];
363
385
  references: AnySQLiteColumn[];
386
+ optional?: boolean;
387
+ alias?: string;
364
388
  } | undefined;
365
389
  constructor(foreignTable: T, config?: {
366
390
  fields: AnySQLiteColumn[];
367
391
  references: AnySQLiteColumn[];
392
+ optional?: boolean;
393
+ alias?: string;
368
394
  } | undefined);
369
395
  }
370
396
  declare class ManyRelation<T extends AnyTable = AnyTable> extends Relation<T> {
371
- constructor(foreignTable: T);
397
+ config?: {
398
+ from?: AnySQLiteColumn[];
399
+ to?: AnySQLiteColumn[];
400
+ through?: {
401
+ junctionTable: AnyTable;
402
+ fromRef: {
403
+ column: AnySQLiteColumn;
404
+ junctionColumn: AnySQLiteColumn;
405
+ };
406
+ toRef: {
407
+ column: AnySQLiteColumn;
408
+ junctionColumn: AnySQLiteColumn;
409
+ };
410
+ };
411
+ optional?: boolean;
412
+ alias?: string;
413
+ where?: (alias: string) => unknown;
414
+ } | undefined;
415
+ constructor(foreignTable: T, config?: {
416
+ from?: AnySQLiteColumn[];
417
+ to?: AnySQLiteColumn[];
418
+ through?: {
419
+ junctionTable: AnyTable;
420
+ fromRef: {
421
+ column: AnySQLiteColumn;
422
+ junctionColumn: AnySQLiteColumn;
423
+ };
424
+ toRef: {
425
+ column: AnySQLiteColumn;
426
+ junctionColumn: AnySQLiteColumn;
427
+ };
428
+ };
429
+ optional?: boolean;
430
+ alias?: string;
431
+ where?: (alias: string) => unknown;
432
+ } | undefined);
372
433
  }
373
434
  declare const relations: <T extends AnyTable, R extends Record<string, Relation>>(table: T, relationsCallback: (helpers: RelationsBuilder) => R) => R;
374
435
  declare const getTableColumns: <T extends AnyTable>(table: T) => Record<string, AnySQLiteColumn>;
@@ -411,9 +472,18 @@ declare const enumType: <TName extends string, TValues extends readonly [string,
411
472
 
412
473
  /** Maps relations() return type to typed relation configs with foreign table preserved */
413
474
  type InferRelationsMap<R extends Record<string, OneRelation | ManyRelation>> = {
414
- [K in keyof R]: R[K] extends OneRelation<infer T> ? {
475
+ [K in keyof R]: R[K] extends OneRelation<infer T> ? R[K] extends {
476
+ config?: {
477
+ optional?: infer O;
478
+ };
479
+ } ? {
480
+ type: 'one';
481
+ foreignTable: T;
482
+ optional: O;
483
+ } : {
415
484
  type: 'one';
416
485
  foreignTable: T;
486
+ optional?: true;
417
487
  } : R[K] extends ManyRelation<infer T> ? {
418
488
  type: 'many';
419
489
  foreignTable: T;
@@ -440,7 +510,8 @@ type InferRelationFields<TTable extends AnyTable, TRelationsMap extends Record<s
440
510
  [K in keyof TWith & keyof TRelationsMap]: TWith[K] extends false | undefined ? never : TRelationsMap[K] extends {
441
511
  type: 'one';
442
512
  foreignTable: infer T;
443
- } ? T extends AnyTable ? InferSelectModel<T> & InferNestedFields<T, TWith[K], TAllRelations> : never : TRelationsMap[K] extends {
513
+ optional?: infer O;
514
+ } ? T extends AnyTable ? ([O] extends [false] ? InferSelectModel<T> & InferNestedFields<T, TWith[K], TAllRelations> : (InferSelectModel<T> & InferNestedFields<T, TWith[K], TAllRelations>) | null) : never : TRelationsMap[K] extends {
444
515
  type: 'many';
445
516
  foreignTable: infer T;
446
517
  } ? T extends AnyTable ? (InferSelectModel<T> & InferNestedFields<T, TWith[K], TAllRelations>)[] : never : never;
@@ -484,6 +555,81 @@ type InferRelationalSelectModel<TTable extends AnyTable, TRelations extends Reco
484
555
  [K in TTable['_']['name']]: TRelations;
485
556
  }> = InferSelectModel<TTable> & InferRelationFields<TTable, InferRelationsMap<TRelations>, TAllRelations, TWith>;
486
557
 
558
+ /**
559
+ * Relations v2 API - Drizzle-style defineRelations with from/to and many-without-one.
560
+ * Use defineRelations() for a single place to define all relations, or defineRelationsPart() to split into parts.
561
+ */
562
+
563
+ /** Options for one() relation - from/to replace fields/references */
564
+ interface OneRelationOptions {
565
+ from: AnySQLiteColumn | AnySQLiteColumn[];
566
+ to: AnySQLiteColumn | AnySQLiteColumn[];
567
+ optional?: boolean;
568
+ alias?: string;
569
+ }
570
+ /** Reference for through() - column with junction column for many-to-many */
571
+ interface ThroughRef {
572
+ column: AnySQLiteColumn;
573
+ junctionColumn: AnySQLiteColumn;
574
+ junctionTable: AnyTable;
575
+ }
576
+ /** Create a through reference for many-to-many: through(column, junctionColumn, junctionTable) */
577
+ declare function through(column: AnySQLiteColumn, junctionColumn: AnySQLiteColumn, junctionTable: AnyTable): ThroughRef;
578
+ /** Options for many() relation - optional explicit from/to for many-without-one, or through() for many-to-many */
579
+ interface ManyRelationOptions {
580
+ from?: AnySQLiteColumn | AnySQLiteColumn[] | ThroughRef;
581
+ to?: AnySQLiteColumn | AnySQLiteColumn[] | ThroughRef;
582
+ optional?: boolean;
583
+ alias?: string;
584
+ /** Predefined filter: (alias) => Condition. Applied to the joined relation table. */
585
+ where?: (alias: string) => Condition;
586
+ }
587
+ /** Extract table keys from schema (values that extend AnyTable) */
588
+ type ExtractTableKeys<T> = {
589
+ [K in keyof T]: T[K] extends AnyTable ? K : never;
590
+ }[keyof T];
591
+ type ExtractTables<T extends Record<string, unknown>> = Pick<T, Extract<ExtractTableKeys<T>, keyof T>> extends infer R ? R extends Record<string, AnyTable> ? R : Record<string, AnyTable> : Record<string, AnyTable>;
592
+ /** Build the r object passed to defineRelations callback - all properties required to avoid TS2722 */
593
+ type BuildR<Tables extends Record<string, AnyTable>> = {
594
+ [K in keyof Tables]: Tables[K]['_']['columns'];
595
+ } & {
596
+ one: {
597
+ [K in keyof Tables]: (opts: OneRelationOptions) => OneRelation<Tables[K]>;
598
+ };
599
+ many: {
600
+ [K in keyof Tables]: (opts?: ManyRelationOptions) => ManyRelation<Tables[K]>;
601
+ };
602
+ };
603
+ type DefineRelationsCallback<Tables extends Record<string, AnyTable>> = (r: BuildR<Tables>) => {
604
+ [K in keyof Tables]?: Record<string, OneRelation | ManyRelation>;
605
+ };
606
+ /**
607
+ * Define all relations for your schema in one place (v2 API).
608
+ * Uses from/to instead of fields/references, and supports many-without-one.
609
+ *
610
+ * @example
611
+ * ```ts
612
+ * import * as schema from './schema'
613
+ * import { defineRelations } from '@type32/tauri-sqlite-orm'
614
+ *
615
+ * export const relations = defineRelations(schema, (r) => ({
616
+ * users: {
617
+ * posts: r.many.posts({ from: r.users.id, to: r.posts.userId }),
618
+ * },
619
+ * posts: {
620
+ * user: r.one.users({ from: r.posts.userId, to: r.users.id }),
621
+ * postTags: r.many.postTags({ from: r.posts.id, to: r.postTags.postId }),
622
+ * },
623
+ * }))
624
+ * ```
625
+ */
626
+ declare function defineRelations<TSchema extends Record<string, unknown>>(schema: TSchema, callback: DefineRelationsCallback<ExtractTables<TSchema>>): Record<string, Record<string, OneRelation | ManyRelation>>;
627
+ /**
628
+ * Define a part of relations - merge multiple parts when passing to TauriORM.
629
+ * Useful for splitting large schema definitions.
630
+ */
631
+ declare function defineRelationsPart<TSchema extends Record<string, unknown>>(schema: TSchema, callback: DefineRelationsCallback<Record<string, AnyTable>>): Record<string, Record<string, OneRelation | ManyRelation>>;
632
+
487
633
  declare class TauriORMError extends Error {
488
634
  constructor(message: string);
489
635
  }
@@ -515,4 +661,4 @@ declare class TableNotFoundError extends TauriORMError {
515
661
  constructor(tableName: string);
516
662
  }
517
663
 
518
- export { type AnySQLiteColumn, type AnyTable, type ColumnDataType, ColumnNotFoundError, type ColumnOptions, type ColumnValueTypes, type Condition, type DatabaseLike, DeleteQueryBuilder, type ExtractColumnType, type InferInsertModel, type InferRelationalSelectModel, type InferRelationsMap, type InferSelectModel, InsertQueryBuilder, InsertValidationError, ManyRelation, MigrationError, MissingWhereClauseError, type Mode, OneRelation, QueryBuilderError, Relation, type RelationConfig, RelationError, type RelationType, type RelationsBuilder, type SQLAggregate, type SQLCondition, type SQLSubquery, SQLiteColumn, SelectQueryBuilder, Table, TableNotFoundError, TauriDialect, TauriORM, TauriORMError, UpdateQueryBuilder, UpdateValidationError, ValidationError, WithQueryBuilder, alias, and, as, asc, avg, between, blob, boolean, contains, count, countDistinct, desc, endsWith, enumType, eq, eqSubquery, exists, getTableColumns, groupConcat, gt, gtSubquery, gte, gteSubquery, ilike, inArray, integer, isNotNull, isNull, like, lt, ltSubquery, lte, lteSubquery, max, min, ne, neSubquery, not, notExists, notIn, numeric, or, real, relations, scalarSubquery, sqliteTable, startsWith, subquery, sum, text };
664
+ export { type AnySQLiteColumn, type AnyTable, type BuildR, type ColumnDataType, ColumnNotFoundError, type ColumnOptions, type ColumnValueTypes, type Condition, type DatabaseLike, type DefineRelationsCallback, DeleteQueryBuilder, type ExtractColumnType, type InferInsertModel, type InferRelationalSelectModel, type InferRelationsMap, type InferSelectModel, InsertQueryBuilder, InsertValidationError, ManyRelation, type ManyRelationOptions, MigrationError, MissingWhereClauseError, type Mode, OneRelation, type OneRelationOptions, QueryBuilderError, Relation, type RelationConfig, RelationError, type RelationType, type RelationsBuilder, type SQLAggregate, type SQLCondition, type SQLSubquery, SQLiteColumn, SelectQueryBuilder, Table, TableNotFoundError, TauriDialect, TauriORM, TauriORMError, type ThroughRef, UpdateQueryBuilder, UpdateValidationError, ValidationError, WithQueryBuilder, alias, and, as, asc, avg, between, blob, boolean, contains, count, countDistinct, defineRelations, defineRelationsPart, desc, endsWith, enumType, eq, eqSubquery, exists, getTableColumns, groupConcat, gt, gtSubquery, gte, gteSubquery, ilike, inArray, integer, isNotNull, isNull, like, lt, ltSubquery, lte, lteSubquery, max, min, ne, neSubquery, not, notExists, notIn, numeric, or, real, relations, scalarSubquery, sqliteTable, startsWith, subquery, sum, text, through };