@livestore/common 0.0.0-snapshot-909cdd1ac2fd591945c2be2b0f53e14d87f3c9d4
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 -0
- package/dist/__tests__/fixture.d.ts +72 -0
- package/dist/__tests__/fixture.d.ts.map +1 -0
- package/dist/__tests__/fixture.js +16 -0
- package/dist/__tests__/fixture.js.map +1 -0
- package/dist/adapter-types.d.ts +202 -0
- package/dist/adapter-types.d.ts.map +1 -0
- package/dist/adapter-types.js +49 -0
- package/dist/adapter-types.js.map +1 -0
- package/dist/bounded-collections.d.ts +36 -0
- package/dist/bounded-collections.d.ts.map +1 -0
- package/dist/bounded-collections.js +98 -0
- package/dist/bounded-collections.js.map +1 -0
- package/dist/debug-info.d.ts +122 -0
- package/dist/debug-info.d.ts.map +1 -0
- package/dist/debug-info.js +47 -0
- package/dist/debug-info.js.map +1 -0
- package/dist/derived-mutations.d.ts +109 -0
- package/dist/derived-mutations.d.ts.map +1 -0
- package/dist/derived-mutations.js +54 -0
- package/dist/derived-mutations.js.map +1 -0
- package/dist/derived-mutations.test.d.ts +2 -0
- package/dist/derived-mutations.test.d.ts.map +1 -0
- package/dist/derived-mutations.test.js +93 -0
- package/dist/derived-mutations.test.js.map +1 -0
- package/dist/devtools/devtools-bridge.d.ts +12 -0
- package/dist/devtools/devtools-bridge.d.ts.map +1 -0
- package/dist/devtools/devtools-bridge.js +2 -0
- package/dist/devtools/devtools-bridge.js.map +1 -0
- package/dist/devtools/devtools-messages.d.ts +705 -0
- package/dist/devtools/devtools-messages.d.ts.map +1 -0
- package/dist/devtools/devtools-messages.js +178 -0
- package/dist/devtools/devtools-messages.js.map +1 -0
- package/dist/devtools/devtools-window-message.d.ts +29 -0
- package/dist/devtools/devtools-window-message.d.ts.map +1 -0
- package/dist/devtools/devtools-window-message.js +33 -0
- package/dist/devtools/devtools-window-message.js.map +1 -0
- package/dist/devtools/index.d.ts +42 -0
- package/dist/devtools/index.d.ts.map +1 -0
- package/dist/devtools/index.js +49 -0
- package/dist/devtools/index.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/init-singleton-tables.d.ts +4 -0
- package/dist/init-singleton-tables.d.ts.map +1 -0
- package/dist/init-singleton-tables.js +16 -0
- package/dist/init-singleton-tables.js.map +1 -0
- package/dist/mutation.d.ts +13 -0
- package/dist/mutation.d.ts.map +1 -0
- package/dist/mutation.js +43 -0
- package/dist/mutation.js.map +1 -0
- package/dist/query-info.d.ts +47 -0
- package/dist/query-info.d.ts.map +1 -0
- package/dist/query-info.js +38 -0
- package/dist/query-info.js.map +1 -0
- package/dist/rehydrate-from-mutationlog.d.ts +14 -0
- package/dist/rehydrate-from-mutationlog.d.ts.map +1 -0
- package/dist/rehydrate-from-mutationlog.js +72 -0
- package/dist/rehydrate-from-mutationlog.js.map +1 -0
- package/dist/schema/index.d.ts +60 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +66 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/mutations.d.ts +227 -0
- package/dist/schema/mutations.d.ts.map +1 -0
- package/dist/schema/mutations.js +68 -0
- package/dist/schema/mutations.js.map +1 -0
- package/dist/schema/schema-helpers.d.ts +4 -0
- package/dist/schema/schema-helpers.d.ts.map +1 -0
- package/dist/schema/schema-helpers.js +30 -0
- package/dist/schema/schema-helpers.js.map +1 -0
- package/dist/schema/system-tables.d.ts +331 -0
- package/dist/schema/system-tables.d.ts.map +1 -0
- package/dist/schema/system-tables.js +46 -0
- package/dist/schema/system-tables.js.map +1 -0
- package/dist/schema/table-def.d.ts +135 -0
- package/dist/schema/table-def.d.ts.map +1 -0
- package/dist/schema/table-def.js +70 -0
- package/dist/schema/table-def.js.map +1 -0
- package/dist/schema-management/common.d.ts +13 -0
- package/dist/schema-management/common.d.ts.map +1 -0
- package/dist/schema-management/common.js +25 -0
- package/dist/schema-management/common.js.map +1 -0
- package/dist/schema-management/migrations.d.ts +23 -0
- package/dist/schema-management/migrations.d.ts.map +1 -0
- package/dist/schema-management/migrations.js +116 -0
- package/dist/schema-management/migrations.js.map +1 -0
- package/dist/schema-management/validate-mutation-defs.d.ts +8 -0
- package/dist/schema-management/validate-mutation-defs.d.ts.map +1 -0
- package/dist/schema-management/validate-mutation-defs.js +39 -0
- package/dist/schema-management/validate-mutation-defs.js.map +1 -0
- package/dist/sql-queries/index.d.ts +4 -0
- package/dist/sql-queries/index.d.ts.map +1 -0
- package/dist/sql-queries/index.js +4 -0
- package/dist/sql-queries/index.js.map +1 -0
- package/dist/sql-queries/misc.d.ts +2 -0
- package/dist/sql-queries/misc.d.ts.map +1 -0
- package/dist/sql-queries/misc.js +2 -0
- package/dist/sql-queries/misc.js.map +1 -0
- package/dist/sql-queries/sql-queries.d.ts +72 -0
- package/dist/sql-queries/sql-queries.d.ts.map +1 -0
- package/dist/sql-queries/sql-queries.js +191 -0
- package/dist/sql-queries/sql-queries.js.map +1 -0
- package/dist/sql-queries/sql-query-builder.d.ts +47 -0
- package/dist/sql-queries/sql-query-builder.d.ts.map +1 -0
- package/dist/sql-queries/sql-query-builder.js +60 -0
- package/dist/sql-queries/sql-query-builder.js.map +1 -0
- package/dist/sql-queries/types.d.ts +50 -0
- package/dist/sql-queries/types.d.ts.map +1 -0
- package/dist/sql-queries/types.js +5 -0
- package/dist/sql-queries/types.js.map +1 -0
- package/dist/sync/index.d.ts +2 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +2 -0
- package/dist/sync/index.js.map +1 -0
- 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 +156 -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/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 +45 -0
- package/dist/sync/sync.d.ts.map +1 -0
- package/dist/sync/sync.js +12 -0
- package/dist/sync/sync.js.map +1 -0
- package/dist/util.d.ts +25 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +38 -0
- package/dist/util.js.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +12 -0
- package/dist/version.js.map +1 -0
- package/package.json +61 -0
- package/src/__tests__/fixture.ts +23 -0
- package/src/adapter-types.ts +216 -0
- package/src/ambient.d.ts +3 -0
- package/src/bounded-collections.ts +121 -0
- package/src/debug-info.ts +76 -0
- package/src/derived-mutations.test.ts +101 -0
- package/src/derived-mutations.ts +170 -0
- package/src/devtools/devtools-bridge.ts +13 -0
- package/src/devtools/devtools-messages.ts +247 -0
- package/src/devtools/devtools-window-message.ts +27 -0
- package/src/devtools/index.ts +49 -0
- package/src/index.ts +20 -0
- package/src/init-singleton-tables.ts +24 -0
- package/src/mutation.ts +69 -0
- package/src/query-info.ts +104 -0
- package/src/rehydrate-from-mutationlog.ts +131 -0
- package/src/schema/index.ts +144 -0
- package/src/schema/mutations.ts +313 -0
- package/src/schema/schema-helpers.ts +49 -0
- package/src/schema/system-tables.ts +84 -0
- package/src/schema/table-def.ts +312 -0
- package/src/schema-management/common.ts +44 -0
- package/src/schema-management/migrations.ts +188 -0
- package/src/schema-management/validate-mutation-defs.ts +63 -0
- package/src/sql-queries/index.ts +3 -0
- package/src/sql-queries/misc.ts +2 -0
- package/src/sql-queries/sql-queries.ts +359 -0
- package/src/sql-queries/sql-query-builder.ts +135 -0
- package/src/sql-queries/types.ts +97 -0
- package/src/sync/index.ts +1 -0
- package/src/sync/next/ambient.d.ts +3 -0
- package/src/sync/next/compact-events.ts +218 -0
- package/src/sync/next/facts.ts +229 -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 +46 -0
- package/src/util.ts +56 -0
- package/src/version.ts +13 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import type { SqliteDsl } from '@livestore/db-schema'
|
|
2
|
+
import { SqliteAst } from '@livestore/db-schema'
|
|
3
|
+
import { isReadonlyArray, shouldNeverHappen } from '@livestore/utils'
|
|
4
|
+
|
|
5
|
+
import type { MigrationOptions } from '../adapter-types.js'
|
|
6
|
+
import { makeDerivedMutationDefsForTable } from '../derived-mutations.js'
|
|
7
|
+
import {
|
|
8
|
+
type MutationDef,
|
|
9
|
+
type MutationDefMap,
|
|
10
|
+
type MutationDefRecord,
|
|
11
|
+
type RawSqlMutation,
|
|
12
|
+
rawSqlMutation,
|
|
13
|
+
} from './mutations.js'
|
|
14
|
+
import { systemTables } from './system-tables.js'
|
|
15
|
+
import { type TableDef, tableHasDerivedMutations } from './table-def.js'
|
|
16
|
+
|
|
17
|
+
export * from './system-tables.js'
|
|
18
|
+
export * as DbSchema from './table-def.js'
|
|
19
|
+
export * from './mutations.js'
|
|
20
|
+
export * from './schema-helpers.js'
|
|
21
|
+
|
|
22
|
+
export const LiveStoreSchemaSymbol = Symbol.for('livestore.LiveStoreSchema')
|
|
23
|
+
export type LiveStoreSchemaSymbol = typeof LiveStoreSchemaSymbol
|
|
24
|
+
|
|
25
|
+
export type LiveStoreSchema<
|
|
26
|
+
TDbSchema extends SqliteDsl.DbSchema = SqliteDsl.DbSchema,
|
|
27
|
+
TMutationsDefRecord extends MutationDefRecord = MutationDefRecord,
|
|
28
|
+
> = {
|
|
29
|
+
readonly _Type: LiveStoreSchemaSymbol
|
|
30
|
+
/** Only used on type-level */
|
|
31
|
+
readonly _DbSchemaType: TDbSchema
|
|
32
|
+
/** Only used on type-level */
|
|
33
|
+
readonly _MutationDefMapType: TMutationsDefRecord
|
|
34
|
+
|
|
35
|
+
readonly tables: Map<string, TableDef>
|
|
36
|
+
readonly mutations: MutationDefMap
|
|
37
|
+
/** Compound hash of all table defs etc */
|
|
38
|
+
readonly hash: number
|
|
39
|
+
|
|
40
|
+
migrationOptions: MigrationOptions
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type InputSchema = {
|
|
44
|
+
readonly tables: Record<string, TableDef> | ReadonlyArray<TableDef>
|
|
45
|
+
readonly mutations?: ReadonlyArray<MutationDef.Any> | Record<string, MutationDef.Any>
|
|
46
|
+
/**
|
|
47
|
+
* Can be used to isolate multiple LiveStore apps running in the same origin
|
|
48
|
+
*/
|
|
49
|
+
// TODO remove this in favour of storeId
|
|
50
|
+
readonly key?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const makeSchema = <TInputSchema extends InputSchema>(
|
|
54
|
+
/** Note when using the object-notation for tables/mutations, the object keys are ignored and not used as table/mutation names */
|
|
55
|
+
inputSchema: TInputSchema & {
|
|
56
|
+
/** "hard-reset" is currently the default strategy */
|
|
57
|
+
migrations?: MigrationOptions<FromInputSchema.DeriveSchema<TInputSchema>>
|
|
58
|
+
},
|
|
59
|
+
): FromInputSchema.DeriveSchema<TInputSchema> => {
|
|
60
|
+
const inputTables: ReadonlyArray<TableDef> = Array.isArray(inputSchema.tables)
|
|
61
|
+
? inputSchema.tables
|
|
62
|
+
: Object.values(inputSchema.tables)
|
|
63
|
+
|
|
64
|
+
const tables = new Map<string, TableDef>()
|
|
65
|
+
|
|
66
|
+
for (const tableDef of inputTables) {
|
|
67
|
+
// TODO validate tables (e.g. index names are unique)
|
|
68
|
+
if (tables.has(tableDef.sqliteDef.ast.name)) {
|
|
69
|
+
shouldNeverHappen(`Duplicate table name: ${tableDef.sqliteDef.ast.name}. Please use unique names for tables.`)
|
|
70
|
+
}
|
|
71
|
+
tables.set(tableDef.sqliteDef.ast.name, tableDef)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const tableDef of systemTables) {
|
|
75
|
+
tables.set(tableDef.sqliteDef.name, tableDef)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const mutations: MutationDefMap = new Map()
|
|
79
|
+
|
|
80
|
+
if (isReadonlyArray(inputSchema.mutations)) {
|
|
81
|
+
for (const mutation of inputSchema.mutations) {
|
|
82
|
+
mutations.set(mutation.name, mutation)
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
for (const mutation of Object.values(inputSchema.mutations ?? {})) {
|
|
86
|
+
if (mutations.has(mutation.name)) {
|
|
87
|
+
shouldNeverHappen(`Duplicate mutation name: ${mutation.name}. Please use unique names for mutations.`)
|
|
88
|
+
}
|
|
89
|
+
mutations.set(mutation.name, mutation)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
mutations.set(rawSqlMutation.name, rawSqlMutation)
|
|
94
|
+
|
|
95
|
+
for (const tableDef of tables.values()) {
|
|
96
|
+
if (tableHasDerivedMutations(tableDef)) {
|
|
97
|
+
const derivedMutationDefs = makeDerivedMutationDefsForTable(tableDef)
|
|
98
|
+
mutations.set(derivedMutationDefs.insert.name, derivedMutationDefs.insert)
|
|
99
|
+
mutations.set(derivedMutationDefs.update.name, derivedMutationDefs.update)
|
|
100
|
+
mutations.set(derivedMutationDefs.delete.name, derivedMutationDefs.delete)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const hash = SqliteAst.hash({
|
|
105
|
+
_tag: 'dbSchema',
|
|
106
|
+
tables: [...tables.values()].map((_) => _.sqliteDef.ast),
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
_Type: LiveStoreSchemaSymbol,
|
|
111
|
+
_DbSchemaType: Symbol.for('livestore.DbSchemaType') as any,
|
|
112
|
+
_MutationDefMapType: Symbol.for('livestore.MutationDefMapType') as any,
|
|
113
|
+
tables,
|
|
114
|
+
mutations,
|
|
115
|
+
migrationOptions: inputSchema.migrations ?? { strategy: 'hard-reset' },
|
|
116
|
+
hash,
|
|
117
|
+
} satisfies LiveStoreSchema
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export namespace FromInputSchema {
|
|
121
|
+
export type DeriveSchema<TInputSchema extends InputSchema> = LiveStoreSchema<
|
|
122
|
+
DbSchemaFromInputSchemaTables<TInputSchema['tables']>,
|
|
123
|
+
MutationDefRecordFromInputSchemaMutations<TInputSchema['mutations']>
|
|
124
|
+
>
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* In case of ...
|
|
128
|
+
* - array: we use the table name of each array item (= table definition) as the object key
|
|
129
|
+
* - object: we discard the keys of the input object and use the table name of each object value (= table definition) as the new object key
|
|
130
|
+
*/
|
|
131
|
+
type DbSchemaFromInputSchemaTables<TTables extends InputSchema['tables']> =
|
|
132
|
+
TTables extends ReadonlyArray<TableDef>
|
|
133
|
+
? { [K in TTables[number] as K['sqliteDef']['name']]: K['sqliteDef'] }
|
|
134
|
+
: TTables extends Record<string, TableDef>
|
|
135
|
+
? { [K in keyof TTables as TTables[K]['sqliteDef']['name']]: TTables[K]['sqliteDef'] }
|
|
136
|
+
: never
|
|
137
|
+
|
|
138
|
+
type MutationDefRecordFromInputSchemaMutations<TMutations extends InputSchema['mutations']> =
|
|
139
|
+
TMutations extends ReadonlyArray<MutationDef.Any>
|
|
140
|
+
? { [K in TMutations[number] as K['name']]: K } & { 'livestore.RawSql': RawSqlMutation }
|
|
141
|
+
: TMutations extends { [name: string]: MutationDef.Any }
|
|
142
|
+
? { [K in keyof TMutations as TMutations[K]['name']]: TMutations[K] } & { 'livestore.RawSql': RawSqlMutation }
|
|
143
|
+
: never
|
|
144
|
+
}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { memoizeByRef } from '@livestore/utils'
|
|
2
|
+
import { Schema } from '@livestore/utils/effect'
|
|
3
|
+
|
|
4
|
+
import { EventId } from '../adapter-types.js'
|
|
5
|
+
import type { BindValues } from '../sql-queries/sql-queries.js'
|
|
6
|
+
import type { LiveStoreSchema } from './index.js'
|
|
7
|
+
|
|
8
|
+
export type MutationDefMap = Map<string | 'livestore.RawSql', MutationDef.Any>
|
|
9
|
+
export type MutationDefRecord = {
|
|
10
|
+
'livestore.RawSql': RawSqlMutation
|
|
11
|
+
[name: string]: MutationDef.Any
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type InternalMutationSchema<TRecord extends MutationDefRecord = MutationDefRecord> = {
|
|
15
|
+
_DefRecord: TRecord
|
|
16
|
+
|
|
17
|
+
map: Map<keyof TRecord, TRecord[keyof TRecord]>
|
|
18
|
+
schemaHashMap: Map<keyof TRecord, number>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type MutationDefSqlResult<TTo> =
|
|
22
|
+
| SingleOrReadonlyArray<string>
|
|
23
|
+
| ((args: TTo) => SingleOrReadonlyArray<
|
|
24
|
+
| string
|
|
25
|
+
| {
|
|
26
|
+
sql: string
|
|
27
|
+
/** Note args need to be manually encoded to `BindValues` when returning this argument */
|
|
28
|
+
bindValues: BindValues
|
|
29
|
+
writeTables?: ReadonlySet<string>
|
|
30
|
+
}
|
|
31
|
+
>)
|
|
32
|
+
|
|
33
|
+
export type SingleOrReadonlyArray<T> = T | ReadonlyArray<T>
|
|
34
|
+
|
|
35
|
+
export type MutationDef<TName extends string, TFrom, TTo> = {
|
|
36
|
+
name: TName
|
|
37
|
+
schema: Schema.Schema<TTo, TFrom>
|
|
38
|
+
sql: MutationDefSqlResult<NoInfer<TTo>>
|
|
39
|
+
options: {
|
|
40
|
+
/** Warning: This feature is not fully implemented yet */
|
|
41
|
+
historyId: string
|
|
42
|
+
/**
|
|
43
|
+
* When set to true, the mutation won't be synced over the network
|
|
44
|
+
*/
|
|
45
|
+
localOnly: boolean
|
|
46
|
+
/** Warning: This feature is not fully implemented yet */
|
|
47
|
+
facts: FactsCallback<TTo> | undefined
|
|
48
|
+
}
|
|
49
|
+
|
|
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>
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export namespace MutationDef {
|
|
76
|
+
export type Any = MutationDef<string, any, any>
|
|
77
|
+
}
|
|
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
|
+
|
|
126
|
+
// TODO possibly also allow for mutation event subsumption behaviour
|
|
127
|
+
export const defineMutation = <TName extends string, TFrom, TTo>(
|
|
128
|
+
name: TName,
|
|
129
|
+
schema: Schema.Schema<TTo, TFrom>,
|
|
130
|
+
sql: MutationDefSqlResult<NoInfer<TTo>>,
|
|
131
|
+
options?: DefineMutationOptions<TTo>,
|
|
132
|
+
): MutationDef<TName, TFrom, TTo> => {
|
|
133
|
+
const makePartialEvent = (
|
|
134
|
+
args: TTo,
|
|
135
|
+
options?: {
|
|
136
|
+
id?: EventId
|
|
137
|
+
},
|
|
138
|
+
) => ({ mutation: name, args, ...options })
|
|
139
|
+
|
|
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
|
+
})
|
|
161
|
+
|
|
162
|
+
return makePartialEvent as MutationDef<TName, TFrom, TTo>
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export const makeMutationDefRecord = <TInputRecord extends Record<string, MutationDef.Any>>(
|
|
166
|
+
inputRecord: TInputRecord,
|
|
167
|
+
): {
|
|
168
|
+
[K in TInputRecord[keyof TInputRecord]['name']]: Extract<TInputRecord[keyof TInputRecord], { name: K }>
|
|
169
|
+
} => {
|
|
170
|
+
const result: any = {}
|
|
171
|
+
|
|
172
|
+
for (const [name, def] of Object.entries(inputRecord)) {
|
|
173
|
+
result[name] = def
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
result['livestore.RawSql'] = rawSqlMutation
|
|
177
|
+
|
|
178
|
+
return result
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const rawSqlMutation = defineMutation(
|
|
182
|
+
'livestore.RawSql',
|
|
183
|
+
Schema.Struct({
|
|
184
|
+
sql: Schema.String,
|
|
185
|
+
bindValues: Schema.optional(Schema.Record({ key: Schema.String, value: Schema.Any })),
|
|
186
|
+
writeTables: Schema.optional(Schema.ReadonlySet(Schema.String)),
|
|
187
|
+
}),
|
|
188
|
+
({ sql, bindValues, writeTables }) => ({ sql, bindValues: bindValues ?? {}, writeTables }),
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
export type RawSqlMutation = typeof rawSqlMutation
|
|
192
|
+
export type RawSqlMutationEvent = ReturnType<typeof rawSqlMutation>
|
|
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
|
+
|
|
204
|
+
export type MutationEvent<TMutationsDef extends MutationDef.Any> = {
|
|
205
|
+
mutation: TMutationsDef['name']
|
|
206
|
+
args: Schema.Schema.Type<TMutationsDef['schema']>
|
|
207
|
+
id: EventId
|
|
208
|
+
parentId: EventId
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export type MutationEventEncoded<TMutationsDef extends MutationDef.Any> = {
|
|
212
|
+
mutation: TMutationsDef['name']
|
|
213
|
+
args: Schema.Schema.Encoded<TMutationsDef['schema']>
|
|
214
|
+
id: EventId
|
|
215
|
+
parentId: EventId
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export namespace MutationEvent {
|
|
219
|
+
export type Any = MutationEvent<MutationDef.Any>
|
|
220
|
+
export type AnyEncoded = MutationEventEncoded<MutationDef.Any>
|
|
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
|
+
|
|
229
|
+
export type ForSchema<TSchema extends LiveStoreSchema> = {
|
|
230
|
+
[K in keyof TSchema['_MutationDefMapType']]: MutationEvent<TSchema['_MutationDefMapType'][K]>
|
|
231
|
+
}[keyof TSchema['_MutationDefMapType']]
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export const isPartialMutationEvent = (
|
|
235
|
+
mutationEvent: MutationEvent.Any | MutationEvent.PartialAny,
|
|
236
|
+
): mutationEvent is MutationEvent.PartialAny => 'id' in mutationEvent === false && 'parentId' in mutationEvent === false
|
|
237
|
+
|
|
238
|
+
export type MutationEventSchema<TMutationsDefRecord extends MutationDefRecord> = Schema.Schema<
|
|
239
|
+
{
|
|
240
|
+
[K in keyof TMutationsDefRecord]: {
|
|
241
|
+
mutation: K
|
|
242
|
+
args: Schema.Schema.Type<TMutationsDefRecord[K]['schema']>
|
|
243
|
+
id: EventId
|
|
244
|
+
parentId: EventId
|
|
245
|
+
}
|
|
246
|
+
}[keyof TMutationsDefRecord],
|
|
247
|
+
{
|
|
248
|
+
[K in keyof TMutationsDefRecord]: {
|
|
249
|
+
mutation: K
|
|
250
|
+
args: Schema.Schema.Encoded<TMutationsDefRecord[K]['schema']>
|
|
251
|
+
id: EventId
|
|
252
|
+
parentId: EventId
|
|
253
|
+
}
|
|
254
|
+
}[keyof TMutationsDefRecord]
|
|
255
|
+
>
|
|
256
|
+
|
|
257
|
+
export type MutationEventPartialSchema<TMutationsDefRecord extends MutationDefRecord> = Schema.Schema<
|
|
258
|
+
{
|
|
259
|
+
[K in keyof TMutationsDefRecord]: {
|
|
260
|
+
mutation: K
|
|
261
|
+
args: Schema.Schema.Type<TMutationsDefRecord[K]['schema']>
|
|
262
|
+
}
|
|
263
|
+
}[keyof TMutationsDefRecord],
|
|
264
|
+
{
|
|
265
|
+
[K in keyof TMutationsDefRecord]: {
|
|
266
|
+
mutation: K
|
|
267
|
+
args: Schema.Schema.Encoded<TMutationsDefRecord[K]['schema']>
|
|
268
|
+
}
|
|
269
|
+
}[keyof TMutationsDefRecord]
|
|
270
|
+
>
|
|
271
|
+
|
|
272
|
+
export const makeMutationEventSchema = <TSchema extends LiveStoreSchema>(
|
|
273
|
+
schema: TSchema,
|
|
274
|
+
): MutationEventSchema<TSchema['_MutationDefMapType']> =>
|
|
275
|
+
Schema.Union(
|
|
276
|
+
...[...schema.mutations.values()].map((def) =>
|
|
277
|
+
Schema.Struct({
|
|
278
|
+
mutation: Schema.Literal(def.name),
|
|
279
|
+
args: def.schema,
|
|
280
|
+
id: EventId,
|
|
281
|
+
parentId: EventId,
|
|
282
|
+
}),
|
|
283
|
+
),
|
|
284
|
+
).annotations({ title: 'MutationEventSchema' }) as any
|
|
285
|
+
|
|
286
|
+
export const makeMutationEventPartialSchema = <TSchema extends LiveStoreSchema>(
|
|
287
|
+
schema: TSchema,
|
|
288
|
+
): MutationEventPartialSchema<TSchema['_MutationDefMapType']> =>
|
|
289
|
+
Schema.Union(
|
|
290
|
+
...[...schema.mutations.values()].map((def) =>
|
|
291
|
+
Schema.Struct({
|
|
292
|
+
mutation: Schema.Literal(def.name),
|
|
293
|
+
args: def.schema,
|
|
294
|
+
}),
|
|
295
|
+
),
|
|
296
|
+
).annotations({ title: 'MutationEventSchemaPartial' }) as any
|
|
297
|
+
|
|
298
|
+
export const makeMutationEventSchemaMemo = memoizeByRef(makeMutationEventSchema)
|
|
299
|
+
|
|
300
|
+
export const mutationEventSchemaAny = Schema.Struct({
|
|
301
|
+
mutation: Schema.String,
|
|
302
|
+
args: Schema.Any,
|
|
303
|
+
id: EventId,
|
|
304
|
+
parentId: EventId,
|
|
305
|
+
}).annotations({ title: 'MutationEventSchema.Any' })
|
|
306
|
+
|
|
307
|
+
export const mutationEventSchemaDecodedAny = Schema.typeSchema(mutationEventSchemaAny).annotations({
|
|
308
|
+
title: 'MutationEventSchema.DecodedAny',
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
export const mutationEventSchemaEncodedAny = Schema.encodedSchema(mutationEventSchemaAny).annotations({
|
|
312
|
+
title: 'MutationEventSchema.EncodedAny',
|
|
313
|
+
})
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { SqliteDsl } from '@livestore/db-schema'
|
|
2
|
+
import { shouldNeverHappen } from '@livestore/utils'
|
|
3
|
+
import { pipe, ReadonlyRecord, Schema } from '@livestore/utils/effect'
|
|
4
|
+
|
|
5
|
+
import type { TableDef } from './table-def.js'
|
|
6
|
+
|
|
7
|
+
export const getDefaultValuesEncoded = <TTableDef extends TableDef>(
|
|
8
|
+
tableDef: TTableDef,
|
|
9
|
+
fallbackValues?: Record<string, any>,
|
|
10
|
+
) =>
|
|
11
|
+
pipe(
|
|
12
|
+
tableDef.sqliteDef.columns,
|
|
13
|
+
ReadonlyRecord.filter((col, key) => {
|
|
14
|
+
if (fallbackValues?.[key] !== undefined) return true
|
|
15
|
+
if (key === 'id') return false
|
|
16
|
+
return col!.default._tag === 'None' || SqliteDsl.isSqlDefaultValue(col!.default.value) === false
|
|
17
|
+
}),
|
|
18
|
+
ReadonlyRecord.map((column, columnName) =>
|
|
19
|
+
fallbackValues?.[columnName] === undefined
|
|
20
|
+
? column!.default._tag === 'None'
|
|
21
|
+
? column!.nullable === true
|
|
22
|
+
? null
|
|
23
|
+
: shouldNeverHappen(`Column ${columnName} has no default value and is not nullable`)
|
|
24
|
+
: Schema.encodeSync(column!.schema)(column!.default.value)
|
|
25
|
+
: fallbackValues[columnName],
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
export const getDefaultValuesDecoded = <TTableDef extends TableDef>(
|
|
30
|
+
tableDef: TTableDef,
|
|
31
|
+
fallbackValues?: Record<string, any>,
|
|
32
|
+
) =>
|
|
33
|
+
pipe(
|
|
34
|
+
tableDef.sqliteDef.columns,
|
|
35
|
+
ReadonlyRecord.filter((col, key) => {
|
|
36
|
+
if (fallbackValues?.[key] !== undefined) return true
|
|
37
|
+
if (key === 'id') return false
|
|
38
|
+
return col!.default._tag === 'None' || SqliteDsl.isSqlDefaultValue(col!.default.value) === false
|
|
39
|
+
}),
|
|
40
|
+
ReadonlyRecord.map((column, columnName) =>
|
|
41
|
+
fallbackValues?.[columnName] === undefined
|
|
42
|
+
? column!.default._tag === 'None'
|
|
43
|
+
? column!.nullable === true
|
|
44
|
+
? null
|
|
45
|
+
: shouldNeverHappen(`Column ${columnName} has no default value and is not nullable`)
|
|
46
|
+
: Schema.validateSync(column!.schema)(column!.default.value)
|
|
47
|
+
: fallbackValues[columnName],
|
|
48
|
+
),
|
|
49
|
+
)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type SqliteAst as __SqliteAst, SqliteDsl } from '@livestore/db-schema'
|
|
2
|
+
import { Schema } from '@livestore/utils/effect'
|
|
3
|
+
|
|
4
|
+
import type { FromTable } from './table-def.js'
|
|
5
|
+
import { table } from './table-def.js'
|
|
6
|
+
|
|
7
|
+
/// App DB
|
|
8
|
+
|
|
9
|
+
export const SCHEMA_META_TABLE = '__livestore_schema'
|
|
10
|
+
|
|
11
|
+
export const schemaMetaTable = table(
|
|
12
|
+
SCHEMA_META_TABLE,
|
|
13
|
+
{
|
|
14
|
+
tableName: SqliteDsl.text({ primaryKey: true }),
|
|
15
|
+
schemaHash: SqliteDsl.integer({ nullable: false }),
|
|
16
|
+
/** ISO date format */
|
|
17
|
+
updatedAt: SqliteDsl.text({ nullable: false }),
|
|
18
|
+
},
|
|
19
|
+
{ disableAutomaticIdColumn: true },
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
export type SchemaMetaRow = FromTable.RowDecoded<typeof schemaMetaTable>
|
|
23
|
+
|
|
24
|
+
export const SCHEMA_MUTATIONS_META_TABLE = '__livestore_schema_mutations'
|
|
25
|
+
|
|
26
|
+
export const schemaMutationsMetaTable = table(
|
|
27
|
+
SCHEMA_MUTATIONS_META_TABLE,
|
|
28
|
+
{
|
|
29
|
+
mutationName: SqliteDsl.text({ primaryKey: true }),
|
|
30
|
+
schemaHash: SqliteDsl.integer({ nullable: false }),
|
|
31
|
+
/** ISO date format */
|
|
32
|
+
updatedAt: SqliteDsl.text({ nullable: false }),
|
|
33
|
+
},
|
|
34
|
+
{ disableAutomaticIdColumn: true },
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
export type SchemaMutationsMetaRow = FromTable.RowDecoded<typeof schemaMutationsMetaTable>
|
|
38
|
+
|
|
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]
|
|
58
|
+
|
|
59
|
+
/// Mutation log DB
|
|
60
|
+
|
|
61
|
+
export const SyncStatus = Schema.Literal('synced', 'pending', 'error', 'localOnly')
|
|
62
|
+
export type SyncStatus = typeof SyncStatus.Type
|
|
63
|
+
|
|
64
|
+
export const MUTATION_LOG_META_TABLE = 'mutation_log'
|
|
65
|
+
|
|
66
|
+
export const mutationLogMetaTable = table(
|
|
67
|
+
MUTATION_LOG_META_TABLE,
|
|
68
|
+
{
|
|
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
|
+
/** ISO date format */
|
|
77
|
+
createdAt: SqliteDsl.text({}),
|
|
78
|
+
syncStatus: SqliteDsl.text({ schema: SyncStatus }),
|
|
79
|
+
syncMetadataJson: SqliteDsl.text({ schema: Schema.parseJson(Schema.Option(Schema.JsonValue)) }),
|
|
80
|
+
},
|
|
81
|
+
{ disableAutomaticIdColumn: true, indexes: [] },
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
export type MutationLogMetaRow = FromTable.RowDecoded<typeof mutationLogMetaTable>
|