@livestore/common 0.0.54-dev.21 → 0.0.54-dev.23
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/__tests__/fixture.d.ts +0 -2
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/adapter-types.d.ts +75 -16
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +18 -0
- package/dist/adapter-types.js.map +1 -1
- package/dist/debug-info.d.ts +8 -8
- package/dist/devtools/devtools-messages.d.ts +54 -35
- package/dist/devtools/devtools-messages.d.ts.map +1 -1
- package/dist/devtools/devtools-messages.js +38 -28
- package/dist/devtools/devtools-messages.js.map +1 -1
- package/dist/devtools/devtools-window-message.d.ts +26 -0
- package/dist/devtools/devtools-window-message.d.ts.map +1 -0
- package/dist/devtools/devtools-window-message.js +30 -0
- package/dist/devtools/devtools-window-message.js.map +1 -0
- package/dist/devtools/index.d.ts +1 -0
- package/dist/devtools/index.d.ts.map +1 -1
- package/dist/devtools/index.js +1 -0
- package/dist/devtools/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/rehydrate-from-mutationlog.d.ts +8 -3
- package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
- package/dist/rehydrate-from-mutationlog.js +71 -56
- package/dist/rehydrate-from-mutationlog.js.map +1 -1
- package/dist/schema/system-tables.d.ts +0 -5
- package/dist/schema/system-tables.d.ts.map +1 -1
- package/dist/schema/table-def.d.ts +0 -2
- package/dist/schema/table-def.d.ts.map +1 -1
- package/dist/schema/table-def.js +0 -1
- package/dist/schema/table-def.js.map +1 -1
- package/dist/schema-management/migrations.d.ts +9 -4
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js +24 -13
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +3 -3
- package/src/adapter-types.ts +53 -15
- package/src/devtools/devtools-messages.ts +67 -47
- package/src/devtools/devtools-window-message.ts +25 -0
- package/src/devtools/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/rehydrate-from-mutationlog.ts +100 -64
- package/src/schema/table-def.ts +0 -4
- package/src/schema-management/migrations.ts +104 -84
- package/src/version.ts +3 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memoizeByStringifyArgs } from '@livestore/utils'
|
|
2
|
-
import { Schema as EffectSchema } from '@livestore/utils/effect'
|
|
2
|
+
import { Effect, Schema as EffectSchema } from '@livestore/utils/effect'
|
|
3
3
|
import * as otel from '@opentelemetry/api'
|
|
4
4
|
import { SqliteAst, SqliteDsl } from 'effect-db-schema'
|
|
5
5
|
|
|
@@ -20,85 +20,104 @@ import { validateSchema } from './validate-mutation-defs.js'
|
|
|
20
20
|
|
|
21
21
|
const getMemoizedTimestamp = memoizeByStringifyArgs(() => new Date().toISOString())
|
|
22
22
|
|
|
23
|
-
export const makeSchemaManager = (db: InMemoryDatabase): SchemaManager =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
export const makeSchemaManager = (db: InMemoryDatabase): Effect.Effect<SchemaManager> =>
|
|
24
|
+
Effect.gen(function* () {
|
|
25
|
+
yield* migrateTable({
|
|
26
|
+
db,
|
|
27
|
+
otelContext: otel.context.active(),
|
|
28
|
+
tableAst: schemaMutationsMetaTable.sqliteDef.ast,
|
|
29
|
+
behaviour: 'create-if-not-exists',
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
getMutationDefInfos: () => {
|
|
34
|
+
const schemaMutationsMetaRows = dbSelect<SchemaMutationsMetaRow>(
|
|
35
|
+
db,
|
|
36
|
+
sql`SELECT * FROM ${SCHEMA_MUTATIONS_META_TABLE}`,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return schemaMutationsMetaRows
|
|
40
|
+
},
|
|
41
|
+
setMutationDefInfo: (info) => {
|
|
42
|
+
dbExecute(
|
|
43
|
+
db,
|
|
44
|
+
sql`INSERT OR REPLACE INTO ${SCHEMA_MUTATIONS_META_TABLE} (mutationName, schemaHash, updatedAt) VALUES ($mutationName, $schemaHash, $updatedAt)`,
|
|
45
|
+
{
|
|
46
|
+
mutationName: info.mutationName,
|
|
47
|
+
schemaHash: info.schemaHash,
|
|
48
|
+
updatedAt: new Date().toISOString(),
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
},
|
|
52
|
+
}
|
|
29
53
|
})
|
|
30
54
|
|
|
31
|
-
return {
|
|
32
|
-
getMutationDefInfos: () => {
|
|
33
|
-
const schemaMutationsMetaRows = dbSelect<SchemaMutationsMetaRow>(
|
|
34
|
-
db,
|
|
35
|
-
sql`SELECT * FROM ${SCHEMA_MUTATIONS_META_TABLE}`,
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
return schemaMutationsMetaRows
|
|
39
|
-
},
|
|
40
|
-
setMutationDefInfo: (info) => {
|
|
41
|
-
dbExecute(
|
|
42
|
-
db,
|
|
43
|
-
sql`INSERT OR REPLACE INTO ${SCHEMA_MUTATIONS_META_TABLE} (mutationName, schemaHash, updatedAt) VALUES ($mutationName, $schemaHash, $updatedAt)`,
|
|
44
|
-
{
|
|
45
|
-
mutationName: info.mutationName,
|
|
46
|
-
schemaHash: info.schemaHash,
|
|
47
|
-
updatedAt: new Date().toISOString(),
|
|
48
|
-
},
|
|
49
|
-
)
|
|
50
|
-
},
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
55
|
// TODO more graceful DB migration (e.g. backup DB before destructive migrations)
|
|
55
56
|
export const migrateDb = ({
|
|
56
57
|
db,
|
|
57
58
|
otelContext = otel.context.active(),
|
|
58
59
|
schema,
|
|
60
|
+
onProgress,
|
|
59
61
|
}: {
|
|
60
62
|
db: InMemoryDatabase
|
|
61
63
|
otelContext?: otel.Context
|
|
62
64
|
schema: LiveStoreSchema
|
|
63
|
-
}) =>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
onProgress?: (opts: { done: number; total: number }) => Effect.Effect<void>
|
|
66
|
+
}) =>
|
|
67
|
+
Effect.gen(function* () {
|
|
68
|
+
yield* migrateTable({
|
|
69
|
+
db,
|
|
70
|
+
otelContext,
|
|
71
|
+
tableAst: schemaMetaTable.sqliteDef.ast,
|
|
72
|
+
behaviour: 'create-if-not-exists',
|
|
73
|
+
})
|
|
70
74
|
|
|
71
|
-
|
|
75
|
+
// TODO enforce that migrating tables isn't allowed once the store is running
|
|
72
76
|
|
|
73
|
-
|
|
77
|
+
const schemaManager = yield* makeSchemaManager(db)
|
|
78
|
+
validateSchema(schema, schemaManager)
|
|
74
79
|
|
|
75
|
-
|
|
76
|
-
schemaMetaRows.map(({ tableName, schemaHash }) => [tableName, schemaHash]),
|
|
77
|
-
)
|
|
80
|
+
const schemaMetaRows = dbSelect<SchemaMetaRow>(db, sql`SELECT * FROM ${SCHEMA_META_TABLE}`)
|
|
78
81
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
...Array.from(schema.tables.values()).filter((_) => _.sqliteDef.name !== SCHEMA_META_TABLE),
|
|
83
|
-
])
|
|
82
|
+
const dbSchemaHashByTable = Object.fromEntries(
|
|
83
|
+
schemaMetaRows.map(({ tableName, schemaHash }) => [tableName, schemaHash]),
|
|
84
|
+
)
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
const tableDefs = new Set([
|
|
87
|
+
// NOTE it's important the `SCHEMA_META_TABLE` comes first since we're writing to it below
|
|
88
|
+
...systemTables,
|
|
89
|
+
...Array.from(schema.tables.values()).filter((_) => _.sqliteDef.name !== SCHEMA_META_TABLE),
|
|
90
|
+
])
|
|
90
91
|
|
|
91
|
-
const
|
|
92
|
+
const tablesToMigrate = new Set<{ tableAst: SqliteAst.Table; schemaHash: number }>()
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
for (const tableDef of tableDefs) {
|
|
95
|
+
const tableAst = tableDef.sqliteDef.ast
|
|
96
|
+
const tableName = tableAst.name
|
|
97
|
+
const dbSchemaHash = dbSchemaHashByTable[tableName]
|
|
98
|
+
const schemaHash = SqliteAst.hash(tableAst)
|
|
99
|
+
|
|
100
|
+
if (schemaHash !== dbSchemaHash) {
|
|
101
|
+
tablesToMigrate.add({ tableAst, schemaHash })
|
|
97
102
|
|
|
98
|
-
|
|
103
|
+
console.log(
|
|
104
|
+
`Schema hash mismatch for table '${tableName}' (DB: ${dbSchemaHash}, expected: ${schemaHash}), migrating table...`,
|
|
105
|
+
)
|
|
106
|
+
}
|
|
99
107
|
}
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
|
|
109
|
+
let processedTables = 0
|
|
110
|
+
const tablesCount = tablesToMigrate.size
|
|
111
|
+
|
|
112
|
+
for (const { tableAst, schemaHash } of tablesToMigrate) {
|
|
113
|
+
yield* migrateTable({ db, tableAst, otelContext, schemaHash, behaviour: 'create-if-not-exists' })
|
|
114
|
+
|
|
115
|
+
if (onProgress !== undefined) {
|
|
116
|
+
processedTables++
|
|
117
|
+
yield* onProgress({ done: processedTables, total: tablesCount })
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
})
|
|
102
121
|
|
|
103
122
|
export const migrateTable = ({
|
|
104
123
|
db,
|
|
@@ -114,36 +133,37 @@ export const migrateTable = ({
|
|
|
114
133
|
schemaHash?: number
|
|
115
134
|
behaviour: 'drop-and-recreate' | 'create-if-not-exists'
|
|
116
135
|
skipMetaTable?: boolean
|
|
117
|
-
}) =>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
136
|
+
}) =>
|
|
137
|
+
Effect.gen(function* () {
|
|
138
|
+
console.log(`Migrating table '${tableAst.name}'...`)
|
|
139
|
+
const tableName = tableAst.name
|
|
140
|
+
const columnSpec = makeColumnSpec(tableAst)
|
|
141
|
+
|
|
142
|
+
if (behaviour === 'drop-and-recreate') {
|
|
143
|
+
// TODO need to possibly handle cascading deletes due to foreign keys
|
|
144
|
+
dbExecute(db, sql`drop table if exists ${tableName}`)
|
|
145
|
+
dbExecute(db, sql`create table if not exists ${tableName} (${columnSpec}) strict`)
|
|
146
|
+
} else if (behaviour === 'create-if-not-exists') {
|
|
147
|
+
dbExecute(db, sql`create table if not exists ${tableName} (${columnSpec}) strict`)
|
|
148
|
+
}
|
|
129
149
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
150
|
+
for (const index of tableAst.indexes) {
|
|
151
|
+
dbExecute(db, createIndexFromDefinition(tableName, index))
|
|
152
|
+
}
|
|
133
153
|
|
|
134
|
-
|
|
135
|
-
|
|
154
|
+
if (skipMetaTable !== true) {
|
|
155
|
+
const updatedAt = getMemoizedTimestamp()
|
|
136
156
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
157
|
+
dbExecute(
|
|
158
|
+
db,
|
|
159
|
+
sql`
|
|
140
160
|
INSERT INTO ${SCHEMA_META_TABLE} (tableName, schemaHash, updatedAt) VALUES ($tableName, $schemaHash, $updatedAt)
|
|
141
161
|
ON CONFLICT (tableName) DO UPDATE SET schemaHash = $schemaHash, updatedAt = $updatedAt;
|
|
142
162
|
`,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
163
|
+
{ tableName, schemaHash, updatedAt },
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
}).pipe(Effect.withSpan('@livestore/common:migrateTable', { attributes: { tableName: tableAst.name } }))
|
|
147
167
|
|
|
148
168
|
const createIndexFromDefinition = (tableName: string, index: SqliteAst.Index) => {
|
|
149
169
|
const uniqueStr = index.unique ? 'UNIQUE' : ''
|
package/src/version.ts
ADDED