Skip to content

Commit 09ffcf5

Browse files
committed
feat(builder)!: improve type and define column util
- improve some internal types and provide FilterGenerated util - defineColumn->defineObject/defineString - reduce trace log when diff table
1 parent 6956427 commit 09ffcf5

File tree

5 files changed

+91
-66
lines changed

5 files changed

+91
-66
lines changed

packages/sqlite-builder/src/sync/core.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type Kysely, sql } from 'kysely'
22
import type { DBLogger } from '../types'
3-
import type { Columns, Table, Tables } from './types'
3+
import type { Columns, Schema, Table } from './types'
44
import {
55
parseColumnType,
66
runCreateTable,
@@ -11,7 +11,7 @@ import {
1111
} from './run'
1212
import { type ParsedCreateTableSQL, parseExistDB } from './parseExist'
1313

14-
export type SyncOptions<T extends Tables> = {
14+
export type SyncOptions<T extends Schema> = {
1515
/**
1616
* whether to enable logger
1717
*/
@@ -26,7 +26,7 @@ export type SyncOptions<T extends Tables> = {
2626
reserveOldData?: boolean
2727
}
2828

29-
export async function syncTables<T extends Tables>(
29+
export async function syncTables<T extends Schema>(
3030
db: Kysely<any>,
3131
targetTables: T,
3232
options: SyncOptions<T> = {},
@@ -84,8 +84,6 @@ export async function syncTables<T extends Tables>(
8484
}
8585
const { index, ...props } = targetColumns
8686

87-
const traceMessages: string[] = []
88-
8987
const restoreColumnList = getRestoreColumnList(existColumns.columns, targetColumns.columns)
9088

9189
// if all columns are in same table structure, skip
@@ -100,47 +98,35 @@ export async function syncTables<T extends Tables>(
10098
// 1. copy struct to temporary table
10199
// @ts-expect-error existColumns.columns has parsed column type
102100
await runCreateTable(trx, tempTableName, existColumns, true)
103-
traceMessages.push(`copy table ${tableName} structure to ${tempTableName}`)
104101

105102
// 2. copy all data to temporary table
106103
await trx.insertInto(tempTableName)
107104
.expression(eb => eb.selectFrom(tableName).selectAll())
108105
.execute()
109-
traceMessages.push(`copy table ${tableName} all data to ${tempTableName}`)
110106

111107
// 3. remove exist table
112108
await runDropTable(trx, tableName)
113-
traceMessages.push(`remove table ${tableName}`)
114109

115110
// 4. create target table
116111
const _triggerOptions = await runCreateTable(trx, tableName, props)
117-
traceMessages.push(`create table ${tableName}`)
118112

119113
// 5. diff and restore data from temporary table to target table
120114
if (restoreColumnList.length) {
121115
await trx.insertInto(tableName)
122116
.columns(restoreColumnList)
123117
.expression(eb => eb.selectFrom(tempTableName).select(restoreColumnList))
124118
.execute()
125-
traceMessages.push(`copy columns: ${JSON.stringify(restoreColumnList)}`)
126119
}
127120

128121
// 6. add indexes and triggers
129122
await runCreateTableIndex(trx, tableName, index)
130123
await runCreateTimeTrigger(trx, tableName, _triggerOptions)
131-
traceMessages.push(`add index and trigger for ${tableName}`)
132124

133125
// 7. if not reserve old data, remove temporary table
134-
if (!reserveOldData) {
135-
await runDropTable(trx, tempTableName)
136-
traceMessages.push(`remove temporary table ${tempTableName}`)
137-
}
126+
!reserveOldData && await runDropTable(trx, tempTableName)
138127
})
139-
.then(() => logger?.trace?.(traceMessages.join('\n')))
140-
.catch((e) => {
141-
logger?.trace?.(traceMessages.join('\n'))
142-
logger?.error(`fail to sync ${tableName}`, e)
143-
})
128+
.then(() => logger?.debug(`restore columns: ${restoreColumnList}`))
129+
.catch(e => logger?.error(`fail to sync ${tableName}`, e))
144130
}
145131
}
146132

packages/sqlite-builder/src/sync/define.ts

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { RawBuilder } from 'kysely'
22
import type {
33
Columns,
44
ColumnsWithErrorInfo,
5+
IsNotNull,
56
Table,
67
TableProperty,
78
TimeTriggerOptions,
@@ -48,25 +49,54 @@ export function defineTable<
4849
}
4950

5051
/**
51-
* explicitly declare column type
52+
* explicitly declare object column type
5253
*
53-
* **if have error, please make sure
54-
* the first generic type is set**
5554
* @example
5655
* ```ts
5756
* const pet = defineTable({
58-
* owner: defineColumn<{ name: string }, true>({
59-
* type: 'object',
60-
* defaultTo: { name: 'owner' },
61-
* notNull: true
62-
* }),
57+
* // NotNull is optional
58+
* owner: defineColumn<{ name: string }>().NotNull(),
6359
* }
6460
* ```
6561
*/
66-
export function defineColumn<T, NotNull extends true | null = null>(prop: {
67-
type: T extends string ? 'string' : 'object'
68-
defaultTo?: T | RawBuilder<unknown> | null
69-
notNull?: NotNull
70-
}) {
71-
return prop
72-
}
62+
export function defineObject<T extends object>(defaultTo?: T | RawBuilder<unknown> | null) {
63+
const base = {
64+
type: 'object',
65+
defaultTo: defaultTo as IsNotNull<typeof defaultTo> extends true ? T : T | null,
66+
} as const
67+
return {
68+
...base,
69+
NotNull() {
70+
return {
71+
...base,
72+
notNull: true,
73+
} as const
74+
},
75+
}
76+
}
77+
/**
78+
* explicitly declare string column type
79+
*
80+
* @example
81+
* ```ts
82+
* const typeTable = defineTable({
83+
* // NotNull is optional
84+
* type: defineColumn<'generic' | 'custom'>().NotNull(),
85+
* }
86+
* ```
87+
*/
88+
export function defineLiteral<T extends string>(defaultTo?: T | RawBuilder<unknown> | null) {
89+
const base = {
90+
type: 'string',
91+
defaultTo: defaultTo as IsNotNull<typeof defaultTo> extends true ? T : T | null,
92+
} as const
93+
return {
94+
...base,
95+
NotNull() {
96+
return {
97+
...base,
98+
notNull: true,
99+
} as const
100+
},
101+
}
102+
}

packages/sqlite-builder/src/sync/index.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import type { Generated, Kysely } from 'kysely'
22
import type { DBLogger, SyncTableFn } from '../types'
3-
import type { ColumnProperty, Columns, Tables, TimeTriggerOptions } from './types'
3+
import type { ColumnProperty, Columns, IsNotNull, Schema, TimeTriggerOptions } from './types'
44
import type { SyncOptions } from './core'
55
import { syncTables } from './core'
66

77
export * from './types'
8-
export { defineColumn, defineTable } from './define'
8+
export { defineTable } from './define'
99

10-
export function createAutoSyncTableFn<T extends Tables>(
10+
export function createAutoSyncTableFn<T extends Schema>(
1111
tables: T,
1212
options: SyncOptions<T> = {},
1313
): SyncTableFn {
@@ -40,8 +40,6 @@ type ParseTableWithTrigger<
4040
)
4141
: never
4242

43-
type IsNotNull<T> = (T extends null ? T : never) extends never ? true : false
44-
4543
/**
4644
* util type for infering type of table
4745
*/
@@ -71,7 +69,7 @@ export type InferTable<
7169
*
7270
* use {@link InferTable} to check details
7371
*/
74-
export type InferDatabase<T extends Tables> = Prettify<{
72+
export type InferDatabase<T extends Schema> = Prettify<{
7573
[K in keyof T]: T[K] extends {
7674
columns: Columns
7775
timeTrigger?: TimeTriggerOptions<any, any>

packages/sqlite-builder/src/sync/run.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ export async function runCreateTable(
131131
return builder.defaultTo(sql`CURRENT_TIMESTAMP`)
132132
}
133133

134-
if (notNull) {
134+
if (notNull === true) {
135135
builder = builder.notNull()
136136
}
137137

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Generated, RawBuilder } from 'kysely'
22

33
export type Arrayable<T> = T | Array<T>
4+
45
export type ColumnType =
56
| 'string'
67
| 'boolean'
@@ -9,7 +10,7 @@ export type ColumnType =
910
| 'date'
1011
| 'blob'
1112
| 'object'
12-
13+
export type InferGenereated<T> = T extends Generated<infer P> ? P : T
1314
export type InferColumnTypeByString<T> =
1415
T extends 'string' ? string :
1516
T extends 'boolean' ? boolean :
@@ -23,40 +24,41 @@ export type InferColumnTypeByString<T> =
2324
export type InferStringByColumnType<T> =
2425
T extends string ? 'string' :
2526
T extends boolean ? 'boolean' :
26-
T extends Generated<number> ? 'increments' :
27+
T extends Generated<number> ? 'increments' | 'int' | 'float' :
2728
T extends number ? 'int' | 'float' :
2829
T extends Date ? 'date' :
2930
T extends ArrayBufferLike ? 'blob' :
30-
T extends object ? 'object' :
31-
never
31+
T extends Generated<infer P> ? InferStringByColumnType<P> :
32+
T extends object ? 'object' :
33+
never
3234

3335
export type ColumnProperty<
34-
T extends ColumnType = ColumnType,
35-
DefaultTo extends InferColumnTypeByString<T> | null = InferColumnTypeByString<T> | null,
36+
ColType extends ColumnType = ColumnType,
37+
DefaultTo extends InferColumnTypeByString<ColType> | null = InferColumnTypeByString<ColType> | null,
3638
NotNull extends true | null = true | null,
3739
> = {
38-
type: T
40+
type: ColType
3941
defaultTo?: DefaultTo | RawBuilder<unknown>
4042
notNull?: NotNull
4143
}
4244

4345
export type TimeTriggerOptions<
44-
C extends string | true | null,
45-
U extends string | true | null,
46+
Create extends string | true | null,
47+
Update extends string | true | null,
4648
> = {
47-
create?: C
48-
update?: U
49+
create?: Create
50+
update?: Update
4951
}
5052

5153
export type TableProperty<
52-
T extends Columns,
53-
C extends string | true | null = null,
54-
U extends string | true | null = null,
54+
Cols extends Columns,
55+
Create extends string | true | null = null,
56+
Update extends string | true | null = null,
5557
> = {
56-
primary?: Arrayable<keyof T & string>
57-
unique?: Arrayable<keyof T & string>[]
58-
index?: Arrayable<keyof T & string>[]
59-
timeTrigger?: TimeTriggerOptions<C, U>
58+
primary?: Arrayable<keyof Cols & string>
59+
unique?: Arrayable<keyof Cols & string>[]
60+
index?: Arrayable<keyof Cols & string>[]
61+
timeTrigger?: TimeTriggerOptions<Create, Update>
6062
}
6163
export type Columns = Record<string, ColumnProperty>
6264

@@ -81,10 +83,19 @@ export type ColumnsWithErrorInfo<T extends Columns> = {
8183
};
8284
}
8385
export type Table<
84-
T extends Columns = any,
85-
C extends string | true | null = null,
86-
U extends string | true | null = null,
86+
Cols extends Columns = any,
87+
Create extends string | true | null = null,
88+
Update extends string | true | null = null,
8789
> = {
88-
columns: ColumnsWithErrorInfo<T>
89-
} & TableProperty<T, C, U>
90-
export type Tables = Record<string, Table<any, any, any>>
90+
columns: ColumnsWithErrorInfo<Cols>
91+
} & TableProperty<Cols, Create, Update>
92+
export type Schema = Record<string, Table<any, any, any>>
93+
export type FilterGenerated<
94+
Table extends object,
95+
EscapeKeys extends string = never,
96+
> = {
97+
[K in keyof Table]: K extends EscapeKeys
98+
? Table[K]
99+
: InferGenereated<Table[K]>
100+
}
101+
export type IsNotNull<T> = (T extends null ? T : never) extends never ? true : false

0 commit comments

Comments
 (0)