@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.
Files changed (52) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/fixture.d.ts +0 -2
  3. package/dist/__tests__/fixture.d.ts.map +1 -1
  4. package/dist/adapter-types.d.ts +75 -16
  5. package/dist/adapter-types.d.ts.map +1 -1
  6. package/dist/adapter-types.js +18 -0
  7. package/dist/adapter-types.js.map +1 -1
  8. package/dist/debug-info.d.ts +8 -8
  9. package/dist/devtools/devtools-messages.d.ts +54 -35
  10. package/dist/devtools/devtools-messages.d.ts.map +1 -1
  11. package/dist/devtools/devtools-messages.js +38 -28
  12. package/dist/devtools/devtools-messages.js.map +1 -1
  13. package/dist/devtools/devtools-window-message.d.ts +26 -0
  14. package/dist/devtools/devtools-window-message.d.ts.map +1 -0
  15. package/dist/devtools/devtools-window-message.js +30 -0
  16. package/dist/devtools/devtools-window-message.js.map +1 -0
  17. package/dist/devtools/index.d.ts +1 -0
  18. package/dist/devtools/index.d.ts.map +1 -1
  19. package/dist/devtools/index.js +1 -0
  20. package/dist/devtools/index.js.map +1 -1
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +1 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/rehydrate-from-mutationlog.d.ts +8 -3
  26. package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
  27. package/dist/rehydrate-from-mutationlog.js +71 -56
  28. package/dist/rehydrate-from-mutationlog.js.map +1 -1
  29. package/dist/schema/system-tables.d.ts +0 -5
  30. package/dist/schema/system-tables.d.ts.map +1 -1
  31. package/dist/schema/table-def.d.ts +0 -2
  32. package/dist/schema/table-def.d.ts.map +1 -1
  33. package/dist/schema/table-def.js +0 -1
  34. package/dist/schema/table-def.js.map +1 -1
  35. package/dist/schema-management/migrations.d.ts +9 -4
  36. package/dist/schema-management/migrations.d.ts.map +1 -1
  37. package/dist/schema-management/migrations.js +24 -13
  38. package/dist/schema-management/migrations.js.map +1 -1
  39. package/dist/version.d.ts +2 -0
  40. package/dist/version.d.ts.map +1 -0
  41. package/dist/version.js +3 -0
  42. package/dist/version.js.map +1 -0
  43. package/package.json +3 -3
  44. package/src/adapter-types.ts +53 -15
  45. package/src/devtools/devtools-messages.ts +67 -47
  46. package/src/devtools/devtools-window-message.ts +25 -0
  47. package/src/devtools/index.ts +1 -0
  48. package/src/index.ts +1 -0
  49. package/src/rehydrate-from-mutationlog.ts +100 -64
  50. package/src/schema/table-def.ts +0 -4
  51. package/src/schema-management/migrations.ts +104 -84
  52. 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
- migrateTable({
25
- db,
26
- otelContext: otel.context.active(),
27
- tableAst: schemaMutationsMetaTable.sqliteDef.ast,
28
- behaviour: 'create-if-not-exists',
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
- migrateTable({
65
- db,
66
- otelContext,
67
- tableAst: schemaMetaTable.sqliteDef.ast,
68
- behaviour: 'create-if-not-exists',
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
- validateSchema(schema, makeSchemaManager(db))
75
+ // TODO enforce that migrating tables isn't allowed once the store is running
72
76
 
73
- const schemaMetaRows = dbSelect<SchemaMetaRow>(db, sql`SELECT * FROM ${SCHEMA_META_TABLE}`)
77
+ const schemaManager = yield* makeSchemaManager(db)
78
+ validateSchema(schema, schemaManager)
74
79
 
75
- const dbSchemaHashByTable = Object.fromEntries(
76
- schemaMetaRows.map(({ tableName, schemaHash }) => [tableName, schemaHash]),
77
- )
80
+ const schemaMetaRows = dbSelect<SchemaMetaRow>(db, sql`SELECT * FROM ${SCHEMA_META_TABLE}`)
78
81
 
79
- const tableDefs = new Set([
80
- // NOTE it's important the `SCHEMA_META_TABLE` comes first since we're writing to it below
81
- ...systemTables,
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
- for (const tableDef of tableDefs) {
86
- const tableAst = tableDef.sqliteDef.ast
87
- const tableName = tableAst.name
88
- const dbSchemaHash = dbSchemaHashByTable[tableName]
89
- const schemaHash = SqliteAst.hash(tableAst)
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 skipMigrations = import.meta.env.VITE_LIVESTORE_SKIP_MIGRATIONS !== undefined
92
+ const tablesToMigrate = new Set<{ tableAst: SqliteAst.Table; schemaHash: number }>()
92
93
 
93
- if (schemaHash !== dbSchemaHash && skipMigrations === false) {
94
- console.log(
95
- `Schema hash mismatch for table '${tableName}' (DB: ${dbSchemaHash}, expected: ${schemaHash}), migrating table...`,
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
- migrateTable({ db, tableAst, otelContext, schemaHash, behaviour: 'drop-and-recreate' })
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
- console.log(`Migrating table '${tableAst.name}'...`)
119
- const tableName = tableAst.name
120
- const columnSpec = makeColumnSpec(tableAst)
121
-
122
- if (behaviour === 'drop-and-recreate') {
123
- // TODO need to possibly handle cascading deletes due to foreign keys
124
- dbExecute(db, sql`drop table if exists ${tableName}`)
125
- dbExecute(db, sql`create table if not exists ${tableName} (${columnSpec}) strict`)
126
- } else if (behaviour === 'create-if-not-exists') {
127
- dbExecute(db, sql`create table if not exists ${tableName} (${columnSpec}) strict`)
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
- for (const index of tableAst.indexes) {
131
- dbExecute(db, createIndexFromDefinition(tableName, index))
132
- }
150
+ for (const index of tableAst.indexes) {
151
+ dbExecute(db, createIndexFromDefinition(tableName, index))
152
+ }
133
153
 
134
- if (skipMetaTable !== true) {
135
- const updatedAt = getMemoizedTimestamp()
154
+ if (skipMetaTable !== true) {
155
+ const updatedAt = getMemoizedTimestamp()
136
156
 
137
- dbExecute(
138
- db,
139
- sql`
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
- { tableName, schemaHash, updatedAt },
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
@@ -0,0 +1,3 @@
1
+ import packageJson from '../package.json' assert { type: 'json' }
2
+
3
+ export const liveStoreVersion = packageJson.version