@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.
Files changed (220) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/__tests__/fixture.d.ts +72 -0
  3. package/dist/__tests__/fixture.d.ts.map +1 -0
  4. package/dist/__tests__/fixture.js +16 -0
  5. package/dist/__tests__/fixture.js.map +1 -0
  6. package/dist/adapter-types.d.ts +202 -0
  7. package/dist/adapter-types.d.ts.map +1 -0
  8. package/dist/adapter-types.js +49 -0
  9. package/dist/adapter-types.js.map +1 -0
  10. package/dist/bounded-collections.d.ts +36 -0
  11. package/dist/bounded-collections.d.ts.map +1 -0
  12. package/dist/bounded-collections.js +98 -0
  13. package/dist/bounded-collections.js.map +1 -0
  14. package/dist/debug-info.d.ts +122 -0
  15. package/dist/debug-info.d.ts.map +1 -0
  16. package/dist/debug-info.js +47 -0
  17. package/dist/debug-info.js.map +1 -0
  18. package/dist/derived-mutations.d.ts +109 -0
  19. package/dist/derived-mutations.d.ts.map +1 -0
  20. package/dist/derived-mutations.js +54 -0
  21. package/dist/derived-mutations.js.map +1 -0
  22. package/dist/derived-mutations.test.d.ts +2 -0
  23. package/dist/derived-mutations.test.d.ts.map +1 -0
  24. package/dist/derived-mutations.test.js +93 -0
  25. package/dist/derived-mutations.test.js.map +1 -0
  26. package/dist/devtools/devtools-bridge.d.ts +12 -0
  27. package/dist/devtools/devtools-bridge.d.ts.map +1 -0
  28. package/dist/devtools/devtools-bridge.js +2 -0
  29. package/dist/devtools/devtools-bridge.js.map +1 -0
  30. package/dist/devtools/devtools-messages.d.ts +705 -0
  31. package/dist/devtools/devtools-messages.d.ts.map +1 -0
  32. package/dist/devtools/devtools-messages.js +178 -0
  33. package/dist/devtools/devtools-messages.js.map +1 -0
  34. package/dist/devtools/devtools-window-message.d.ts +29 -0
  35. package/dist/devtools/devtools-window-message.d.ts.map +1 -0
  36. package/dist/devtools/devtools-window-message.js +33 -0
  37. package/dist/devtools/devtools-window-message.js.map +1 -0
  38. package/dist/devtools/index.d.ts +42 -0
  39. package/dist/devtools/index.d.ts.map +1 -0
  40. package/dist/devtools/index.js +49 -0
  41. package/dist/devtools/index.js.map +1 -0
  42. package/dist/index.d.ts +19 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +15 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/init-singleton-tables.d.ts +4 -0
  47. package/dist/init-singleton-tables.d.ts.map +1 -0
  48. package/dist/init-singleton-tables.js +16 -0
  49. package/dist/init-singleton-tables.js.map +1 -0
  50. package/dist/mutation.d.ts +13 -0
  51. package/dist/mutation.d.ts.map +1 -0
  52. package/dist/mutation.js +43 -0
  53. package/dist/mutation.js.map +1 -0
  54. package/dist/query-info.d.ts +47 -0
  55. package/dist/query-info.d.ts.map +1 -0
  56. package/dist/query-info.js +38 -0
  57. package/dist/query-info.js.map +1 -0
  58. package/dist/rehydrate-from-mutationlog.d.ts +14 -0
  59. package/dist/rehydrate-from-mutationlog.d.ts.map +1 -0
  60. package/dist/rehydrate-from-mutationlog.js +72 -0
  61. package/dist/rehydrate-from-mutationlog.js.map +1 -0
  62. package/dist/schema/index.d.ts +60 -0
  63. package/dist/schema/index.d.ts.map +1 -0
  64. package/dist/schema/index.js +66 -0
  65. package/dist/schema/index.js.map +1 -0
  66. package/dist/schema/mutations.d.ts +227 -0
  67. package/dist/schema/mutations.d.ts.map +1 -0
  68. package/dist/schema/mutations.js +68 -0
  69. package/dist/schema/mutations.js.map +1 -0
  70. package/dist/schema/schema-helpers.d.ts +4 -0
  71. package/dist/schema/schema-helpers.d.ts.map +1 -0
  72. package/dist/schema/schema-helpers.js +30 -0
  73. package/dist/schema/schema-helpers.js.map +1 -0
  74. package/dist/schema/system-tables.d.ts +331 -0
  75. package/dist/schema/system-tables.d.ts.map +1 -0
  76. package/dist/schema/system-tables.js +46 -0
  77. package/dist/schema/system-tables.js.map +1 -0
  78. package/dist/schema/table-def.d.ts +135 -0
  79. package/dist/schema/table-def.d.ts.map +1 -0
  80. package/dist/schema/table-def.js +70 -0
  81. package/dist/schema/table-def.js.map +1 -0
  82. package/dist/schema-management/common.d.ts +13 -0
  83. package/dist/schema-management/common.d.ts.map +1 -0
  84. package/dist/schema-management/common.js +25 -0
  85. package/dist/schema-management/common.js.map +1 -0
  86. package/dist/schema-management/migrations.d.ts +23 -0
  87. package/dist/schema-management/migrations.d.ts.map +1 -0
  88. package/dist/schema-management/migrations.js +116 -0
  89. package/dist/schema-management/migrations.js.map +1 -0
  90. package/dist/schema-management/validate-mutation-defs.d.ts +8 -0
  91. package/dist/schema-management/validate-mutation-defs.d.ts.map +1 -0
  92. package/dist/schema-management/validate-mutation-defs.js +39 -0
  93. package/dist/schema-management/validate-mutation-defs.js.map +1 -0
  94. package/dist/sql-queries/index.d.ts +4 -0
  95. package/dist/sql-queries/index.d.ts.map +1 -0
  96. package/dist/sql-queries/index.js +4 -0
  97. package/dist/sql-queries/index.js.map +1 -0
  98. package/dist/sql-queries/misc.d.ts +2 -0
  99. package/dist/sql-queries/misc.d.ts.map +1 -0
  100. package/dist/sql-queries/misc.js +2 -0
  101. package/dist/sql-queries/misc.js.map +1 -0
  102. package/dist/sql-queries/sql-queries.d.ts +72 -0
  103. package/dist/sql-queries/sql-queries.d.ts.map +1 -0
  104. package/dist/sql-queries/sql-queries.js +191 -0
  105. package/dist/sql-queries/sql-queries.js.map +1 -0
  106. package/dist/sql-queries/sql-query-builder.d.ts +47 -0
  107. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -0
  108. package/dist/sql-queries/sql-query-builder.js +60 -0
  109. package/dist/sql-queries/sql-query-builder.js.map +1 -0
  110. package/dist/sql-queries/types.d.ts +50 -0
  111. package/dist/sql-queries/types.d.ts.map +1 -0
  112. package/dist/sql-queries/types.js +5 -0
  113. package/dist/sql-queries/types.js.map +1 -0
  114. package/dist/sync/index.d.ts +2 -0
  115. package/dist/sync/index.d.ts.map +1 -0
  116. package/dist/sync/index.js +2 -0
  117. package/dist/sync/index.js.map +1 -0
  118. package/dist/sync/next/compact-events.d.ts +15 -0
  119. package/dist/sync/next/compact-events.d.ts.map +1 -0
  120. package/dist/sync/next/compact-events.js +176 -0
  121. package/dist/sync/next/compact-events.js.map +1 -0
  122. package/dist/sync/next/facts.d.ts +37 -0
  123. package/dist/sync/next/facts.d.ts.map +1 -0
  124. package/dist/sync/next/facts.js +156 -0
  125. package/dist/sync/next/facts.js.map +1 -0
  126. package/dist/sync/next/graphology.d.ts +8 -0
  127. package/dist/sync/next/graphology.d.ts.map +1 -0
  128. package/dist/sync/next/graphology.js +36 -0
  129. package/dist/sync/next/graphology.js.map +1 -0
  130. package/dist/sync/next/graphology_.d.ts +3 -0
  131. package/dist/sync/next/graphology_.d.ts.map +1 -0
  132. package/dist/sync/next/graphology_.js +3 -0
  133. package/dist/sync/next/graphology_.js.map +1 -0
  134. package/dist/sync/next/history-dag.d.ts +30 -0
  135. package/dist/sync/next/history-dag.d.ts.map +1 -0
  136. package/dist/sync/next/history-dag.js +69 -0
  137. package/dist/sync/next/history-dag.js.map +1 -0
  138. package/dist/sync/next/mod.d.ts +5 -0
  139. package/dist/sync/next/mod.d.ts.map +1 -0
  140. package/dist/sync/next/mod.js +5 -0
  141. package/dist/sync/next/mod.js.map +1 -0
  142. package/dist/sync/next/rebase-events.d.ts +27 -0
  143. package/dist/sync/next/rebase-events.d.ts.map +1 -0
  144. package/dist/sync/next/rebase-events.js +41 -0
  145. package/dist/sync/next/rebase-events.js.map +1 -0
  146. package/dist/sync/next/test/compact-events.calculator.test.d.ts +2 -0
  147. package/dist/sync/next/test/compact-events.calculator.test.d.ts.map +1 -0
  148. package/dist/sync/next/test/compact-events.calculator.test.js +101 -0
  149. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -0
  150. package/dist/sync/next/test/compact-events.test.d.ts +2 -0
  151. package/dist/sync/next/test/compact-events.test.d.ts.map +1 -0
  152. package/dist/sync/next/test/compact-events.test.js +201 -0
  153. package/dist/sync/next/test/compact-events.test.js.map +1 -0
  154. package/dist/sync/next/test/mod.d.ts +2 -0
  155. package/dist/sync/next/test/mod.d.ts.map +1 -0
  156. package/dist/sync/next/test/mod.js +2 -0
  157. package/dist/sync/next/test/mod.js.map +1 -0
  158. package/dist/sync/next/test/mutation-fixtures.d.ts +73 -0
  159. package/dist/sync/next/test/mutation-fixtures.d.ts.map +1 -0
  160. package/dist/sync/next/test/mutation-fixtures.js +161 -0
  161. package/dist/sync/next/test/mutation-fixtures.js.map +1 -0
  162. package/dist/sync/sync.d.ts +45 -0
  163. package/dist/sync/sync.d.ts.map +1 -0
  164. package/dist/sync/sync.js +12 -0
  165. package/dist/sync/sync.js.map +1 -0
  166. package/dist/util.d.ts +25 -0
  167. package/dist/util.d.ts.map +1 -0
  168. package/dist/util.js +38 -0
  169. package/dist/util.js.map +1 -0
  170. package/dist/version.d.ts +10 -0
  171. package/dist/version.d.ts.map +1 -0
  172. package/dist/version.js +12 -0
  173. package/dist/version.js.map +1 -0
  174. package/package.json +61 -0
  175. package/src/__tests__/fixture.ts +23 -0
  176. package/src/adapter-types.ts +216 -0
  177. package/src/ambient.d.ts +3 -0
  178. package/src/bounded-collections.ts +121 -0
  179. package/src/debug-info.ts +76 -0
  180. package/src/derived-mutations.test.ts +101 -0
  181. package/src/derived-mutations.ts +170 -0
  182. package/src/devtools/devtools-bridge.ts +13 -0
  183. package/src/devtools/devtools-messages.ts +247 -0
  184. package/src/devtools/devtools-window-message.ts +27 -0
  185. package/src/devtools/index.ts +49 -0
  186. package/src/index.ts +20 -0
  187. package/src/init-singleton-tables.ts +24 -0
  188. package/src/mutation.ts +69 -0
  189. package/src/query-info.ts +104 -0
  190. package/src/rehydrate-from-mutationlog.ts +131 -0
  191. package/src/schema/index.ts +144 -0
  192. package/src/schema/mutations.ts +313 -0
  193. package/src/schema/schema-helpers.ts +49 -0
  194. package/src/schema/system-tables.ts +84 -0
  195. package/src/schema/table-def.ts +312 -0
  196. package/src/schema-management/common.ts +44 -0
  197. package/src/schema-management/migrations.ts +188 -0
  198. package/src/schema-management/validate-mutation-defs.ts +63 -0
  199. package/src/sql-queries/index.ts +3 -0
  200. package/src/sql-queries/misc.ts +2 -0
  201. package/src/sql-queries/sql-queries.ts +359 -0
  202. package/src/sql-queries/sql-query-builder.ts +135 -0
  203. package/src/sql-queries/types.ts +97 -0
  204. package/src/sync/index.ts +1 -0
  205. package/src/sync/next/ambient.d.ts +3 -0
  206. package/src/sync/next/compact-events.ts +218 -0
  207. package/src/sync/next/facts.ts +229 -0
  208. package/src/sync/next/graphology.ts +49 -0
  209. package/src/sync/next/graphology_.ts +2 -0
  210. package/src/sync/next/history-dag.ts +109 -0
  211. package/src/sync/next/mod.ts +4 -0
  212. package/src/sync/next/rebase-events.ts +97 -0
  213. package/src/sync/next/test/compact-events.calculator.test.ts +121 -0
  214. package/src/sync/next/test/compact-events.test.ts +232 -0
  215. package/src/sync/next/test/mod.ts +1 -0
  216. package/src/sync/next/test/mutation-fixtures.ts +230 -0
  217. package/src/sync/sync.ts +46 -0
  218. package/src/util.ts +56 -0
  219. package/src/version.ts +13 -0
  220. 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>