@livestore/common 0.0.58-dev.5 → 0.0.58-dev.7
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 +29 -3
- package/dist/adapter-types.d.ts.map +1 -1
- package/dist/adapter-types.js +6 -1
- package/dist/adapter-types.js.map +1 -1
- package/dist/derived-mutations.d.ts +12 -12
- package/dist/derived-mutations.d.ts.map +1 -1
- package/dist/derived-mutations.test.js +1 -0
- package/dist/derived-mutations.test.js.map +1 -1
- package/dist/devtools/devtools-bridge.d.ts +1 -1
- package/dist/devtools/devtools-bridge.d.ts.map +1 -1
- package/dist/devtools/devtools-messages.d.ts +99 -21
- package/dist/devtools/devtools-messages.d.ts.map +1 -1
- package/dist/devtools/devtools-messages.js +13 -4
- package/dist/devtools/devtools-messages.js.map +1 -1
- package/dist/devtools/index.d.ts +1 -0
- package/dist/devtools/index.d.ts.map +1 -1
- package/dist/devtools/index.js +2 -0
- package/dist/devtools/index.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/rehydrate-from-mutationlog.d.ts.map +1 -1
- package/dist/rehydrate-from-mutationlog.js +11 -5
- package/dist/rehydrate-from-mutationlog.js.map +1 -1
- package/dist/schema/mutations.d.ts +137 -18
- package/dist/schema/mutations.d.ts.map +1 -1
- package/dist/schema/mutations.js +41 -16
- package/dist/schema/mutations.js.map +1 -1
- package/dist/schema/system-tables.d.ts +130 -8
- package/dist/schema/system-tables.d.ts.map +1 -1
- package/dist/schema/system-tables.js +23 -8
- package/dist/schema/system-tables.js.map +1 -1
- package/dist/schema/table-def.d.ts +1 -1
- package/dist/schema-management/migrations.js +1 -1
- package/dist/schema-management/migrations.js.map +1 -1
- package/dist/sync/next/compact-events.d.ts +15 -0
- package/dist/sync/next/compact-events.d.ts.map +1 -0
- package/dist/sync/next/compact-events.js +176 -0
- package/dist/sync/next/compact-events.js.map +1 -0
- package/dist/sync/next/facts.d.ts +37 -0
- package/dist/sync/next/facts.d.ts.map +1 -0
- package/dist/sync/next/facts.js +155 -0
- package/dist/sync/next/facts.js.map +1 -0
- package/dist/sync/next/graphology.d.ts +8 -0
- package/dist/sync/next/graphology.d.ts.map +1 -0
- package/dist/sync/next/graphology.js +36 -0
- package/dist/sync/next/graphology.js.map +1 -0
- package/dist/sync/next/graphology_.d.ts +3 -0
- package/dist/sync/next/graphology_.d.ts.map +1 -0
- package/dist/sync/next/graphology_.js +3 -0
- package/dist/sync/next/graphology_.js.map +1 -0
- package/dist/sync/next/history-dag.d.ts +30 -0
- package/dist/sync/next/history-dag.d.ts.map +1 -0
- package/dist/sync/next/history-dag.js +69 -0
- package/dist/sync/next/history-dag.js.map +1 -0
- package/dist/sync/next/mod.d.ts +5 -0
- package/dist/sync/next/mod.d.ts.map +1 -0
- package/dist/sync/next/mod.js +5 -0
- package/dist/sync/next/mod.js.map +1 -0
- package/dist/sync/next/mutation-fixtures.d.ts +73 -0
- package/dist/sync/next/mutation-fixtures.d.ts.map +1 -0
- package/dist/sync/next/mutation-fixtures.js +160 -0
- package/dist/sync/next/mutation-fixtures.js.map +1 -0
- package/dist/sync/next/rebase-events.d.ts +27 -0
- package/dist/sync/next/rebase-events.d.ts.map +1 -0
- package/dist/sync/next/rebase-events.js +41 -0
- package/dist/sync/next/rebase-events.js.map +1 -0
- package/dist/sync/next/test/compact-events.calculator.test.d.ts +2 -0
- package/dist/sync/next/test/compact-events.calculator.test.d.ts.map +1 -0
- package/dist/sync/next/test/compact-events.calculator.test.js +101 -0
- package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -0
- package/dist/sync/next/test/compact-events.test.d.ts +2 -0
- package/dist/sync/next/test/compact-events.test.d.ts.map +1 -0
- package/dist/sync/next/test/compact-events.test.js +201 -0
- package/dist/sync/next/test/compact-events.test.js.map +1 -0
- package/dist/sync/next/test/mod.d.ts +2 -0
- package/dist/sync/next/test/mod.d.ts.map +1 -0
- package/dist/sync/next/test/mod.js +2 -0
- package/dist/sync/next/test/mod.js.map +1 -0
- package/dist/sync/next/test/mutation-fixtures.d.ts +73 -0
- package/dist/sync/next/test/mutation-fixtures.d.ts.map +1 -0
- package/dist/sync/next/test/mutation-fixtures.js +161 -0
- package/dist/sync/next/test/mutation-fixtures.js.map +1 -0
- package/dist/sync/sync.d.ts +19 -6
- package/dist/sync/sync.d.ts.map +1 -1
- package/dist/sync/sync.js.map +1 -1
- package/package.json +21 -4
- package/src/adapter-types.ts +27 -4
- package/src/derived-mutations.test.ts +2 -1
- package/src/derived-mutations.ts +5 -5
- package/src/devtools/devtools-bridge.ts +1 -1
- package/src/devtools/devtools-messages.ts +12 -2
- package/src/devtools/index.ts +2 -0
- package/src/index.ts +6 -0
- package/src/rehydrate-from-mutationlog.ts +16 -7
- package/src/schema/mutations.ts +171 -30
- package/src/schema/system-tables.ts +31 -8
- package/src/schema-management/migrations.ts +1 -1
- package/src/sync/next/ambient.d.ts +3 -0
- package/src/sync/next/compact-events.ts +219 -0
- package/src/sync/next/facts.ts +228 -0
- package/src/sync/next/graphology.ts +49 -0
- package/src/sync/next/graphology_.ts +2 -0
- package/src/sync/next/history-dag.ts +109 -0
- package/src/sync/next/mod.ts +4 -0
- package/src/sync/next/rebase-events.ts +97 -0
- package/src/sync/next/test/compact-events.calculator.test.ts +121 -0
- package/src/sync/next/test/compact-events.test.ts +232 -0
- package/src/sync/next/test/mod.ts +1 -0
- package/src/sync/next/test/mutation-fixtures.ts +230 -0
- package/src/sync/sync.ts +30 -6
package/src/adapter-types.ts
CHANGED
@@ -9,6 +9,7 @@ export interface PreparedStatement {
|
|
9
9
|
execute(bindValues: PreparedBindValues | undefined, options?: { onRowsChanged?: (rowsChanged: number) => void }): void
|
10
10
|
select<T>(bindValues: PreparedBindValues | undefined): ReadonlyArray<T>
|
11
11
|
finalize(): void
|
12
|
+
sql: string
|
12
13
|
}
|
13
14
|
|
14
15
|
export type StoreAdapter = {
|
@@ -28,6 +29,7 @@ export type SynchronousDatabase = {
|
|
28
29
|
): void
|
29
30
|
select<T>(queryStr: string, bindValues?: PreparedBindValues | undefined): ReadonlyArray<T>
|
30
31
|
export(): Uint8Array
|
32
|
+
close(): void
|
31
33
|
}
|
32
34
|
|
33
35
|
export type ResetMode = 'all-data' | 'only-app-db'
|
@@ -64,9 +66,16 @@ export type Coordinator = {
|
|
64
66
|
}
|
65
67
|
// TODO is exposing the lock status really needed (or only relevant for web adapter?)
|
66
68
|
lockStatus: SubscriptionRef.SubscriptionRef<LockStatus>
|
67
|
-
syncMutations: Stream.Stream<MutationEvent.
|
69
|
+
syncMutations: Stream.Stream<MutationEvent.Any, UnexpectedError>
|
68
70
|
execute(queryStr: string, bindValues: PreparedBindValues | undefined): Effect.Effect<void, UnexpectedError>
|
69
|
-
mutate(
|
71
|
+
mutate(
|
72
|
+
mutationEventEncoded: MutationEvent.AnyEncoded,
|
73
|
+
options: { persisted: boolean },
|
74
|
+
): Effect.Effect<void, UnexpectedError>
|
75
|
+
/** Can be called synchronously */
|
76
|
+
getNextMutationEventId: (opts: { localOnly: boolean }) => Effect.Effect<EventId, UnexpectedError>
|
77
|
+
/** Used to initially get the current mutation event id to use as `parentId` for the next mutation event */
|
78
|
+
getCurrentMutationEventId: Effect.Effect<EventId, UnexpectedError>
|
70
79
|
export: Effect.Effect<Uint8Array | undefined, UnexpectedError>
|
71
80
|
getMutationLogData: Effect.Effect<Uint8Array, UnexpectedError>
|
72
81
|
networkStatus: SubscriptionRef.SubscriptionRef<NetworkStatus>
|
@@ -74,10 +83,25 @@ export type Coordinator = {
|
|
74
83
|
|
75
84
|
export type LockStatus = 'has-lock' | 'no-lock'
|
76
85
|
|
86
|
+
/**
|
87
|
+
* LiveStore event id value consisting of a globally unique event sequence number
|
88
|
+
* and a local sequence number.
|
89
|
+
*
|
90
|
+
* The local sequence number is only used for localOnly mutations and starts from 0 for each global sequence number.
|
91
|
+
*/
|
92
|
+
export type EventId = { global: number; local: number }
|
93
|
+
|
94
|
+
export const EventId = Schema.Struct({
|
95
|
+
global: Schema.Number,
|
96
|
+
local: Schema.Number,
|
97
|
+
}).annotations({ title: 'LiveStore.EventId' })
|
98
|
+
|
99
|
+
export const ROOT_ID = { global: -1, local: 0 } satisfies EventId
|
100
|
+
|
77
101
|
export type BootDb = {
|
78
102
|
_tag: 'BootDb'
|
79
103
|
execute(queryStr: string, bindValues?: PreparedBindValues): void
|
80
|
-
mutate: <const TMutationArg extends ReadonlyArray<MutationEvent.
|
104
|
+
mutate: <const TMutationArg extends ReadonlyArray<MutationEvent.PartialAny>>(...list: TMutationArg) => void
|
81
105
|
select<T>(queryStr: string, bindValues?: PreparedBindValues): ReadonlyArray<T>
|
82
106
|
txn(callback: () => void): void
|
83
107
|
}
|
@@ -89,7 +113,6 @@ export class UnexpectedError extends Schema.TaggedError<UnexpectedError>()('Live
|
|
89
113
|
}) {
|
90
114
|
static mapToUnexpectedError = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
91
115
|
effect.pipe(
|
92
|
-
Effect.tapCauseLogPretty,
|
93
116
|
Effect.mapError((cause) => (Schema.is(UnexpectedError)(cause) ? cause : new UnexpectedError({ cause }))),
|
94
117
|
Effect.catchAllDefect((cause) => new UnexpectedError({ cause })),
|
95
118
|
)
|
@@ -94,7 +94,8 @@ describe('derived mutations', () => {
|
|
94
94
|
})
|
95
95
|
})
|
96
96
|
|
97
|
-
const patchId = (muationEvent: MutationEvent.
|
97
|
+
const patchId = (muationEvent: MutationEvent.PartialAny) => {
|
98
|
+
// TODO use new id paradigm
|
98
99
|
const id = `00000000-0000-0000-0000-000000000000`
|
99
100
|
return { ...muationEvent, id }
|
100
101
|
}
|
package/src/derived-mutations.ts
CHANGED
@@ -140,8 +140,8 @@ export namespace DerivedMutationHelperFns {
|
|
140
140
|
> = SqliteDsl.AnyIfConstained<
|
141
141
|
TColumns,
|
142
142
|
UseShortcut<TOptions> extends true
|
143
|
-
? (values?: GetValForKey<SqliteDsl.FromColumns.InsertRowDecoded<TColumns>, 'value'>) => MutationEvent.
|
144
|
-
: (values: SqliteDsl.FromColumns.InsertRowDecoded<TColumns>) => MutationEvent.
|
143
|
+
? (values?: GetValForKey<SqliteDsl.FromColumns.InsertRowDecoded<TColumns>, 'value'>) => MutationEvent.PartialAny
|
144
|
+
: (values: SqliteDsl.FromColumns.InsertRowDecoded<TColumns>) => MutationEvent.PartialAny
|
145
145
|
>
|
146
146
|
|
147
147
|
export type UpdateMutationFn<
|
@@ -150,17 +150,17 @@ export namespace DerivedMutationHelperFns {
|
|
150
150
|
> = SqliteDsl.AnyIfConstained<
|
151
151
|
TColumns,
|
152
152
|
UseShortcut<TOptions> extends true
|
153
|
-
? (values: Partial<GetValForKey<SqliteDsl.FromColumns.RowDecoded<TColumns>, 'value'>>) => MutationEvent.
|
153
|
+
? (values: Partial<GetValForKey<SqliteDsl.FromColumns.RowDecoded<TColumns>, 'value'>>) => MutationEvent.PartialAny
|
154
154
|
: (args: {
|
155
155
|
where: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>>
|
156
156
|
values: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>>
|
157
|
-
}) => MutationEvent.
|
157
|
+
}) => MutationEvent.PartialAny
|
158
158
|
>
|
159
159
|
|
160
160
|
export type DeleteMutationFn<
|
161
161
|
TColumns extends SqliteDsl.ConstraintColumns,
|
162
162
|
_TOptions extends DbSchema.TableOptions,
|
163
|
-
> = (args: { where: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>> }) => MutationEvent.
|
163
|
+
> = (args: { where: Partial<SqliteDsl.FromColumns.RowDecoded<TColumns>> }) => MutationEvent.PartialAny
|
164
164
|
|
165
165
|
type UseShortcut<TOptions extends DbSchema.TableOptions> = TOptions['isSingleColumn'] extends true
|
166
166
|
? TOptions['isSingleton'] extends true
|
@@ -74,7 +74,7 @@ export class MutationBroadcast extends LSDMessage('LSD.MutationBroadcast', {
|
|
74
74
|
}) {}
|
75
75
|
|
76
76
|
export class RunMutationReq extends LSDReqResMessage('LSD.RunMutationReq', {
|
77
|
-
mutationEventEncoded: mutationEventSchemaEncodedAny,
|
77
|
+
mutationEventEncoded: mutationEventSchemaEncodedAny.pipe(Schema.omit('id', 'parentId')),
|
78
78
|
persisted: Schema.Boolean,
|
79
79
|
}) {}
|
80
80
|
|
@@ -158,12 +158,19 @@ export class SyncingInfoRes extends LSDReqResMessage('LSD.SyncingInfoRes', {
|
|
158
158
|
syncingInfo: SyncingInfo,
|
159
159
|
}) {}
|
160
160
|
|
161
|
+
export class SyncHistorySubscribe extends LSDReqResMessage('LSD.SyncHistorySubscribe', {}) {}
|
162
|
+
export class SyncHistoryUnsubscribe extends LSDReqResMessage('LSD.SyncHistoryUnsubscribe', {}) {}
|
163
|
+
export class SyncHistoryRes extends LSDReqResMessage('LSD.SyncHistoryRes', {
|
164
|
+
mutationEventEncoded: mutationEventSchemaEncodedAny,
|
165
|
+
metadata: Schema.Option(Schema.JsonValue),
|
166
|
+
}) {}
|
167
|
+
|
161
168
|
export class DevtoolsReady extends LSDMessage('LSD.DevtoolsReady', {}) {}
|
162
169
|
|
163
170
|
export class DevtoolsConnected extends LSDChannelMessage('LSD.DevtoolsConnected', {}) {}
|
164
171
|
|
165
172
|
export class AppHostReady extends LSDChannelMessage('LSD.AppHostReady', {
|
166
|
-
|
173
|
+
isLeader: Schema.Boolean,
|
167
174
|
}) {}
|
168
175
|
|
169
176
|
export class Disconnect extends LSDChannelMessage('LSD.Disconnect', {}) {}
|
@@ -186,6 +193,8 @@ export const MessageToAppHostCoordinator = Schema.Union(
|
|
186
193
|
RunMutationReq,
|
187
194
|
Ping,
|
188
195
|
DatabaseFileInfoReq,
|
196
|
+
SyncHistorySubscribe,
|
197
|
+
SyncHistoryUnsubscribe,
|
189
198
|
SyncingInfoReq,
|
190
199
|
).annotations({ identifier: 'LSD.MessageToAppHostCoordinator' })
|
191
200
|
|
@@ -219,6 +228,7 @@ export const MessageFromAppHostCoordinator = Schema.Union(
|
|
219
228
|
RunMutationRes,
|
220
229
|
Pong,
|
221
230
|
DatabaseFileInfoRes,
|
231
|
+
SyncHistoryRes,
|
222
232
|
SyncingInfoRes,
|
223
233
|
).annotations({ identifier: 'LSD.MessageFromAppHostCoordinator' })
|
224
234
|
|
package/src/devtools/index.ts
CHANGED
@@ -8,6 +8,7 @@ export * from './devtools-bridge.js'
|
|
8
8
|
export namespace WebBridge {
|
9
9
|
export class AppHostReady extends Schema.TaggedStruct('LSD.WebBridge.AppHostReady', {
|
10
10
|
appHostId: Schema.String,
|
11
|
+
// storeId: Schema.String,
|
11
12
|
isLeader: Schema.Boolean,
|
12
13
|
}) {}
|
13
14
|
|
@@ -24,6 +25,7 @@ export namespace WebBridge {
|
|
24
25
|
*/
|
25
26
|
webBridgeId: Schema.String,
|
26
27
|
isLeader: Schema.Boolean,
|
28
|
+
storeId: Schema.String,
|
27
29
|
}) {}
|
28
30
|
|
29
31
|
export class AppHostWillDisconnect extends Schema.TaggedStruct('LSD.WebBridge.AppHostWillDisconnect', {
|
package/src/index.ts
CHANGED
@@ -3,7 +3,7 @@ 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
5
|
import { getExecArgsFromMutation } from './mutation.js'
|
6
|
-
import type { LiveStoreSchema, MutationDef, MutationLogMetaRow } from './schema/index.js'
|
6
|
+
import type { LiveStoreSchema, MutationDef, MutationEvent, MutationLogMetaRow } from './schema/index.js'
|
7
7
|
import { MUTATION_LOG_META_TABLE } from './schema/index.js'
|
8
8
|
import type { PreparedBindValues } from './util.js'
|
9
9
|
import { sql } from './util.js'
|
@@ -54,10 +54,11 @@ This likely means the schema has changed in an incompatible way.
|
|
54
54
|
)
|
55
55
|
|
56
56
|
const mutationEventDecoded = {
|
57
|
-
id: row.
|
57
|
+
id: { global: row.idGlobal, local: row.idLocal },
|
58
|
+
parentId: { global: row.parentIdGlobal, local: row.parentIdLocal },
|
58
59
|
mutation: row.mutation,
|
59
60
|
args: argsDecoded,
|
60
|
-
}
|
61
|
+
} satisfies MutationEvent.Any
|
61
62
|
|
62
63
|
const execArgsArr = getExecArgsFromMutation({ mutationDef, mutationEventDecoded })
|
63
64
|
|
@@ -84,8 +85,8 @@ This likely means the schema has changed in an incompatible way.
|
|
84
85
|
|
85
86
|
const stmt = logDb.prepare(sql`\
|
86
87
|
SELECT * FROM ${MUTATION_LOG_META_TABLE}
|
87
|
-
WHERE
|
88
|
-
ORDER BY
|
88
|
+
WHERE idGlobal > COALESCE($idGlobal, '') AND idLocal > COALESCE($idLocal, '')
|
89
|
+
ORDER BY idGlobal ASC, idLocal ASC
|
89
90
|
LIMIT ${CHUNK_SIZE}
|
90
91
|
`)
|
91
92
|
|
@@ -97,9 +98,17 @@ LIMIT ${CHUNK_SIZE}
|
|
97
98
|
// End stream if no more rows
|
98
99
|
if (Chunk.isChunk(item) && item.length === 0) return Option.none()
|
99
100
|
|
100
|
-
const lastId = Chunk.isChunk(item)
|
101
|
+
const lastId = Chunk.isChunk(item)
|
102
|
+
? Chunk.last(item).pipe(
|
103
|
+
Option.map((_) => ({ global: _.idGlobal, local: _.idLocal })),
|
104
|
+
Option.getOrUndefined,
|
105
|
+
)
|
106
|
+
: undefined
|
101
107
|
const nextItem = Chunk.fromIterable(
|
102
|
-
stmt.select<MutationLogMetaRow>({
|
108
|
+
stmt.select<MutationLogMetaRow>({
|
109
|
+
$idGlobal: lastId?.global,
|
110
|
+
$idLocal: lastId?.local,
|
111
|
+
} as any as PreparedBindValues),
|
103
112
|
)
|
104
113
|
const prevItem = Chunk.isChunk(item) ? item : Chunk.empty()
|
105
114
|
return Option.some([prevItem, nextItem])
|
package/src/schema/mutations.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import { memoizeByRef } from '@livestore/utils'
|
2
|
-
import { cuid } from '@livestore/utils/cuid'
|
3
2
|
import { Schema } from '@livestore/utils/effect'
|
4
3
|
|
4
|
+
import { EventId } from '../adapter-types.js'
|
5
5
|
import type { BindValues } from '../sql-queries/sql-queries.js'
|
6
6
|
import type { LiveStoreSchema } from './index.js'
|
7
7
|
|
@@ -35,42 +35,131 @@ export type SingleOrReadonlyArray<T> = T | ReadonlyArray<T>
|
|
35
35
|
export type MutationDef<TName extends string, TFrom, TTo> = {
|
36
36
|
name: TName
|
37
37
|
schema: Schema.Schema<TTo, TFrom>
|
38
|
-
sql: MutationDefSqlResult<TTo
|
38
|
+
sql: MutationDefSqlResult<NoInfer<TTo>>
|
39
39
|
options: {
|
40
|
+
/** Warning: This feature is not fully implemented yet */
|
41
|
+
historyId: string
|
40
42
|
/**
|
41
43
|
* When set to true, the mutation won't be synced over the network
|
42
44
|
*/
|
43
45
|
localOnly: boolean
|
46
|
+
/** Warning: This feature is not fully implemented yet */
|
47
|
+
facts: FactsCallback<TTo> | undefined
|
44
48
|
}
|
45
49
|
|
46
|
-
/** Helper function to construct mutation event */
|
47
|
-
(
|
50
|
+
/** Helper function to construct a partial mutation event */
|
51
|
+
(
|
52
|
+
args: TTo,
|
53
|
+
options?: {
|
54
|
+
id?: number
|
55
|
+
},
|
56
|
+
): {
|
57
|
+
mutation: TName
|
58
|
+
args: TTo
|
59
|
+
// TODO remove/clean up after sync-next is fully implemented
|
60
|
+
id?: EventId
|
61
|
+
}
|
62
|
+
}
|
63
|
+
|
64
|
+
export type FactsCallback<TTo> = (
|
65
|
+
args: TTo,
|
66
|
+
currentFacts: MutationEventFacts,
|
67
|
+
) => {
|
68
|
+
modify: {
|
69
|
+
set: Iterable<MutationEventFactInput>
|
70
|
+
unset: Iterable<MutationEventFactInput>
|
71
|
+
}
|
72
|
+
require: Iterable<MutationEventFactInput>
|
48
73
|
}
|
49
74
|
|
50
75
|
export namespace MutationDef {
|
51
76
|
export type Any = MutationDef<string, any, any>
|
52
77
|
}
|
53
78
|
|
79
|
+
export type MutationEventKey = string
|
80
|
+
export type MutationEventFact = string
|
81
|
+
export type MutationEventFacts = ReadonlyMap<string, any>
|
82
|
+
|
83
|
+
export type MutationEventFactsGroup = {
|
84
|
+
modifySet: MutationEventFacts
|
85
|
+
modifyUnset: MutationEventFacts
|
86
|
+
|
87
|
+
/**
|
88
|
+
* Events on independent "dependency" branches are commutative which can facilitate more prioritized syncing
|
89
|
+
*/
|
90
|
+
depRequire: MutationEventFacts
|
91
|
+
depRead: MutationEventFacts
|
92
|
+
}
|
93
|
+
|
94
|
+
export type MutationEventFactsSnapshot = Map<string, any>
|
95
|
+
|
96
|
+
export type MutationEventFactInput = string | readonly [string, any]
|
97
|
+
|
98
|
+
export const defineFacts = <
|
99
|
+
TRecord extends Record<string, MutationEventFactInput | ((...args: any[]) => MutationEventFactInput)>,
|
100
|
+
>(
|
101
|
+
record: TRecord,
|
102
|
+
): TRecord => record
|
103
|
+
|
104
|
+
export type DefineMutationOptions<TTo> = {
|
105
|
+
historyId?: string
|
106
|
+
/** Warning: This feature is not fully implemented yet */
|
107
|
+
facts?: (
|
108
|
+
args: TTo,
|
109
|
+
currentFacts: MutationEventFacts,
|
110
|
+
) => {
|
111
|
+
modify?: {
|
112
|
+
set?: Iterable<MutationEventFactInput>
|
113
|
+
unset?: Iterable<MutationEventFactInput>
|
114
|
+
}
|
115
|
+
/**
|
116
|
+
* Two purposes: constrain history and constrain compaction
|
117
|
+
*/
|
118
|
+
require?: Iterable<MutationEventFactInput>
|
119
|
+
}
|
120
|
+
/**
|
121
|
+
* When set to true, the mutation won't be synced over the network
|
122
|
+
*/
|
123
|
+
localOnly?: boolean
|
124
|
+
}
|
125
|
+
|
54
126
|
// TODO possibly also allow for mutation event subsumption behaviour
|
55
127
|
export const defineMutation = <TName extends string, TFrom, TTo>(
|
56
128
|
name: TName,
|
57
129
|
schema: Schema.Schema<TTo, TFrom>,
|
58
|
-
sql: MutationDefSqlResult<TTo
|
59
|
-
options?:
|
60
|
-
/**
|
61
|
-
* When set to true, the mutation won't be synced over the network
|
62
|
-
*/
|
63
|
-
localOnly?: boolean
|
64
|
-
},
|
130
|
+
sql: MutationDefSqlResult<NoInfer<TTo>>,
|
131
|
+
options?: DefineMutationOptions<TTo>,
|
65
132
|
): MutationDef<TName, TFrom, TTo> => {
|
66
|
-
const
|
133
|
+
const makePartialEvent = (
|
134
|
+
args: TTo,
|
135
|
+
options?: {
|
136
|
+
id?: EventId
|
137
|
+
},
|
138
|
+
) => ({ mutation: name, args, ...options })
|
67
139
|
|
68
|
-
Object.defineProperty(
|
69
|
-
Object.defineProperty(
|
70
|
-
Object.defineProperty(
|
71
|
-
Object.defineProperty(
|
140
|
+
Object.defineProperty(makePartialEvent, 'name', { value: name })
|
141
|
+
Object.defineProperty(makePartialEvent, 'schema', { value: schema })
|
142
|
+
Object.defineProperty(makePartialEvent, 'sql', { value: sql })
|
143
|
+
Object.defineProperty(makePartialEvent, 'options', {
|
144
|
+
value: {
|
145
|
+
historyId: options?.historyId ?? 'main',
|
146
|
+
localOnly: options?.localOnly ?? false,
|
147
|
+
facts: options?.facts
|
148
|
+
? (args, currentFacts) => {
|
149
|
+
const res = options.facts!(args, currentFacts)
|
150
|
+
return {
|
151
|
+
modify: {
|
152
|
+
set: res.modify?.set ? new Set(res.modify.set) : new Set(),
|
153
|
+
unset: res.modify?.unset ? new Set(res.modify.unset) : new Set(),
|
154
|
+
},
|
155
|
+
require: res.require ? new Set(res.require) : new Set(),
|
156
|
+
}
|
157
|
+
}
|
158
|
+
: undefined,
|
159
|
+
} satisfies MutationDef.Any['options'],
|
160
|
+
})
|
72
161
|
|
73
|
-
return
|
162
|
+
return makePartialEvent as MutationDef<TName, TFrom, TTo>
|
74
163
|
}
|
75
164
|
|
76
165
|
export const makeMutationDefRecord = <TInputRecord extends Record<string, MutationDef.Any>>(
|
@@ -102,22 +191,41 @@ export const rawSqlMutation = defineMutation(
|
|
102
191
|
export type RawSqlMutation = typeof rawSqlMutation
|
103
192
|
export type RawSqlMutationEvent = ReturnType<typeof rawSqlMutation>
|
104
193
|
|
194
|
+
export type MutationEventPartial<TMutationsDef extends MutationDef.Any> = {
|
195
|
+
mutation: TMutationsDef['name']
|
196
|
+
args: Schema.Schema.Type<TMutationsDef['schema']>
|
197
|
+
}
|
198
|
+
|
199
|
+
export type MutationEventPartialEncoded<TMutationsDef extends MutationDef.Any> = {
|
200
|
+
mutation: TMutationsDef['name']
|
201
|
+
args: Schema.Schema.Encoded<TMutationsDef['schema']>
|
202
|
+
}
|
203
|
+
|
105
204
|
export type MutationEvent<TMutationsDef extends MutationDef.Any> = {
|
106
205
|
mutation: TMutationsDef['name']
|
107
206
|
args: Schema.Schema.Type<TMutationsDef['schema']>
|
108
|
-
id:
|
207
|
+
id: EventId
|
208
|
+
parentId: EventId
|
109
209
|
}
|
110
210
|
|
111
211
|
export type MutationEventEncoded<TMutationsDef extends MutationDef.Any> = {
|
112
212
|
mutation: TMutationsDef['name']
|
113
213
|
args: Schema.Schema.Encoded<TMutationsDef['schema']>
|
114
|
-
id:
|
214
|
+
id: EventId
|
215
|
+
parentId: EventId
|
115
216
|
}
|
116
217
|
|
117
218
|
export namespace MutationEvent {
|
118
219
|
export type Any = MutationEvent<MutationDef.Any>
|
119
220
|
export type AnyEncoded = MutationEventEncoded<MutationDef.Any>
|
120
221
|
|
222
|
+
export type PartialAny = MutationEventPartial<MutationDef.Any>
|
223
|
+
export type PartialAnyEncoded = MutationEventPartialEncoded<MutationDef.Any>
|
224
|
+
|
225
|
+
export type PartialForSchema<TSchema extends LiveStoreSchema> = {
|
226
|
+
[K in keyof TSchema['_MutationDefMapType']]: MutationEventPartial<TSchema['_MutationDefMapType'][K]>
|
227
|
+
}[keyof TSchema['_MutationDefMapType']]
|
228
|
+
|
121
229
|
export type ForSchema<TSchema extends LiveStoreSchema> = {
|
122
230
|
[K in keyof TSchema['_MutationDefMapType']]: MutationEvent<TSchema['_MutationDefMapType'][K]>
|
123
231
|
}[keyof TSchema['_MutationDefMapType']]
|
@@ -128,14 +236,31 @@ export type MutationEventSchema<TMutationsDefRecord extends MutationDefRecord> =
|
|
128
236
|
[K in keyof TMutationsDefRecord]: {
|
129
237
|
mutation: K
|
130
238
|
args: Schema.Schema.Type<TMutationsDefRecord[K]['schema']>
|
131
|
-
id:
|
239
|
+
id: EventId
|
240
|
+
parentId: EventId
|
241
|
+
}
|
242
|
+
}[keyof TMutationsDefRecord],
|
243
|
+
{
|
244
|
+
[K in keyof TMutationsDefRecord]: {
|
245
|
+
mutation: K
|
246
|
+
args: Schema.Schema.Encoded<TMutationsDefRecord[K]['schema']>
|
247
|
+
id: EventId
|
248
|
+
parentId: EventId
|
249
|
+
}
|
250
|
+
}[keyof TMutationsDefRecord]
|
251
|
+
>
|
252
|
+
|
253
|
+
export type MutationEventPartialSchema<TMutationsDefRecord extends MutationDefRecord> = Schema.Schema<
|
254
|
+
{
|
255
|
+
[K in keyof TMutationsDefRecord]: {
|
256
|
+
mutation: K
|
257
|
+
args: Schema.Schema.Type<TMutationsDefRecord[K]['schema']>
|
132
258
|
}
|
133
259
|
}[keyof TMutationsDefRecord],
|
134
260
|
{
|
135
261
|
[K in keyof TMutationsDefRecord]: {
|
136
262
|
mutation: K
|
137
263
|
args: Schema.Schema.Encoded<TMutationsDefRecord[K]['schema']>
|
138
|
-
id: string
|
139
264
|
}
|
140
265
|
}[keyof TMutationsDefRecord]
|
141
266
|
>
|
@@ -148,21 +273,37 @@ export const makeMutationEventSchema = <TSchema extends LiveStoreSchema>(
|
|
148
273
|
Schema.Struct({
|
149
274
|
mutation: Schema.Literal(def.name),
|
150
275
|
args: def.schema,
|
151
|
-
id:
|
276
|
+
id: EventId,
|
277
|
+
parentId: EventId,
|
152
278
|
}),
|
153
279
|
),
|
154
280
|
).annotations({ title: 'MutationEventSchema' }) as any
|
155
281
|
|
282
|
+
export const makeMutationEventPartialSchema = <TSchema extends LiveStoreSchema>(
|
283
|
+
schema: TSchema,
|
284
|
+
): MutationEventPartialSchema<TSchema['_MutationDefMapType']> =>
|
285
|
+
Schema.Union(
|
286
|
+
...[...schema.mutations.values()].map((def) =>
|
287
|
+
Schema.Struct({
|
288
|
+
mutation: Schema.Literal(def.name),
|
289
|
+
args: def.schema,
|
290
|
+
}),
|
291
|
+
),
|
292
|
+
).annotations({ title: 'MutationEventSchemaPartial' }) as any
|
293
|
+
|
156
294
|
export const makeMutationEventSchemaMemo = memoizeByRef(makeMutationEventSchema)
|
157
295
|
|
158
|
-
export const
|
296
|
+
export const mutationEventSchemaAny = Schema.Struct({
|
159
297
|
mutation: Schema.String,
|
160
298
|
args: Schema.Any,
|
161
|
-
id:
|
162
|
-
|
299
|
+
id: EventId,
|
300
|
+
parentId: EventId,
|
301
|
+
}).annotations({ title: 'MutationEventSchema.Any' })
|
163
302
|
|
164
|
-
export const
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
303
|
+
export const mutationEventSchemaDecodedAny = Schema.typeSchema(mutationEventSchemaAny).annotations({
|
304
|
+
title: 'MutationEventSchema.DecodedAny',
|
305
|
+
})
|
306
|
+
|
307
|
+
export const mutationEventSchemaEncodedAny = Schema.encodedSchema(mutationEventSchemaAny).annotations({
|
308
|
+
title: 'MutationEventSchema.EncodedAny',
|
309
|
+
})
|
@@ -36,7 +36,25 @@ export const schemaMutationsMetaTable = table(
|
|
36
36
|
|
37
37
|
export type SchemaMutationsMetaRow = FromTable.RowDecoded<typeof schemaMutationsMetaTable>
|
38
38
|
|
39
|
-
|
39
|
+
/**
|
40
|
+
* Table which stores SQLite changeset blobs which is used for rolling back
|
41
|
+
* read-model state during rebasing.
|
42
|
+
*/
|
43
|
+
export const SESSION_CHANGESET_META_TABLE = '__livestore_session_changeset'
|
44
|
+
|
45
|
+
export const sessionChangesetMetaTable = table(
|
46
|
+
SESSION_CHANGESET_META_TABLE,
|
47
|
+
{
|
48
|
+
idGlobal: SqliteDsl.integer({ primaryKey: true }),
|
49
|
+
idLocal: SqliteDsl.integer({ primaryKey: true }),
|
50
|
+
changeset: SqliteDsl.blob({}),
|
51
|
+
},
|
52
|
+
{ disableAutomaticIdColumn: true },
|
53
|
+
)
|
54
|
+
|
55
|
+
export type SessionChangesetMetaRow = FromTable.RowDecoded<typeof sessionChangesetMetaTable>
|
56
|
+
|
57
|
+
export const systemTables = [schemaMetaTable, schemaMutationsMetaTable, sessionChangesetMetaTable]
|
40
58
|
|
41
59
|
/// Mutation log DB
|
42
60
|
|
@@ -48,16 +66,21 @@ export const MUTATION_LOG_META_TABLE = 'mutation_log'
|
|
48
66
|
export const mutationLogMetaTable = table(
|
49
67
|
MUTATION_LOG_META_TABLE,
|
50
68
|
{
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
69
|
+
idGlobal: SqliteDsl.integer({ primaryKey: true }),
|
70
|
+
idLocal: SqliteDsl.integer({ primaryKey: true }),
|
71
|
+
parentIdGlobal: SqliteDsl.integer({}),
|
72
|
+
parentIdLocal: SqliteDsl.integer({}),
|
73
|
+
mutation: SqliteDsl.text({}),
|
74
|
+
argsJson: SqliteDsl.text({ schema: Schema.parseJson(Schema.Any) }),
|
75
|
+
schemaHash: SqliteDsl.integer({}),
|
76
|
+
/** Local only, used for ordered queries to avoid recursive id traversal */
|
77
|
+
orderKey: SqliteDsl.integer({}),
|
56
78
|
/** ISO date format */
|
57
|
-
createdAt: SqliteDsl.text({
|
79
|
+
createdAt: SqliteDsl.text({}),
|
58
80
|
syncStatus: SqliteDsl.text({ schema: SyncStatus }),
|
81
|
+
syncMetadataJson: SqliteDsl.text({ schema: Schema.parseJson(Schema.Option(Schema.JsonValue)) }),
|
59
82
|
},
|
60
|
-
{ disableAutomaticIdColumn: true },
|
83
|
+
{ disableAutomaticIdColumn: true, indexes: [{ columns: ['orderKey'], name: 'mutation_log_order_key_idx' }] },
|
61
84
|
)
|
62
85
|
|
63
86
|
export type MutationLogMetaRow = FromTable.RowDecoded<typeof mutationLogMetaTable>
|
@@ -155,7 +155,7 @@ export const migrateTable = ({
|
|
155
155
|
|
156
156
|
const createIndexFromDefinition = (tableName: string, index: SqliteAst.Index) => {
|
157
157
|
const uniqueStr = index.unique ? 'UNIQUE' : ''
|
158
|
-
return sql`create ${uniqueStr} index ${index.name} on ${tableName} (${index.columns.join(', ')})`
|
158
|
+
return sql`create ${uniqueStr} index if not exists ${index.name} on ${tableName} (${index.columns.join(', ')})`
|
159
159
|
}
|
160
160
|
|
161
161
|
export const makeColumnSpec = (tableAst: SqliteAst.Table) => {
|