@livestore/common 0.3.0-dev.10 → 0.3.0-dev.11
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/adapter-types.d.ts +22 -13
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js.map +1 -1
- package/dist/devtools/devtool-message-leader.d.ts +2 -0
- package/dist/devtools/devtool-message-leader.d.ts.map +1 -0
- package/dist/devtools/devtool-message-leader.js +2 -0
- package/dist/devtools/devtool-message-leader.js.map +1 -0
- package/dist/devtools/devtools-messages-client-session.d.ts +297 -0
- package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -0
- package/dist/devtools/devtools-messages-client-session.js +61 -0
- package/dist/devtools/devtools-messages-client-session.js.map +1 -0
- package/dist/devtools/devtools-messages-common.d.ts +65 -0
- package/dist/devtools/devtools-messages-common.d.ts.map +1 -0
- package/dist/devtools/devtools-messages-common.js +35 -0
- package/dist/devtools/devtools-messages-common.js.map +1 -0
- package/dist/devtools/devtools-messages-leader.d.ts +261 -0
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -0
- package/dist/devtools/devtools-messages-leader.js +85 -0
- package/dist/devtools/devtools-messages-leader.js.map +1 -0
- package/dist/devtools/devtools-messages.d.ts +3 -580
- package/dist/devtools/devtools-messages.d.ts.map +1 -1
- package/dist/devtools/devtools-messages.js +3 -174
- package/dist/devtools/devtools-messages.js.map +1 -1
- package/dist/init-singleton-tables.d.ts +2 -2
- package/dist/init-singleton-tables.d.ts.map +1 -1
- package/dist/init-singleton-tables.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts +4 -4
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +50 -35
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/apply-mutation.d.ts.map +1 -1
- package/dist/leader-thread/apply-mutation.js +4 -4
- package/dist/leader-thread/apply-mutation.js.map +1 -1
- package/dist/leader-thread/connection.d.ts +4 -4
- package/dist/leader-thread/connection.d.ts.map +1 -1
- package/dist/leader-thread/connection.js +5 -5
- package/dist/leader-thread/connection.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +17 -17
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts +6 -6
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +23 -10
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/mutationlog.d.ts +4 -4
- package/dist/leader-thread/mutationlog.d.ts.map +1 -1
- package/dist/leader-thread/mutationlog.js +6 -6
- package/dist/leader-thread/mutationlog.js.map +1 -1
- package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +19 -19
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/types.d.ts +20 -8
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/rehydrate-from-mutationlog.d.ts +3 -3
- package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
- package/dist/rehydrate-from-mutationlog.js.map +1 -1
- package/dist/schema-management/common.d.ts +3 -3
- package/dist/schema-management/common.d.ts.map +1 -1
- package/dist/schema-management/common.js.map +1 -1
- package/dist/schema-management/migrations.d.ts +4 -4
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +5 -7
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +13 -6
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/sync/next/test/mutation-fixtures.d.ts +7 -7
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/adapter-types.ts +18 -17
- package/src/devtools/devtools-messages-client-session.ts +109 -0
- package/src/devtools/devtools-messages-common.ts +52 -0
- package/src/devtools/devtools-messages-leader.ts +115 -0
- package/src/devtools/devtools-messages.ts +3 -246
- package/src/init-singleton-tables.ts +2 -2
- package/src/leader-thread/LeaderSyncProcessor.ts +78 -45
- package/src/leader-thread/apply-mutation.ts +5 -5
- package/src/leader-thread/connection.ts +7 -7
- package/src/leader-thread/leader-worker-devtools.ts +25 -18
- package/src/leader-thread/make-leader-thread-layer.ts +38 -17
- package/src/leader-thread/mutationlog.ts +9 -9
- package/src/leader-thread/recreate-db.ts +19 -19
- package/src/leader-thread/types.ts +24 -10
- package/src/rehydrate-from-mutationlog.ts +3 -3
- package/src/schema-management/common.ts +3 -3
- package/src/schema-management/migrations.ts +4 -4
- package/src/sync/ClientSessionSyncProcessor.ts +18 -13
- package/src/version.ts +1 -1
@@ -1,7 +1,7 @@
|
|
1
1
|
import type { HttpClient, Scope } from '@livestore/utils/effect'
|
2
2
|
import { Deferred, Effect, Layer, Queue, SubscriptionRef } from '@livestore/utils/effect'
|
3
3
|
|
4
|
-
import type { BootStatus,
|
4
|
+
import type { BootStatus, MakeSqliteDb, SqliteError } from '../adapter-types.js'
|
5
5
|
import { UnexpectedError } from '../adapter-types.js'
|
6
6
|
import type * as Devtools from '../devtools/index.js'
|
7
7
|
import type { LiveStoreSchema } from '../schema/mod.js'
|
@@ -15,27 +15,33 @@ import { makeLeaderSyncProcessor } from './LeaderSyncProcessor.js'
|
|
15
15
|
import { makePullQueueSet } from './pull-queue-set.js'
|
16
16
|
import { recreateDb } from './recreate-db.js'
|
17
17
|
import type { ShutdownChannel } from './shutdown-channel.js'
|
18
|
-
import type {
|
18
|
+
import type {
|
19
|
+
DevtoolsOptions,
|
20
|
+
InitialBlockingSyncContext,
|
21
|
+
InitialSyncOptions,
|
22
|
+
LeaderSqliteDb,
|
23
|
+
ShutdownState,
|
24
|
+
} from './types.js'
|
19
25
|
import { LeaderThreadCtx } from './types.js'
|
20
26
|
|
21
27
|
export const makeLeaderThreadLayer = ({
|
22
28
|
schema,
|
23
29
|
storeId,
|
24
30
|
clientId,
|
25
|
-
|
31
|
+
makeSqliteDb,
|
26
32
|
syncOptions,
|
27
|
-
|
28
|
-
|
33
|
+
dbReadModel,
|
34
|
+
dbMutationLog,
|
29
35
|
devtoolsOptions,
|
30
36
|
shutdownChannel,
|
31
37
|
}: {
|
32
38
|
storeId: string
|
33
39
|
clientId: string
|
34
40
|
schema: LiveStoreSchema
|
35
|
-
|
41
|
+
makeSqliteDb: MakeSqliteDb
|
36
42
|
syncOptions: SyncOptions | undefined
|
37
|
-
|
38
|
-
|
43
|
+
dbReadModel: LeaderSqliteDb
|
44
|
+
dbMutationLog: LeaderSqliteDb
|
39
45
|
devtoolsOptions: DevtoolsOptions
|
40
46
|
shutdownChannel: ShutdownChannel
|
41
47
|
}): Layer.Layer<LeaderThreadCtx, UnexpectedError, Scope.Scope | HttpClient.HttpClient> =>
|
@@ -44,7 +50,8 @@ export const makeLeaderThreadLayer = ({
|
|
44
50
|
|
45
51
|
// TODO do more validation here than just checking the count of tables
|
46
52
|
// Either happens on initial boot or if schema changes
|
47
|
-
const dbMissing =
|
53
|
+
const dbMissing =
|
54
|
+
dbReadModel.select<{ count: number }>(sql`select count(*) as count from sqlite_master`)[0]!.count === 0
|
48
55
|
|
49
56
|
const syncBackend = syncOptions === undefined ? undefined : yield* syncOptions.makeBackend({ storeId, clientId })
|
50
57
|
|
@@ -53,20 +60,33 @@ export const makeLeaderThreadLayer = ({
|
|
53
60
|
bootStatusQueue,
|
54
61
|
})
|
55
62
|
|
56
|
-
const syncProcessor = yield* makeLeaderSyncProcessor({
|
63
|
+
const syncProcessor = yield* makeLeaderSyncProcessor({
|
64
|
+
schema,
|
65
|
+
dbMissing,
|
66
|
+
dbMutationLog,
|
67
|
+
initialBlockingSyncContext,
|
68
|
+
})
|
57
69
|
|
58
70
|
const extraIncomingMessagesQueue = yield* Queue.unbounded<Devtools.MessageToAppLeader>().pipe(
|
59
71
|
Effect.acquireRelease(Queue.shutdown),
|
60
72
|
)
|
61
73
|
|
74
|
+
const devtoolsContext = devtoolsOptions.enabled
|
75
|
+
? {
|
76
|
+
enabled: true as const,
|
77
|
+
syncBackendPullLatch: yield* Effect.makeLatch(true),
|
78
|
+
syncBackendPushLatch: yield* Effect.makeLatch(true),
|
79
|
+
}
|
80
|
+
: { enabled: false as const }
|
81
|
+
|
62
82
|
const ctx = {
|
63
83
|
schema,
|
64
84
|
bootStatusQueue,
|
65
85
|
storeId,
|
66
86
|
clientId,
|
67
|
-
|
68
|
-
|
69
|
-
|
87
|
+
dbReadModel,
|
88
|
+
dbMutationLog,
|
89
|
+
makeSqliteDb,
|
70
90
|
mutationEventSchema: MutationEvent.makeMutationEventSchema(schema),
|
71
91
|
shutdownStateSubRef: yield* SubscriptionRef.make<ShutdownState>('running'),
|
72
92
|
shutdownChannel,
|
@@ -74,6 +94,7 @@ export const makeLeaderThreadLayer = ({
|
|
74
94
|
syncProcessor,
|
75
95
|
connectedClientSessionPullQueues: yield* makePullQueueSet,
|
76
96
|
extraIncomingMessagesQueue,
|
97
|
+
devtools: devtoolsContext,
|
77
98
|
} satisfies typeof LeaderThreadCtx.Service
|
78
99
|
|
79
100
|
// @ts-expect-error For debugging purposes
|
@@ -156,17 +177,17 @@ const bootLeaderThread = ({
|
|
156
177
|
LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient
|
157
178
|
> =>
|
158
179
|
Effect.gen(function* () {
|
159
|
-
const {
|
180
|
+
const { dbMutationLog, bootStatusQueue, syncProcessor } = yield* LeaderThreadCtx
|
160
181
|
|
161
182
|
yield* migrateTable({
|
162
|
-
db:
|
183
|
+
db: dbMutationLog,
|
163
184
|
behaviour: 'create-if-not-exists',
|
164
185
|
tableAst: mutationLogMetaTable.sqliteDef.ast,
|
165
186
|
skipMetaTable: true,
|
166
187
|
})
|
167
188
|
|
168
189
|
yield* migrateTable({
|
169
|
-
db:
|
190
|
+
db: dbMutationLog,
|
170
191
|
behaviour: 'create-if-not-exists',
|
171
192
|
tableAst: syncStatusTable.sqliteDef.ast,
|
172
193
|
skipMetaTable: true,
|
@@ -174,7 +195,7 @@ const bootLeaderThread = ({
|
|
174
195
|
|
175
196
|
// Create sync status row if it doesn't exist
|
176
197
|
yield* execSql(
|
177
|
-
|
198
|
+
dbMutationLog,
|
178
199
|
sql`INSERT INTO ${SYNC_STATUS_TABLE} (head)
|
179
200
|
SELECT ${EventId.ROOT.global}
|
180
201
|
WHERE NOT EXISTS (SELECT 1 FROM ${SYNC_STATUS_TABLE})`,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import { Effect, Schema } from '@livestore/utils/effect'
|
2
2
|
|
3
|
-
import type {
|
3
|
+
import type { SqliteDb } from '../adapter-types.js'
|
4
4
|
import * as EventId from '../schema/EventId.js'
|
5
5
|
import type * as MutationEvent from '../schema/MutationEvent.js'
|
6
6
|
import { MUTATION_LOG_META_TABLE, mutationLogMetaTable, SYNC_STATUS_TABLE } from '../schema/system-tables.js'
|
@@ -11,10 +11,10 @@ export const getMutationEventsSince = (
|
|
11
11
|
since: EventId.EventId,
|
12
12
|
): Effect.Effect<ReadonlyArray<MutationEvent.AnyEncoded>, never, LeaderThreadCtx> =>
|
13
13
|
Effect.gen(function* () {
|
14
|
-
const {
|
14
|
+
const { dbMutationLog } = yield* LeaderThreadCtx
|
15
15
|
|
16
16
|
const query = mutationLogMetaTable.query.where('idGlobal', '>=', since.global).asSql()
|
17
|
-
const pendingMutationEventsRaw =
|
17
|
+
const pendingMutationEventsRaw = dbMutationLog.select(query.query, prepareBindValues(query.bindValues, query.query))
|
18
18
|
const pendingMutationEvents = Schema.decodeUnknownSync(mutationLogMetaTable.schema.pipe(Schema.Array))(
|
19
19
|
pendingMutationEventsRaw,
|
20
20
|
)
|
@@ -29,18 +29,18 @@ export const getMutationEventsSince = (
|
|
29
29
|
.filter((_) => EventId.compare(_.id, since) > 0)
|
30
30
|
})
|
31
31
|
|
32
|
-
export const getLocalHeadFromDb = (
|
33
|
-
const res =
|
32
|
+
export const getLocalHeadFromDb = (dbMutationLog: SqliteDb): EventId.EventId => {
|
33
|
+
const res = dbMutationLog.select<{ idGlobal: EventId.GlobalEventId; idLocal: EventId.LocalEventId }>(
|
34
34
|
sql`select idGlobal, idLocal from ${MUTATION_LOG_META_TABLE} order by idGlobal DESC, idLocal DESC limit 1`,
|
35
35
|
)[0]
|
36
36
|
|
37
37
|
return res ? { global: res.idGlobal, local: res.idLocal } : EventId.ROOT
|
38
38
|
}
|
39
39
|
|
40
|
-
export const getBackendHeadFromDb = (
|
41
|
-
|
40
|
+
export const getBackendHeadFromDb = (dbMutationLog: SqliteDb): EventId.GlobalEventId =>
|
41
|
+
dbMutationLog.select<{ head: EventId.GlobalEventId }>(sql`select head from ${SYNC_STATUS_TABLE}`)[0]?.head ??
|
42
42
|
EventId.ROOT.global
|
43
43
|
|
44
44
|
// TODO use prepared statements
|
45
|
-
export const updateBackendHead = (
|
46
|
-
|
45
|
+
export const updateBackendHead = (dbMutationLog: SqliteDb, head: EventId.EventId) =>
|
46
|
+
dbMutationLog.execute(sql`UPDATE ${SYNC_STATUS_TABLE} SET head = ${head.global}`)
|
@@ -12,56 +12,56 @@ export const recreateDb: Effect.Effect<
|
|
12
12
|
UnexpectedError | SqliteError | IsOfflineError | InvalidPullError,
|
13
13
|
LeaderThreadCtx | HttpClient.HttpClient
|
14
14
|
> = Effect.gen(function* () {
|
15
|
-
const {
|
15
|
+
const { dbReadModel, dbMutationLog, schema, bootStatusQueue } = yield* LeaderThreadCtx
|
16
16
|
|
17
17
|
const migrationOptions = schema.migrationOptions
|
18
18
|
|
19
19
|
yield* Effect.addFinalizer(
|
20
20
|
Effect.fn('recreateDb:finalizer')(function* (ex) {
|
21
|
-
if (ex._tag === 'Failure')
|
21
|
+
if (ex._tag === 'Failure') dbReadModel.destroy()
|
22
22
|
}),
|
23
23
|
)
|
24
24
|
|
25
25
|
// NOTE to speed up the operations below, we're creating a temporary in-memory database
|
26
26
|
// and later we'll overwrite the persisted database with the new data
|
27
27
|
// TODO bring back this optimization
|
28
|
-
// const
|
29
|
-
const
|
30
|
-
yield* configureConnection(
|
28
|
+
// const tmpDb = yield* makeSqliteDb({ _tag: 'in-memory' })
|
29
|
+
const tmpDb = dbReadModel
|
30
|
+
yield* configureConnection(tmpDb, { fkEnabled: true })
|
31
31
|
|
32
32
|
const initDb = (hooks: Partial<MigrationHooks> | undefined) =>
|
33
33
|
Effect.gen(function* () {
|
34
|
-
yield* Effect.tryAll(() => hooks?.init?.(
|
34
|
+
yield* Effect.tryAll(() => hooks?.init?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
35
35
|
|
36
36
|
yield* migrateDb({
|
37
|
-
db:
|
37
|
+
db: tmpDb,
|
38
38
|
schema,
|
39
39
|
onProgress: ({ done, total }) =>
|
40
40
|
Queue.offer(bootStatusQueue, { stage: 'migrating', progress: { done, total } }),
|
41
41
|
})
|
42
42
|
|
43
|
-
initializeSingletonTables(schema,
|
43
|
+
initializeSingletonTables(schema, tmpDb)
|
44
44
|
|
45
|
-
yield* Effect.tryAll(() => hooks?.pre?.(
|
45
|
+
yield* Effect.tryAll(() => hooks?.pre?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
46
46
|
|
47
|
-
return
|
47
|
+
return tmpDb
|
48
48
|
})
|
49
49
|
|
50
50
|
switch (migrationOptions.strategy) {
|
51
51
|
case 'from-mutation-log': {
|
52
52
|
const hooks = migrationOptions.hooks
|
53
|
-
const
|
53
|
+
const tmpDb = yield* initDb(hooks)
|
54
54
|
|
55
55
|
yield* rehydrateFromMutationLog({
|
56
|
-
db:
|
57
|
-
logDb:
|
56
|
+
db: tmpDb,
|
57
|
+
logDb: dbMutationLog,
|
58
58
|
schema,
|
59
59
|
migrationOptions,
|
60
60
|
onProgress: ({ done, total }) =>
|
61
61
|
Queue.offer(bootStatusQueue, { stage: 'rehydrating', progress: { done, total } }),
|
62
62
|
})
|
63
63
|
|
64
|
-
yield* Effect.tryAll(() => hooks?.post?.(
|
64
|
+
yield* Effect.tryAll(() => hooks?.post?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
65
65
|
|
66
66
|
break
|
67
67
|
}
|
@@ -76,13 +76,13 @@ export const recreateDb: Effect.Effect<
|
|
76
76
|
break
|
77
77
|
}
|
78
78
|
case 'manual': {
|
79
|
-
const oldDbData =
|
79
|
+
const oldDbData = dbReadModel.export()
|
80
80
|
|
81
81
|
const newDbData = yield* Effect.tryAll(() => migrationOptions.migrate(oldDbData)).pipe(
|
82
82
|
UnexpectedError.mapToUnexpectedError,
|
83
83
|
)
|
84
84
|
|
85
|
-
|
85
|
+
tmpDb.import(newDbData)
|
86
86
|
|
87
87
|
// TODO validate schema
|
88
88
|
|
@@ -95,17 +95,17 @@ export const recreateDb: Effect.Effect<
|
|
95
95
|
|
96
96
|
// TODO bring back
|
97
97
|
// Import the temporary in-memory database into the persistent database
|
98
|
-
// yield* Effect.sync(() => db.import(
|
98
|
+
// yield* Effect.sync(() => db.import(tmpDb)).pipe(
|
99
99
|
// Effect.withSpan('@livestore/common:leader-thread:recreateDb:import'),
|
100
100
|
// )
|
101
101
|
|
102
102
|
// TODO maybe bring back re-using this initial snapshot to avoid calling `.export()` again
|
103
103
|
// We've disabled this for now as it made the code too complex, as we often run syncing right after
|
104
104
|
// so the snapshot is no longer up to date
|
105
|
-
// const snapshotFromTmpDb =
|
105
|
+
// const snapshotFromTmpDb = tmpDb.export()
|
106
106
|
|
107
107
|
// TODO bring back
|
108
|
-
//
|
108
|
+
// tmpDb.close()
|
109
109
|
}).pipe(
|
110
110
|
Effect.scoped, // NOTE we're closing the scope here so finalizers are called when the effect is done
|
111
111
|
Effect.withSpan('@livestore/common:leader-thread:recreateDb'),
|
@@ -16,10 +16,10 @@ import type {
|
|
16
16
|
BootStatus,
|
17
17
|
Devtools,
|
18
18
|
InvalidPushError,
|
19
|
-
|
19
|
+
MakeSqliteDb,
|
20
20
|
PersistenceInfo,
|
21
|
+
SqliteDb,
|
21
22
|
SyncBackend,
|
22
|
-
SynchronousDatabase,
|
23
23
|
UnexpectedError,
|
24
24
|
} from '../index.js'
|
25
25
|
import type { EventId, LiveStoreSchema, MutationEvent } from '../schema/mod.js'
|
@@ -56,8 +56,8 @@ export type InitialSyncInfo = Option.Option<{
|
|
56
56
|
// | { _tag: 'Recreate'; snapshotRef: Ref.Ref<Uint8Array | undefined>; syncInfo: InitialSyncInfo }
|
57
57
|
// | { _tag: 'Reuse'; syncInfo: InitialSyncInfo }
|
58
58
|
|
59
|
-
export type
|
60
|
-
export type PersistenceInfoPair = {
|
59
|
+
export type LeaderSqliteDb = SqliteDb<{ dbPointer: number; persistenceInfo: PersistenceInfo }>
|
60
|
+
export type PersistenceInfoPair = { readModel: PersistenceInfo; mutationLog: PersistenceInfo }
|
61
61
|
|
62
62
|
export type DevtoolsOptions =
|
63
63
|
| {
|
@@ -65,7 +65,7 @@ export type DevtoolsOptions =
|
|
65
65
|
}
|
66
66
|
| {
|
67
67
|
enabled: true
|
68
|
-
|
68
|
+
makeBootContext: Effect.Effect<
|
69
69
|
{
|
70
70
|
devtoolsWebChannel: WebChannel.WebChannel<Devtools.MessageToAppLeader, Devtools.MessageFromAppLeader>
|
71
71
|
persistenceInfo: PersistenceInfoPair
|
@@ -75,25 +75,39 @@ export type DevtoolsOptions =
|
|
75
75
|
>
|
76
76
|
}
|
77
77
|
|
78
|
+
export type DevtoolsContext =
|
79
|
+
| {
|
80
|
+
enabled: true
|
81
|
+
syncBackendPullLatch: Effect.Latch
|
82
|
+
syncBackendPushLatch: Effect.Latch
|
83
|
+
}
|
84
|
+
| {
|
85
|
+
enabled: false
|
86
|
+
}
|
87
|
+
|
78
88
|
export class LeaderThreadCtx extends Context.Tag('LeaderThreadCtx')<
|
79
89
|
LeaderThreadCtx,
|
80
90
|
{
|
81
91
|
schema: LiveStoreSchema
|
82
92
|
storeId: string
|
83
93
|
clientId: string
|
84
|
-
|
85
|
-
|
86
|
-
|
94
|
+
makeSqliteDb: MakeSqliteDb
|
95
|
+
dbReadModel: LeaderSqliteDb
|
96
|
+
dbMutationLog: LeaderSqliteDb
|
87
97
|
bootStatusQueue: Queue.Queue<BootStatus>
|
88
98
|
// TODO we should find a more elegant way to handle cases which need this ref for their implementation
|
89
99
|
shutdownStateSubRef: SubscriptionRef.SubscriptionRef<ShutdownState>
|
90
100
|
shutdownChannel: ShutdownChannel
|
91
101
|
mutationEventSchema: MutationEvent.ForMutationDefRecord<any>
|
92
|
-
|
102
|
+
devtools: DevtoolsContext
|
93
103
|
syncBackend: SyncBackend | undefined
|
94
104
|
syncProcessor: LeaderSyncProcessor
|
95
105
|
connectedClientSessionPullQueues: PullQueueSet
|
96
|
-
/**
|
106
|
+
/**
|
107
|
+
* e.g. used for `store._dev` APIs
|
108
|
+
*
|
109
|
+
* This is currently separated from `.devtools` as it also needs to work when devtools are disabled
|
110
|
+
*/
|
97
111
|
extraIncomingMessagesQueue: Queue.Queue<Devtools.MessageToAppLeader>
|
98
112
|
}
|
99
113
|
>() {}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { memoizeByRef, shouldNeverHappen } from '@livestore/utils'
|
2
2
|
import { Chunk, Effect, Option, Schema, Stream } from '@livestore/utils/effect'
|
3
3
|
|
4
|
-
import { type MigrationOptionsFromMutationLog, type
|
4
|
+
import { type MigrationOptionsFromMutationLog, type SqliteDb, UnexpectedError } from './adapter-types.js'
|
5
5
|
import { makeApplyMutation } from './leader-thread/apply-mutation.js'
|
6
6
|
import type { LiveStoreSchema, MutationDef, MutationEvent, MutationLogMetaRow } from './schema/mod.js'
|
7
7
|
import { EventId, MUTATION_LOG_META_TABLE } from './schema/mod.js'
|
@@ -16,8 +16,8 @@ export const rehydrateFromMutationLog = ({
|
|
16
16
|
migrationOptions,
|
17
17
|
onProgress,
|
18
18
|
}: {
|
19
|
-
logDb:
|
20
|
-
db:
|
19
|
+
logDb: SqliteDb
|
20
|
+
db: SqliteDb
|
21
21
|
schema: LiveStoreSchema
|
22
22
|
migrationOptions: MigrationOptionsFromMutationLog
|
23
23
|
onProgress: (_: { done: number; total: number }) => Effect.Effect<void>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import type {
|
1
|
+
import type { SqliteDb } from '../adapter-types.js'
|
2
2
|
import type { ParamsObject } from '../util.js'
|
3
3
|
import { prepareBindValues } from '../util.js'
|
4
4
|
|
@@ -6,7 +6,7 @@ import { prepareBindValues } from '../util.js'
|
|
6
6
|
// will require proper scope-aware cleanup etc (for testing and apps with multiple LiveStore instances)
|
7
7
|
// const cachedStmts = new Map<string, PreparedStatement>()
|
8
8
|
|
9
|
-
export const dbExecute = (db:
|
9
|
+
export const dbExecute = (db: SqliteDb, queryStr: string, bindValues?: ParamsObject) => {
|
10
10
|
// let stmt = cachedStmts.get(queryStr)
|
11
11
|
// if (!stmt) {
|
12
12
|
const stmt = db.prepare(queryStr)
|
@@ -20,7 +20,7 @@ export const dbExecute = (db: SynchronousDatabase, queryStr: string, bindValues?
|
|
20
20
|
stmt.finalize()
|
21
21
|
}
|
22
22
|
|
23
|
-
export const dbSelect = <T>(db:
|
23
|
+
export const dbSelect = <T>(db: SqliteDb, queryStr: string, bindValues?: ParamsObject) => {
|
24
24
|
// let stmt = cachedStmts.get(queryStr)
|
25
25
|
// if (!stmt) {
|
26
26
|
const stmt = db.prepare(queryStr)
|
@@ -2,7 +2,7 @@ import { SqliteAst, SqliteDsl } from '@livestore/db-schema'
|
|
2
2
|
import { memoizeByStringifyArgs } from '@livestore/utils'
|
3
3
|
import { Effect, Schema as EffectSchema } from '@livestore/utils/effect'
|
4
4
|
|
5
|
-
import type {
|
5
|
+
import type { SqliteDb } from '../adapter-types.js'
|
6
6
|
import type { LiveStoreSchema } from '../schema/mod.js'
|
7
7
|
import type { SchemaMetaRow, SchemaMutationsMetaRow } from '../schema/system-tables.js'
|
8
8
|
import {
|
@@ -19,7 +19,7 @@ import { validateSchema } from './validate-mutation-defs.js'
|
|
19
19
|
|
20
20
|
const getMemoizedTimestamp = memoizeByStringifyArgs(() => new Date().toISOString())
|
21
21
|
|
22
|
-
export const makeSchemaManager = (db:
|
22
|
+
export const makeSchemaManager = (db: SqliteDb): Effect.Effect<SchemaManager> =>
|
23
23
|
Effect.gen(function* () {
|
24
24
|
yield* migrateTable({
|
25
25
|
db,
|
@@ -51,7 +51,7 @@ export const migrateDb = ({
|
|
51
51
|
schema,
|
52
52
|
onProgress,
|
53
53
|
}: {
|
54
|
-
db:
|
54
|
+
db: SqliteDb
|
55
55
|
schema: LiveStoreSchema
|
56
56
|
onProgress?: (opts: { done: number; total: number }) => Effect.Effect<void>
|
57
57
|
}) =>
|
@@ -116,7 +116,7 @@ export const migrateTable = ({
|
|
116
116
|
behaviour,
|
117
117
|
skipMetaTable = false,
|
118
118
|
}: {
|
119
|
-
db:
|
119
|
+
db: SqliteDb
|
120
120
|
tableAst: SqliteAst.Table
|
121
121
|
schemaHash?: number
|
122
122
|
behaviour: 'drop-and-recreate' | 'create-if-not-exists'
|
@@ -1,9 +1,9 @@
|
|
1
1
|
import { LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils'
|
2
|
-
import type { Scope } from '@livestore/utils/effect'
|
2
|
+
import type { Runtime, Scope } from '@livestore/utils/effect'
|
3
3
|
import { Effect, Schema, Stream } from '@livestore/utils/effect'
|
4
4
|
import * as otel from '@opentelemetry/api'
|
5
5
|
|
6
|
-
import type {
|
6
|
+
import type { ClientSession, UnexpectedError } from '../adapter-types.js'
|
7
7
|
import * as EventId from '../schema/EventId.js'
|
8
8
|
import { type LiveStoreSchema } from '../schema/mod.js'
|
9
9
|
import * as MutationEvent from '../schema/MutationEvent.js'
|
@@ -20,18 +20,16 @@ import { SyncState, updateSyncState } from './syncstate.js'
|
|
20
20
|
*/
|
21
21
|
export const makeClientSessionSyncProcessor = ({
|
22
22
|
schema,
|
23
|
-
|
24
|
-
|
25
|
-
pullFromLeader,
|
23
|
+
clientSession,
|
24
|
+
runtime,
|
26
25
|
applyMutation,
|
27
26
|
rollback,
|
28
27
|
refreshTables,
|
29
28
|
span,
|
30
29
|
}: {
|
31
30
|
schema: LiveStoreSchema
|
32
|
-
|
33
|
-
|
34
|
-
pullFromLeader: ClientSessionLeaderThreadProxy['mutations']['pull']
|
31
|
+
clientSession: ClientSession
|
32
|
+
runtime: Runtime.Runtime<Scope.Scope>
|
35
33
|
applyMutation: (
|
36
34
|
mutationEventDecoded: MutationEvent.PartialAnyDecoded,
|
37
35
|
options: { otelContext: otel.Context; withChangeset: boolean },
|
@@ -47,8 +45,8 @@ export const makeClientSessionSyncProcessor = ({
|
|
47
45
|
|
48
46
|
const syncStateRef = {
|
49
47
|
current: new SyncState({
|
50
|
-
localHead:
|
51
|
-
upstreamHead:
|
48
|
+
localHead: clientSession.leaderThread.mutations.initialMutationEventId,
|
49
|
+
upstreamHead: clientSession.leaderThread.mutations.initialMutationEventId,
|
52
50
|
pending: [],
|
53
51
|
// TODO init rollbackTail from leader to be ready for backend rebasing
|
54
52
|
rollbackTail: [],
|
@@ -103,7 +101,9 @@ export const makeClientSessionSyncProcessor = ({
|
|
103
101
|
}
|
104
102
|
|
105
103
|
// console.debug('pushToLeader', encodedMutationEvents.length, ...encodedMutationEvents.map((_) => _.toJSON()))
|
106
|
-
|
104
|
+
clientSession.leaderThread.mutations
|
105
|
+
.push(encodedMutationEvents)
|
106
|
+
.pipe(Effect.tapCauseLogPretty, Effect.provide(runtime), Effect.runFork)
|
107
107
|
|
108
108
|
return { writeTables }
|
109
109
|
}
|
@@ -111,10 +111,13 @@ export const makeClientSessionSyncProcessor = ({
|
|
111
111
|
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
112
112
|
|
113
113
|
const boot: ClientSessionSyncProcessor['boot'] = Effect.gen(function* () {
|
114
|
-
yield*
|
114
|
+
yield* clientSession.leaderThread.mutations.pull.pipe(
|
115
115
|
Stream.tap(({ payload, remaining }) =>
|
116
116
|
Effect.gen(function* () {
|
117
117
|
// console.log('pulled payload from leader', { payload, remaining })
|
118
|
+
if (clientSession.devtools.enabled) {
|
119
|
+
yield* clientSession.devtools.pullLatch.await
|
120
|
+
}
|
118
121
|
|
119
122
|
const updateResult = updateSyncState({
|
120
123
|
syncState: syncStateRef.current,
|
@@ -155,7 +158,9 @@ export const makeClientSessionSyncProcessor = ({
|
|
155
158
|
}
|
156
159
|
}
|
157
160
|
|
158
|
-
|
161
|
+
clientSession.leaderThread.mutations
|
162
|
+
.push(updateResult.newSyncState.pending)
|
163
|
+
.pipe(Effect.tapCauseLogPretty, Effect.provide(runtime), Effect.runFork)
|
159
164
|
} else {
|
160
165
|
span.addEvent('pull:advance', {
|
161
166
|
payloadTag: payload._tag,
|
package/src/version.ts
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
// import packageJson from '../package.json' with { type: 'json' }
|
3
3
|
// export const liveStoreVersion = packageJson.version
|
4
4
|
|
5
|
-
export const liveStoreVersion = '0.3.0-dev.
|
5
|
+
export const liveStoreVersion = '0.3.0-dev.11' as const
|
6
6
|
|
7
7
|
/**
|
8
8
|
* This version number is incremented whenever the internal storage format changes in a breaking way.
|