@livestore/common 0.3.0-dev.5 → 0.3.0-dev.6
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/derived-mutations.d.ts +4 -4
- package/dist/derived-mutations.d.ts.map +1 -1
- package/dist/derived-mutations.test.js.map +1 -1
- package/dist/devtools/devtools-messages.d.ts +39 -39
- package/dist/leader-thread/apply-mutation.d.ts +5 -2
- package/dist/leader-thread/apply-mutation.d.ts.map +1 -1
- package/dist/leader-thread/apply-mutation.js +37 -25
- package/dist/leader-thread/apply-mutation.js.map +1 -1
- package/dist/leader-thread/leader-sync-processor.js +10 -5
- package/dist/leader-thread/leader-sync-processor.js.map +1 -1
- package/dist/leader-thread/recreate-db.d.ts.map +1 -1
- package/dist/leader-thread/recreate-db.js +9 -3
- package/dist/leader-thread/recreate-db.js.map +1 -1
- package/dist/mutation.d.ts +9 -2
- package/dist/mutation.d.ts.map +1 -1
- package/dist/mutation.js +5 -5
- package/dist/mutation.js.map +1 -1
- package/dist/query-builder/impl.d.ts +1 -1
- package/dist/rehydrate-from-mutationlog.d.ts +2 -2
- package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
- package/dist/rehydrate-from-mutationlog.js +13 -19
- package/dist/rehydrate-from-mutationlog.js.map +1 -1
- package/dist/schema/MutationEvent.d.ts +6 -6
- package/dist/schema/MutationEvent.d.ts.map +1 -1
- package/dist/schema/MutationEvent.js.map +1 -1
- package/dist/schema/system-tables.d.ts +11 -11
- package/dist/schema/system-tables.d.ts.map +1 -1
- package/dist/schema/system-tables.js +6 -5
- package/dist/schema/system-tables.js.map +1 -1
- package/dist/sync/client-session-sync-processor.d.ts +2 -2
- package/dist/sync/client-session-sync-processor.d.ts.map +1 -1
- package/dist/sync/next/rebase-events.d.ts +2 -2
- package/dist/sync/next/rebase-events.d.ts.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 +6 -5
- package/src/derived-mutations.test.ts +1 -1
- package/src/derived-mutations.ts +9 -5
- package/src/leader-thread/apply-mutation.ts +48 -30
- package/src/leader-thread/leader-sync-processor.ts +12 -5
- package/src/leader-thread/recreate-db.ts +9 -5
- package/src/mutation.ts +17 -7
- package/src/rehydrate-from-mutationlog.ts +15 -23
- package/src/schema/MutationEvent.ts +4 -3
- package/src/schema/system-tables.ts +6 -5
- package/src/sync/client-session-sync-processor.ts +2 -2
- package/src/sync/next/rebase-events.ts +2 -2
- package/src/version.ts +1 -1
@@ -10,18 +10,18 @@ export declare const facts: {
|
|
10
10
|
};
|
11
11
|
export declare const mutations: {
|
12
12
|
createTodo: MutationDef<"createTodo", {
|
13
|
-
readonly id: string;
|
14
13
|
readonly text: string;
|
15
|
-
}, {
|
16
14
|
readonly id: string;
|
15
|
+
}, {
|
17
16
|
readonly text: string;
|
17
|
+
readonly id: string;
|
18
18
|
}>;
|
19
19
|
upsertTodo: MutationDef<"upsertTodo", {
|
20
20
|
readonly id: string;
|
21
21
|
readonly text?: string | undefined;
|
22
22
|
}, {
|
23
|
-
readonly id: string;
|
24
23
|
readonly text?: string | undefined;
|
24
|
+
readonly id: string;
|
25
25
|
}>;
|
26
26
|
completeTodo: MutationDef<"completeTodo", {
|
27
27
|
readonly id: string;
|
@@ -51,18 +51,18 @@ export declare const mutations: {
|
|
51
51
|
readonly readonly: boolean;
|
52
52
|
}>;
|
53
53
|
setTextTodo: MutationDef<"setTextTodo", {
|
54
|
-
readonly id: string;
|
55
54
|
readonly text: string;
|
56
|
-
}, {
|
57
55
|
readonly id: string;
|
56
|
+
}, {
|
58
57
|
readonly text: string;
|
58
|
+
readonly id: string;
|
59
59
|
}>;
|
60
60
|
setInputValue: MutationDef<"setInputValue", {
|
61
|
-
readonly id: string;
|
62
61
|
readonly text: string;
|
63
|
-
}, {
|
64
62
|
readonly id: string;
|
63
|
+
}, {
|
65
64
|
readonly text: string;
|
65
|
+
readonly id: string;
|
66
66
|
}>;
|
67
67
|
};
|
68
68
|
export type PartialEvent = {
|
package/dist/version.d.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
export declare const liveStoreVersion: "0.3.0-dev.
|
1
|
+
export declare const liveStoreVersion: "0.3.0-dev.6";
|
2
2
|
/**
|
3
3
|
* This version number is incremented whenever the internal storage format changes in a breaking way.
|
4
4
|
* Whenever this version changes, LiveStore will start with fresh database files. Old database files are not deleted.
|
package/dist/version.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
// TODO bring back when Expo and Playwright supports `with` imports
|
2
2
|
// import packageJson from '../package.json' with { type: 'json' }
|
3
3
|
// export const liveStoreVersion = packageJson.version
|
4
|
-
export const liveStoreVersion = '0.3.0-dev.
|
4
|
+
export const liveStoreVersion = '0.3.0-dev.6';
|
5
5
|
/**
|
6
6
|
* This version number is incremented whenever the internal storage format changes in a breaking way.
|
7
7
|
* Whenever this version changes, LiveStore will start with fresh database files. Old database files are not deleted.
|
package/package.json
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
{
|
2
2
|
"name": "@livestore/common",
|
3
|
-
"version": "0.3.0-dev.
|
3
|
+
"version": "0.3.0-dev.6",
|
4
4
|
"type": "module",
|
5
|
+
"sideEffects": false,
|
5
6
|
"exports": {
|
6
7
|
".": {
|
7
8
|
"types": "./dist/index.d.ts",
|
@@ -49,12 +50,12 @@
|
|
49
50
|
}
|
50
51
|
},
|
51
52
|
"dependencies": {
|
52
|
-
"@opentelemetry/api": "
|
53
|
+
"@opentelemetry/api": "1.9.0",
|
53
54
|
"graphology": "0.26.0-alpha1",
|
54
55
|
"graphology-dag": "0.4.1",
|
55
|
-
"graphology-types": "0.24.
|
56
|
-
"@livestore/
|
57
|
-
"@livestore/
|
56
|
+
"graphology-types": "0.24.8",
|
57
|
+
"@livestore/db-schema": "0.3.0-dev.6",
|
58
|
+
"@livestore/utils": "0.3.0-dev.6"
|
58
59
|
},
|
59
60
|
"devDependencies": {
|
60
61
|
"vitest": "^2.1.4"
|
@@ -94,7 +94,7 @@ describe('derived mutations', () => {
|
|
94
94
|
})
|
95
95
|
})
|
96
96
|
|
97
|
-
const patchId = (muationEvent: MutationEvent.
|
97
|
+
const patchId = (muationEvent: MutationEvent.PartialAnyDecoded) => {
|
98
98
|
// TODO use new id paradigm
|
99
99
|
const id = `00000000-0000-0000-0000-000000000000`
|
100
100
|
return { ...muationEvent, id }
|
package/src/derived-mutations.ts
CHANGED
@@ -136,8 +136,10 @@ export namespace DerivedMutationHelperFns {
|
|
136
136
|
> = SqliteDsl.AnyIfConstained<
|
137
137
|
TColumns,
|
138
138
|
UseShortcut<TOptions> extends true
|
139
|
-
? (
|
140
|
-
|
139
|
+
? (
|
140
|
+
values?: GetValForKey<SqliteDsl.FromColumns.InsertRowDecoded<TColumns>, 'value'>,
|
141
|
+
) => MutationEvent.PartialAnyDecoded
|
142
|
+
: (values: SqliteDsl.FromColumns.InsertRowDecoded<TColumns>) => MutationEvent.PartialAnyDecoded
|
141
143
|
>
|
142
144
|
|
143
145
|
export type UpdateMutationFn<
|
@@ -146,17 +148,19 @@ export namespace DerivedMutationHelperFns {
|
|
146
148
|
> = SqliteDsl.AnyIfConstained<
|
147
149
|
TColumns,
|
148
150
|
UseShortcut<TOptions> extends true
|
149
|
-
? (
|
151
|
+
? (
|
152
|
+
values: Partial<GetValForKey<SqliteDsl.FromColumns.RowDecoded<TColumns>, 'value'>>,
|
153
|
+
) => MutationEvent.PartialAnyDecoded
|
150
154
|
: (args: {
|
151
155
|
where: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>>
|
152
156
|
values: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>>
|
153
|
-
}) => MutationEvent.
|
157
|
+
}) => MutationEvent.PartialAnyDecoded
|
154
158
|
>
|
155
159
|
|
156
160
|
export type DeleteMutationFn<
|
157
161
|
TColumns extends SqliteDsl.ConstraintColumns,
|
158
162
|
_TOptions extends DbSchema.TableOptions,
|
159
|
-
> = (args: { where: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>> }) => MutationEvent.
|
163
|
+
> = (args: { where: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>> }) => MutationEvent.PartialAnyDecoded
|
160
164
|
|
161
165
|
type UseShortcut<TOptions extends DbSchema.TableOptions> = TOptions['isSingleColumn'] extends true
|
162
166
|
? TOptions['isSingleton'] extends true
|
@@ -3,20 +3,25 @@ import type { Scope } from '@livestore/utils/effect'
|
|
3
3
|
import { Effect, Option, Schema } from '@livestore/utils/effect'
|
4
4
|
|
5
5
|
import type { SqliteError, SynchronousDatabase, UnexpectedError } from '../index.js'
|
6
|
+
import { getExecArgsFromMutation } from '../mutation.js'
|
6
7
|
import {
|
7
|
-
|
8
|
+
type LiveStoreSchema,
|
8
9
|
MUTATION_LOG_META_TABLE,
|
10
|
+
type MutationEvent,
|
9
11
|
mutationLogMetaTable,
|
10
12
|
SESSION_CHANGESET_META_TABLE,
|
11
13
|
sessionChangesetMetaTable,
|
12
|
-
} from '../
|
13
|
-
import type { LiveStoreSchema, MutationEvent } from '../schema/mod.js'
|
14
|
+
} from '../schema/mod.js'
|
14
15
|
import { insertRow } from '../sql-queries/index.js'
|
15
16
|
import { execSql, execSqlPrepared } from './connection.js'
|
16
17
|
import { LeaderThreadCtx } from './types.js'
|
17
18
|
|
18
19
|
export type ApplyMutation = (
|
19
20
|
mutationEventEncoded: MutationEvent.AnyEncoded,
|
21
|
+
options?: {
|
22
|
+
/** Needed for rehydrateFromMutationLog */
|
23
|
+
skipMutationLog?: boolean
|
24
|
+
},
|
20
25
|
) => Effect.Effect<void, SqliteError | UnexpectedError>
|
21
26
|
|
22
27
|
export const makeApplyMutation: Effect.Effect<ApplyMutation, never, Scope.Scope | LeaderThreadCtx> = Effect.gen(
|
@@ -31,15 +36,27 @@ export const makeApplyMutation: Effect.Effect<ApplyMutation, never, Scope.Scope
|
|
31
36
|
[...leaderThreadCtx.schema.mutations.entries()].map(([k, v]) => [k, Schema.hash(v.schema)] as const),
|
32
37
|
)
|
33
38
|
|
34
|
-
return (mutationEventEncoded) =>
|
39
|
+
return (mutationEventEncoded, options) =>
|
35
40
|
Effect.gen(function* () {
|
36
|
-
const {
|
37
|
-
const
|
41
|
+
const { schema, db, dbLog } = leaderThreadCtx
|
42
|
+
const skipMutationLog = options?.skipMutationLog ?? false
|
38
43
|
|
39
|
-
const mutationName =
|
44
|
+
const mutationName = mutationEventEncoded.mutation
|
40
45
|
const mutationDef = schema.mutations.get(mutationName) ?? shouldNeverHappen(`Unknown mutation: ${mutationName}`)
|
41
46
|
|
42
|
-
const execArgsArr = getExecArgsFromMutation({
|
47
|
+
const execArgsArr = getExecArgsFromMutation({
|
48
|
+
mutationDef,
|
49
|
+
mutationEvent: { decoded: undefined, encoded: mutationEventEncoded },
|
50
|
+
})
|
51
|
+
|
52
|
+
// NOTE we might want to bring this back if we want to debug no-op mutations
|
53
|
+
// const makeExecuteOptions = (statementSql: string, bindValues: any) => ({
|
54
|
+
// onRowsChanged: (rowsChanged: number) => {
|
55
|
+
// if (rowsChanged === 0) {
|
56
|
+
// console.warn(`Mutation "${mutationDef.name}" did not affect any rows:`, statementSql, bindValues)
|
57
|
+
// }
|
58
|
+
// },
|
59
|
+
// })
|
43
60
|
|
44
61
|
// console.group('[@livestore/common:leader-thread:applyMutation]', { mutationName })
|
45
62
|
|
@@ -53,30 +70,28 @@ export const makeApplyMutation: Effect.Effect<ApplyMutation, never, Scope.Scope
|
|
53
70
|
|
54
71
|
const changeset = session.changeset()
|
55
72
|
session.finish()
|
56
|
-
|
57
|
-
// TODO
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
)
|
73
|
-
}
|
73
|
+
|
74
|
+
// TODO use prepared statements
|
75
|
+
yield* execSql(
|
76
|
+
db,
|
77
|
+
...insertRow({
|
78
|
+
tableName: SESSION_CHANGESET_META_TABLE,
|
79
|
+
columns: sessionChangesetMetaTable.sqliteDef.columns,
|
80
|
+
values: {
|
81
|
+
idGlobal: mutationEventEncoded.id.global,
|
82
|
+
idLocal: mutationEventEncoded.id.local,
|
83
|
+
// NOTE the changeset will be empty (i.e. null) for no-op mutations
|
84
|
+
changeset: changeset ?? null,
|
85
|
+
debug: execArgsArr,
|
86
|
+
},
|
87
|
+
}),
|
88
|
+
)
|
74
89
|
|
75
90
|
// console.groupEnd()
|
76
91
|
|
77
92
|
// write to mutation_log
|
78
|
-
const excludeFromMutationLog = shouldExcludeMutationFromLog(mutationName,
|
79
|
-
if (excludeFromMutationLog === false) {
|
93
|
+
const excludeFromMutationLog = shouldExcludeMutationFromLog(mutationName, mutationEventEncoded)
|
94
|
+
if (skipMutationLog === false && excludeFromMutationLog === false) {
|
80
95
|
yield* insertIntoMutationLog(mutationEventEncoded, dbLog, mutationDefSchemaHashMap)
|
81
96
|
} else {
|
82
97
|
// console.debug('[@livestore/common:leader-thread] skipping mutation log write', mutation, statementSql, bindValues)
|
@@ -132,11 +147,14 @@ const makeShouldExcludeMutationFromLog = memoizeByRef((schema: LiveStoreSchema)
|
|
132
147
|
? (migrationOptions.excludeMutations ?? new Set(['livestore.RawSql']))
|
133
148
|
: new Set(['livestore.RawSql'])
|
134
149
|
|
135
|
-
return (mutationName: string,
|
150
|
+
return (mutationName: string, mutationEventEncoded: MutationEvent.AnyEncoded): boolean => {
|
136
151
|
if (mutationLogExclude.has(mutationName)) return true
|
137
152
|
|
138
153
|
const mutationDef = schema.mutations.get(mutationName) ?? shouldNeverHappen(`Unknown mutation: ${mutationName}`)
|
139
|
-
const execArgsArr = getExecArgsFromMutation({
|
154
|
+
const execArgsArr = getExecArgsFromMutation({
|
155
|
+
mutationDef,
|
156
|
+
mutationEvent: { decoded: undefined, encoded: mutationEventEncoded },
|
157
|
+
})
|
140
158
|
|
141
159
|
return execArgsArr.some((_) => _.statementSql.includes('__livestore'))
|
142
160
|
}
|
@@ -509,9 +509,9 @@ const backgroundBackendPulling = ({
|
|
509
509
|
})
|
510
510
|
}
|
511
511
|
|
512
|
-
|
513
|
-
|
514
|
-
}).pipe(Effect.fork)
|
512
|
+
trimChangesetRows(db, newBackendHead)
|
513
|
+
|
514
|
+
const fiber = yield* applyMutationItemsRef.current!({ batchItems: updateResult.newEvents }).pipe(Effect.fork)
|
515
515
|
|
516
516
|
yield* Ref.set(stateRef, {
|
517
517
|
_tag: 'applying-syncstate-advance',
|
@@ -519,7 +519,6 @@ const backgroundBackendPulling = ({
|
|
519
519
|
syncState: updateResult.newSyncState,
|
520
520
|
fiber,
|
521
521
|
})
|
522
|
-
// console.log('setRef:applying-syncstate-advance after backgroundBackendPulling', -1)
|
523
522
|
})
|
524
523
|
|
525
524
|
yield* syncBackend.pull(cursorInfo).pipe(
|
@@ -574,7 +573,9 @@ const rollback = ({
|
|
574
573
|
// Apply changesets in reverse order
|
575
574
|
for (let i = rollbackEvents.length - 1; i >= 0; i--) {
|
576
575
|
const { changeset } = rollbackEvents[i]!
|
577
|
-
|
576
|
+
if (changeset !== null) {
|
577
|
+
db.makeChangeset(changeset).invert().apply()
|
578
|
+
}
|
578
579
|
}
|
579
580
|
|
580
581
|
// Delete the changeset rows
|
@@ -668,3 +669,9 @@ const backgroundBackendPushing = ({
|
|
668
669
|
}
|
669
670
|
}
|
670
671
|
}).pipe(Effect.interruptible, Effect.withSpan('@livestore/common:leader-thread:syncing:backend-pushing'))
|
672
|
+
|
673
|
+
const trimChangesetRows = (db: SynchronousDatabase, newHead: EventId.EventId) => {
|
674
|
+
// Since we're using the session changeset rows to query for the current head,
|
675
|
+
// we're keeping at least one row for the current head, and thus are using `<` instead of `<=`
|
676
|
+
db.execute(sql`DELETE FROM ${SESSION_CHANGESET_META_TABLE} WHERE idGlobal < ${newHead.global}`)
|
677
|
+
}
|
@@ -24,7 +24,9 @@ export const recreateDb: Effect.Effect<
|
|
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 tmpSyncDb = yield* makeSyncDb({ _tag: 'in-memory' })
|
29
|
+
const tmpSyncDb = db
|
28
30
|
yield* configureConnection(tmpSyncDb, { fkEnabled: true })
|
29
31
|
|
30
32
|
const initDb = (hooks: Partial<MigrationHooks> | undefined) =>
|
@@ -91,17 +93,19 @@ export const recreateDb: Effect.Effect<
|
|
91
93
|
}
|
92
94
|
}
|
93
95
|
|
96
|
+
// TODO bring back
|
94
97
|
// Import the temporary in-memory database into the persistent database
|
95
|
-
yield* Effect.sync(() => db.import(tmpSyncDb)).pipe(
|
96
|
-
|
97
|
-
)
|
98
|
+
// yield* Effect.sync(() => db.import(tmpSyncDb)).pipe(
|
99
|
+
// Effect.withSpan('@livestore/common:leader-thread:recreateDb:import'),
|
100
|
+
// )
|
98
101
|
|
99
102
|
// TODO maybe bring back re-using this initial snapshot to avoid calling `.export()` again
|
100
103
|
// We've disabled this for now as it made the code too complex, as we often run syncing right after
|
101
104
|
// so the snapshot is no longer up to date
|
102
105
|
// const snapshotFromTmpDb = tmpSyncDb.export()
|
103
106
|
|
104
|
-
|
107
|
+
// TODO bring back
|
108
|
+
// tmpSyncDb.close()
|
105
109
|
}).pipe(
|
106
110
|
Effect.scoped, // NOTE we're closing the scope here so finalizers are called when the effect is done
|
107
111
|
Effect.withSpan('@livestore/common:leader-thread:recreateDb'),
|
package/src/mutation.ts
CHANGED
@@ -8,10 +8,19 @@ import { prepareBindValues } from './util.js'
|
|
8
8
|
|
9
9
|
export const getExecArgsFromMutation = ({
|
10
10
|
mutationDef,
|
11
|
-
|
11
|
+
mutationEvent,
|
12
12
|
}: {
|
13
13
|
mutationDef: MutationDef.Any
|
14
|
-
|
14
|
+
/** Both encoded and decoded mutation events are supported to reduce the number of times we need to decode/encode */
|
15
|
+
mutationEvent:
|
16
|
+
| {
|
17
|
+
decoded: MutationEvent.AnyDecoded | MutationEvent.PartialAnyDecoded
|
18
|
+
encoded: undefined
|
19
|
+
}
|
20
|
+
| {
|
21
|
+
decoded: undefined
|
22
|
+
encoded: MutationEvent.AnyEncoded | MutationEvent.PartialAnyEncoded
|
23
|
+
}
|
15
24
|
}): ReadonlyArray<{
|
16
25
|
statementSql: string
|
17
26
|
bindValues: PreparedBindValues
|
@@ -23,7 +32,9 @@ export const getExecArgsFromMutation = ({
|
|
23
32
|
|
24
33
|
switch (typeof mutationDef.sql) {
|
25
34
|
case 'function': {
|
26
|
-
const
|
35
|
+
const mutationArgsDecoded =
|
36
|
+
mutationEvent.decoded?.args ?? Schema.decodeUnknownSync(mutationDef.schema)(mutationEvent.encoded!.args)
|
37
|
+
const res = mutationDef.sql(mutationArgsDecoded)
|
27
38
|
statementRes = Array.isArray(res) ? res : [res]
|
28
39
|
break
|
29
40
|
}
|
@@ -40,10 +51,9 @@ export const getExecArgsFromMutation = ({
|
|
40
51
|
return statementRes.map((statementRes) => {
|
41
52
|
const statementSql = typeof statementRes === 'string' ? statementRes : statementRes.sql
|
42
53
|
|
43
|
-
const
|
44
|
-
|
45
|
-
|
46
|
-
: statementRes.bindValues
|
54
|
+
const mutationArgsEncoded =
|
55
|
+
mutationEvent.encoded?.args ?? Schema.encodeUnknownSync(mutationDef.schema)(mutationEvent.decoded!.args)
|
56
|
+
const bindValues = typeof statementRes === 'string' ? mutationArgsEncoded : statementRes.bindValues
|
47
57
|
|
48
58
|
const writeTables = typeof statementRes === 'string' ? undefined : statementRes.writeTables
|
49
59
|
|
@@ -1,8 +1,8 @@
|
|
1
|
-
import {
|
1
|
+
import { memoizeByRef, shouldNeverHappen } from '@livestore/utils'
|
2
2
|
import { Chunk, Effect, Option, Schema, Stream } from '@livestore/utils/effect'
|
3
3
|
|
4
4
|
import { type MigrationOptionsFromMutationLog, type SynchronousDatabase, UnexpectedError } from './adapter-types.js'
|
5
|
-
import {
|
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'
|
8
8
|
import type { PreparedBindValues } from './util.js'
|
@@ -10,7 +10,8 @@ import { sql } from './util.js'
|
|
10
10
|
|
11
11
|
export const rehydrateFromMutationLog = ({
|
12
12
|
logDb,
|
13
|
-
db
|
13
|
+
// TODO re-use this db when bringing back the boot in-memory db implementation
|
14
|
+
// db,
|
14
15
|
schema,
|
15
16
|
migrationOptions,
|
16
17
|
onProgress,
|
@@ -28,6 +29,8 @@ export const rehydrateFromMutationLog = ({
|
|
28
29
|
|
29
30
|
const hashMutation = memoizeByRef((mutation: MutationDef.Any) => Schema.hash(mutation.schema))
|
30
31
|
|
32
|
+
const applyMutation = yield* makeApplyMutation
|
33
|
+
|
31
34
|
const processMutation = (row: MutationLogMetaRow) =>
|
32
35
|
Effect.gen(function* () {
|
33
36
|
const mutationDef = schema.mutations.get(row.mutation) ?? shouldNeverHappen(`Unknown mutation ${row.mutation}`)
|
@@ -40,7 +43,10 @@ export const rehydrateFromMutationLog = ({
|
|
40
43
|
)
|
41
44
|
}
|
42
45
|
|
43
|
-
const
|
46
|
+
const args = JSON.parse(row.argsJson)
|
47
|
+
|
48
|
+
// Checking whether the schema has changed in an incompatible way
|
49
|
+
yield* Schema.decodeUnknown(mutationDef.schema)(args).pipe(
|
44
50
|
Effect.mapError((cause) =>
|
45
51
|
UnexpectedError.make({
|
46
52
|
cause,
|
@@ -53,28 +59,14 @@ This likely means the schema has changed in an incompatible way.
|
|
53
59
|
),
|
54
60
|
)
|
55
61
|
|
56
|
-
const
|
62
|
+
const mutationEventEncoded = {
|
57
63
|
id: { global: row.idGlobal, local: row.idLocal },
|
58
64
|
parentId: { global: row.parentIdGlobal, local: row.parentIdLocal },
|
59
65
|
mutation: row.mutation,
|
60
|
-
args
|
61
|
-
} satisfies MutationEvent.
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
const makeExecuteOptions = (statementSql: string, bindValues: any) => ({
|
66
|
-
onRowsChanged: (rowsChanged: number) => {
|
67
|
-
if (rowsChanged === 0 && migrationOptions.logging?.excludeAffectedRows?.(statementSql) !== true) {
|
68
|
-
console.warn(`Mutation "${mutationDef.name}" did not affect any rows:`, statementSql, bindValues)
|
69
|
-
}
|
70
|
-
},
|
71
|
-
})
|
72
|
-
|
73
|
-
for (const { statementSql, bindValues } of execArgsArr) {
|
74
|
-
// TODO cache prepared statements for mutations
|
75
|
-
db.execute(statementSql, bindValues, isDevEnv() ? makeExecuteOptions(statementSql, bindValues) : undefined)
|
76
|
-
// console.log(`Re-executed mutation ${mutationSql}`, bindValues)
|
77
|
-
}
|
66
|
+
args,
|
67
|
+
} satisfies MutationEvent.AnyEncoded
|
68
|
+
|
69
|
+
yield* applyMutation(mutationEventEncoded, { skipMutationLog: true })
|
78
70
|
}).pipe(Effect.withSpan(`@livestore/common:rehydrateFromMutationLog:processMutation`))
|
79
71
|
|
80
72
|
const CHUNK_SIZE = 100
|
@@ -54,7 +54,7 @@ export const AnyEncodedGlobal = Schema.Struct({
|
|
54
54
|
}).annotations({ title: 'MutationEvent.AnyEncodedGlobal' })
|
55
55
|
export type AnyEncodedGlobal = typeof AnyEncodedGlobal.Type
|
56
56
|
|
57
|
-
export type
|
57
|
+
export type PartialAnyDecoded = MutationEventPartial<MutationDef.Any>
|
58
58
|
export type PartialAnyEncoded = MutationEventPartialEncoded<MutationDef.Any>
|
59
59
|
|
60
60
|
export type PartialForSchema<TSchema extends LiveStoreSchema> = {
|
@@ -65,8 +65,9 @@ export type ForSchema<TSchema extends LiveStoreSchema> = {
|
|
65
65
|
[K in keyof TSchema['_MutationDefMapType']]: MutationEvent<TSchema['_MutationDefMapType'][K]>
|
66
66
|
}[keyof TSchema['_MutationDefMapType']]
|
67
67
|
|
68
|
-
export const isPartialMutationEvent = (
|
69
|
-
|
68
|
+
export const isPartialMutationEvent = (
|
69
|
+
mutationEvent: AnyDecoded | PartialAnyDecoded,
|
70
|
+
): mutationEvent is PartialAnyDecoded => 'id' in mutationEvent === false && 'parentId' in mutationEvent === false
|
70
71
|
|
71
72
|
export type ForMutationDefRecord<TMutationsDefRecord extends MutationDefRecord> = Schema.Schema<
|
72
73
|
{
|
@@ -49,12 +49,13 @@ export const sessionChangesetMetaTable = table(
|
|
49
49
|
// TODO bring back primary key
|
50
50
|
idGlobal: SqliteDsl.integer({ schema: EventId.GlobalEventId }),
|
51
51
|
idLocal: SqliteDsl.integer({ schema: EventId.LocalEventId }),
|
52
|
-
|
53
|
-
// idLocal: SqliteDsl.integer({ primaryKey: true }),
|
54
|
-
changeset: SqliteDsl.blob({}),
|
52
|
+
changeset: SqliteDsl.blob({ nullable: true }),
|
55
53
|
debug: SqliteDsl.json({ nullable: true }),
|
56
54
|
},
|
57
|
-
{
|
55
|
+
{
|
56
|
+
disableAutomaticIdColumn: true,
|
57
|
+
indexes: [{ columns: ['idGlobal', 'idLocal'], name: 'idx_session_changeset_id' }],
|
58
|
+
},
|
58
59
|
)
|
59
60
|
|
60
61
|
export type SessionChangesetMetaRow = FromTable.RowDecoded<typeof sessionChangesetMetaTable>
|
@@ -84,7 +85,7 @@ export const mutationLogMetaTable = table(
|
|
84
85
|
disableAutomaticIdColumn: true,
|
85
86
|
indexes: [
|
86
87
|
{ columns: ['idGlobal'], name: 'idx_idGlobal' },
|
87
|
-
{ columns: ['idGlobal', 'idLocal'], name: '
|
88
|
+
{ columns: ['idGlobal', 'idLocal'], name: 'idx_mutationlog_id' },
|
88
89
|
],
|
89
90
|
},
|
90
91
|
)
|
@@ -34,7 +34,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
34
34
|
pushToLeader: (batch: ReadonlyArray<MutationEvent.AnyEncoded>) => void
|
35
35
|
pullFromLeader: ClientSessionLeaderThreadProxy['mutations']['pull']
|
36
36
|
applyMutation: (
|
37
|
-
mutationEventDecoded: MutationEvent.
|
37
|
+
mutationEventDecoded: MutationEvent.PartialAnyDecoded,
|
38
38
|
options: { otelContext: otel.Context; withChangeset: boolean },
|
39
39
|
) => {
|
40
40
|
writeTables: Set<string>
|
@@ -196,7 +196,7 @@ export const makeClientSessionSyncProcessor = ({
|
|
196
196
|
|
197
197
|
export interface ClientSessionSyncProcessor {
|
198
198
|
push: (
|
199
|
-
batch: ReadonlyArray<MutationEvent.
|
199
|
+
batch: ReadonlyArray<MutationEvent.PartialAnyDecoded>,
|
200
200
|
options: { otelContext: otel.Context },
|
201
201
|
) => {
|
202
202
|
writeTables: Set<string>
|
@@ -19,13 +19,13 @@ export type RebaseInput = {
|
|
19
19
|
newRemoteEvents: RebaseEventWithConflict[]
|
20
20
|
pendingLocalEvents: RebaseEventWithConflict[]
|
21
21
|
validate: (args: {
|
22
|
-
rebasedLocalEvents: MutationEvent.
|
22
|
+
rebasedLocalEvents: MutationEvent.PartialAnyDecoded[]
|
23
23
|
mutationDefs: Record<string, MutationDef.Any>
|
24
24
|
}) => FactValidationResult
|
25
25
|
}
|
26
26
|
|
27
27
|
export type RebaseOutput = {
|
28
|
-
rebasedLocalEvents: MutationEvent.
|
28
|
+
rebasedLocalEvents: MutationEvent.PartialAnyDecoded[]
|
29
29
|
}
|
30
30
|
|
31
31
|
export type RebaseFn = (input: RebaseInput) => RebaseOutput
|
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.6' as const
|
6
6
|
|
7
7
|
/**
|
8
8
|
* This version number is incremented whenever the internal storage format changes in a breaking way.
|