@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,6 +1,5 @@
1
- import type { Stream, SubscriptionRef, TRef } from '@livestore/utils/effect'
1
+ import type { Effect, Queue, Stream, SubscriptionRef } from '@livestore/utils/effect'
2
2
  import { Schema } from '@livestore/utils/effect'
3
- import type * as otel from '@opentelemetry/api'
4
3
 
5
4
  import type { LiveStoreSchema, MutationEvent } from './schema/index.js'
6
5
  import type { PreparedBindValues } from './util.js'
@@ -22,7 +21,6 @@ export type InMemoryDatabase = {
22
21
  _tag: 'InMemoryDatabase'
23
22
  prepare(queryStr: string): PreparedStatement
24
23
  execute(queryStr: string, bindValues: PreparedBindValues | undefined): GetRowsChangedCount
25
- dangerouslyReset(): Promise<void>
26
24
  export(): Uint8Array
27
25
  }
28
26
 
@@ -38,27 +36,54 @@ export type NetworkStatus = {
38
36
  timestampMs: number
39
37
  }
40
38
 
39
+ export const BootStateProgress = Schema.Struct({
40
+ done: Schema.Number,
41
+ total: Schema.Number,
42
+ })
43
+
44
+ export const BootStatus = Schema.Union(
45
+ Schema.Struct({ stage: Schema.Literal('loading') }),
46
+ Schema.Struct({ stage: Schema.Literal('migrating'), progress: BootStateProgress }),
47
+ Schema.Struct({ stage: Schema.Literal('rehydrating'), progress: BootStateProgress }),
48
+ Schema.Struct({ stage: Schema.Literal('syncing'), progress: BootStateProgress }),
49
+ Schema.Struct({ stage: Schema.Literal('done') }),
50
+ )
51
+
52
+ export type BootStatus = typeof BootStatus.Type
53
+
41
54
  export type Coordinator = {
55
+ isShutdownRef: { current: boolean }
42
56
  devtools: {
57
+ enabled: boolean
43
58
  channelId: string
59
+ /**
60
+ * Returns a dedicated message port for the store which is established over the message port passed in
61
+ */
62
+ connect: (options: {
63
+ port: MessagePort
64
+ connectionId: string
65
+ }) => Effect.Effect<{ storeMessagePort: MessagePort }, UnexpectedError>
44
66
  }
45
- hasLock: TRef.TRef<boolean>
46
- syncMutations: Stream.Stream<MutationEvent.AnyEncoded>
47
- execute(queryStr: string, bindValues: PreparedBindValues | undefined, span: otel.Span | undefined): Promise<void>
48
- mutate(mutationEventEncoded: MutationEvent.Any, options: { span: otel.Span; persisted: boolean }): Promise<void>
49
- dangerouslyReset(mode: ResetMode): Promise<void>
50
- export(span: otel.Span | undefined): Promise<Uint8Array | undefined>
67
+ // TODO is exposing the lock status really needed (or only relevant for web adapter?)
68
+ lockStatus: SubscriptionRef.SubscriptionRef<LockStatus>
69
+ syncMutations: Stream.Stream<MutationEvent.AnyEncoded, UnexpectedError>
70
+ execute(queryStr: string, bindValues: PreparedBindValues | undefined): Effect.Effect<void, UnexpectedError>
71
+ mutate(mutationEventEncoded: MutationEvent.Any, options: { persisted: boolean }): Effect.Effect<void, UnexpectedError>
72
+ dangerouslyReset(mode: ResetMode): Effect.Effect<void, UnexpectedError>
73
+ export: Effect.Effect<Uint8Array | undefined, UnexpectedError>
51
74
  /**
52
75
  * This is different from `export` since in `getInitialSnapshot` is usually the place for migrations etc to happen
53
76
  */
54
- getInitialSnapshot(): Promise<Uint8Array>
55
- getMutationLogData(): Promise<Uint8Array>
56
- shutdown(): Promise<void>
77
+ getInitialSnapshot: Effect.Effect<Uint8Array, UnexpectedError>
78
+ getMutationLogData: Effect.Effect<Uint8Array, UnexpectedError>
79
+ shutdown: Effect.Effect<void, UnexpectedError>
57
80
  networkStatus: SubscriptionRef.SubscriptionRef<NetworkStatus>
58
81
  }
59
82
 
60
83
  export type GetRowsChangedCount = () => number
61
84
 
85
+ export type LockStatus = 'has-lock' | 'no-lock'
86
+
62
87
  export type BootDb = {
63
88
  _tag: 'BootDb'
64
89
  execute(queryStr: string, bindValues?: PreparedBindValues): void
@@ -67,6 +92,19 @@ export type BootDb = {
67
92
  txn(callback: () => void): void
68
93
  }
69
94
 
95
+ export class UnexpectedError extends Schema.TaggedError<UnexpectedError>()('LiveStore.UnexpectedError', {
96
+ cause: Schema.AnyError,
97
+ }) {}
98
+
99
+ export class SqliteError extends Schema.TaggedError<SqliteError>()('LiveStore.SqliteError', {
100
+ sql: Schema.String,
101
+ bindValues: Schema.Record(Schema.String, Schema.Any),
102
+ /** The SQLite result code */
103
+ code: Schema.Number,
104
+ /** The original SQLite3 error */
105
+ cause: Schema.AnyError,
106
+ }) {}
107
+
70
108
  // TODO possibly allow a combination of these options
71
109
  // TODO allow a way to stream the migration progress back to the app
72
110
  export type MigrationOptions<TSchema extends LiveStoreSchema = LiveStoreSchema> =
@@ -107,7 +145,7 @@ export type MigrationOptionsFromMutationLog<TSchema extends LiveStoreSchema = Li
107
145
  }
108
146
 
109
147
  export type StoreAdapterFactory = (opts: {
110
- otelTracer: otel.Tracer
111
- otelContext: otel.Context
112
148
  schema: LiveStoreSchema
113
- }) => StoreAdapter | Promise<StoreAdapter>
149
+ devtoolsEnabled: boolean
150
+ bootStatusQueue: Queue.Queue<BootStatus>
151
+ }) => Effect.Effect<StoreAdapter, UnexpectedError>
@@ -1,11 +1,10 @@
1
- import { version as pkgVersion } from '@livestore/common/package.json'
2
- import { Schema } from '@livestore/utils/effect'
3
- import { type SqliteDsl as __SqliteDsl } from 'effect-db-schema'
1
+ import { Schema, Transferable } from '@livestore/utils/effect'
4
2
 
5
3
  import { NetworkStatus } from '../adapter-types.js'
6
4
  import { DebugInfo } from '../debug-info.js'
7
5
  import { mutationEventSchemaEncodedAny } from '../schema/mutations.js'
8
6
  import { PreparedBindValues } from '../util.js'
7
+ import { liveStoreVersion as pkgVersion } from '../version.js'
9
8
 
10
9
  const requestId = Schema.String
11
10
  const channelId = Schema.String
@@ -20,20 +19,21 @@ export class SnapshotReq extends Schema.TaggedStruct('LSD.SnapshotReq', {
20
19
  export class SnapshotRes extends Schema.TaggedStruct('LSD.SnapshotRes', {
21
20
  liveStoreVersion,
22
21
  requestId,
23
- snapshot: Schema.Uint8Array,
22
+ snapshot: Transferable.Uint8Array,
24
23
  }).annotations({ identifier: 'LSD.SnapshotRes' }) {}
25
24
 
26
- export class LoadSnapshotReq extends Schema.TaggedStruct('LSD.LoadSnapshotReq', {
25
+ export class LoadDatabaseFileReq extends Schema.TaggedStruct('LSD.LoadDatabaseFileReq', {
27
26
  liveStoreVersion,
28
27
  requestId,
29
28
  channelId,
30
- snapshot: Schema.Uint8Array,
31
- }).annotations({ identifier: 'LSD.LoadSnapshotReq' }) {}
29
+ data: Transferable.Uint8Array,
30
+ }).annotations({ identifier: 'LSD.LoadDatabaseFileReq' }) {}
32
31
 
33
- export class LoadSnapshotRes extends Schema.TaggedStruct('LSD.LoadSnapshotRes', {
32
+ export class LoadDatabaseFileRes extends Schema.TaggedStruct('LSD.LoadDatabaseFileRes', {
34
33
  liveStoreVersion,
35
34
  requestId,
36
- }).annotations({ identifier: 'LSD.LoadSnapshotRes' }) {}
35
+ status: Schema.Literal('ok', 'unsupported-file', 'unsupported-database'),
36
+ }).annotations({ identifier: 'LSD.LoadDatabaseFileRes' }) {}
37
37
 
38
38
  export class DebugInfoReq extends Schema.TaggedStruct('LSD.DebugInfoReq', {
39
39
  liveStoreVersion,
@@ -102,21 +102,9 @@ export class MutationLogRes extends Schema.TaggedStruct('LSD.MutationLogRes', {
102
102
  liveStoreVersion,
103
103
  requestId,
104
104
  channelId,
105
- mutationLog: Schema.Uint8Array,
105
+ mutationLog: Transferable.Uint8Array,
106
106
  }).annotations({ identifier: 'LSD.MutationLogRes' }) {}
107
107
 
108
- export class LoadMutationLogReq extends Schema.TaggedStruct('LSD.LoadMutationLogReq', {
109
- liveStoreVersion,
110
- requestId,
111
- channelId,
112
- mutationLog: Schema.Uint8Array,
113
- }).annotations({ identifier: 'LSD.LoadMutationLogReq' }) {}
114
-
115
- export class LoadMutationLogRes extends Schema.TaggedStruct('LSD.LoadMutationLogRes', {
116
- liveStoreVersion,
117
- requestId,
118
- }).annotations({ identifier: 'LSD.LoadMutationLogRes' }) {}
119
-
120
108
  export class ReactivityGraphSubscribe extends Schema.TaggedStruct('LSD.ReactivityGraphSubscribe', {
121
109
  liveStoreVersion,
122
110
  requestId,
@@ -178,6 +166,19 @@ export class ResetAllDataRes extends Schema.TaggedStruct('LSD.ResetAllDataRes',
178
166
  requestId,
179
167
  }).annotations({ identifier: 'LSD.ResetAllDataRes' }) {}
180
168
 
169
+ export class MessagePortForStoreReq extends Schema.TaggedStruct('LSD.MessagePortForStoreReq', {
170
+ liveStoreVersion,
171
+ requestId,
172
+ channelId,
173
+ }).annotations({ identifier: 'LSD.MessagePortForStoreReq' }) {}
174
+
175
+ export class MessagePortForStoreRes extends Schema.TaggedStruct('LSD.MessagePortForStoreRes', {
176
+ liveStoreVersion,
177
+ requestId,
178
+ channelId,
179
+ port: Transferable.MessagePort,
180
+ }).annotations({ identifier: 'LSD.MessagePortForStoreRes' }) {}
181
+
181
182
  export class NetworkStatusChanged extends Schema.TaggedStruct('LSD.NetworkStatusChanged', {
182
183
  liveStoreVersion,
183
184
  channelId,
@@ -204,11 +205,33 @@ export class Disconnect extends Schema.TaggedStruct('LSD.Disconnect', {
204
205
  channelId,
205
206
  }).annotations({ identifier: 'LSD.Disconnect' }) {}
206
207
 
207
- export const MessageToAppHost = Schema.Union(
208
+ export class Ping extends Schema.TaggedStruct('LSD.Ping', {
209
+ liveStoreVersion,
210
+ requestId,
211
+ channelId,
212
+ }).annotations({ identifier: 'LSD.Ping' }) {}
213
+
214
+ export class Pong extends Schema.TaggedStruct('LSD.Pong', {
215
+ liveStoreVersion,
216
+ requestId,
217
+ }).annotations({ identifier: 'LSD.Pong' }) {}
218
+
219
+ export const MessageToAppHostCoordinator = Schema.Union(
208
220
  SnapshotReq,
209
- LoadSnapshotReq,
221
+ LoadDatabaseFileReq,
210
222
  MutationLogReq,
211
- LoadMutationLogReq,
223
+ ResetAllDataReq,
224
+ MessagePortForStoreRes,
225
+ DevtoolsReady,
226
+ Disconnect,
227
+ DevtoolsConnected,
228
+ RunMutationReq,
229
+ Ping,
230
+ ).annotations({ identifier: 'LSD.MessageToAppHostCoordinator' })
231
+
232
+ export type MessageToAppHostCoordinator = typeof MessageToAppHostCoordinator.Type
233
+
234
+ export const MessageToAppHostStore = Schema.Union(
212
235
  DebugInfoReq,
213
236
  DebugInfoResetReq,
214
237
  DebugInfoRerunQueryReq,
@@ -216,37 +239,34 @@ export const MessageToAppHost = Schema.Union(
216
239
  ReactivityGraphUnsubscribe,
217
240
  LiveQueriesSubscribe,
218
241
  LiveQueriesUnsubscribe,
219
- ResetAllDataReq,
220
- DevtoolsReady,
221
- Disconnect,
222
- DevtoolsConnected,
223
- RunMutationReq,
224
- ).annotations({ identifier: 'LSD.MessageToAppHost' })
242
+ // Ping,
243
+ ).annotations({ identifier: 'LSD.MessageToAppHostStore' })
225
244
 
226
- export type MessageToAppHost = typeof MessageToAppHost.Type
245
+ export type MessageToAppHostStore = typeof MessageToAppHostStore.Type
227
246
 
228
- export const MessageFromAppHost = Schema.Union(
247
+ export const MessageFromAppHostCoordinator = Schema.Union(
229
248
  SnapshotRes,
230
- LoadSnapshotRes,
249
+ LoadDatabaseFileRes,
231
250
  MutationLogRes,
232
- LoadMutationLogRes,
233
- DebugInfoRes,
234
- DebugInfoResetRes,
235
- DebugInfoRerunQueryRes,
236
- ReactivityGraphRes,
237
- LiveQueriesRes,
238
251
  ResetAllDataRes,
252
+ MessagePortForStoreReq,
239
253
  Disconnect,
240
254
  MutationBroadcast,
241
255
  AppHostReady,
242
256
  NetworkStatusChanged,
243
257
  RunMutationRes,
244
- ).annotations({ identifier: 'LSD.MessageFromAppHost' })
258
+ Pong,
259
+ ).annotations({ identifier: 'LSD.MessageFromAppHostCoordinator' })
260
+
261
+ export type MessageFromAppHostCoordinator = typeof MessageFromAppHostCoordinator.Type
245
262
 
246
- export type MessageFromAppHost = typeof MessageFromAppHost.Type
263
+ export const MessageFromAppHostStore = Schema.Union(
264
+ DebugInfoRes,
265
+ DebugInfoResetRes,
266
+ DebugInfoRerunQueryRes,
267
+ ReactivityGraphRes,
268
+ LiveQueriesRes,
269
+ // Pong,
270
+ ).annotations({ identifier: 'LSD.MessageFromAppHostStore' })
247
271
 
248
- // TODO make specific over app key
249
- export const makeBroadcastChannels = () => ({
250
- fromAppHost: new BroadcastChannel(`livestore-devtools-from-app-host`),
251
- toAppHost: new BroadcastChannel(`livestore-devtools-to-app-host`),
252
- })
272
+ export type MessageFromAppHostStore = typeof MessageFromAppHostStore.Type
@@ -0,0 +1,25 @@
1
+ import { Schema, Transferable } from '@livestore/utils/effect'
2
+
3
+ const channelId = Schema.String
4
+
5
+ export namespace DevtoolsWindowMessage {
6
+ /** Message is being created in contentscript-iframe, sent to contentscript and then sent to Store */
7
+ export class MessagePortReady extends Schema.TaggedStruct('LSD.WindowMessage.MessagePortForStore', {
8
+ port: Transferable.MessagePort,
9
+ channelId,
10
+ }) {}
11
+
12
+ export class ContentscriptListening extends Schema.TaggedStruct('LSD.WindowMessage.ContentscriptListening', {}) {}
13
+
14
+ // export class ContentscriptReady extends Schema.TaggedStruct('LSD.WindowMessage.ContentscriptReady', {
15
+ // channelId,
16
+ // }) {}
17
+
18
+ export class StoreReady extends Schema.TaggedStruct('LSD.WindowMessage.StoreReady', {
19
+ channelId,
20
+ }) {}
21
+
22
+ export class MessageForStore extends Schema.Union(MessagePortReady, ContentscriptListening) {}
23
+
24
+ export class MessageForContentscript extends Schema.Union(StoreReady) {}
25
+ }
@@ -1 +1,2 @@
1
1
  export * from './devtools-messages.js'
2
+ export * from './devtools-window-message.js'
package/src/index.ts CHANGED
@@ -11,3 +11,4 @@ export * from './sync/index.js'
11
11
  export * as Devtools from './devtools/index.js'
12
12
  export * from './debug-info.js'
13
13
  export * from './bounded-collections.js'
14
+ export * from './version.js'
@@ -1,91 +1,127 @@
1
1
  import { shouldNeverHappen } from '@livestore/utils'
2
- import { Schema } from '@livestore/utils/effect'
2
+ import { Chunk, Effect, Option, Schema, Stream } from '@livestore/utils/effect'
3
3
 
4
- import type { InMemoryDatabase, MigrationOptionsFromMutationLog } from './adapter-types.js'
4
+ import { type InMemoryDatabase, type MigrationOptionsFromMutationLog, SqliteError } from './adapter-types.js'
5
5
  import { getExecArgsFromMutation } from './mutation.js'
6
6
  import type { LiveStoreSchema, MutationLogMetaRow } from './schema/index.js'
7
7
  import { MUTATION_LOG_META_TABLE } from './schema/index.js'
8
+ import type { PreparedBindValues } from './util.js'
9
+ import { sql } from './util.js'
8
10
 
9
- export const rehydrateFromMutationLog = async ({
11
+ export const rehydrateFromMutationLog = ({
10
12
  logDb,
11
13
  db,
12
14
  schema,
13
15
  migrationOptions,
16
+ onProgress,
14
17
  }: {
15
18
  logDb: InMemoryDatabase
16
19
  db: InMemoryDatabase
17
20
  schema: LiveStoreSchema
18
21
  migrationOptions: MigrationOptionsFromMutationLog
19
- }) => {
20
- try {
21
- // TODO possibly implement this in a streaming fashion
22
- const stmt = logDb.prepare(`SELECT * FROM ${MUTATION_LOG_META_TABLE} ORDER BY id ASC`)
23
- const results = stmt.select<MutationLogMetaRow>(undefined)
22
+ onProgress: (_: { done: number; total: number }) => Effect.Effect<void>
23
+ }) =>
24
+ Effect.gen(function* () {
25
+ const mutationsCount = logDb
26
+ .prepare(`SELECT COUNT(*) AS count FROM ${MUTATION_LOG_META_TABLE}`)
27
+ .select<{ count: number }>(undefined)[0]!.count
24
28
 
25
- performance.mark('livestore:hydrate-from-mutationlog:start')
29
+ const processMutation = (row: MutationLogMetaRow) =>
30
+ Effect.gen(function* () {
31
+ const mutationDef = schema.mutations.get(row.mutation) ?? shouldNeverHappen(`Unknown mutation ${row.mutation}`)
26
32
 
27
- for (const row of results) {
28
- const mutationDef = schema.mutations.get(row.mutation) ?? shouldNeverHappen(`Unknown mutation ${row.mutation}`)
33
+ if (migrationOptions.excludeMutations?.has(row.mutation) === true) return
29
34
 
30
- if (migrationOptions.excludeMutations?.has(row.mutation) === true) continue
31
-
32
- if (Schema.hash(mutationDef.schema) !== row.schemaHash) {
33
- console.warn(`Schema hash mismatch for mutation ${row.mutation}. Trying to apply mutation anyway.`)
34
- }
35
+ if (Schema.hash(mutationDef.schema) !== row.schemaHash) {
36
+ console.warn(`Schema hash mismatch for mutation ${row.mutation}. Trying to apply mutation anyway.`)
37
+ }
35
38
 
36
- const argsDecodedEither = Schema.decodeUnknownEither(Schema.parseJson(mutationDef.schema))(row.argsJson)
37
- if (argsDecodedEither._tag === 'Left') {
38
- return shouldNeverHappen(`\
39
+ const argsDecodedEither = Schema.decodeUnknownEither(Schema.parseJson(mutationDef.schema))(row.argsJson)
40
+ if (argsDecodedEither._tag === 'Left') {
41
+ return shouldNeverHappen(`\
39
42
  There was an error decoding the persisted mutation event args for mutation "${row.mutation}".
40
43
  This likely means the schema has changed in an incompatible way.
41
44
 
42
45
  Error: ${argsDecodedEither.left}
43
46
  `)
44
- }
45
-
46
- const mutationEventDecoded = {
47
- id: row.id,
48
- mutation: row.mutation,
49
- args: argsDecodedEither.right,
50
- }
51
- // const argsEncoded = JSON.parse(row.args_json)
52
- // const mutationSqlRes =
53
- // typeof mutation.sql === 'string'
54
- // ? mutation.sql
55
- // : mutation.sql(Schema.decodeUnknownSync(mutation.schema)(argsEncoded))
56
- // const mutationSql = typeof mutationSqlRes === 'string' ? mutationSqlRes : mutationSqlRes.sql
57
- // const bindValues = typeof mutationSqlRes === 'string' ? argsEncoded : mutationSqlRes.bindValues
58
-
59
- const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
60
-
61
- for (const { statementSql, bindValues } of execArgsArr) {
62
- try {
63
- const getRowsChanged = db.execute(statementSql, bindValues)
64
- if (
65
- import.meta.env.DEV &&
66
- getRowsChanged() === 0 &&
67
- migrationOptions.logging?.excludeAffectedRows?.(statementSql) !== true
68
- ) {
69
- console.warn(`Mutation "${mutationDef.name}" did not affect any rows:`, statementSql, bindValues)
47
+ }
48
+
49
+ const mutationEventDecoded = {
50
+ id: row.id,
51
+ mutation: row.mutation,
52
+ args: argsDecodedEither.right,
53
+ }
54
+ // const argsEncoded = JSON.parse(row.args_json)
55
+ // const mutationSqlRes =
56
+ // typeof mutation.sql === 'string'
57
+ // ? mutation.sql
58
+ // : mutation.sql(Schema.decodeUnknownSync(mutation.schema)(argsEncoded))
59
+ // const mutationSql = typeof mutationSqlRes === 'string' ? mutationSqlRes : mutationSqlRes.sql
60
+ // const bindValues = typeof mutationSqlRes === 'string' ? argsEncoded : mutationSqlRes.bindValues
61
+
62
+ const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
63
+
64
+ for (const { statementSql, bindValues } of execArgsArr) {
65
+ try {
66
+ // TODO cache prepared statements for mutations
67
+ const getRowsChanged = db.execute(statementSql, bindValues)
68
+ if (
69
+ import.meta.env.DEV &&
70
+ getRowsChanged() === 0 &&
71
+ migrationOptions.logging?.excludeAffectedRows?.(statementSql) !== true
72
+ ) {
73
+ console.warn(`Mutation "${mutationDef.name}" did not affect any rows:`, statementSql, bindValues)
74
+ }
75
+ // console.log(`Re-executed mutation ${mutationSql}`, bindValues)
76
+ } catch (e) {
77
+ yield* new SqliteError({
78
+ sql: statementSql,
79
+ bindValues,
80
+ code: (e as any).resultCode,
81
+ cause: e,
82
+ })
70
83
  }
71
- // console.log(`Re-executed mutation ${mutationSql}`, bindValues)
72
- } catch (e) {
73
- console.error(`Error executing migration for mutation ${statementSql}`, bindValues, e)
74
- debugger
75
- throw e
76
84
  }
77
- }
78
- }
79
- } catch (e) {
80
- console.error('Error while rehydrating database from mutation log', e)
81
- debugger
82
- throw e
83
- } finally {
84
- performance.mark('livestore:hydrate-from-mutationlog:end')
85
- performance.measure(
86
- 'livestore:hydrate-from-mutationlog',
87
- 'livestore:hydrate-from-mutationlog:start',
88
- 'livestore:hydrate-from-mutationlog:end',
85
+ }).pipe(Effect.withSpan(`@livestore/common:rehydrateFromMutationLog:processMutation`))
86
+
87
+ const CHUNK_SIZE = 100
88
+
89
+ const stmt = logDb.prepare(sql`\
90
+ SELECT * FROM ${MUTATION_LOG_META_TABLE}
91
+ WHERE id > COALESCE($id, '')
92
+ ORDER BY id ASC
93
+ LIMIT ${CHUNK_SIZE}
94
+ `)
95
+
96
+ let processedMutations = 0
97
+
98
+ yield* Stream.unfoldChunk<Chunk.Chunk<MutationLogMetaRow> | { _tag: 'Initial ' }, MutationLogMetaRow>(
99
+ { _tag: 'Initial ' },
100
+ (item) => {
101
+ // End stream if no more rows
102
+ if (Chunk.isChunk(item) && item.length === 0) return Option.none()
103
+
104
+ const lastId = Chunk.isChunk(item) ? Chunk.last(item).pipe(Option.getOrUndefined)?.id : undefined
105
+ const nextItem = Chunk.fromIterable(
106
+ stmt.select<MutationLogMetaRow>({ $id: lastId } as any as PreparedBindValues),
107
+ )
108
+ const prevItem = Chunk.isChunk(item) ? item : Chunk.empty()
109
+ return Option.some([prevItem, nextItem])
110
+ },
111
+ ).pipe(
112
+ (_) => _,
113
+ Stream.bufferChunks({ capacity: 2 }),
114
+ Stream.tap((row) =>
115
+ Effect.gen(function* () {
116
+ yield* processMutation(row)
117
+
118
+ processedMutations++
119
+ yield* onProgress({ done: processedMutations, total: mutationsCount })
120
+ }),
121
+ ),
122
+ Stream.runDrain,
89
123
  )
90
- }
91
- }
124
+ }).pipe(
125
+ Effect.withPerformanceMeasure('@livestore/common:rehydrateFromMutationLog'),
126
+ Effect.withSpan('@livestore/common:rehydrateFromMutationLog'),
127
+ )
@@ -86,8 +86,6 @@ export type TableOptions = {
86
86
  * @default false
87
87
  */
88
88
  isSingleton: boolean
89
- // TODO remove
90
- dynamicRegistration: boolean
91
89
  disableAutomaticIdColumn: boolean
92
90
  /**
93
91
  * Setting this to true will automatically derive insert, update and delete mutations for this table. Example:
@@ -139,7 +137,6 @@ export const table = <
139
137
 
140
138
  const options_: TableOptions = {
141
139
  isSingleton: options?.isSingleton ?? false,
142
- dynamicRegistration: options?.dynamicRegistration ?? false,
143
140
  disableAutomaticIdColumn: options?.disableAutomaticIdColumn ?? false,
144
141
  deriveMutations:
145
142
  options?.deriveMutations === true
@@ -239,7 +236,6 @@ type WithId<TColumns extends SqliteDsl.Columns, TOptions extends TableOptions> =
239
236
 
240
237
  type WithDefaults<TOptionsInput extends TableOptionsInput, TIsSingleColumn extends boolean> = {
241
238
  isSingleton: TOptionsInput['isSingleton'] extends true ? true : false
242
- dynamicRegistration: TOptionsInput['dynamicRegistration'] extends true ? true : false
243
239
  disableAutomaticIdColumn: TOptionsInput['disableAutomaticIdColumn'] extends true ? true : false
244
240
  deriveMutations: TOptionsInput['deriveMutations'] extends true
245
241
  ? { enabled: true; localOnly: boolean }