@livestore/common 0.4.0-dev.17 → 0.4.0-dev.19

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 (290) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ClientSessionLeaderThreadProxy.d.ts +10 -10
  3. package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
  4. package/dist/adapter-types.d.ts +5 -5
  5. package/dist/adapter-types.d.ts.map +1 -1
  6. package/dist/devtools/devtools-messages-client-session.d.ts +28 -23
  7. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
  8. package/dist/devtools/devtools-messages-client-session.js +2 -2
  9. package/dist/devtools/devtools-messages-client-session.js.map +1 -1
  10. package/dist/devtools/devtools-messages-common.d.ts +6 -6
  11. package/dist/devtools/devtools-messages-leader.d.ts +33 -28
  12. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  13. package/dist/devtools/devtools-messages-leader.js +8 -8
  14. package/dist/devtools/devtools-messages-leader.js.map +1 -1
  15. package/dist/errors.d.ts +6 -6
  16. package/dist/errors.d.ts.map +1 -1
  17. package/dist/errors.js +7 -7
  18. package/dist/errors.js.map +1 -1
  19. package/dist/leader-thread/LeaderSyncProcessor.d.ts +2 -2
  20. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  21. package/dist/leader-thread/LeaderSyncProcessor.js +28 -28
  22. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  23. package/dist/leader-thread/eventlog.d.ts +13 -13
  24. package/dist/leader-thread/eventlog.d.ts.map +1 -1
  25. package/dist/leader-thread/eventlog.js +12 -11
  26. package/dist/leader-thread/eventlog.js.map +1 -1
  27. package/dist/leader-thread/leader-worker-devtools.d.ts +2 -2
  28. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  29. package/dist/leader-thread/leader-worker-devtools.js +3 -3
  30. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  31. package/dist/leader-thread/make-leader-thread-layer.d.ts +2 -2
  32. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  33. package/dist/leader-thread/make-leader-thread-layer.js +10 -8
  34. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  35. package/dist/leader-thread/materialize-event.d.ts +1 -1
  36. package/dist/leader-thread/materialize-event.d.ts.map +1 -1
  37. package/dist/leader-thread/materialize-event.js +2 -2
  38. package/dist/leader-thread/materialize-event.js.map +1 -1
  39. package/dist/leader-thread/recreate-db.d.ts +2 -2
  40. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  41. package/dist/leader-thread/recreate-db.js +5 -5
  42. package/dist/leader-thread/recreate-db.js.map +1 -1
  43. package/dist/leader-thread/shutdown-channel.d.ts +2 -2
  44. package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
  45. package/dist/leader-thread/shutdown-channel.js +2 -2
  46. package/dist/leader-thread/shutdown-channel.js.map +1 -1
  47. package/dist/leader-thread/types.d.ts +15 -15
  48. package/dist/leader-thread/types.d.ts.map +1 -1
  49. package/dist/make-client-session.d.ts +2 -2
  50. package/dist/make-client-session.d.ts.map +1 -1
  51. package/dist/materializer-helper.d.ts +5 -5
  52. package/dist/materializer-helper.d.ts.map +1 -1
  53. package/dist/materializer-helper.js.map +1 -1
  54. package/dist/rematerialize-from-eventlog.d.ts +2 -2
  55. package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
  56. package/dist/rematerialize-from-eventlog.js +6 -6
  57. package/dist/rematerialize-from-eventlog.js.map +1 -1
  58. package/dist/schema/EventDef/define.d.ts +147 -0
  59. package/dist/schema/EventDef/define.d.ts.map +1 -0
  60. package/dist/schema/EventDef/define.js +139 -0
  61. package/dist/schema/EventDef/define.js.map +1 -0
  62. package/dist/schema/EventDef/event-def.d.ts +106 -0
  63. package/dist/schema/EventDef/event-def.d.ts.map +1 -0
  64. package/dist/schema/EventDef/event-def.js +2 -0
  65. package/dist/schema/EventDef/event-def.js.map +1 -0
  66. package/dist/schema/EventDef/facts.d.ts +118 -0
  67. package/dist/schema/EventDef/facts.d.ts.map +1 -0
  68. package/dist/schema/EventDef/facts.js +53 -0
  69. package/dist/schema/EventDef/facts.js.map +1 -0
  70. package/dist/schema/EventDef/materializer.d.ts +155 -0
  71. package/dist/schema/EventDef/materializer.d.ts.map +1 -0
  72. package/dist/schema/EventDef/materializer.js +83 -0
  73. package/dist/schema/EventDef/materializer.js.map +1 -0
  74. package/dist/schema/EventDef/mod.d.ts +5 -0
  75. package/dist/schema/EventDef/mod.d.ts.map +1 -0
  76. package/dist/schema/EventDef/mod.js +5 -0
  77. package/dist/schema/EventDef/mod.js.map +1 -0
  78. package/dist/schema/EventSequenceNumber/client.d.ts +136 -0
  79. package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -0
  80. package/dist/schema/{EventSequenceNumber.js → EventSequenceNumber/client.js} +86 -39
  81. package/dist/schema/EventSequenceNumber/client.js.map +1 -0
  82. package/dist/schema/EventSequenceNumber/global.d.ts +15 -0
  83. package/dist/schema/EventSequenceNumber/global.d.ts.map +1 -0
  84. package/dist/schema/EventSequenceNumber/global.js +14 -0
  85. package/dist/schema/EventSequenceNumber/global.js.map +1 -0
  86. package/dist/schema/EventSequenceNumber/mod.d.ts +37 -0
  87. package/dist/schema/EventSequenceNumber/mod.d.ts.map +1 -0
  88. package/dist/schema/EventSequenceNumber/mod.js +37 -0
  89. package/dist/schema/EventSequenceNumber/mod.js.map +1 -0
  90. package/dist/schema/EventSequenceNumber.test.js +41 -41
  91. package/dist/schema/EventSequenceNumber.test.js.map +1 -1
  92. package/dist/schema/{LiveStoreEvent.d.ts → LiveStoreEvent/client.d.ts} +84 -101
  93. package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -0
  94. package/dist/schema/{LiveStoreEvent.js → LiveStoreEvent/client.js} +69 -52
  95. package/dist/schema/LiveStoreEvent/client.js.map +1 -0
  96. package/dist/schema/LiveStoreEvent/for-event-def.d.ts +52 -0
  97. package/dist/schema/LiveStoreEvent/for-event-def.d.ts.map +1 -0
  98. package/dist/schema/LiveStoreEvent/for-event-def.js +2 -0
  99. package/dist/schema/LiveStoreEvent/for-event-def.js.map +1 -0
  100. package/dist/schema/LiveStoreEvent/global.d.ts +36 -0
  101. package/dist/schema/LiveStoreEvent/global.d.ts.map +1 -0
  102. package/dist/schema/LiveStoreEvent/global.js +31 -0
  103. package/dist/schema/LiveStoreEvent/global.js.map +1 -0
  104. package/dist/schema/LiveStoreEvent/input.d.ts +46 -0
  105. package/dist/schema/LiveStoreEvent/input.d.ts.map +1 -0
  106. package/dist/schema/LiveStoreEvent/input.js +26 -0
  107. package/dist/schema/LiveStoreEvent/input.js.map +1 -0
  108. package/dist/schema/LiveStoreEvent/mod.d.ts +5 -0
  109. package/dist/schema/LiveStoreEvent/mod.d.ts.map +1 -0
  110. package/dist/schema/LiveStoreEvent/mod.js +5 -0
  111. package/dist/schema/LiveStoreEvent/mod.js.map +1 -0
  112. package/dist/schema/events.d.ts +1 -1
  113. package/dist/schema/events.d.ts.map +1 -1
  114. package/dist/schema/events.js +1 -1
  115. package/dist/schema/events.js.map +1 -1
  116. package/dist/schema/mod.d.ts +3 -3
  117. package/dist/schema/mod.d.ts.map +1 -1
  118. package/dist/schema/mod.js +3 -3
  119. package/dist/schema/mod.js.map +1 -1
  120. package/dist/schema/schema.d.ts +1 -1
  121. package/dist/schema/schema.d.ts.map +1 -1
  122. package/dist/schema/state/sqlite/client-document-def.d.ts +1 -1
  123. package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
  124. package/dist/schema/state/sqlite/client-document-def.js +2 -2
  125. package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
  126. package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
  127. package/dist/schema/state/sqlite/column-def.js +60 -28
  128. package/dist/schema/state/sqlite/column-def.js.map +1 -1
  129. package/dist/schema/state/sqlite/column-def.test.js +12 -1
  130. package/dist/schema/state/sqlite/column-def.test.js.map +1 -1
  131. package/dist/schema/state/sqlite/mod.d.ts +2 -2
  132. package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
  133. package/dist/schema/state/sqlite/mod.js +1 -1
  134. package/dist/schema/state/sqlite/mod.js.map +1 -1
  135. package/dist/schema/state/sqlite/query-builder/api.d.ts +12 -8
  136. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
  137. package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -1
  138. package/dist/schema/state/sqlite/query-builder/astToSql.js +18 -11
  139. package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
  140. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
  141. package/dist/schema/state/sqlite/query-builder/impl.js +0 -1
  142. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
  143. package/dist/schema/state/sqlite/query-builder/impl.test.js +119 -90
  144. package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
  145. package/dist/schema/state/sqlite/system-tables/eventlog-tables.js +5 -5
  146. package/dist/schema/state/sqlite/system-tables/eventlog-tables.js.map +1 -1
  147. package/dist/schema/state/sqlite/system-tables/state-tables.js +3 -3
  148. package/dist/schema/state/sqlite/system-tables/state-tables.js.map +1 -1
  149. package/dist/schema/unknown-events.d.ts +3 -3
  150. package/dist/schema/unknown-events.d.ts.map +1 -1
  151. package/dist/schema-management/migrations.d.ts +2 -2
  152. package/dist/schema-management/migrations.d.ts.map +1 -1
  153. package/dist/schema-management/migrations.js.map +1 -1
  154. package/dist/schema-management/validate-schema.d.ts +3 -3
  155. package/dist/schema-management/validate-schema.d.ts.map +1 -1
  156. package/dist/schema-management/validate-schema.js +2 -2
  157. package/dist/schema-management/validate-schema.js.map +1 -1
  158. package/dist/sqlite-types.d.ts +3 -3
  159. package/dist/sqlite-types.d.ts.map +1 -1
  160. package/dist/sync/ClientSessionSyncProcessor.d.ts +5 -5
  161. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  162. package/dist/sync/ClientSessionSyncProcessor.js +12 -12
  163. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  164. package/dist/sync/errors.d.ts +9 -4
  165. package/dist/sync/errors.d.ts.map +1 -1
  166. package/dist/sync/errors.js +6 -6
  167. package/dist/sync/errors.js.map +1 -1
  168. package/dist/sync/mock-sync-backend.d.ts +6 -6
  169. package/dist/sync/mock-sync-backend.d.ts.map +1 -1
  170. package/dist/sync/mock-sync-backend.js +4 -4
  171. package/dist/sync/mock-sync-backend.js.map +1 -1
  172. package/dist/sync/next/compact-events.js +2 -2
  173. package/dist/sync/next/compact-events.js.map +1 -1
  174. package/dist/sync/next/facts.d.ts +5 -5
  175. package/dist/sync/next/facts.d.ts.map +1 -1
  176. package/dist/sync/next/facts.js.map +1 -1
  177. package/dist/sync/next/history-dag-common.d.ts +5 -5
  178. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  179. package/dist/sync/next/history-dag-common.js +5 -5
  180. package/dist/sync/next/history-dag-common.js.map +1 -1
  181. package/dist/sync/next/history-dag.d.ts.map +1 -1
  182. package/dist/sync/next/history-dag.js +8 -8
  183. package/dist/sync/next/history-dag.js.map +1 -1
  184. package/dist/sync/next/rebase-events.d.ts +5 -5
  185. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  186. package/dist/sync/next/rebase-events.js +5 -5
  187. package/dist/sync/next/rebase-events.js.map +1 -1
  188. package/dist/sync/next/test/event-fixtures.d.ts +2 -2
  189. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
  190. package/dist/sync/next/test/event-fixtures.js +9 -9
  191. package/dist/sync/next/test/event-fixtures.js.map +1 -1
  192. package/dist/sync/sync-backend-kv.d.ts +3 -3
  193. package/dist/sync/sync-backend-kv.d.ts.map +1 -1
  194. package/dist/sync/sync-backend-kv.js +3 -3
  195. package/dist/sync/sync-backend-kv.js.map +1 -1
  196. package/dist/sync/sync-backend.d.ts +9 -9
  197. package/dist/sync/sync-backend.d.ts.map +1 -1
  198. package/dist/sync/syncstate.d.ts +55 -42
  199. package/dist/sync/syncstate.d.ts.map +1 -1
  200. package/dist/sync/syncstate.js +42 -42
  201. package/dist/sync/syncstate.js.map +1 -1
  202. package/dist/sync/syncstate.test.js +40 -40
  203. package/dist/sync/syncstate.test.js.map +1 -1
  204. package/dist/sync/validate-push-payload.d.ts +1 -1
  205. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  206. package/dist/sync/validate-push-payload.js +2 -2
  207. package/dist/sync/validate-push-payload.js.map +1 -1
  208. package/dist/testing/event-factory.d.ts +3 -3
  209. package/dist/testing/event-factory.d.ts.map +1 -1
  210. package/dist/testing/event-factory.js +5 -7
  211. package/dist/testing/event-factory.js.map +1 -1
  212. package/dist/version.d.ts +1 -1
  213. package/dist/version.js +1 -1
  214. package/package.json +4 -4
  215. package/src/ClientSessionLeaderThreadProxy.ts +10 -10
  216. package/src/adapter-types.ts +5 -5
  217. package/src/devtools/devtools-messages-client-session.ts +2 -2
  218. package/src/devtools/devtools-messages-leader.ts +8 -8
  219. package/src/errors.ts +11 -13
  220. package/src/leader-thread/LeaderSyncProcessor.ts +54 -56
  221. package/src/leader-thread/eventlog.ts +21 -26
  222. package/src/leader-thread/leader-worker-devtools.ts +3 -3
  223. package/src/leader-thread/make-leader-thread-layer.ts +12 -10
  224. package/src/leader-thread/materialize-event.ts +3 -3
  225. package/src/leader-thread/recreate-db.ts +6 -6
  226. package/src/leader-thread/shutdown-channel.ts +2 -2
  227. package/src/leader-thread/types.ts +15 -15
  228. package/src/make-client-session.ts +2 -2
  229. package/src/materializer-helper.ts +5 -5
  230. package/src/rematerialize-from-eventlog.ts +6 -6
  231. package/src/schema/EventDef/define.ts +201 -0
  232. package/src/schema/EventDef/event-def.ts +120 -0
  233. package/src/schema/EventDef/facts.ts +135 -0
  234. package/src/schema/EventDef/materializer.ts +172 -0
  235. package/src/schema/EventDef/mod.ts +4 -0
  236. package/src/schema/EventSequenceNumber/client.ts +257 -0
  237. package/src/schema/EventSequenceNumber/global.ts +19 -0
  238. package/src/schema/EventSequenceNumber/mod.ts +37 -0
  239. package/src/schema/EventSequenceNumber.test.ts +68 -50
  240. package/src/schema/LiveStoreEvent/client.ts +221 -0
  241. package/src/schema/LiveStoreEvent/for-event-def.ts +60 -0
  242. package/src/schema/LiveStoreEvent/global.ts +45 -0
  243. package/src/schema/LiveStoreEvent/input.ts +63 -0
  244. package/src/schema/LiveStoreEvent/mod.ts +4 -0
  245. package/src/schema/events.ts +1 -1
  246. package/src/schema/mod.ts +3 -3
  247. package/src/schema/schema.ts +1 -1
  248. package/src/schema/state/sqlite/client-document-def.test.ts +2 -2
  249. package/src/schema/state/sqlite/client-document-def.ts +3 -3
  250. package/src/schema/state/sqlite/column-def.test.ts +18 -1
  251. package/src/schema/state/sqlite/column-def.ts +73 -30
  252. package/src/schema/state/sqlite/mod.ts +2 -2
  253. package/src/schema/state/sqlite/query-builder/api.ts +12 -8
  254. package/src/schema/state/sqlite/query-builder/astToSql.ts +20 -11
  255. package/src/schema/state/sqlite/query-builder/impl.test.ts +122 -90
  256. package/src/schema/state/sqlite/query-builder/impl.ts +0 -1
  257. package/src/schema/state/sqlite/system-tables/eventlog-tables.ts +5 -5
  258. package/src/schema/state/sqlite/system-tables/state-tables.ts +3 -3
  259. package/src/schema/unknown-events.ts +3 -3
  260. package/src/schema-management/migrations.ts +2 -2
  261. package/src/schema-management/validate-schema.ts +3 -3
  262. package/src/sqlite-types.ts +3 -3
  263. package/src/sync/ClientSessionSyncProcessor.ts +17 -17
  264. package/src/sync/errors.ts +6 -6
  265. package/src/sync/mock-sync-backend.ts +16 -16
  266. package/src/sync/next/compact-events.ts +2 -2
  267. package/src/sync/next/facts.ts +6 -6
  268. package/src/sync/next/history-dag-common.ts +8 -8
  269. package/src/sync/next/history-dag.ts +14 -10
  270. package/src/sync/next/rebase-events.ts +11 -11
  271. package/src/sync/next/test/event-fixtures.ts +11 -11
  272. package/src/sync/sync-backend-kv.ts +3 -3
  273. package/src/sync/sync-backend.ts +9 -9
  274. package/src/sync/syncstate.test.ts +46 -46
  275. package/src/sync/syncstate.ts +59 -55
  276. package/src/sync/validate-push-payload.ts +4 -4
  277. package/src/testing/event-factory.ts +10 -12
  278. package/src/version.ts +1 -1
  279. package/dist/schema/EventDef.d.ts +0 -126
  280. package/dist/schema/EventDef.d.ts.map +0 -1
  281. package/dist/schema/EventDef.js +0 -46
  282. package/dist/schema/EventDef.js.map +0 -1
  283. package/dist/schema/EventSequenceNumber.d.ts +0 -89
  284. package/dist/schema/EventSequenceNumber.d.ts.map +0 -1
  285. package/dist/schema/EventSequenceNumber.js.map +0 -1
  286. package/dist/schema/LiveStoreEvent.d.ts.map +0 -1
  287. package/dist/schema/LiveStoreEvent.js.map +0 -1
  288. package/src/schema/EventDef.ts +0 -222
  289. package/src/schema/EventSequenceNumber.ts +0 -208
  290. package/src/schema/LiveStoreEvent.ts +0 -286
@@ -278,12 +278,29 @@ describe('getColumnDefForSchema', () => {
278
278
  expect(State.SQLite.getColumnDefForSchema(StatusEnum).columnType).toBe('text')
279
279
  expect(State.SQLite.getColumnDefForSchema(StatusUnion).columnType).toBe('text')
280
280
  })
281
+
282
+ it('should handle unions of numeric literals as integer column', () => {
283
+ const IntervalSchema = Schema.Literal(1, 5, 15, 30)
284
+
285
+ const columnDef = State.SQLite.getColumnDefForSchema(IntervalSchema)
286
+
287
+ expect(columnDef.columnType).toBe('integer')
288
+ })
289
+
290
+ it('should handle unions of non-integer numeric literals as real column', () => {
291
+ const PercentSchema = Schema.Literal(0.1, 0.2, 0.25)
292
+
293
+ const columnDef = State.SQLite.getColumnDefForSchema(PercentSchema)
294
+
295
+ expect(columnDef.columnType).toBe('real')
296
+ })
281
297
  })
282
298
 
283
299
  describe('binary data', () => {
284
300
  it('should handle Uint8Array as blob column', () => {
285
301
  const columnDef = State.SQLite.getColumnDefForSchema(Schema.Uint8Array)
286
- expect(columnDef.columnType).toBe('text') // Stored as JSON
302
+ expect(columnDef.columnType).toBe('blob')
303
+ expect(columnDef.schema.toString()).toBe('Uint8ArrayFromSelf')
287
304
  })
288
305
  })
289
306
 
@@ -179,33 +179,16 @@ const getColumnForSchema = (schema: Schema.Schema.AnyNoContext, nullable = false
179
179
  return SqliteDsl.real({ schema: coreSchema, nullable })
180
180
  }
181
181
 
182
- // Literals based on their type
183
- if (SchemaAST.isLiteral(coreAst)) {
184
- const value = coreAst.literal
185
- if (typeof value === 'boolean') return SqliteDsl.boolean({ nullable })
182
+ if (isUint8ArraySchema(coreAst) || isUint8ArraySchema(encodedAst)) {
183
+ return SqliteDsl.blob({ schema: Schema.Uint8ArrayFromSelf as Schema.Schema<Uint8Array<ArrayBuffer>>, nullable })
186
184
  }
187
185
 
188
- if (isLiteralUnionOf(coreAst, (value): value is string => typeof value === 'string')) {
189
- return SqliteDsl.text({ schema: coreSchema, nullable })
190
- }
191
-
192
- // Literals based on their encoded type
193
- if (SchemaAST.isLiteral(encodedAst)) {
194
- const value = encodedAst.literal
195
- if (typeof value === 'string') return SqliteDsl.text({ schema: coreSchema, nullable })
196
- if (typeof value === 'number') {
197
- // Check if the original schema is Int
198
- const id = SchemaAST.getIdentifierAnnotation(coreAst).pipe(Option.getOrElse(() => ''))
199
- if (id === 'Int') {
200
- return SqliteDsl.integer({ schema: coreSchema, nullable })
201
- }
202
- return SqliteDsl.real({ schema: coreSchema, nullable })
203
- }
204
- }
186
+ const literalColumn = getLiteralColumnDefinition(encodedAst, coreSchema, nullable, coreAst)
187
+ if (literalColumn) return literalColumn
205
188
 
206
- if (isLiteralUnionOf(encodedAst, (value): value is string => typeof value === 'string')) {
207
- return SqliteDsl.text({ schema: coreSchema, nullable })
208
- }
189
+ // Fallback to checking the original AST in case the encoded schema differs
190
+ const coreLiteralColumn = getLiteralColumnDefinition(coreAst, coreSchema, nullable, coreAst)
191
+ if (coreLiteralColumn) return coreLiteralColumn
209
192
 
210
193
  // Everything else needs JSON encoding
211
194
  return SqliteDsl.json({ schema: coreSchema, nullable })
@@ -230,10 +213,70 @@ const stripNullable = (ast: SchemaAST.AST): SchemaAST.AST => {
230
213
  return SchemaAST.Union.make(coreTypes, ast.annotations)
231
214
  }
232
215
 
233
- const isLiteralUnionOf = <T extends SchemaAST.LiteralValue>(
216
+ const getLiteralColumnDefinition = (
234
217
  ast: SchemaAST.AST,
235
- predicate: (value: SchemaAST.LiteralValue) => value is T,
236
- ): ast is SchemaAST.Union & { types: ReadonlyArray<SchemaAST.Literal & { literal: T }> } =>
237
- SchemaAST.isUnion(ast) &&
238
- ast.types.length > 0 &&
239
- ast.types.every((type) => SchemaAST.isLiteral(type) && predicate(type.literal))
218
+ schema: Schema.Schema.AnyNoContext,
219
+ nullable: boolean,
220
+ sourceAst: SchemaAST.AST,
221
+ ): SqliteDsl.ColumnDefinition.Any | null => {
222
+ const literalValues = extractLiteralValues(ast)
223
+ if (!literalValues) return null
224
+
225
+ const literalType = getLiteralValueType(literalValues)
226
+ switch (literalType) {
227
+ case 'string':
228
+ return SqliteDsl.text({ schema, nullable })
229
+ case 'number': {
230
+ const id = SchemaAST.getIdentifierAnnotation(sourceAst).pipe(Option.getOrElse(() => ''))
231
+ if (id === 'Int' || id === 'DateFromNumber') {
232
+ return SqliteDsl.integer({ schema, nullable })
233
+ }
234
+
235
+ const useIntegerColumn =
236
+ literalValues.length > 1 && literalValues.every((value) => typeof value === 'number' && Number.isInteger(value))
237
+
238
+ return useIntegerColumn ? SqliteDsl.integer({ schema, nullable }) : SqliteDsl.real({ schema, nullable })
239
+ }
240
+ case 'boolean':
241
+ return SqliteDsl.boolean({ nullable })
242
+ case 'bigint':
243
+ return SqliteDsl.integer({ schema, nullable })
244
+ default:
245
+ return null
246
+ }
247
+ }
248
+
249
+ const extractLiteralValues = (ast: SchemaAST.AST): ReadonlyArray<SchemaAST.LiteralValue> | null => {
250
+ if (SchemaAST.isLiteral(ast)) return [ast.literal]
251
+
252
+ if (SchemaAST.isUnion(ast) && ast.types.length > 0 && ast.types.every((type) => SchemaAST.isLiteral(type))) {
253
+ return ast.types.map((type) => (type as SchemaAST.Literal).literal)
254
+ }
255
+
256
+ return null
257
+ }
258
+
259
+ const getLiteralValueType = (
260
+ literals: ReadonlyArray<SchemaAST.LiteralValue>,
261
+ ): 'string' | 'number' | 'boolean' | 'bigint' | null => {
262
+ const literalTypes = new Set(literals.map((value) => (value === null ? 'null' : typeof value)))
263
+ if (literalTypes.size !== 1) return null
264
+
265
+ const [literalType] = literalTypes
266
+ return literalType === 'string' || literalType === 'number' || literalType === 'boolean' || literalType === 'bigint'
267
+ ? literalType
268
+ : null
269
+ }
270
+
271
+ const isUint8ArraySchema = (ast: SchemaAST.AST): boolean => {
272
+ const identifier = SchemaAST.getIdentifierAnnotation(ast)
273
+ if (Option.isSome(identifier) && identifier.value.includes('Uint8Array')) {
274
+ return true
275
+ }
276
+
277
+ if (SchemaAST.isTupleType(ast)) {
278
+ return ast.elements.length === 0 && ast.rest.length === 1 && SchemaAST.isNumberKeyword(ast.rest[0]!.type)
279
+ }
280
+
281
+ return false
282
+ }
@@ -1,14 +1,14 @@
1
1
  import { shouldNeverHappen } from '@livestore/utils'
2
2
 
3
3
  import type { MigrationOptions } from '../../../adapter-types.ts'
4
- import type { Materializer } from '../../EventDef.ts'
4
+ import type { Materializer } from '../../EventDef/mod.ts'
5
5
  import type { InternalState } from '../../schema.ts'
6
6
  import { ClientDocumentTableDefSymbol, tableIsClientDocumentTable } from './client-document-def.ts'
7
7
  import { SqliteAst } from './db-schema/mod.ts'
8
8
  import { stateSystemTables } from './system-tables/state-tables.ts'
9
9
  import type { TableDef, TableDefBase } from './table-def.ts'
10
10
 
11
- export * from '../../EventDef.ts'
11
+ export * from '../../EventDef/mod.ts'
12
12
  export {
13
13
  type ClientDocumentTableDef,
14
14
  ClientDocumentTableDefSymbol,
@@ -347,22 +347,26 @@ export namespace QueryBuilder {
347
347
  >
348
348
 
349
349
  /**
350
- * Example: If the row already exists, it will be ignored.
350
+ * Upsert: insert a row, or handle conflicts on existing rows.
351
+ * Equivalent to SQLite's `INSERT ... ON CONFLICT` clause.
352
+ *
353
+ * Actions:
354
+ * - `'ignore'`: Skip the insert if a row with the same key exists
355
+ * - `'replace'`: Delete the existing row and insert the new one
356
+ * - `'update'`: Update specific columns on the existing row
357
+ *
351
358
  * ```ts
359
+ * // Ignore: skip if row exists
352
360
  * db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'ignore')
353
- * ```
354
361
  *
355
- * Example: If the row already exists, it will be replaced.
356
- * ```ts
362
+ * // Replace: delete existing row and insert new one
357
363
  * db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'replace')
358
- * ```
359
364
  *
360
- * Example: If the row already exists, it will be updated.
361
- * ```ts
365
+ * // Update: merge specific columns into existing row
362
366
  * db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' }).onConflict('id', 'update', { text: 'Buy soy milk' })
363
367
  * ```
364
368
  *
365
- * NOTE This API doesn't yet support composite primary keys.
369
+ * NOTE: Composite primary keys are not yet supported.
366
370
  */
367
371
  readonly onConflict: {
368
372
  <TTarget extends SingleOrReadonlyArray<keyof TTableDef['sqliteDef']['columns']>>(
@@ -7,6 +7,8 @@ import type { State } from '../../../mod.ts'
7
7
  import type { QueryBuilderAst } from './api.ts'
8
8
 
9
9
  // Helper functions for SQL generation
10
+ const quoteIdentifier = (identifier: string): string => `"${identifier.replace(/"/g, '""')}"`
11
+
10
12
  const formatWhereClause = (
11
13
  whereConditions: ReadonlyArray<QueryBuilderAst.Where>,
12
14
  tableDef: State.SQLite.TableDefBase,
@@ -16,13 +18,15 @@ const formatWhereClause = (
16
18
 
17
19
  const whereClause = whereConditions
18
20
  .map(({ col, op, value }) => {
21
+ const quotedCol = quoteIdentifier(col)
22
+
19
23
  // Handle NULL values
20
24
  if (value === null) {
21
25
  if (op !== '=' && op !== '!=') {
22
26
  throw new Error(`Unsupported operator for NULL value: ${op}`)
23
27
  }
24
28
  const opStmt = op === '=' ? 'IS' : 'IS NOT'
25
- return `${col} ${opStmt} NULL`
29
+ return `${quotedCol} ${opStmt} NULL`
26
30
  }
27
31
 
28
32
  // Get column definition and encode value
@@ -48,11 +52,11 @@ const formatWhereClause = (
48
52
  const encodedValues = value.map((v) => Schema.encodeSync(colDef.schema)(v)) as SqlValue[]
49
53
  bindValues.push(...encodedValues)
50
54
  const placeholders = encodedValues.map(() => '?').join(', ')
51
- return `${col} ${op} (${placeholders})`
55
+ return `${quotedCol} ${op} (${placeholders})`
52
56
  } else {
53
57
  const encodedValue = Schema.encodeSync(colDef.schema)(value)
54
58
  bindValues.push(encodedValue as SqlValue)
55
- return `${col} ${op} ?`
59
+ return `${quotedCol} ${op} ?`
56
60
  }
57
61
  })
58
62
  .join(' AND ')
@@ -62,7 +66,7 @@ const formatWhereClause = (
62
66
 
63
67
  const formatReturningClause = (returning?: string[]): string => {
64
68
  if (!returning || returning.length === 0) return ''
65
- return ` RETURNING ${returning.join(', ')}`
69
+ return ` RETURNING ${returning.map(quoteIdentifier).join(', ')}`
66
70
  }
67
71
 
68
72
  export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: SqlValue[]; usedTables: Set<string> } => {
@@ -72,6 +76,7 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
72
76
  // INSERT query
73
77
  if (ast._tag === 'InsertQuery') {
74
78
  const columns = Object.keys(ast.values)
79
+ const quotedColumns = columns.map(quoteIdentifier)
75
80
  const placeholders = columns.map(() => '?').join(', ')
76
81
  const encodedValues = Schema.encodeSync(ast.tableDef.insertSchema)(ast.values)
77
82
 
@@ -91,7 +96,8 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
91
96
  // For REPLACE, the conflict target is implied and no further clause is needed
92
97
  } else {
93
98
  // Build the ON CONFLICT clause for IGNORE or UPDATE
94
- conflictClause = ` ON CONFLICT (${ast.onConflict.targets.join(', ')}) `
99
+ const conflictTargets = ast.onConflict.targets.map(quoteIdentifier).join(', ')
100
+ conflictClause = ` ON CONFLICT (${conflictTargets}) `
95
101
  if (ast.onConflict.action._tag === 'ignore') {
96
102
  conflictClause += 'DO NOTHING'
97
103
  } else {
@@ -105,8 +111,9 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
105
111
  const updates = updateCols
106
112
  .map((col) => {
107
113
  const value = updateValues[col]
114
+ const quotedCol = quoteIdentifier(col)
108
115
  // If the value is undefined, use excluded.col
109
- return value === undefined ? `${col} = excluded.${col}` : `${col} = ?`
116
+ return value === undefined ? `${quotedCol} = excluded.${quotedCol}` : `${quotedCol} = ?`
110
117
  })
111
118
  .join(', ')
112
119
 
@@ -129,7 +136,7 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
129
136
  }
130
137
 
131
138
  // Construct the main query part
132
- let query = `${insertVerb} INTO '${ast.tableDef.sqliteDef.name}' (${columns.join(', ')}) VALUES (${placeholders})`
139
+ let query = `${insertVerb} INTO '${ast.tableDef.sqliteDef.name}' (${quotedColumns.join(', ')}) VALUES (${placeholders})`
133
140
 
134
141
  // Append the conflict clause if it was generated (i.e., not for REPLACE)
135
142
  query += conflictClause
@@ -157,7 +164,9 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
157
164
  bindValues.push(encodedValues[col] as SqlValue)
158
165
  })
159
166
 
160
- let query = `UPDATE '${ast.tableDef.sqliteDef.name}' SET ${setColumns.map((col) => `${col} = ?`).join(', ')}`
167
+ let query = `UPDATE '${ast.tableDef.sqliteDef.name}' SET ${setColumns
168
+ .map((col) => `${quoteIdentifier(col)} = ?`)
169
+ .join(', ')}`
161
170
 
162
171
  const whereClause = formatWhereClause(ast.where, ast.tableDef, bindValues)
163
172
  if (whereClause) query += ` ${whereClause}`
@@ -201,21 +210,21 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
201
210
  const encodedId = ast.id === SessionIdSymbol ? ast.id : Schema.encodeSync(idColDef.schema)(ast.id)
202
211
 
203
212
  return {
204
- query: `SELECT * FROM '${ast.tableDef.sqliteDef.name}' WHERE id = ?`,
213
+ query: `SELECT * FROM '${ast.tableDef.sqliteDef.name}' WHERE ${quoteIdentifier('id')} = ?`,
205
214
  bindValues: [encodedId as SqlValue],
206
215
  usedTables,
207
216
  }
208
217
  }
209
218
 
210
219
  // SELECT query
211
- const columnsStmt = ast.select.columns.length === 0 ? '*' : ast.select.columns.join(', ')
220
+ const columnsStmt = ast.select.columns.length === 0 ? '*' : ast.select.columns.map(quoteIdentifier).join(', ')
212
221
  const selectStmt = `SELECT ${columnsStmt}`
213
222
  const fromStmt = `FROM '${ast.tableDef.sqliteDef.name}'`
214
223
  const whereStmt = formatWhereClause(ast.where, ast.tableDef, bindValues)
215
224
 
216
225
  const orderByStmt =
217
226
  ast.orderBy.length > 0
218
- ? `ORDER BY ${ast.orderBy.map(({ col, direction }) => `${col} ${direction}`).join(', ')}`
227
+ ? `ORDER BY ${ast.orderBy.map(({ col, direction }) => `${quoteIdentifier(col)} ${direction}`).join(', ')}`
219
228
  : ''
220
229
 
221
230
  const limitStmt = ast.limit._tag === 'Some' ? `LIMIT ?` : ''