@livestore/common 0.3.0-dev.11 → 0.3.0-dev.12
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 +41 -18
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +12 -0
- package/dist/adapter-types.js.map +1 -1
- package/dist/devtools/devtools-bridge.d.ts +10 -7
- package/dist/devtools/devtools-bridge.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-client-session.d.ts +101 -28
- package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-client-session.js +19 -3
- package/dist/devtools/devtools-messages-client-session.js.map +1 -1
- package/dist/devtools/devtools-messages-common.d.ts +24 -32
- package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-common.js +19 -10
- package/dist/devtools/devtools-messages-common.js.map +1 -1
- package/dist/devtools/devtools-messages-leader.d.ts +210 -34
- package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
- package/dist/devtools/devtools-messages-leader.js +53 -6
- package/dist/devtools/devtools-messages-leader.js.map +1 -1
- package/dist/devtools/devtools-messages.d.ts +2 -2
- package/dist/devtools/devtools-messages.d.ts.map +1 -1
- package/dist/devtools/devtools-messages.js +2 -2
- package/dist/devtools/devtools-messages.js.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
- package/dist/leader-thread/LeaderSyncProcessor.js +26 -13
- package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
- package/dist/leader-thread/connection.d.ts +31 -3
- package/dist/leader-thread/connection.d.ts.map +1 -1
- package/dist/leader-thread/connection.js +18 -3
- package/dist/leader-thread/connection.js.map +1 -1
- package/dist/leader-thread/leader-worker-devtools.js +51 -20
- package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
- package/dist/leader-thread/make-leader-thread-layer.js +17 -5
- package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
- package/dist/leader-thread/pull-queue-set.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts +4 -2
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +13 -8
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/leader-thread/types.d.ts +15 -12
- package/dist/leader-thread/types.d.ts.map +1 -1
- package/dist/leader-thread/types.js +0 -2
- package/dist/leader-thread/types.js.map +1 -1
- package/dist/query-builder/api.d.ts +2 -2
- package/dist/query-builder/api.d.ts.map +1 -1
- package/dist/query-builder/impl.js.map +1 -1
- package/dist/query-builder/impl.test.js +16 -1
- package/dist/query-builder/impl.test.js.map +1 -1
- package/dist/query-info.d.ts +3 -3
- package/dist/query-info.d.ts.map +1 -1
- package/dist/schema/EventId.d.ts +1 -0
- package/dist/schema/EventId.d.ts.map +1 -1
- package/dist/schema/EventId.js +3 -0
- package/dist/schema/EventId.js.map +1 -1
- package/dist/schema/mutations.d.ts +1 -1
- package/dist/schema/system-tables.d.ts +1 -1
- package/dist/schema-management/migrations.d.ts +2 -2
- package/dist/schema-management/migrations.d.ts.map +1 -1
- package/dist/schema-management/migrations.js +6 -1
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.d.ts +3 -5
- package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
- package/dist/sync/ClientSessionSyncProcessor.js +20 -9
- package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +3 -3
- package/src/adapter-types.ts +35 -17
- package/src/devtools/devtools-bridge.ts +10 -7
- package/src/devtools/devtools-messages-client-session.ts +26 -10
- package/src/devtools/devtools-messages-common.ts +37 -8
- package/src/devtools/devtools-messages-leader.ts +73 -12
- package/src/devtools/devtools-messages.ts +2 -2
- package/src/leader-thread/LeaderSyncProcessor.ts +31 -16
- package/src/leader-thread/connection.ts +48 -3
- package/src/leader-thread/leader-worker-devtools.ts +80 -23
- package/src/leader-thread/make-leader-thread-layer.ts +20 -8
- package/src/leader-thread/recreate-db.ts +19 -10
- package/src/leader-thread/types.ts +16 -13
- package/src/query-builder/api.ts +3 -3
- package/src/query-builder/impl.test.ts +22 -1
- package/src/query-builder/impl.ts +2 -2
- package/src/query-info.ts +3 -3
- package/src/schema/EventId.ts +4 -0
- package/src/schema-management/migrations.ts +9 -5
- package/src/sync/ClientSessionSyncProcessor.ts +22 -11
- package/src/version.ts +1 -1
@@ -2,19 +2,20 @@ import { casesHandled } from '@livestore/utils'
|
|
2
2
|
import type { HttpClient } from '@livestore/utils/effect'
|
3
3
|
import { Effect, Queue } from '@livestore/utils/effect'
|
4
4
|
|
5
|
-
import type { InvalidPullError, IsOfflineError, MigrationHooks, SqliteError } from '../index.js'
|
5
|
+
import type { InvalidPullError, IsOfflineError, MigrationHooks, MigrationsReport, SqliteError } from '../index.js'
|
6
6
|
import { initializeSingletonTables, migrateDb, rehydrateFromMutationLog, UnexpectedError } from '../index.js'
|
7
7
|
import { configureConnection } from './connection.js'
|
8
8
|
import { LeaderThreadCtx } from './types.js'
|
9
9
|
|
10
10
|
export const recreateDb: Effect.Effect<
|
11
|
-
|
11
|
+
{ migrationsReport: MigrationsReport },
|
12
12
|
UnexpectedError | SqliteError | IsOfflineError | InvalidPullError,
|
13
13
|
LeaderThreadCtx | HttpClient.HttpClient
|
14
14
|
> = Effect.gen(function* () {
|
15
15
|
const { dbReadModel, dbMutationLog, schema, bootStatusQueue } = yield* LeaderThreadCtx
|
16
16
|
|
17
17
|
const migrationOptions = schema.migrationOptions
|
18
|
+
let migrationsReport: MigrationsReport
|
18
19
|
|
19
20
|
yield* Effect.addFinalizer(
|
20
21
|
Effect.fn('recreateDb:finalizer')(function* (ex) {
|
@@ -27,13 +28,13 @@ export const recreateDb: Effect.Effect<
|
|
27
28
|
// TODO bring back this optimization
|
28
29
|
// const tmpDb = yield* makeSqliteDb({ _tag: 'in-memory' })
|
29
30
|
const tmpDb = dbReadModel
|
30
|
-
yield* configureConnection(tmpDb, {
|
31
|
+
yield* configureConnection(tmpDb, { foreignKeys: true })
|
31
32
|
|
32
33
|
const initDb = (hooks: Partial<MigrationHooks> | undefined) =>
|
33
34
|
Effect.gen(function* () {
|
34
35
|
yield* Effect.tryAll(() => hooks?.init?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
35
36
|
|
36
|
-
yield* migrateDb({
|
37
|
+
const migrationsReport = yield* migrateDb({
|
37
38
|
db: tmpDb,
|
38
39
|
schema,
|
39
40
|
onProgress: ({ done, total }) =>
|
@@ -44,16 +45,18 @@ export const recreateDb: Effect.Effect<
|
|
44
45
|
|
45
46
|
yield* Effect.tryAll(() => hooks?.pre?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
46
47
|
|
47
|
-
return tmpDb
|
48
|
+
return { migrationsReport, tmpDb }
|
48
49
|
})
|
49
50
|
|
50
51
|
switch (migrationOptions.strategy) {
|
51
52
|
case 'from-mutation-log': {
|
52
53
|
const hooks = migrationOptions.hooks
|
53
|
-
const
|
54
|
+
const initResult = yield* initDb(hooks)
|
55
|
+
|
56
|
+
migrationsReport = initResult.migrationsReport
|
54
57
|
|
55
58
|
yield* rehydrateFromMutationLog({
|
56
|
-
db: tmpDb,
|
59
|
+
db: initResult.tmpDb,
|
57
60
|
logDb: dbMutationLog,
|
58
61
|
schema,
|
59
62
|
migrationOptions,
|
@@ -61,23 +64,27 @@ export const recreateDb: Effect.Effect<
|
|
61
64
|
Queue.offer(bootStatusQueue, { stage: 'rehydrating', progress: { done, total } }),
|
62
65
|
})
|
63
66
|
|
64
|
-
yield* Effect.tryAll(() => hooks?.post?.(tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
67
|
+
yield* Effect.tryAll(() => hooks?.post?.(initResult.tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
65
68
|
|
66
69
|
break
|
67
70
|
}
|
68
71
|
case 'hard-reset': {
|
69
72
|
const hooks = migrationOptions.hooks
|
70
|
-
const
|
73
|
+
const initResult = yield* initDb(hooks)
|
74
|
+
|
75
|
+
migrationsReport = initResult.migrationsReport
|
71
76
|
|
72
77
|
// The database is migrated but empty now, so nothing else to do
|
73
78
|
|
74
|
-
yield* Effect.tryAll(() => hooks?.post?.(
|
79
|
+
yield* Effect.tryAll(() => hooks?.post?.(initResult.tmpDb)).pipe(UnexpectedError.mapToUnexpectedError)
|
75
80
|
|
76
81
|
break
|
77
82
|
}
|
78
83
|
case 'manual': {
|
79
84
|
const oldDbData = dbReadModel.export()
|
80
85
|
|
86
|
+
migrationsReport = { migrations: [] }
|
87
|
+
|
81
88
|
const newDbData = yield* Effect.tryAll(() => migrationOptions.migrate(oldDbData)).pipe(
|
82
89
|
UnexpectedError.mapToUnexpectedError,
|
83
90
|
)
|
@@ -106,6 +113,8 @@ export const recreateDb: Effect.Effect<
|
|
106
113
|
|
107
114
|
// TODO bring back
|
108
115
|
// tmpDb.close()
|
116
|
+
|
117
|
+
return { migrationsReport }
|
109
118
|
}).pipe(
|
110
119
|
Effect.scoped, // NOTE we're closing the scope here so finalizers are called when the effect is done
|
111
120
|
Effect.withSpan('@livestore/common:leader-thread:recreateDb'),
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import type {
|
2
2
|
Deferred,
|
3
3
|
Effect,
|
4
|
-
Fiber,
|
5
4
|
HttpClient,
|
6
5
|
Option,
|
7
6
|
Queue,
|
@@ -17,6 +16,7 @@ import type {
|
|
17
16
|
Devtools,
|
18
17
|
InvalidPushError,
|
19
18
|
MakeSqliteDb,
|
19
|
+
MigrationsReport,
|
20
20
|
PersistenceInfo,
|
21
21
|
SqliteDb,
|
22
22
|
SyncBackend,
|
@@ -28,13 +28,6 @@ import type { ShutdownChannel } from './shutdown-channel.js'
|
|
28
28
|
|
29
29
|
export type ShutdownState = 'running' | 'shutting-down'
|
30
30
|
|
31
|
-
export class OuterWorkerCtx extends Context.Tag('OuterWorkerCtx')<
|
32
|
-
OuterWorkerCtx,
|
33
|
-
{
|
34
|
-
innerFiber: Fiber.RuntimeFiber<any, any>
|
35
|
-
}
|
36
|
-
>() {}
|
37
|
-
|
38
31
|
export const InitialSyncOptionsSkip = Schema.TaggedStruct('Skip', {})
|
39
32
|
export type InitialSyncOptionsSkip = typeof InitialSyncOptionsSkip.Type
|
40
33
|
|
@@ -67,7 +60,7 @@ export type DevtoolsOptions =
|
|
67
60
|
enabled: true
|
68
61
|
makeBootContext: Effect.Effect<
|
69
62
|
{
|
70
|
-
devtoolsWebChannel: WebChannel.WebChannel<Devtools.
|
63
|
+
devtoolsWebChannel: WebChannel.WebChannel<Devtools.Leader.MessageToApp, Devtools.Leader.MessageFromApp>
|
71
64
|
persistenceInfo: PersistenceInfoPair
|
72
65
|
},
|
73
66
|
UnexpectedError,
|
@@ -78,8 +71,10 @@ export type DevtoolsOptions =
|
|
78
71
|
export type DevtoolsContext =
|
79
72
|
| {
|
80
73
|
enabled: true
|
81
|
-
syncBackendPullLatch: Effect.Latch
|
82
|
-
syncBackendPushLatch: Effect.Latch
|
74
|
+
// syncBackendPullLatch: Effect.Latch
|
75
|
+
// syncBackendPushLatch: Effect.Latch
|
76
|
+
syncBackendLatch: Effect.Latch
|
77
|
+
syncBackendLatchState: SubscriptionRef.SubscriptionRef<{ latchClosed: boolean }>
|
83
78
|
}
|
84
79
|
| {
|
85
80
|
enabled: false
|
@@ -103,12 +98,16 @@ export class LeaderThreadCtx extends Context.Tag('LeaderThreadCtx')<
|
|
103
98
|
syncBackend: SyncBackend | undefined
|
104
99
|
syncProcessor: LeaderSyncProcessor
|
105
100
|
connectedClientSessionPullQueues: PullQueueSet
|
101
|
+
initialState: {
|
102
|
+
leaderHead: EventId.EventId
|
103
|
+
migrationsReport: MigrationsReport
|
104
|
+
}
|
106
105
|
/**
|
107
106
|
* e.g. used for `store._dev` APIs
|
108
107
|
*
|
109
108
|
* This is currently separated from `.devtools` as it also needs to work when devtools are disabled
|
110
109
|
*/
|
111
|
-
extraIncomingMessagesQueue: Queue.Queue<Devtools.
|
110
|
+
extraIncomingMessagesQueue: Queue.Queue<Devtools.Leader.MessageToApp>
|
112
111
|
}
|
113
112
|
>() {}
|
114
113
|
|
@@ -138,7 +137,11 @@ export interface LeaderSyncProcessor {
|
|
138
137
|
pushPartial: (mutationEvent: MutationEvent.PartialAnyEncoded) => Effect.Effect<void, UnexpectedError, LeaderThreadCtx>
|
139
138
|
boot: (args: {
|
140
139
|
dbReady: Deferred.Deferred<void>
|
141
|
-
}) => Effect.Effect<
|
140
|
+
}) => Effect.Effect<
|
141
|
+
{ initialLeaderHead: EventId.EventId },
|
142
|
+
UnexpectedError,
|
143
|
+
LeaderThreadCtx | Scope.Scope | HttpClient.HttpClient
|
144
|
+
>
|
142
145
|
syncState: Subscribable.Subscribable<SyncState.SyncState>
|
143
146
|
}
|
144
147
|
|
package/src/query-builder/api.ts
CHANGED
@@ -35,7 +35,7 @@ export namespace QueryBuilderAst {
|
|
35
35
|
export type RowQuery = {
|
36
36
|
readonly _tag: 'RowQuery'
|
37
37
|
readonly tableDef: DbSchema.TableDefBase
|
38
|
-
readonly id: string | SessionIdSymbol
|
38
|
+
readonly id: string | SessionIdSymbol | number
|
39
39
|
readonly insertValues: Record<string, unknown>
|
40
40
|
}
|
41
41
|
|
@@ -252,10 +252,10 @@ export namespace QueryBuilder {
|
|
252
252
|
? (_: 'Error: Need to enable deriveMutations to use row()') => any
|
253
253
|
: TTableDef['options']['requiredInsertColumnNames'] extends never
|
254
254
|
? (
|
255
|
-
id: string | SessionIdSymbol,
|
255
|
+
id: string | SessionIdSymbol | number,
|
256
256
|
) => QueryBuilder<RowQuery.Result<TTableDef>, TTableDef, QueryBuilder.ApiFeature, QueryInfo.Row>
|
257
257
|
: <TOptions extends RowQuery.RequiredColumnsOptions<TTableDef>>(
|
258
|
-
id: string | SessionIdSymbol,
|
258
|
+
id: string | SessionIdSymbol | number,
|
259
259
|
opts: TOptions,
|
260
260
|
) => QueryBuilder<RowQuery.Result<TTableDef>, TTableDef, QueryBuilder.ApiFeature, QueryInfo.Row>
|
261
261
|
}
|
@@ -18,13 +18,23 @@ const todos = DbSchema.table(
|
|
18
18
|
{ deriveMutations: true },
|
19
19
|
)
|
20
20
|
|
21
|
+
const todosWithIntId = DbSchema.table(
|
22
|
+
'todos_with_int_id',
|
23
|
+
{
|
24
|
+
id: DbSchema.integer({ primaryKey: true }),
|
25
|
+
text: DbSchema.text({ default: '', nullable: false }),
|
26
|
+
status: DbSchema.text({ schema: Schema.Literal('active', 'completed') }),
|
27
|
+
},
|
28
|
+
{ deriveMutations: true },
|
29
|
+
)
|
30
|
+
|
21
31
|
const comments = DbSchema.table('comments', {
|
22
32
|
id: DbSchema.text({ primaryKey: true }),
|
23
33
|
text: DbSchema.text({ default: '', nullable: false }),
|
24
34
|
todoId: DbSchema.text({}),
|
25
35
|
})
|
26
36
|
|
27
|
-
const db = { todos: todos.query, comments: comments.query }
|
37
|
+
const db = { todos: todos.query, todosWithIntId: todosWithIntId.query, comments: comments.query }
|
28
38
|
|
29
39
|
describe('query builder', () => {
|
30
40
|
describe('result schema', () => {
|
@@ -204,6 +214,17 @@ describe('query builder', () => {
|
|
204
214
|
}
|
205
215
|
`)
|
206
216
|
})
|
217
|
+
|
218
|
+
it('should handle row queries with numbers', () => {
|
219
|
+
expect(db.todosWithIntId.row(123, { insertValues: { status: 'active' } }).asSql()).toMatchInlineSnapshot(`
|
220
|
+
{
|
221
|
+
"bindValues": [
|
222
|
+
123,
|
223
|
+
],
|
224
|
+
"query": "SELECT * FROM 'todos_with_int_id' WHERE id = ?",
|
225
|
+
}
|
226
|
+
`)
|
227
|
+
})
|
207
228
|
})
|
208
229
|
})
|
209
230
|
|
@@ -128,12 +128,12 @@ export const makeQueryBuilder = <TResult, TTableDef extends DbSchema.TableDefBas
|
|
128
128
|
// eslint-disable-next-line prefer-rest-params
|
129
129
|
const params = [...arguments]
|
130
130
|
|
131
|
-
let id: string
|
131
|
+
let id: string | number
|
132
132
|
|
133
133
|
if (tableDef.options.isSingleton) {
|
134
134
|
id = tableDef.sqliteDef.columns.id!.default.pipe(Option.getOrThrow)
|
135
135
|
} else {
|
136
|
-
id = params[0] as string
|
136
|
+
id = params[0] as string | number
|
137
137
|
if (id === undefined) {
|
138
138
|
invalidQueryBuilder(`Id missing for row query on non-singleton table ${tableDef.sqliteDef.name}`)
|
139
139
|
}
|
package/src/query-info.ts
CHANGED
@@ -24,20 +24,20 @@ export namespace QueryInfo {
|
|
24
24
|
export type Row = {
|
25
25
|
_tag: 'Row'
|
26
26
|
table: DbSchema.TableDefBase
|
27
|
-
id: string | SessionIdSymbol
|
27
|
+
id: string | SessionIdSymbol | number
|
28
28
|
}
|
29
29
|
|
30
30
|
export type Col = {
|
31
31
|
_tag: 'Col'
|
32
32
|
table: DbSchema.TableDefBase
|
33
|
-
id: string | SessionIdSymbol
|
33
|
+
id: string | SessionIdSymbol | number
|
34
34
|
column: string
|
35
35
|
}
|
36
36
|
|
37
37
|
export type ColJsonValue = {
|
38
38
|
_tag: 'ColJsonValue'
|
39
39
|
table: DbSchema.TableDefBase
|
40
|
-
id: string | SessionIdSymbol
|
40
|
+
id: string | SessionIdSymbol | number
|
41
41
|
column: string
|
42
42
|
/**
|
43
43
|
* example: `$.tabs[3].items[2]` (`$` referring to the column value)
|
package/src/schema/EventId.ts
CHANGED
@@ -43,6 +43,10 @@ export const isGreaterThan = (a: EventId, b: EventId) => {
|
|
43
43
|
return a.global > b.global || (a.global === b.global && a.local > b.local)
|
44
44
|
}
|
45
45
|
|
46
|
+
export const isGreaterThanOrEqual = (a: EventId, b: EventId) => {
|
47
|
+
return a.global > b.global || (a.global === b.global && a.local >= b.local)
|
48
|
+
}
|
49
|
+
|
46
50
|
export const make = (id: EventId | typeof EventId.Encoded): EventId => {
|
47
51
|
return Schema.is(EventId)(id) ? id : Schema.decodeSync(EventId)(id)
|
48
52
|
}
|
@@ -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 { SqliteDb } from '../adapter-types.js'
|
5
|
+
import type { MigrationsReport, MigrationsReportEntry, SqliteDb, UnexpectedError } 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 {
|
@@ -54,7 +54,7 @@ export const migrateDb = ({
|
|
54
54
|
db: SqliteDb
|
55
55
|
schema: LiveStoreSchema
|
56
56
|
onProgress?: (opts: { done: number; total: number }) => Effect.Effect<void>
|
57
|
-
}) =>
|
57
|
+
}): Effect.Effect<MigrationsReport, UnexpectedError> =>
|
58
58
|
Effect.gen(function* () {
|
59
59
|
yield* migrateTable({
|
60
60
|
db,
|
@@ -81,6 +81,7 @@ export const migrateDb = ({
|
|
81
81
|
|
82
82
|
const tablesToMigrate = new Set<{ tableAst: SqliteAst.Table; schemaHash: number }>()
|
83
83
|
|
84
|
+
const migrationsReportEntries: MigrationsReportEntry[] = []
|
84
85
|
for (const tableDef of tableDefs) {
|
85
86
|
const tableAst = tableDef.sqliteDef.ast
|
86
87
|
const tableName = tableAst.name
|
@@ -90,9 +91,10 @@ export const migrateDb = ({
|
|
90
91
|
if (schemaHash !== dbSchemaHash) {
|
91
92
|
tablesToMigrate.add({ tableAst, schemaHash })
|
92
93
|
|
93
|
-
|
94
|
-
|
95
|
-
|
94
|
+
migrationsReportEntries.push({
|
95
|
+
tableName,
|
96
|
+
hashes: { expected: schemaHash, actual: dbSchemaHash },
|
97
|
+
})
|
96
98
|
}
|
97
99
|
}
|
98
100
|
|
@@ -107,6 +109,8 @@ export const migrateDb = ({
|
|
107
109
|
yield* onProgress({ done: processedTables, total: tablesCount })
|
108
110
|
}
|
109
111
|
}
|
112
|
+
|
113
|
+
return { migrations: migrationsReportEntries }
|
110
114
|
})
|
111
115
|
|
112
116
|
export const migrateTable = ({
|
@@ -1,13 +1,13 @@
|
|
1
1
|
import { LS_DEV, shouldNeverHappen, TRACE_VERBOSE } from '@livestore/utils'
|
2
2
|
import type { Runtime, Scope } from '@livestore/utils/effect'
|
3
|
-
import { Effect, Schema, Stream } from '@livestore/utils/effect'
|
3
|
+
import { Effect, Queue, Schema, Stream, Subscribable } from '@livestore/utils/effect'
|
4
4
|
import * as otel from '@opentelemetry/api'
|
5
5
|
|
6
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'
|
10
|
-
import
|
10
|
+
import * as SyncState from './syncstate.js'
|
11
11
|
|
12
12
|
/**
|
13
13
|
* Rebase behaviour:
|
@@ -44,15 +44,17 @@ export const makeClientSessionSyncProcessor = ({
|
|
44
44
|
const mutationEventSchema = MutationEvent.makeMutationEventSchemaMemo(schema)
|
45
45
|
|
46
46
|
const syncStateRef = {
|
47
|
-
current: new SyncState({
|
48
|
-
localHead: clientSession.leaderThread.
|
49
|
-
upstreamHead: clientSession.leaderThread.
|
47
|
+
current: new SyncState.SyncState({
|
48
|
+
localHead: clientSession.leaderThread.initialState.leaderHead,
|
49
|
+
upstreamHead: clientSession.leaderThread.initialState.leaderHead,
|
50
50
|
pending: [],
|
51
51
|
// TODO init rollbackTail from leader to be ready for backend rebasing
|
52
52
|
rollbackTail: [],
|
53
53
|
}),
|
54
54
|
}
|
55
55
|
|
56
|
+
const syncStateUpdateQueue = Queue.unbounded<SyncState.SyncState>().pipe(Effect.runSync)
|
57
|
+
|
56
58
|
const isLocalEvent = (mutationEventEncoded: MutationEvent.EncodedWithMeta) => {
|
57
59
|
const mutationDef = schema.mutations.get(mutationEventEncoded.mutation)!
|
58
60
|
return mutationDef.options.localOnly
|
@@ -71,7 +73,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
71
73
|
)
|
72
74
|
})
|
73
75
|
|
74
|
-
const updateResult = updateSyncState({
|
76
|
+
const updateResult = SyncState.updateSyncState({
|
75
77
|
syncState: syncStateRef.current,
|
76
78
|
payload: { _tag: 'local-push', newEvents: encodedMutationEvents },
|
77
79
|
isLocalEvent,
|
@@ -88,6 +90,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
88
90
|
}
|
89
91
|
|
90
92
|
syncStateRef.current = updateResult.newSyncState
|
93
|
+
syncStateUpdateQueue.offer(updateResult.newSyncState).pipe(Effect.runSync)
|
91
94
|
|
92
95
|
const writeTables = new Set<string>()
|
93
96
|
for (const mutationEvent of updateResult.newEvents) {
|
@@ -119,7 +122,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
119
122
|
yield* clientSession.devtools.pullLatch.await
|
120
123
|
}
|
121
124
|
|
122
|
-
const updateResult = updateSyncState({
|
125
|
+
const updateResult = SyncState.updateSyncState({
|
123
126
|
syncState: syncStateRef.current,
|
124
127
|
payload,
|
125
128
|
isLocalEvent,
|
@@ -132,6 +135,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
132
135
|
}
|
133
136
|
|
134
137
|
syncStateRef.current = updateResult.newSyncState
|
138
|
+
syncStateUpdateQueue.offer(updateResult.newSyncState).pipe(Effect.runSync)
|
135
139
|
|
136
140
|
if (updateResult._tag === 'rebase') {
|
137
141
|
span.addEvent('pull:rebase', {
|
@@ -143,11 +147,11 @@ export const makeClientSessionSyncProcessor = ({
|
|
143
147
|
remaining,
|
144
148
|
})
|
145
149
|
if (LS_DEV) {
|
146
|
-
|
150
|
+
Effect.logDebug(
|
147
151
|
'pull:rebase: rollback',
|
148
152
|
updateResult.eventsToRollback.length,
|
149
153
|
...updateResult.eventsToRollback.map((_) => _.toJSON()),
|
150
|
-
)
|
154
|
+
).pipe(Effect.provide(runtime), Effect.runSync)
|
151
155
|
}
|
152
156
|
|
153
157
|
for (let i = updateResult.eventsToRollback.length - 1; i >= 0; i--) {
|
@@ -197,7 +201,14 @@ export const makeClientSessionSyncProcessor = ({
|
|
197
201
|
return {
|
198
202
|
push,
|
199
203
|
boot,
|
200
|
-
|
204
|
+
syncState: Subscribable.make({
|
205
|
+
get: Effect.gen(function* () {
|
206
|
+
const syncState = syncStateRef.current
|
207
|
+
if (syncStateRef === undefined) return shouldNeverHappen('Not initialized')
|
208
|
+
return syncState
|
209
|
+
}),
|
210
|
+
changes: Stream.fromQueue(syncStateUpdateQueue),
|
211
|
+
}),
|
201
212
|
} satisfies ClientSessionSyncProcessor
|
202
213
|
}
|
203
214
|
|
@@ -210,5 +221,5 @@ export interface ClientSessionSyncProcessor {
|
|
210
221
|
}
|
211
222
|
boot: Effect.Effect<void, UnexpectedError, Scope.Scope>
|
212
223
|
|
213
|
-
|
224
|
+
syncState: Subscribable.Subscribable<SyncState.SyncState>
|
214
225
|
}
|
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.12' as const
|
6
6
|
|
7
7
|
/**
|
8
8
|
* This version number is incremented whenever the internal storage format changes in a breaking way.
|