@livestore/common 0.0.46 → 0.0.47
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/dist/.tsbuildinfo +1 -1
- package/dist/database.d.ts +20 -3
- package/dist/database.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/init-singleton-tables.d.ts +4 -0
- package/dist/init-singleton-tables.d.ts.map +1 -0
- package/dist/init-singleton-tables.js +16 -0
- package/dist/init-singleton-tables.js.map +1 -0
- package/dist/migrations.d.ts +16 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +99 -0
- package/dist/migrations.js.map +1 -0
- package/dist/mutation.d.ts +11 -0
- package/dist/mutation.d.ts.map +1 -0
- package/dist/mutation.js +29 -0
- package/dist/mutation.js.map +1 -0
- package/dist/rehydrate-from-mutationlog.d.ts +8 -0
- package/dist/rehydrate-from-mutationlog.d.ts.map +1 -0
- package/dist/rehydrate-from-mutationlog.js +51 -0
- package/dist/rehydrate-from-mutationlog.js.map +1 -0
- package/dist/schema/index.d.ts +2 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +5 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/mutations.d.ts +9 -11
- package/dist/schema/mutations.d.ts.map +1 -1
- package/dist/schema/mutations.js.map +1 -1
- package/dist/schema/table-def.d.ts +1 -1
- package/dist/schema/table-def.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/database.ts +25 -3
- package/src/index.ts +4 -0
- package/src/init-singleton-tables.ts +24 -0
- package/src/migrations.ts +155 -0
- package/src/mutation.ts +50 -0
- package/src/rehydrate-from-mutationlog.ts +77 -0
- package/src/schema/index.ts +7 -1
- package/src/schema/mutations.ts +16 -11
- package/src/schema/table-def.ts +10 -8
package/src/index.ts
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { MainDatabase } from './database.js'
|
|
2
|
+
import type { LiveStoreSchema } from './schema/index.js'
|
|
3
|
+
import { DbSchema } from './schema/index.js'
|
|
4
|
+
import { prepareBindValues, sql } from './util.js'
|
|
5
|
+
|
|
6
|
+
export const initializeSingletonTables = (schema: LiveStoreSchema, db: MainDatabase) => {
|
|
7
|
+
for (const [, tableDef] of schema.tables) {
|
|
8
|
+
if (tableDef.options.isSingleton) {
|
|
9
|
+
const defaultValues = DbSchema.getDefaultValuesEncoded(tableDef, undefined)
|
|
10
|
+
|
|
11
|
+
const defaultColumnNames = [...Object.keys(defaultValues), 'id']
|
|
12
|
+
const columnValues = defaultColumnNames.map((name) => `$${name}`).join(', ')
|
|
13
|
+
|
|
14
|
+
const tableName = tableDef.sqliteDef.name
|
|
15
|
+
const insertQuery = sql`insert into ${tableName} (${defaultColumnNames.join(
|
|
16
|
+
', ',
|
|
17
|
+
)}) select ${columnValues} where not exists(select 1 from ${tableName} where id = 'singleton')`
|
|
18
|
+
|
|
19
|
+
const bindValues = prepareBindValues({ ...defaultValues, id: 'singleton' }, insertQuery)
|
|
20
|
+
|
|
21
|
+
db.execute(insertQuery, bindValues)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { memoize } from '@livestore/utils'
|
|
2
|
+
import { Schema as EffectSchema } from '@livestore/utils/effect'
|
|
3
|
+
import type * as otel from '@opentelemetry/api'
|
|
4
|
+
import { SqliteAst, SqliteDsl } from 'effect-db-schema'
|
|
5
|
+
|
|
6
|
+
import type { MainDatabase } from './database.js'
|
|
7
|
+
import type { LiveStoreSchema } from './schema/index.js'
|
|
8
|
+
import type { SchemaMetaRow } from './schema/system-tables.js'
|
|
9
|
+
import { SCHEMA_META_TABLE, systemTables } from './schema/system-tables.js'
|
|
10
|
+
import type { ParamsObject } from './util.js'
|
|
11
|
+
import { prepareBindValues, sql } from './util.js'
|
|
12
|
+
|
|
13
|
+
const getMemoizedTimestamp = memoize(() => new Date().toISOString())
|
|
14
|
+
|
|
15
|
+
// TODO bring back statement caching
|
|
16
|
+
// will require proper scope-aware cleanup etc (for testing and apps with multiple LiveStore instances)
|
|
17
|
+
// const cachedStmts = new Map<string, PreparedStatement>()
|
|
18
|
+
|
|
19
|
+
const dbExecute = (db: MainDatabase, queryStr: string, bindValues?: ParamsObject) => {
|
|
20
|
+
// let stmt = cachedStmts.get(queryStr)
|
|
21
|
+
// if (!stmt) {
|
|
22
|
+
const stmt = db.prepare(queryStr)
|
|
23
|
+
// cachedStmts.set(queryStr, stmt)
|
|
24
|
+
// }
|
|
25
|
+
|
|
26
|
+
const preparedBindValues = bindValues ? prepareBindValues(bindValues, queryStr) : undefined
|
|
27
|
+
|
|
28
|
+
stmt.execute(preparedBindValues)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const dbSelect = <T>(db: MainDatabase, queryStr: string, bindValues?: ParamsObject) => {
|
|
32
|
+
// let stmt = cachedStmts.get(queryStr)
|
|
33
|
+
// if (!stmt) {
|
|
34
|
+
const stmt = db.prepare(queryStr)
|
|
35
|
+
// cachedStmts.set(queryStr, stmt)
|
|
36
|
+
// }
|
|
37
|
+
|
|
38
|
+
return stmt.select<T>(bindValues ? prepareBindValues(bindValues, queryStr) : undefined)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// TODO more graceful DB migration (e.g. backup DB before destructive migrations)
|
|
42
|
+
export const migrateDb = ({
|
|
43
|
+
db,
|
|
44
|
+
otelContext,
|
|
45
|
+
schema,
|
|
46
|
+
}: {
|
|
47
|
+
db: MainDatabase
|
|
48
|
+
otelContext: otel.Context
|
|
49
|
+
schema: LiveStoreSchema
|
|
50
|
+
}) => {
|
|
51
|
+
dbExecute(
|
|
52
|
+
db,
|
|
53
|
+
// TODO use schema migration definition from schema.ts instead
|
|
54
|
+
sql`create table if not exists ${SCHEMA_META_TABLE} (tableName text primary key, schemaHash text, updatedAt text);`,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
const schemaMetaRows = dbSelect<SchemaMetaRow>(db, sql`SELECT * FROM ${SCHEMA_META_TABLE}`)
|
|
58
|
+
|
|
59
|
+
const dbSchemaHashByTable = Object.fromEntries(
|
|
60
|
+
schemaMetaRows.map(({ tableName, schemaHash }) => [tableName, schemaHash]),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
const tableDefs = new Set([
|
|
64
|
+
// NOTE it's important the `SCHEMA_META_TABLE` comes first since we're writing to it below
|
|
65
|
+
...systemTables,
|
|
66
|
+
...Array.from(schema.tables.values()).filter((_) => _.sqliteDef.name !== SCHEMA_META_TABLE),
|
|
67
|
+
])
|
|
68
|
+
|
|
69
|
+
for (const tableDef of tableDefs) {
|
|
70
|
+
const tableAst = tableDef.sqliteDef.ast
|
|
71
|
+
const tableName = tableAst.name
|
|
72
|
+
const dbSchemaHash = dbSchemaHashByTable[tableName]
|
|
73
|
+
const schemaHash = SqliteAst.hash(tableAst)
|
|
74
|
+
|
|
75
|
+
// @ts-expect-error TODO fix typing
|
|
76
|
+
const skipMigrations = import.meta.env.VITE_LIVESTORE_SKIP_MIGRATIONS !== undefined
|
|
77
|
+
|
|
78
|
+
if (schemaHash !== dbSchemaHash && skipMigrations === false) {
|
|
79
|
+
console.log(
|
|
80
|
+
`Schema hash mismatch for table '${tableName}' (DB: ${dbSchemaHash}, expected: ${schemaHash}), migrating table...`,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
migrateTable({ db, tableAst, otelContext, schemaHash })
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const migrateTable = ({
|
|
89
|
+
db,
|
|
90
|
+
tableAst,
|
|
91
|
+
// otelContext,
|
|
92
|
+
schemaHash,
|
|
93
|
+
}: {
|
|
94
|
+
db: MainDatabase
|
|
95
|
+
tableAst: SqliteAst.Table
|
|
96
|
+
otelContext: otel.Context
|
|
97
|
+
schemaHash: number
|
|
98
|
+
}) => {
|
|
99
|
+
console.log(`Migrating table '${tableAst.name}'...`)
|
|
100
|
+
const tableName = tableAst.name
|
|
101
|
+
const columnSpec = makeColumnSpec(tableAst)
|
|
102
|
+
|
|
103
|
+
// TODO need to possibly handle cascading deletes due to foreign keys
|
|
104
|
+
dbExecute(db, sql`drop table if exists ${tableName}`)
|
|
105
|
+
dbExecute(db, sql`create table if not exists ${tableName} (${columnSpec})`)
|
|
106
|
+
|
|
107
|
+
for (const index of tableAst.indexes) {
|
|
108
|
+
dbExecute(db, createIndexFromDefinition(tableName, index))
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const updatedAt = getMemoizedTimestamp()
|
|
112
|
+
|
|
113
|
+
dbExecute(
|
|
114
|
+
db,
|
|
115
|
+
sql`
|
|
116
|
+
INSERT INTO ${SCHEMA_META_TABLE} (tableName, schemaHash, updatedAt) VALUES ($tableName, $schemaHash, $updatedAt)
|
|
117
|
+
ON CONFLICT (tableName) DO UPDATE SET schemaHash = $schemaHash, updatedAt = $updatedAt;
|
|
118
|
+
`,
|
|
119
|
+
{ tableName, schemaHash, updatedAt },
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const createIndexFromDefinition = (tableName: string, index: SqliteAst.Index) => {
|
|
124
|
+
const uniqueStr = index.unique ? 'UNIQUE' : ''
|
|
125
|
+
return sql`create ${uniqueStr} index ${index.name} on ${tableName} (${index.columns.join(', ')})`
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const makeColumnSpec = (tableAst: SqliteAst.Table) => {
|
|
129
|
+
const primaryKeys = tableAst.columns.filter((_) => _.primaryKey).map((_) => _.name)
|
|
130
|
+
const columnDefStrs = tableAst.columns.map(toSqliteColumnSpec)
|
|
131
|
+
if (primaryKeys.length > 0) {
|
|
132
|
+
columnDefStrs.push(`PRIMARY KEY (${primaryKeys.join(', ')})`)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return columnDefStrs.join(', ')
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** NOTE primary keys are applied on a table level not on a column level to account for multi-column primary keys */
|
|
139
|
+
const toSqliteColumnSpec = (column: SqliteAst.Column) => {
|
|
140
|
+
const columnTypeStr = column.type._tag
|
|
141
|
+
const nullableStr = column.nullable === false ? 'not null' : ''
|
|
142
|
+
const defaultValueStr = (() => {
|
|
143
|
+
if (column.default._tag === 'None') return ''
|
|
144
|
+
|
|
145
|
+
if (SqliteDsl.isSqlDefaultValue(column.default.value)) return `default ${column.default.value.sql}`
|
|
146
|
+
|
|
147
|
+
const encodeValue = EffectSchema.encodeSync(column.schema)
|
|
148
|
+
const encodedDefaultValue = encodeValue(column.default.value)
|
|
149
|
+
|
|
150
|
+
if (columnTypeStr === 'text') return `default '${encodedDefaultValue}'`
|
|
151
|
+
return `default ${encodedDefaultValue}`
|
|
152
|
+
})()
|
|
153
|
+
|
|
154
|
+
return `${column.name} ${columnTypeStr} ${nullableStr} ${defaultValueStr}`
|
|
155
|
+
}
|
package/src/mutation.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Schema } from '@livestore/utils/effect'
|
|
2
|
+
|
|
3
|
+
import type { MutationDef, MutationEvent } from './schema/mutations.js'
|
|
4
|
+
import type { PreparedBindValues } from './util.js'
|
|
5
|
+
import { prepareBindValues } from './util.js'
|
|
6
|
+
|
|
7
|
+
export const getExecArgsFromMutation = ({
|
|
8
|
+
mutationDef,
|
|
9
|
+
mutationEventDecoded,
|
|
10
|
+
}: {
|
|
11
|
+
mutationDef: MutationDef.Any
|
|
12
|
+
mutationEventDecoded: MutationEvent.Any
|
|
13
|
+
}): ReadonlyArray<{
|
|
14
|
+
statementSql: string
|
|
15
|
+
bindValues: PreparedBindValues
|
|
16
|
+
writeTables: ReadonlySet<string> | undefined
|
|
17
|
+
}> => {
|
|
18
|
+
let statementRes: ReadonlyArray<
|
|
19
|
+
string | { sql: string; bindValues: Record<string, unknown>; writeTables?: ReadonlySet<string> }
|
|
20
|
+
>
|
|
21
|
+
|
|
22
|
+
switch (typeof mutationDef.sql) {
|
|
23
|
+
case 'function': {
|
|
24
|
+
const res = mutationDef.sql(mutationEventDecoded.args)
|
|
25
|
+
statementRes = Array.isArray(res) ? res : [res]
|
|
26
|
+
break
|
|
27
|
+
}
|
|
28
|
+
case 'string': {
|
|
29
|
+
statementRes = [mutationDef.sql]
|
|
30
|
+
break
|
|
31
|
+
}
|
|
32
|
+
default: {
|
|
33
|
+
statementRes = mutationDef.sql
|
|
34
|
+
break
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return statementRes.map((statementRes) => {
|
|
39
|
+
const statementSql = typeof statementRes === 'string' ? statementRes : statementRes.sql
|
|
40
|
+
|
|
41
|
+
const bindValues =
|
|
42
|
+
typeof statementRes === 'string'
|
|
43
|
+
? Schema.encodeUnknownSync(mutationDef.schema)(mutationEventDecoded.args)
|
|
44
|
+
: statementRes.bindValues
|
|
45
|
+
|
|
46
|
+
const writeTables = typeof statementRes === 'string' ? undefined : statementRes.writeTables
|
|
47
|
+
|
|
48
|
+
return { statementSql, bindValues: prepareBindValues(bindValues ?? {}, statementSql), writeTables }
|
|
49
|
+
})
|
|
50
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { shouldNeverHappen } from '@livestore/utils'
|
|
2
|
+
import { Schema } from '@livestore/utils/effect'
|
|
3
|
+
|
|
4
|
+
import type { MainDatabase } from './database.js'
|
|
5
|
+
import { getExecArgsFromMutation } from './mutation.js'
|
|
6
|
+
import type { LiveStoreSchema } from './schema/index.js'
|
|
7
|
+
|
|
8
|
+
type MutationLogRow = {
|
|
9
|
+
id: string
|
|
10
|
+
mutation: string
|
|
11
|
+
args_json: string
|
|
12
|
+
schema_hash: number
|
|
13
|
+
created_at: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const rehydrateFromMutationLog = ({
|
|
17
|
+
logDb,
|
|
18
|
+
db,
|
|
19
|
+
schema,
|
|
20
|
+
}: {
|
|
21
|
+
logDb: MainDatabase
|
|
22
|
+
db: MainDatabase
|
|
23
|
+
schema: LiveStoreSchema
|
|
24
|
+
}) => {
|
|
25
|
+
try {
|
|
26
|
+
const stmt = logDb.prepare('SELECT * FROM mutation_log ORDER BY id ASC')
|
|
27
|
+
const results = stmt.select<MutationLogRow>(undefined)
|
|
28
|
+
|
|
29
|
+
performance.mark('livestore:hydrate-from-mutationlog:start')
|
|
30
|
+
|
|
31
|
+
for (const row of results) {
|
|
32
|
+
const mutationDef = schema.mutations.get(row.mutation) ?? shouldNeverHappen(`Unknown mutation ${row.mutation}`)
|
|
33
|
+
|
|
34
|
+
if (Schema.hash(mutationDef.schema) !== row.schema_hash) {
|
|
35
|
+
throw new Error(`Schema hash mismatch for mutation ${row.mutation}`)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const argsDecoded = Schema.decodeUnknownSync(Schema.parseJson(mutationDef.schema))(row.args_json)
|
|
39
|
+
const mutationEventDecoded = {
|
|
40
|
+
id: row.id,
|
|
41
|
+
mutation: row.mutation,
|
|
42
|
+
args: argsDecoded,
|
|
43
|
+
}
|
|
44
|
+
// const argsEncoded = JSON.parse(row.args_json)
|
|
45
|
+
// const mutationSqlRes =
|
|
46
|
+
// typeof mutation.sql === 'string'
|
|
47
|
+
// ? mutation.sql
|
|
48
|
+
// : mutation.sql(Schema.decodeUnknownSync(mutation.schema)(argsEncoded))
|
|
49
|
+
// const mutationSql = typeof mutationSqlRes === 'string' ? mutationSqlRes : mutationSqlRes.sql
|
|
50
|
+
// const bindValues = typeof mutationSqlRes === 'string' ? argsEncoded : mutationSqlRes.bindValues
|
|
51
|
+
|
|
52
|
+
const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
|
|
53
|
+
|
|
54
|
+
for (const { statementSql, bindValues } of execArgsArr) {
|
|
55
|
+
try {
|
|
56
|
+
db.execute(statementSql, bindValues)
|
|
57
|
+
// console.log(`Re-executed mutation ${mutationSql}`, bindValues)
|
|
58
|
+
} catch (e) {
|
|
59
|
+
console.error(`Error executing migration for mutation ${statementSql}`, bindValues, e)
|
|
60
|
+
debugger
|
|
61
|
+
throw e
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
console.error('Error while rehydrating database from mutation log', e)
|
|
67
|
+
debugger
|
|
68
|
+
throw e
|
|
69
|
+
} finally {
|
|
70
|
+
performance.mark('livestore:hydrate-from-mutationlog:end')
|
|
71
|
+
performance.measure(
|
|
72
|
+
'livestore:hydrate-from-mutationlog',
|
|
73
|
+
'livestore:hydrate-from-mutationlog:start',
|
|
74
|
+
'livestore:hydrate-from-mutationlog:end',
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
}
|
package/src/schema/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isReadonlyArray } from '@livestore/utils'
|
|
2
2
|
import type { ReadonlyArray } from '@livestore/utils/effect'
|
|
3
|
-
import type
|
|
3
|
+
import { SqliteAst, type SqliteDsl } from 'effect-db-schema'
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
type MutationDef,
|
|
@@ -80,6 +80,12 @@ export const makeSchema = <TInputSchema extends InputSchema>(
|
|
|
80
80
|
} satisfies LiveStoreSchema
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
export const makeSchemaHash = (schema: LiveStoreSchema) =>
|
|
84
|
+
SqliteAst.hash({
|
|
85
|
+
_tag: 'dbSchema',
|
|
86
|
+
tables: [...schema.tables.values()].map((_) => _.sqliteDef.ast),
|
|
87
|
+
})
|
|
88
|
+
|
|
83
89
|
/**
|
|
84
90
|
* In case of ...
|
|
85
91
|
* - array: we use the table name of each array item (= table definition) as the object key
|
package/src/schema/mutations.ts
CHANGED
|
@@ -17,19 +17,24 @@ export type InternalMutationSchema<TRecord extends MutationDefRecord = MutationD
|
|
|
17
17
|
schemaHashMap: Map<keyof TRecord, number>
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export type MutationDefSqlResult<TTo> =
|
|
21
|
+
| SingleOrReadonlyArray<string>
|
|
22
|
+
| ((args: TTo) => SingleOrReadonlyArray<
|
|
23
|
+
| string
|
|
24
|
+
| {
|
|
25
|
+
sql: string
|
|
26
|
+
/** Note args need to be manually encoded to `BindValues` when returning this argument */
|
|
27
|
+
bindValues: BindValues
|
|
28
|
+
writeTables?: ReadonlySet<string>
|
|
29
|
+
}
|
|
30
|
+
>)
|
|
31
|
+
|
|
32
|
+
export type SingleOrReadonlyArray<T> = T | ReadonlyArray<T>
|
|
33
|
+
|
|
20
34
|
export type MutationDef<TName extends string, TFrom, TTo> = {
|
|
21
35
|
name: TName
|
|
22
36
|
schema: Schema.Schema<TTo, TFrom>
|
|
23
|
-
sql:
|
|
24
|
-
| string
|
|
25
|
-
| ((args: TTo) =>
|
|
26
|
-
| string
|
|
27
|
-
| {
|
|
28
|
-
sql: string
|
|
29
|
-
/** Note args need to be manually encoded to `BindValues` when returning this argument */
|
|
30
|
-
bindValues: BindValues
|
|
31
|
-
writeTables?: ReadonlySet<string>
|
|
32
|
-
})
|
|
37
|
+
sql: MutationDefSqlResult<TTo>
|
|
33
38
|
|
|
34
39
|
/** Helper function to construct mutation event */
|
|
35
40
|
(args: TTo): { mutation: TName; args: TTo; id: string }
|
|
@@ -43,7 +48,7 @@ export namespace MutationDef {
|
|
|
43
48
|
export const defineMutation = <TName extends string, TFrom, TTo>(
|
|
44
49
|
name: TName,
|
|
45
50
|
schema: Schema.Schema<TTo, TFrom>,
|
|
46
|
-
sql:
|
|
51
|
+
sql: MutationDefSqlResult<TTo>,
|
|
47
52
|
): MutationDef<TName, TFrom, TTo> => {
|
|
48
53
|
const makeEvent = (args: TTo) => ({ mutation: name, args, id: cuid() })
|
|
49
54
|
|
package/src/schema/table-def.ts
CHANGED
|
@@ -198,15 +198,17 @@ export const getDefaultValuesDecoded = <TTableDef extends TableDef>(
|
|
|
198
198
|
)
|
|
199
199
|
|
|
200
200
|
type WithId<TColumns extends SqliteDsl.Columns, TOptions extends TableOptions> = TColumns &
|
|
201
|
-
(
|
|
201
|
+
('id' extends keyof TColumns
|
|
202
202
|
? {}
|
|
203
|
-
: TOptions['
|
|
204
|
-
? {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
203
|
+
: TOptions['disableAutomaticIdColumn'] extends true
|
|
204
|
+
? {}
|
|
205
|
+
: TOptions['isSingleton'] extends true
|
|
206
|
+
? {
|
|
207
|
+
id: SqliteDsl.ColumnDefinition<'singleton', 'singleton'>
|
|
208
|
+
}
|
|
209
|
+
: {
|
|
210
|
+
id: SqliteDsl.ColumnDefinition<string, string>
|
|
211
|
+
})
|
|
210
212
|
|
|
211
213
|
type WithDefaults<TOptionsInput extends TableOptionsInput> = {
|
|
212
214
|
isSingleton: TOptionsInput['isSingleton'] extends true ? true : false
|