@type32/tauri-sqlite-orm 0.3.0 → 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 (41) 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 +11 -6
  26. package/dist/index.d.ts +11 -6
  27. package/dist/operators.d.ts +30 -0
  28. package/dist/operators.js +84 -0
  29. package/dist/orm.d.ts +180 -0
  30. package/dist/orm.js +556 -0
  31. package/dist/relational-types.d.ts +87 -0
  32. package/dist/relational-types.js +1 -0
  33. package/dist/relations-v2.d.ts +77 -0
  34. package/dist/relations-v2.js +157 -0
  35. package/dist/serialization.d.ts +14 -0
  36. package/dist/serialization.js +135 -0
  37. package/dist/subquery.d.ts +5 -0
  38. package/dist/subquery.js +6 -0
  39. package/dist/types.d.ts +58 -0
  40. package/dist/types.js +1 -0
  41. package/package.json +2 -1
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Relations v2 API - Drizzle-style defineRelations with from/to and many-without-one.
3
+ * Use defineRelations() for a single place to define all relations, or defineRelationsPart() to split into parts.
4
+ */
5
+ import { AnySQLiteColumn, AnyTable } from './types';
6
+ import { OneRelation, ManyRelation } from './orm';
7
+ import type { Condition } from './operators';
8
+ /** Options for one() relation - from/to replace fields/references */
9
+ export interface OneRelationOptions {
10
+ from: AnySQLiteColumn | AnySQLiteColumn[];
11
+ to: AnySQLiteColumn | AnySQLiteColumn[];
12
+ optional?: boolean;
13
+ alias?: string;
14
+ }
15
+ /** Reference for through() - column with junction column for many-to-many */
16
+ export interface ThroughRef {
17
+ column: AnySQLiteColumn;
18
+ junctionColumn: AnySQLiteColumn;
19
+ junctionTable: AnyTable;
20
+ }
21
+ /** Create a through reference for many-to-many: through(column, junctionColumn, junctionTable) */
22
+ export declare function through(column: AnySQLiteColumn, junctionColumn: AnySQLiteColumn, junctionTable: AnyTable): ThroughRef;
23
+ /** Options for many() relation - optional explicit from/to for many-without-one, or through() for many-to-many */
24
+ export interface ManyRelationOptions {
25
+ from?: AnySQLiteColumn | AnySQLiteColumn[] | ThroughRef;
26
+ to?: AnySQLiteColumn | AnySQLiteColumn[] | ThroughRef;
27
+ optional?: boolean;
28
+ alias?: string;
29
+ /** Predefined filter: (alias) => Condition. Applied to the joined relation table. */
30
+ where?: (alias: string) => Condition;
31
+ }
32
+ /** Extract table keys from schema (values that extend AnyTable) */
33
+ type ExtractTableKeys<T> = {
34
+ [K in keyof T]: T[K] extends AnyTable ? K : never;
35
+ }[keyof T];
36
+ 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>;
37
+ /** Build the r object passed to defineRelations callback - all properties required to avoid TS2722 */
38
+ export type BuildR<Tables extends Record<string, AnyTable>> = {
39
+ [K in keyof Tables]: Tables[K]['_']['columns'];
40
+ } & {
41
+ one: {
42
+ [K in keyof Tables]: (opts: OneRelationOptions) => OneRelation<Tables[K]>;
43
+ };
44
+ many: {
45
+ [K in keyof Tables]: (opts?: ManyRelationOptions) => ManyRelation<Tables[K]>;
46
+ };
47
+ };
48
+ export type DefineRelationsCallback<Tables extends Record<string, AnyTable>> = (r: BuildR<Tables>) => {
49
+ [K in keyof Tables]?: Record<string, OneRelation | ManyRelation>;
50
+ };
51
+ /**
52
+ * Define all relations for your schema in one place (v2 API).
53
+ * Uses from/to instead of fields/references, and supports many-without-one.
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * import * as schema from './schema'
58
+ * import { defineRelations } from '@type32/tauri-sqlite-orm'
59
+ *
60
+ * export const relations = defineRelations(schema, (r) => ({
61
+ * users: {
62
+ * posts: r.many.posts({ from: r.users.id, to: r.posts.userId }),
63
+ * },
64
+ * posts: {
65
+ * user: r.one.users({ from: r.posts.userId, to: r.users.id }),
66
+ * postTags: r.many.postTags({ from: r.posts.id, to: r.postTags.postId }),
67
+ * },
68
+ * }))
69
+ * ```
70
+ */
71
+ export declare function defineRelations<TSchema extends Record<string, unknown>>(schema: TSchema, callback: DefineRelationsCallback<ExtractTables<TSchema>>): Record<string, Record<string, OneRelation | ManyRelation>>;
72
+ /**
73
+ * Define a part of relations - merge multiple parts when passing to TauriORM.
74
+ * Useful for splitting large schema definitions.
75
+ */
76
+ export declare function defineRelationsPart<TSchema extends Record<string, unknown>>(schema: TSchema, callback: DefineRelationsCallback<Record<string, AnyTable>>): Record<string, Record<string, OneRelation | ManyRelation>>;
77
+ export {};
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Relations v2 API - Drizzle-style defineRelations with from/to and many-without-one.
3
+ * Use defineRelations() for a single place to define all relations, or defineRelationsPart() to split into parts.
4
+ */
5
+ import { OneRelation, ManyRelation } from './orm';
6
+ /** Extract tables from a schema object (filters to Table instances with _ and relations) */
7
+ function extractTables(schema) {
8
+ const tables = {};
9
+ for (const [key, value] of Object.entries(schema)) {
10
+ const v = value;
11
+ if (v && typeof v === 'object' && v._?.name && v._?.columns && typeof v.relations === 'object') {
12
+ tables[key] = v;
13
+ }
14
+ }
15
+ return tables;
16
+ }
17
+ /** Create a through reference for many-to-many: through(column, junctionColumn, junctionTable) */
18
+ export function through(column, junctionColumn, junctionTable) {
19
+ return { column, junctionColumn, junctionTable };
20
+ }
21
+ /** Normalize column(s) to array */
22
+ function toArray(col) {
23
+ return Array.isArray(col) ? col : [col];
24
+ }
25
+ /** Check if value is ThroughRef */
26
+ function isThroughRef(v) {
27
+ return v && typeof v === 'object' && 'column' in v && 'junctionColumn' in v && 'junctionTable' in v;
28
+ }
29
+ /** Build table column references for use in from/to - r.users.id etc. */
30
+ function buildTableRef(table) {
31
+ return { ...table._.columns };
32
+ }
33
+ function buildR(tables) {
34
+ const tableRefs = {};
35
+ const oneFns = {};
36
+ const manyFns = {};
37
+ for (const [tableKey, table] of Object.entries(tables)) {
38
+ tableRefs[tableKey] = buildTableRef(table);
39
+ const foreignTable = table;
40
+ oneFns[tableKey] = (opts) => {
41
+ return new OneRelation(foreignTable, {
42
+ fields: toArray(opts.from),
43
+ references: toArray(opts.to),
44
+ optional: opts.optional,
45
+ alias: opts.alias,
46
+ });
47
+ };
48
+ manyFns[tableKey] = (opts) => {
49
+ if (!opts?.from || !opts?.to)
50
+ return new ManyRelation(foreignTable);
51
+ if (isThroughRef(opts.from) && isThroughRef(opts.to)) {
52
+ return new ManyRelation(foreignTable, {
53
+ through: {
54
+ junctionTable: opts.from.junctionTable,
55
+ fromRef: { column: opts.from.column, junctionColumn: opts.from.junctionColumn },
56
+ toRef: { column: opts.to.column, junctionColumn: opts.to.junctionColumn },
57
+ },
58
+ optional: opts.optional,
59
+ alias: opts.alias,
60
+ where: opts.where,
61
+ });
62
+ }
63
+ return new ManyRelation(foreignTable, {
64
+ from: toArray(opts.from),
65
+ to: toArray(opts.to),
66
+ optional: opts.optional,
67
+ alias: opts.alias,
68
+ where: opts.where,
69
+ });
70
+ };
71
+ }
72
+ return {
73
+ ...tableRefs,
74
+ one: oneFns,
75
+ many: manyFns,
76
+ };
77
+ }
78
+ /** Apply relations from defineRelations result to tables */
79
+ function applyRelationsToTables(tables, relationsResult) {
80
+ for (const [tableKey, rels] of Object.entries(relationsResult)) {
81
+ const table = tables[tableKey];
82
+ if (!table)
83
+ continue;
84
+ for (const [relName, relation] of Object.entries(rels)) {
85
+ if (relation instanceof OneRelation) {
86
+ table.relations[relName] = {
87
+ type: 'one',
88
+ foreignTable: relation.foreignTable,
89
+ fields: relation.config?.fields,
90
+ references: relation.config?.references,
91
+ optional: relation.config?.optional,
92
+ alias: relation.config?.alias,
93
+ };
94
+ }
95
+ else if (relation instanceof ManyRelation) {
96
+ const config = relation.config;
97
+ if (config?.through) {
98
+ table.relations[relName] = {
99
+ type: 'many',
100
+ foreignTable: relation.foreignTable,
101
+ junctionTable: config.through.junctionTable,
102
+ fromJunction: config.through.fromRef,
103
+ toJunction: config.through.toRef,
104
+ optional: config.optional,
105
+ alias: config.alias,
106
+ where: config.where,
107
+ };
108
+ }
109
+ else {
110
+ table.relations[relName] = {
111
+ type: 'many',
112
+ foreignTable: relation.foreignTable,
113
+ fields: config ? config.to : undefined,
114
+ references: config ? config.from : undefined,
115
+ optional: config?.optional,
116
+ alias: config?.alias,
117
+ where: config?.where,
118
+ };
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * Define all relations for your schema in one place (v2 API).
126
+ * Uses from/to instead of fields/references, and supports many-without-one.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * import * as schema from './schema'
131
+ * import { defineRelations } from '@type32/tauri-sqlite-orm'
132
+ *
133
+ * export const relations = defineRelations(schema, (r) => ({
134
+ * users: {
135
+ * posts: r.many.posts({ from: r.users.id, to: r.posts.userId }),
136
+ * },
137
+ * posts: {
138
+ * user: r.one.users({ from: r.posts.userId, to: r.users.id }),
139
+ * postTags: r.many.postTags({ from: r.posts.id, to: r.postTags.postId }),
140
+ * },
141
+ * }))
142
+ * ```
143
+ */
144
+ export function defineRelations(schema, callback) {
145
+ const tables = extractTables(schema);
146
+ const r = buildR(tables);
147
+ const result = callback(r);
148
+ applyRelationsToTables(tables, result);
149
+ return result;
150
+ }
151
+ /**
152
+ * Define a part of relations - merge multiple parts when passing to TauriORM.
153
+ * Useful for splitting large schema definitions.
154
+ */
155
+ export function defineRelationsPart(schema, callback) {
156
+ return defineRelations(schema, callback);
157
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Serialization and Deserialization utilities for SQLite type conversion
3
+ */
4
+ import { AnySQLiteColumn } from './types';
5
+ /**
6
+ * Serialize JavaScript value to SQLite-compatible format
7
+ * Used for INSERT and UPDATE operations
8
+ */
9
+ export declare function serializeValue(value: any, column: AnySQLiteColumn): any;
10
+ /**
11
+ * Deserialize SQLite value to proper TypeScript type
12
+ * Used for SELECT operations
13
+ */
14
+ export declare function deserializeValue(value: any, column: AnySQLiteColumn): any;
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Serialization and Deserialization utilities for SQLite type conversion
3
+ */
4
+ /**
5
+ * Serialize JavaScript value to SQLite-compatible format
6
+ * Used for INSERT and UPDATE operations
7
+ */
8
+ export function serializeValue(value, column) {
9
+ // Handle null/undefined
10
+ if (value === null || value === undefined) {
11
+ return null;
12
+ }
13
+ const { dataType, mode } = column._;
14
+ // TEXT type
15
+ if (dataType === 'TEXT') {
16
+ if (mode === 'json') {
17
+ return typeof value === 'string' ? value : JSON.stringify(value);
18
+ }
19
+ return String(value);
20
+ }
21
+ // INTEGER type
22
+ if (dataType === 'INTEGER') {
23
+ if (mode === 'timestamp' || mode === 'timestamp_ms') {
24
+ // Convert Date to timestamp
25
+ if (value instanceof Date) {
26
+ return mode === 'timestamp_ms' ? value.getTime() : Math.floor(value.getTime() / 1000);
27
+ }
28
+ // If already a number, pass through
29
+ return typeof value === 'number' ? value : parseInt(String(value), 10);
30
+ }
31
+ if (mode === 'boolean') {
32
+ // Convert boolean to 0/1
33
+ return value ? 1 : 0;
34
+ }
35
+ // Regular integer
36
+ return typeof value === 'number' ? Math.floor(value) : parseInt(String(value), 10);
37
+ }
38
+ // REAL type
39
+ if (dataType === 'REAL') {
40
+ return typeof value === 'number' ? value : parseFloat(String(value));
41
+ }
42
+ // BOOLEAN type
43
+ if (dataType === 'BOOLEAN') {
44
+ return value ? 1 : 0;
45
+ }
46
+ // BLOB type
47
+ if (dataType === 'BLOB') {
48
+ if (mode === 'json') {
49
+ return typeof value === 'string' ? value : JSON.stringify(value);
50
+ }
51
+ if (mode === 'bigint') {
52
+ return String(value); // SQLite stores bigint as string
53
+ }
54
+ return value;
55
+ }
56
+ // NUMERIC type
57
+ if (dataType === 'NUMERIC') {
58
+ if (mode === 'bigint') {
59
+ return String(value); // SQLite stores bigint as string
60
+ }
61
+ return typeof value === 'number' ? value : parseFloat(String(value));
62
+ }
63
+ return value;
64
+ }
65
+ /**
66
+ * Deserialize SQLite value to proper TypeScript type
67
+ * Used for SELECT operations
68
+ */
69
+ export function deserializeValue(value, column) {
70
+ // Handle null/undefined
71
+ if (value === null || value === undefined) {
72
+ return null;
73
+ }
74
+ const { dataType, mode } = column._;
75
+ // TEXT type
76
+ if (dataType === 'TEXT') {
77
+ if (mode === 'json') {
78
+ try {
79
+ return JSON.parse(value);
80
+ }
81
+ catch {
82
+ return value;
83
+ }
84
+ }
85
+ return value; // Already string
86
+ }
87
+ // INTEGER type
88
+ if (dataType === 'INTEGER') {
89
+ if (mode === 'timestamp' || mode === 'timestamp_ms') {
90
+ // SQLite stores timestamps as numbers, convert to Date
91
+ const num = typeof value === 'string' ? parseInt(value, 10) : value;
92
+ if (isNaN(num))
93
+ return null;
94
+ return mode === 'timestamp_ms' ? new Date(num) : new Date(num * 1000);
95
+ }
96
+ if (mode === 'boolean') {
97
+ // SQLite stores booleans as 0/1
98
+ return value === 1 || value === '1' || value === true;
99
+ }
100
+ // Regular integer
101
+ return typeof value === 'string' ? parseInt(value, 10) : value;
102
+ }
103
+ // REAL type
104
+ if (dataType === 'REAL') {
105
+ return typeof value === 'string' ? parseFloat(value) : value;
106
+ }
107
+ // BOOLEAN type
108
+ if (dataType === 'BOOLEAN') {
109
+ return value === 1 || value === '1' || value === true;
110
+ }
111
+ // BLOB type
112
+ if (dataType === 'BLOB') {
113
+ if (mode === 'json') {
114
+ try {
115
+ return JSON.parse(value);
116
+ }
117
+ catch {
118
+ return value;
119
+ }
120
+ }
121
+ if (mode === 'bigint') {
122
+ return typeof value === 'string' ? BigInt(value) : BigInt(value);
123
+ }
124
+ // Assume Uint8Array or similar
125
+ return value;
126
+ }
127
+ // NUMERIC type
128
+ if (dataType === 'NUMERIC') {
129
+ if (mode === 'bigint') {
130
+ return typeof value === 'string' ? BigInt(value) : BigInt(value);
131
+ }
132
+ return typeof value === 'string' ? parseFloat(value) : value;
133
+ }
134
+ return value;
135
+ }
@@ -0,0 +1,5 @@
1
+ import { Expression } from 'kysely';
2
+ import { SelectQueryBuilder } from './builders/select';
3
+ import { AnyTable } from './types';
4
+ export declare const subquery: <T extends AnyTable>(query: SelectQueryBuilder<T, any>) => Expression<any>;
5
+ export declare const scalarSubquery: <T extends AnyTable>(query: SelectQueryBuilder<T, any>) => Expression<any>;
@@ -0,0 +1,6 @@
1
+ export const subquery = (query) => {
2
+ return query.toKyselyExpression();
3
+ };
4
+ export const scalarSubquery = (query) => {
5
+ return query.toKyselyExpression();
6
+ };
@@ -0,0 +1,58 @@
1
+ import { SQLiteColumn, Table } from './orm';
2
+ export type ColumnDataType = 'TEXT' | 'INTEGER' | 'REAL' | 'BLOB' | 'BOOLEAN' | 'NUMERIC';
3
+ export type Mode = 'default' | 'timestamp' | 'timestamp_ms' | 'json' | 'boolean' | 'bigint';
4
+ export type ColumnValueTypes<TType extends ColumnDataType, TMode extends Mode> = TType extends 'TEXT' ? TMode extends 'json' ? unknown : string : TType extends 'INTEGER' ? TMode extends 'timestamp' | 'timestamp_ms' ? Date : TMode extends 'boolean' ? boolean : number : TType extends 'REAL' ? number : TType extends 'BOOLEAN' ? boolean : TType extends 'BLOB' ? TMode extends 'json' ? unknown : TMode extends 'bigint' ? bigint : Uint8Array : TType extends 'NUMERIC' ? TMode extends 'bigint' ? bigint : number : never;
5
+ export interface ColumnOptions<TData, TEnum extends readonly string[] = readonly string[]> {
6
+ notNull?: boolean;
7
+ default?: TData;
8
+ $defaultFn?: () => TData;
9
+ primaryKey?: boolean;
10
+ autoincrement?: boolean;
11
+ unique?: boolean;
12
+ references?: {
13
+ table: AnyTable;
14
+ column: AnySQLiteColumn;
15
+ onDelete?: 'cascade' | 'set null' | 'set default' | 'restrict' | 'no action';
16
+ onUpdate?: 'cascade' | 'set null' | 'set default' | 'restrict' | 'no action';
17
+ };
18
+ mode?: Mode;
19
+ $onUpdateFn?: () => TData;
20
+ enum?: TEnum;
21
+ }
22
+ export type ExtractColumnType<T extends AnySQLiteColumn> = T extends SQLiteColumn<infer _, infer TType, infer TMode, infer TNotNull, infer THasDefault, infer TAutoincrement, infer TEnum, infer TCustomType> ? [
23
+ TCustomType
24
+ ] extends [never] ? [
25
+ TEnum
26
+ ] extends [never] ? TNotNull extends true ? ColumnValueTypes<TType, TMode> : ColumnValueTypes<TType, TMode> | null : TNotNull extends true ? TEnum[number] : TEnum[number] | null : TNotNull extends true ? TCustomType : TCustomType | null : never;
27
+ export type AnySQLiteColumn = SQLiteColumn<any, any, any, any, any, any, any, any>;
28
+ export type AnyTable = Table<Record<string, AnySQLiteColumn>, string>;
29
+ export type InferSelectModel<T extends AnyTable> = {
30
+ [K in keyof T['_']['columns']]: ExtractColumnType<T['_']['columns'][K]>;
31
+ };
32
+ export type RelationType = 'one' | 'many';
33
+ export interface RelationConfig {
34
+ type: RelationType;
35
+ foreignTable: AnyTable;
36
+ /** For one: local FK columns. For many (v2): foreign table's FK columns. */
37
+ fields?: AnySQLiteColumn[];
38
+ /** For one: foreign PK columns. For many (v2): parent table's PK columns. */
39
+ references?: AnySQLiteColumn[];
40
+ /** For many-to-many via through(): junction table between parent and foreign */
41
+ junctionTable?: AnyTable;
42
+ /** Parent column -> junction column (parent PK = junction FK to parent) */
43
+ fromJunction?: {
44
+ column: AnySQLiteColumn;
45
+ junctionColumn: AnySQLiteColumn;
46
+ };
47
+ /** Junction column -> foreign column (junction FK to foreign = foreign PK) */
48
+ toJunction?: {
49
+ junctionColumn: AnySQLiteColumn;
50
+ column: AnySQLiteColumn;
51
+ };
52
+ /** When false, one relation is required (type-level: T not T | null) */
53
+ optional?: boolean;
54
+ /** Alias for the relation (e.g. for self-referential disambiguation) */
55
+ alias?: string;
56
+ /** Predefined filter for many relations: (alias) => Condition. Receives the joined table alias. */
57
+ where?: (alias: string) => unknown;
58
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@type32/tauri-sqlite-orm",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "A Drizzle-like ORM for Tauri v2's SQL JS API plugin.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -19,6 +19,7 @@
19
19
  "dev": "tsup src/index.ts --format esm,cjs --dts --watch",
20
20
  "test": "bun test",
21
21
  "release:minor": "bun run build && bun pm version minor && bun publish",
22
+ "release:prerelease": "bun run build && bun pm version minor && bun publish",
22
23
  "release:patch": "bun run build && bun pm version patch && bun publish"
23
24
  },
24
25
  "keywords": [