@livestore/common 0.4.0-dev.21 → 0.4.0-dev.23

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 (344) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ClientSessionLeaderThreadProxy.d.ts +16 -9
  3. package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
  4. package/dist/ClientSessionLeaderThreadProxy.js.map +1 -1
  5. package/dist/WorkerTransportError.d.ts +11 -0
  6. package/dist/WorkerTransportError.d.ts.map +1 -0
  7. package/dist/WorkerTransportError.js +11 -0
  8. package/dist/WorkerTransportError.js.map +1 -0
  9. package/dist/adapter-types.d.ts +26 -3
  10. package/dist/adapter-types.d.ts.map +1 -1
  11. package/dist/adapter-types.js +27 -1
  12. package/dist/adapter-types.js.map +1 -1
  13. package/dist/bounded-collections.d.ts.map +1 -1
  14. package/dist/bounded-collections.js +6 -4
  15. package/dist/bounded-collections.js.map +1 -1
  16. package/dist/debug-info.js +4 -4
  17. package/dist/debug-info.js.map +1 -1
  18. package/dist/devtools/devtools-messages-client-session.d.ts +42 -22
  19. package/dist/devtools/devtools-messages-client-session.d.ts.map +1 -1
  20. package/dist/devtools/devtools-messages-client-session.js +12 -1
  21. package/dist/devtools/devtools-messages-client-session.js.map +1 -1
  22. package/dist/devtools/devtools-messages-common.d.ts +12 -6
  23. package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
  24. package/dist/devtools/devtools-messages-common.js +8 -3
  25. package/dist/devtools/devtools-messages-common.js.map +1 -1
  26. package/dist/devtools/devtools-messages-leader.d.ts +45 -25
  27. package/dist/devtools/devtools-messages-leader.d.ts.map +1 -1
  28. package/dist/devtools/devtools-messages-leader.js +12 -1
  29. package/dist/devtools/devtools-messages-leader.js.map +1 -1
  30. package/dist/devtools/mod.js +1 -1
  31. package/dist/devtools/mod.js.map +1 -1
  32. package/dist/errors.d.ts +15 -15
  33. package/dist/errors.d.ts.map +1 -1
  34. package/dist/errors.js +11 -11
  35. package/dist/errors.js.map +1 -1
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/index.js +2 -0
  39. package/dist/index.js.map +1 -1
  40. package/dist/leader-thread/LeaderSyncProcessor.d.ts +20 -6
  41. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  42. package/dist/leader-thread/LeaderSyncProcessor.js +283 -253
  43. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  44. package/dist/leader-thread/RejectedPushError.d.ts +107 -0
  45. package/dist/leader-thread/RejectedPushError.d.ts.map +1 -0
  46. package/dist/leader-thread/RejectedPushError.js +78 -0
  47. package/dist/leader-thread/RejectedPushError.js.map +1 -0
  48. package/dist/leader-thread/connection.js +1 -1
  49. package/dist/leader-thread/connection.js.map +1 -1
  50. package/dist/leader-thread/eventlog.d.ts.map +1 -1
  51. package/dist/leader-thread/eventlog.js +12 -11
  52. package/dist/leader-thread/eventlog.js.map +1 -1
  53. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -2
  54. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  55. package/dist/leader-thread/leader-worker-devtools.js +34 -14
  56. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  57. package/dist/leader-thread/make-leader-thread-layer.d.ts +12 -5
  58. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  59. package/dist/leader-thread/make-leader-thread-layer.js +12 -11
  60. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  61. package/dist/leader-thread/make-leader-thread-layer.test.js +1 -1
  62. package/dist/leader-thread/make-leader-thread-layer.test.js.map +1 -1
  63. package/dist/leader-thread/materialize-event.d.ts.map +1 -1
  64. package/dist/leader-thread/materialize-event.js +7 -4
  65. package/dist/leader-thread/materialize-event.js.map +1 -1
  66. package/dist/leader-thread/recreate-db.js +1 -1
  67. package/dist/leader-thread/recreate-db.js.map +1 -1
  68. package/dist/leader-thread/shutdown-channel.d.ts +2 -2
  69. package/dist/leader-thread/shutdown-channel.d.ts.map +1 -1
  70. package/dist/leader-thread/shutdown-channel.js +2 -2
  71. package/dist/leader-thread/shutdown-channel.js.map +1 -1
  72. package/dist/leader-thread/stream-events.d.ts.map +1 -1
  73. package/dist/leader-thread/stream-events.js +4 -3
  74. package/dist/leader-thread/stream-events.js.map +1 -1
  75. package/dist/leader-thread/types.d.ts +7 -6
  76. package/dist/leader-thread/types.d.ts.map +1 -1
  77. package/dist/leader-thread/types.js.map +1 -1
  78. package/dist/logging.js +4 -4
  79. package/dist/logging.js.map +1 -1
  80. package/dist/make-client-session.js +2 -2
  81. package/dist/make-client-session.js.map +1 -1
  82. package/dist/materializer-helper.js +6 -6
  83. package/dist/materializer-helper.js.map +1 -1
  84. package/dist/otel.d.ts +1 -1
  85. package/dist/otel.d.ts.map +1 -1
  86. package/dist/otel.js +2 -2
  87. package/dist/otel.js.map +1 -1
  88. package/dist/rematerialize-from-eventlog.d.ts +1 -1
  89. package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
  90. package/dist/rematerialize-from-eventlog.js +11 -9
  91. package/dist/rematerialize-from-eventlog.js.map +1 -1
  92. package/dist/schema/EventDef/define.d.ts +16 -2
  93. package/dist/schema/EventDef/define.d.ts.map +1 -1
  94. package/dist/schema/EventDef/define.js +5 -4
  95. package/dist/schema/EventDef/define.js.map +1 -1
  96. package/dist/schema/EventDef/deprecated.d.ts +99 -0
  97. package/dist/schema/EventDef/deprecated.d.ts.map +1 -0
  98. package/dist/schema/EventDef/deprecated.js +144 -0
  99. package/dist/schema/EventDef/deprecated.js.map +1 -0
  100. package/dist/schema/EventDef/deprecated.test.d.ts +2 -0
  101. package/dist/schema/EventDef/deprecated.test.d.ts.map +1 -0
  102. package/dist/schema/EventDef/deprecated.test.js +95 -0
  103. package/dist/schema/EventDef/deprecated.test.js.map +1 -0
  104. package/dist/schema/EventDef/event-def.d.ts +4 -0
  105. package/dist/schema/EventDef/event-def.d.ts.map +1 -1
  106. package/dist/schema/EventDef/mod.d.ts +1 -0
  107. package/dist/schema/EventDef/mod.d.ts.map +1 -1
  108. package/dist/schema/EventDef/mod.js +1 -0
  109. package/dist/schema/EventDef/mod.js.map +1 -1
  110. package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -1
  111. package/dist/schema/EventSequenceNumber/client.js +11 -11
  112. package/dist/schema/EventSequenceNumber/client.js.map +1 -1
  113. package/dist/schema/EventSequenceNumber.test.js +1 -1
  114. package/dist/schema/EventSequenceNumber.test.js.map +1 -1
  115. package/dist/schema/LiveStoreEvent/client.d.ts +6 -6
  116. package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -1
  117. package/dist/schema/LiveStoreEvent/client.js +6 -3
  118. package/dist/schema/LiveStoreEvent/client.js.map +1 -1
  119. package/dist/schema/LiveStoreEvent/client.test.d.ts +2 -0
  120. package/dist/schema/LiveStoreEvent/client.test.d.ts.map +1 -0
  121. package/dist/schema/LiveStoreEvent/client.test.js +83 -0
  122. package/dist/schema/LiveStoreEvent/client.test.js.map +1 -0
  123. package/dist/schema/schema.d.ts.map +1 -1
  124. package/dist/schema/schema.js +7 -4
  125. package/dist/schema/schema.js.map +1 -1
  126. package/dist/schema/state/sqlite/client-document-def.d.ts +1 -0
  127. package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
  128. package/dist/schema/state/sqlite/client-document-def.js +34 -13
  129. package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
  130. package/dist/schema/state/sqlite/client-document-def.test.js +121 -2
  131. package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
  132. package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -1
  133. package/dist/schema/state/sqlite/column-annotations.js +1 -1
  134. package/dist/schema/state/sqlite/column-annotations.js.map +1 -1
  135. package/dist/schema/state/sqlite/column-annotations.test.js +1 -1
  136. package/dist/schema/state/sqlite/column-annotations.test.js.map +1 -1
  137. package/dist/schema/state/sqlite/column-def.d.ts.map +1 -1
  138. package/dist/schema/state/sqlite/column-def.js +36 -34
  139. package/dist/schema/state/sqlite/column-def.js.map +1 -1
  140. package/dist/schema/state/sqlite/column-def.test.js +7 -6
  141. package/dist/schema/state/sqlite/column-def.test.js.map +1 -1
  142. package/dist/schema/state/sqlite/column-spec.d.ts.map +1 -1
  143. package/dist/schema/state/sqlite/column-spec.js +8 -8
  144. package/dist/schema/state/sqlite/column-spec.js.map +1 -1
  145. package/dist/schema/state/sqlite/column-spec.test.js +1 -1
  146. package/dist/schema/state/sqlite/column-spec.test.js.map +1 -1
  147. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +2 -2
  148. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -1
  149. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts +2 -2
  150. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -1
  151. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +11 -2
  152. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
  153. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js +1 -1
  154. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js.map +1 -1
  155. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts +1 -1
  156. package/dist/schema/state/sqlite/db-schema/dsl/mod.d.ts.map +1 -1
  157. package/dist/schema/state/sqlite/db-schema/dsl/mod.js +1 -1
  158. package/dist/schema/state/sqlite/db-schema/dsl/mod.js.map +1 -1
  159. package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
  160. package/dist/schema/state/sqlite/mod.js +3 -5
  161. package/dist/schema/state/sqlite/mod.js.map +1 -1
  162. package/dist/schema/state/sqlite/query-builder/api.d.ts +37 -13
  163. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
  164. package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -1
  165. package/dist/schema/state/sqlite/query-builder/astToSql.js +77 -7
  166. package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
  167. package/dist/schema/state/sqlite/query-builder/impl.d.ts +1 -1
  168. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
  169. package/dist/schema/state/sqlite/query-builder/impl.js +28 -14
  170. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
  171. package/dist/schema/state/sqlite/query-builder/impl.test.js +112 -3
  172. package/dist/schema/state/sqlite/query-builder/impl.test.js.map +1 -1
  173. package/dist/schema/state/sqlite/schema-helpers.js +2 -2
  174. package/dist/schema/state/sqlite/schema-helpers.js.map +1 -1
  175. package/dist/schema/state/sqlite/table-def.d.ts +5 -3
  176. package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
  177. package/dist/schema/state/sqlite/table-def.js +1 -1
  178. package/dist/schema/state/sqlite/table-def.js.map +1 -1
  179. package/dist/schema/state/sqlite/table-def.test.js +57 -4
  180. package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
  181. package/dist/schema/unknown-events.d.ts +1 -1
  182. package/dist/schema/unknown-events.d.ts.map +1 -1
  183. package/dist/schema/unknown-events.js +1 -1
  184. package/dist/schema/unknown-events.js.map +1 -1
  185. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js +1 -1
  186. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js.map +1 -1
  187. package/dist/schema-management/common.js +2 -2
  188. package/dist/schema-management/common.js.map +1 -1
  189. package/dist/schema-management/migrations.js +1 -1
  190. package/dist/schema-management/migrations.js.map +1 -1
  191. package/dist/sql-queries/sql-queries.js +8 -6
  192. package/dist/sql-queries/sql-queries.js.map +1 -1
  193. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  194. package/dist/sql-queries/sql-query-builder.js.map +1 -1
  195. package/dist/sqlite-db-helper.js +3 -3
  196. package/dist/sqlite-db-helper.js.map +1 -1
  197. package/dist/sqlite-types.d.ts +2 -2
  198. package/dist/sqlite-types.d.ts.map +1 -1
  199. package/dist/sqlite-types.js.map +1 -1
  200. package/dist/sync/ClientSessionSyncProcessor.d.ts +8 -9
  201. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  202. package/dist/sync/ClientSessionSyncProcessor.js +93 -107
  203. package/dist/sync/ClientSessionSyncProcessor.js.map +1 -1
  204. package/dist/sync/errors.d.ts +0 -38
  205. package/dist/sync/errors.d.ts.map +1 -1
  206. package/dist/sync/errors.js +3 -20
  207. package/dist/sync/errors.js.map +1 -1
  208. package/dist/sync/mock-sync-backend.d.ts +5 -3
  209. package/dist/sync/mock-sync-backend.d.ts.map +1 -1
  210. package/dist/sync/mock-sync-backend.js +70 -68
  211. package/dist/sync/mock-sync-backend.js.map +1 -1
  212. package/dist/sync/next/compact-events.js +6 -6
  213. package/dist/sync/next/compact-events.js.map +1 -1
  214. package/dist/sync/next/facts.d.ts.map +1 -1
  215. package/dist/sync/next/facts.js +6 -6
  216. package/dist/sync/next/facts.js.map +1 -1
  217. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  218. package/dist/sync/next/history-dag-common.js +6 -6
  219. package/dist/sync/next/history-dag-common.js.map +1 -1
  220. package/dist/sync/next/history-dag.js +3 -3
  221. package/dist/sync/next/history-dag.js.map +1 -1
  222. package/dist/sync/next/rebase-events.js +1 -1
  223. package/dist/sync/next/rebase-events.js.map +1 -1
  224. package/dist/sync/next/test/compact-events.calculator.test.js +2 -2
  225. package/dist/sync/next/test/compact-events.calculator.test.js.map +1 -1
  226. package/dist/sync/next/test/compact-events.test.d.ts.map +1 -1
  227. package/dist/sync/next/test/compact-events.test.js +2 -2
  228. package/dist/sync/next/test/compact-events.test.js.map +1 -1
  229. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
  230. package/dist/sync/next/test/event-fixtures.js +2 -2
  231. package/dist/sync/next/test/event-fixtures.js.map +1 -1
  232. package/dist/sync/sync-backend-kv.d.ts.map +1 -1
  233. package/dist/sync/sync-backend-kv.js.map +1 -1
  234. package/dist/sync/sync-backend.d.ts +3 -3
  235. package/dist/sync/sync-backend.d.ts.map +1 -1
  236. package/dist/sync/sync-backend.js +1 -1
  237. package/dist/sync/sync-backend.js.map +1 -1
  238. package/dist/sync/sync.d.ts +20 -0
  239. package/dist/sync/sync.d.ts.map +1 -1
  240. package/dist/sync/syncstate.d.ts +4 -17
  241. package/dist/sync/syncstate.d.ts.map +1 -1
  242. package/dist/sync/syncstate.js +51 -74
  243. package/dist/sync/syncstate.js.map +1 -1
  244. package/dist/sync/syncstate.test.js +112 -96
  245. package/dist/sync/syncstate.test.js.map +1 -1
  246. package/dist/sync/transport-chunking.js +3 -3
  247. package/dist/sync/transport-chunking.js.map +1 -1
  248. package/dist/sync/validate-push-payload.d.ts +2 -2
  249. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  250. package/dist/sync/validate-push-payload.js +4 -6
  251. package/dist/sync/validate-push-payload.js.map +1 -1
  252. package/dist/util.js +2 -2
  253. package/dist/util.js.map +1 -1
  254. package/dist/version.d.ts +7 -1
  255. package/dist/version.d.ts.map +1 -1
  256. package/dist/version.js +8 -4
  257. package/dist/version.js.map +1 -1
  258. package/package.json +66 -12
  259. package/src/ClientSessionLeaderThreadProxy.ts +16 -9
  260. package/src/WorkerTransportError.ts +12 -0
  261. package/src/adapter-types.ts +39 -3
  262. package/src/bounded-collections.ts +6 -5
  263. package/src/debug-info.ts +4 -4
  264. package/src/devtools/devtools-messages-client-session.ts +12 -0
  265. package/src/devtools/devtools-messages-common.ts +8 -4
  266. package/src/devtools/devtools-messages-leader.ts +12 -0
  267. package/src/devtools/mod.ts +1 -1
  268. package/src/errors.ts +18 -17
  269. package/src/index.ts +2 -0
  270. package/src/leader-thread/LeaderSyncProcessor.ts +417 -347
  271. package/src/leader-thread/RejectedPushError.ts +106 -0
  272. package/src/leader-thread/connection.ts +1 -1
  273. package/src/leader-thread/eventlog.ts +16 -14
  274. package/src/leader-thread/leader-worker-devtools.ts +107 -66
  275. package/src/leader-thread/make-leader-thread-layer.test.ts +1 -1
  276. package/src/leader-thread/make-leader-thread-layer.ts +41 -31
  277. package/src/leader-thread/materialize-event.ts +8 -4
  278. package/src/leader-thread/recreate-db.ts +1 -1
  279. package/src/leader-thread/shutdown-channel.ts +2 -6
  280. package/src/leader-thread/stream-events.ts +10 -5
  281. package/src/leader-thread/types.ts +7 -6
  282. package/src/logging.ts +4 -4
  283. package/src/make-client-session.ts +2 -2
  284. package/src/materializer-helper.ts +9 -9
  285. package/src/otel.ts +3 -2
  286. package/src/rematerialize-from-eventlog.ts +60 -60
  287. package/src/schema/EventDef/define.ts +22 -6
  288. package/src/schema/EventDef/deprecated.test.ts +129 -0
  289. package/src/schema/EventDef/deprecated.ts +175 -0
  290. package/src/schema/EventDef/event-def.ts +5 -0
  291. package/src/schema/EventDef/mod.ts +1 -0
  292. package/src/schema/EventSequenceNumber/client.ts +11 -11
  293. package/src/schema/EventSequenceNumber.test.ts +2 -1
  294. package/src/schema/LiveStoreEvent/client.test.ts +97 -0
  295. package/src/schema/LiveStoreEvent/client.ts +6 -3
  296. package/src/schema/schema.ts +9 -4
  297. package/src/schema/state/sqlite/client-document-def.test.ts +142 -3
  298. package/src/schema/state/sqlite/client-document-def.ts +37 -14
  299. package/src/schema/state/sqlite/column-annotations.test.ts +2 -1
  300. package/src/schema/state/sqlite/column-annotations.ts +2 -1
  301. package/src/schema/state/sqlite/column-def.test.ts +8 -6
  302. package/src/schema/state/sqlite/column-def.ts +41 -36
  303. package/src/schema/state/sqlite/column-spec.test.ts +3 -1
  304. package/src/schema/state/sqlite/column-spec.ts +9 -8
  305. package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +2 -2
  306. package/src/schema/state/sqlite/db-schema/dsl/field-defs.test.ts +2 -1
  307. package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +13 -4
  308. package/src/schema/state/sqlite/db-schema/dsl/mod.ts +3 -3
  309. package/src/schema/state/sqlite/mod.ts +4 -5
  310. package/src/schema/state/sqlite/query-builder/api.ts +37 -8
  311. package/src/schema/state/sqlite/query-builder/astToSql.ts +87 -7
  312. package/src/schema/state/sqlite/query-builder/impl.test.ts +145 -3
  313. package/src/schema/state/sqlite/query-builder/impl.ts +26 -12
  314. package/src/schema/state/sqlite/schema-helpers.ts +2 -2
  315. package/src/schema/state/sqlite/table-def.test.ts +67 -4
  316. package/src/schema/state/sqlite/table-def.ts +8 -15
  317. package/src/schema/unknown-events.ts +2 -2
  318. package/src/schema-management/__tests__/migrations-autoincrement-quoting.test.ts +3 -1
  319. package/src/schema-management/common.ts +2 -2
  320. package/src/schema-management/migrations.ts +1 -1
  321. package/src/sql-queries/sql-queries.ts +10 -6
  322. package/src/sql-queries/sql-query-builder.ts +1 -0
  323. package/src/sqlite-db-helper.ts +3 -3
  324. package/src/sqlite-types.ts +3 -2
  325. package/src/sync/ClientSessionSyncProcessor.ts +142 -133
  326. package/src/sync/errors.ts +10 -22
  327. package/src/sync/mock-sync-backend.ts +139 -97
  328. package/src/sync/next/compact-events.ts +5 -5
  329. package/src/sync/next/facts.ts +7 -6
  330. package/src/sync/next/history-dag-common.ts +9 -6
  331. package/src/sync/next/history-dag.ts +3 -3
  332. package/src/sync/next/rebase-events.ts +1 -1
  333. package/src/sync/next/test/compact-events.calculator.test.ts +3 -2
  334. package/src/sync/next/test/compact-events.test.ts +4 -3
  335. package/src/sync/next/test/event-fixtures.ts +2 -2
  336. package/src/sync/sync-backend-kv.ts +1 -0
  337. package/src/sync/sync-backend.ts +5 -4
  338. package/src/sync/sync.ts +21 -0
  339. package/src/sync/syncstate.test.ts +513 -435
  340. package/src/sync/syncstate.ts +80 -86
  341. package/src/sync/transport-chunking.ts +3 -3
  342. package/src/sync/validate-push-payload.ts +4 -6
  343. package/src/util.ts +2 -2
  344. package/src/version.ts +8 -4
@@ -22,16 +22,15 @@ export * from './column-spec.ts'
22
22
  export * from './table-def.ts'
23
23
 
24
24
  export const makeState = <TStateInput extends InputState>(inputSchema: TStateInput): InternalState => {
25
- const inputTables: ReadonlyArray<TableDef> = Array.isArray(inputSchema.tables)
26
- ? inputSchema.tables
27
- : Object.values(inputSchema.tables)
25
+ const inputTables: ReadonlyArray<TableDef> =
26
+ Array.isArray(inputSchema.tables) === true ? inputSchema.tables : Object.values(inputSchema.tables)
28
27
 
29
28
  const tables = new Map<string, TableDef.Any>()
30
29
 
31
30
  for (const tableDef of inputTables) {
32
31
  const sqliteDef = tableDef.sqliteDef
33
32
  // TODO validate tables (e.g. index names are unique)
34
- if (tables.has(sqliteDef.ast.name)) {
33
+ if (tables.has(sqliteDef.ast.name) === true) {
35
34
  shouldNeverHappen(`Duplicate table name: ${sqliteDef.ast.name}. Please use unique names for tables.`)
36
35
  }
37
36
  tables.set(sqliteDef.ast.name, tableDef)
@@ -48,7 +47,7 @@ export const makeState = <TStateInput extends InputState>(inputSchema: TStateInp
48
47
  }
49
48
 
50
49
  for (const tableDef of inputTables) {
51
- if (tableIsClientDocumentTable(tableDef)) {
50
+ if (tableIsClientDocumentTable(tableDef) === true) {
52
51
  materializers.set(
53
52
  tableDef[ClientDocumentTableDefSymbol].derived.setEventDef.name,
54
53
  tableDef[ClientDocumentTableDefSymbol].derived.setMaterializer,
@@ -128,15 +128,25 @@ export type QueryBuilder<
128
128
 
129
129
  export namespace QueryBuilder {
130
130
  export type Any = QueryBuilder<any, any, any>
131
- export type WhereOps = WhereOps.Equality | WhereOps.Order | WhereOps.Like | WhereOps.In
131
+ export type WhereOps = WhereOps.Equality | WhereOps.Order | WhereOps.Like | WhereOps.In | WhereOps.JsonArray
132
132
 
133
133
  export namespace WhereOps {
134
134
  export type Equality = '=' | '!='
135
135
  export type Order = '<' | '>' | '<=' | '>='
136
136
  export type Like = 'LIKE' | 'NOT LIKE' | 'ILIKE' | 'NOT ILIKE'
137
137
  export type In = 'IN' | 'NOT IN'
138
+ /**
139
+ * Operators for checking if a JSON array column contains a value.
140
+ *
141
+ * ⚠️ **Performance note**: These operators use SQLite's `json_each()` table-valued function
142
+ * which **cannot be indexed** and requires a full table scan. For large tables with frequent
143
+ * lookups, consider denormalizing the data into a separate indexed table.
144
+ *
145
+ * @see https://sqlite.org/json1.html#jeach
146
+ */
147
+ export type JsonArray = 'JSON_CONTAINS' | 'JSON_NOT_CONTAINS'
138
148
 
139
- export type SingleValue = Equality | Order | Like
149
+ export type SingleValue = Equality | Order | Like | JsonArray
140
150
  export type MultiValue = In
141
151
  }
142
152
 
@@ -155,14 +165,26 @@ export namespace QueryBuilder {
155
165
  | 'returning'
156
166
  | 'onConflict'
157
167
 
168
+ /** Extracts the element type from an array type, or returns never if not an array */
169
+ type ArrayElement<T> = T extends ReadonlyArray<infer E> ? E : never
170
+
158
171
  export type WhereParams<TTableDef extends TableDefBase> = Partial<{
159
172
  [K in keyof TTableDef['sqliteDef']['columns']]:
160
173
  | TTableDef['sqliteDef']['columns'][K]['schema']['Type']
161
- | { op: QueryBuilder.WhereOps.SingleValue; value: TTableDef['sqliteDef']['columns'][K]['schema']['Type'] }
174
+ | {
175
+ op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>
176
+ value: TTableDef['sqliteDef']['columns'][K]['schema']['Type']
177
+ }
162
178
  | {
163
179
  op: QueryBuilder.WhereOps.MultiValue
164
180
  value: ReadonlyArray<TTableDef['sqliteDef']['columns'][K]['schema']['Type']>
165
181
  }
182
+ | (ArrayElement<TTableDef['sqliteDef']['columns'][K]['schema']['Type']> extends never
183
+ ? never
184
+ : {
185
+ op: QueryBuilder.WhereOps.JsonArray
186
+ value: ArrayElement<TTableDef['sqliteDef']['columns'][K]['schema']['Type']>
187
+ })
166
188
  | undefined
167
189
  }>
168
190
 
@@ -208,9 +230,8 @@ export namespace QueryBuilder {
208
230
  /** Select multiple columns */
209
231
  <TColumns extends keyof TTableDef['sqliteDef']['columns'] & string>(
210
232
  ...columns: TColumns[]
211
- // TODO also support arbitrary SQL selects
212
- // params: QueryBuilderSelectParams,
213
- ): QueryBuilder<
233
+ )// params: QueryBuilderSelectParams, // TODO also support arbitrary SQL selects
234
+ : QueryBuilder<
214
235
  ReadonlyArray<{
215
236
  readonly [K in TColumns]: TTableDef['sqliteDef']['columns'][K]['schema']['Type']
216
237
  }>,
@@ -336,12 +357,20 @@ export namespace QueryBuilder {
336
357
  >
337
358
 
338
359
  /**
339
- * Insert a new row into the table
360
+ * Insert a new row into the table.
361
+ *
362
+ * @remarks
363
+ *
364
+ * Follows SQL semantics: nullable columns and columns with defaults are omittable.
365
+ * `NullOr(S)` and `optional(NullOr(S))` both produce nullable columns, so both are omittable.
366
+ *
367
+ * @example
340
368
  *
341
- * Example:
342
369
  * ```ts
343
370
  * db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' })
344
371
  * ```
372
+ *
373
+ * @param values - The row values to insert.
345
374
  */
346
375
  readonly insert: (
347
376
  values: TTableDef['insertSchema']['Type'],
@@ -1,11 +1,74 @@
1
1
  import { shouldNeverHappen } from '@livestore/utils'
2
- import { Schema } from '@livestore/utils/effect'
2
+ import { Schema, SchemaAST } from '@livestore/utils/effect'
3
3
 
4
4
  import { SessionIdSymbol } from '../../../../adapter-types.ts'
5
5
  import type { SqlValue } from '../../../../util.ts'
6
6
  import type { State } from '../../../mod.ts'
7
7
  import type { QueryBuilderAst } from './api.ts'
8
8
 
9
+ /**
10
+ * Extracts array element schema from a JSON array transformation AST.
11
+ * Returns the element schema, or undefined if not a JSON array transformation.
12
+ */
13
+ const extractArrayElementFromTransformation = (ast: SchemaAST.AST): Schema.Schema.Any | undefined => {
14
+ if (SchemaAST.isTransformation(ast) === false) return undefined
15
+
16
+ const toAst = ast.to
17
+ // Check if the "to" side is a TupleType (Effect's internal representation of Array)
18
+ if (SchemaAST.isTupleType(toAst) === false) return undefined
19
+
20
+ // For Schema.Array, rest contains { type: AST } elements - get the first one's type
21
+ const restElement = toAst.rest[0]
22
+ if (restElement === undefined) return undefined
23
+
24
+ return Schema.make(restElement.type)
25
+ }
26
+
27
+ /**
28
+ * For JSON array columns, extracts the element schema from Schema.parseJson(Schema.Array(ElementSchema)).
29
+ * Also handles nullable JSON arrays (Schema.NullOr(Schema.parseJson(Schema.Array(...)))).
30
+ * Returns the element schema, or undefined if the column is not a JSON array.
31
+ */
32
+ const getJsonArrayElementSchema = (colSchema: Schema.Schema.Any): Schema.Schema.Any | undefined => {
33
+ const ast = colSchema.ast
34
+
35
+ // Case 1: Direct transformation (non-nullable JSON array)
36
+ // Schema.parseJson(Schema.Array(ElementSchema)) creates a Transformation AST
37
+ if (SchemaAST.isTransformation(ast) === true) {
38
+ return extractArrayElementFromTransformation(ast)
39
+ }
40
+
41
+ // Case 2: Nullable JSON array - Schema.NullOr wraps the parseJson in a Union
42
+ // Structure: Union([Transformation (JSON array), Literal (null)])
43
+ if (SchemaAST.isUnion(ast) === true) {
44
+ for (const member of ast.types) {
45
+ const result = extractArrayElementFromTransformation(member)
46
+ if (result !== undefined) return result
47
+ }
48
+ }
49
+
50
+ return undefined
51
+ }
52
+
53
+ /**
54
+ * Encodes a JSON array element to the representation returned by SQLite's json_each().
55
+ * Objects/arrays are stringified so they match json_each's TEXT representation.
56
+ */
57
+ const encodeJsonArrayElementValue = (elementSchema: Schema.Schema.Any, value: unknown): SqlValue => {
58
+ const encoded = Schema.encodeSync(elementSchema as Schema.Schema<unknown, SqlValue>)(value)
59
+
60
+ if (encoded === null) return null
61
+ if (typeof encoded === 'object') {
62
+ // Objects and arrays need to be JSON-stringified to match json_each() output
63
+ return JSON.stringify(encoded)
64
+ }
65
+ if (typeof encoded === 'boolean') {
66
+ return encoded === true ? 1 : 0
67
+ }
68
+
69
+ return encoded
70
+ }
71
+
9
72
  // Helper functions for SQL generation
10
73
  const quoteIdentifier = (identifier: string): string => `"${identifier.replace(/"/g, '""')}"`
11
74
 
@@ -35,12 +98,29 @@ const formatWhereClause = (
35
98
  throw new Error(`Column ${col} not found`)
36
99
  }
37
100
 
101
+ // Handle JSON array containment operators
102
+ if (op === 'JSON_CONTAINS' || op === 'JSON_NOT_CONTAINS') {
103
+ const elementSchema = getJsonArrayElementSchema(colDef.schema)
104
+ if (elementSchema === undefined) {
105
+ throw new Error(
106
+ `${op} operator can only be used on JSON array columns, but column "${col}" is not a JSON array`,
107
+ )
108
+ }
109
+
110
+ const existsOp = op === 'JSON_CONTAINS' ? 'EXISTS' : 'NOT EXISTS'
111
+ // Encode the element value using the element schema
112
+ // Objects are JSON-stringified to match json_each() output
113
+ const encodedValue = encodeJsonArrayElementValue(elementSchema, value)
114
+ bindValues.push(encodedValue)
115
+ return `${existsOp} (SELECT 1 FROM json_each(${quotedCol}) WHERE value = ?)`
116
+ }
117
+
38
118
  // Handle array values for IN/NOT IN operators
39
119
  const isArray = op === 'IN' || op === 'NOT IN'
40
120
 
41
- if (isArray) {
121
+ if (isArray === true) {
42
122
  // Verify value is an array
43
- if (!Array.isArray(value)) {
123
+ if (Array.isArray(value) === false) {
44
124
  return shouldNeverHappen(`Expected array value for ${op} operator but got`, value)
45
125
  }
46
126
 
@@ -65,7 +145,7 @@ const formatWhereClause = (
65
145
  }
66
146
 
67
147
  const formatReturningClause = (returning?: string[]): string => {
68
- if (!returning || returning.length === 0) return ''
148
+ if (returning == null || returning.length === 0) return ''
69
149
  return ` RETURNING ${returning.map(quoteIdentifier).join(', ')}`
70
150
  }
71
151
 
@@ -89,7 +169,7 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
89
169
  let conflictClause = '' // Store the ON CONFLICT clause separately
90
170
 
91
171
  // Handle ON CONFLICT clause
92
- if (ast.onConflict) {
172
+ if (ast.onConflict !== undefined) {
93
173
  // Handle REPLACE specifically as it changes the INSERT verb
94
174
  if (ast.onConflict.action._tag === 'replace') {
95
175
  insertVerb = 'INSERT OR REPLACE'
@@ -169,7 +249,7 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
169
249
  .join(', ')}`
170
250
 
171
251
  const whereClause = formatWhereClause(ast.where, ast.tableDef, bindValues)
172
- if (whereClause) query += ` ${whereClause}`
252
+ if (whereClause !== undefined) query += ` ${whereClause}`
173
253
 
174
254
  query += formatReturningClause(ast.returning)
175
255
  return { query, bindValues, usedTables }
@@ -180,7 +260,7 @@ export const astToSql = (ast: QueryBuilderAst): { query: string; bindValues: Sql
180
260
  let query = `DELETE FROM '${ast.tableDef.sqliteDef.name}'`
181
261
 
182
262
  const whereClause = formatWhereClause(ast.where, ast.tableDef, bindValues)
183
- if (whereClause) query += ` ${whereClause}`
263
+ if (whereClause !== undefined) query += ` ${whereClause}`
184
264
 
185
265
  query += formatReturningClause(ast.returning)
186
266
  return { query, bindValues, usedTables }
@@ -1,6 +1,8 @@
1
- import { Schema } from '@livestore/utils/effect'
2
1
  import { describe, expect, it } from 'vitest'
3
2
 
3
+ import { Schema } from '@livestore/utils/effect'
4
+ import { objectToString } from '@livestore/utils'
5
+
4
6
  import { State } from '../../../mod.ts'
5
7
  import type { QueryBuilder } from './api.ts'
6
8
  import { getResultSchema } from './impl.ts'
@@ -81,12 +83,27 @@ const selections = State.SQLite.table({
81
83
  },
82
84
  })
83
85
 
84
- const db = { todos, todosWithIntId, comments, issue, selections, UiState, UiStateWithDefaultId }
86
+ const Source = Schema.Literal('google', 'linkedin', 'facebook')
87
+ const ProfileAttribute = Schema.Struct({ key: Schema.String, value: Schema.String })
88
+
89
+ const personProfiles = State.SQLite.table({
90
+ name: 'person_profiles',
91
+ columns: {
92
+ personId: State.SQLite.text({ primaryKey: true }),
93
+ sources: State.SQLite.json({ schema: Schema.Array(Source), default: [] }),
94
+ tags: State.SQLite.json({ schema: Schema.Array(Schema.String), default: [] }),
95
+ attributes: State.SQLite.json({ schema: Schema.Array(ProfileAttribute), default: [] }),
96
+ /** Nullable JSON array column for testing JSON_CONTAINS on nullable columns */
97
+ optionalTags: State.SQLite.json({ schema: Schema.Array(Schema.String), nullable: true }),
98
+ },
99
+ })
100
+
101
+ const db = { todos, todosWithIntId, comments, issue, selections, UiState, UiStateWithDefaultId, personProfiles }
85
102
 
86
103
  const dump = (qb: QueryBuilder<any, any, any>) => ({
87
104
  bindValues: qb.asSql().bindValues,
88
105
  query: qb.asSql().query,
89
- schema: getResultSchema(qb).toString(),
106
+ schema: objectToString(getResultSchema(qb)),
90
107
  })
91
108
 
92
109
  describe('query builder', () => {
@@ -364,6 +381,131 @@ describe('query builder', () => {
364
381
  }
365
382
  `)
366
383
  })
384
+
385
+ it('should handle JSON_CONTAINS operator for JSON array columns', () => {
386
+ expect(
387
+ dump(db.personProfiles.where({ sources: { op: 'JSON_CONTAINS', value: 'google' } })),
388
+ ).toMatchInlineSnapshot(`
389
+ {
390
+ "bindValues": [
391
+ "google",
392
+ ],
393
+ "query": "SELECT * FROM 'person_profiles' WHERE EXISTS (SELECT 1 FROM json_each("sources") WHERE value = ?)",
394
+ "schema": "ReadonlyArray<person_profiles>",
395
+ }
396
+ `)
397
+
398
+ // With select
399
+ expect(
400
+ dump(db.personProfiles.select('personId').where({ sources: { op: 'JSON_CONTAINS', value: 'linkedin' } })),
401
+ ).toMatchInlineSnapshot(`
402
+ {
403
+ "bindValues": [
404
+ "linkedin",
405
+ ],
406
+ "query": "SELECT "personId" FROM 'person_profiles' WHERE EXISTS (SELECT 1 FROM json_each("sources") WHERE value = ?)",
407
+ "schema": "ReadonlyArray<({ readonly personId: string } <-> string)>",
408
+ }
409
+ `)
410
+
411
+ // With plain string array column
412
+ expect(
413
+ dump(db.personProfiles.where({ tags: { op: 'JSON_CONTAINS', value: 'important' } })),
414
+ ).toMatchInlineSnapshot(`
415
+ {
416
+ "bindValues": [
417
+ "important",
418
+ ],
419
+ "query": "SELECT * FROM 'person_profiles' WHERE EXISTS (SELECT 1 FROM json_each("tags") WHERE value = ?)",
420
+ "schema": "ReadonlyArray<person_profiles>",
421
+ }
422
+ `)
423
+ })
424
+
425
+ it('should handle JSON_NOT_CONTAINS operator for JSON array columns', () => {
426
+ expect(
427
+ dump(db.personProfiles.where({ sources: { op: 'JSON_NOT_CONTAINS', value: 'google' } })),
428
+ ).toMatchInlineSnapshot(`
429
+ {
430
+ "bindValues": [
431
+ "google",
432
+ ],
433
+ "query": "SELECT * FROM 'person_profiles' WHERE NOT EXISTS (SELECT 1 FROM json_each("sources") WHERE value = ?)",
434
+ "schema": "ReadonlyArray<person_profiles>",
435
+ }
436
+ `)
437
+ })
438
+
439
+ it('should JSON-stringify object elements for JSON_CONTAINS', () => {
440
+ expect(
441
+ dump(
442
+ db.personProfiles.where({
443
+ attributes: { op: 'JSON_CONTAINS', value: { key: 'language', value: 'typescript' } },
444
+ }),
445
+ ),
446
+ ).toMatchInlineSnapshot(`
447
+ {
448
+ "bindValues": [
449
+ "{"key":"language","value":"typescript"}",
450
+ ],
451
+ "query": "SELECT * FROM 'person_profiles' WHERE EXISTS (SELECT 1 FROM json_each("attributes") WHERE value = ?)",
452
+ "schema": "ReadonlyArray<person_profiles>",
453
+ }
454
+ `)
455
+ })
456
+
457
+ it('should handle combining JSON_CONTAINS with other WHERE clauses', () => {
458
+ expect(
459
+ dump(
460
+ db.personProfiles
461
+ .where({ sources: { op: 'JSON_CONTAINS', value: 'google' } })
462
+ .where({ sources: { op: 'JSON_NOT_CONTAINS', value: 'facebook' } }),
463
+ ),
464
+ ).toMatchInlineSnapshot(`
465
+ {
466
+ "bindValues": [
467
+ "google",
468
+ "facebook",
469
+ ],
470
+ "query": "SELECT * FROM 'person_profiles' WHERE EXISTS (SELECT 1 FROM json_each("sources") WHERE value = ?) AND NOT EXISTS (SELECT 1 FROM json_each("sources") WHERE value = ?)",
471
+ "schema": "ReadonlyArray<person_profiles>",
472
+ }
473
+ `)
474
+ })
475
+
476
+ it('should handle JSON_CONTAINS on nullable JSON array columns', () => {
477
+ expect(
478
+ dump(db.personProfiles.where({ optionalTags: { op: 'JSON_CONTAINS', value: 'important' } })),
479
+ ).toMatchInlineSnapshot(`
480
+ {
481
+ "bindValues": [
482
+ "important",
483
+ ],
484
+ "query": "SELECT * FROM 'person_profiles' WHERE EXISTS (SELECT 1 FROM json_each("optionalTags") WHERE value = ?)",
485
+ "schema": "ReadonlyArray<person_profiles>",
486
+ }
487
+ `)
488
+
489
+ // With JSON_NOT_CONTAINS
490
+ expect(
491
+ dump(db.personProfiles.where({ optionalTags: { op: 'JSON_NOT_CONTAINS', value: 'archived' } })),
492
+ ).toMatchInlineSnapshot(`
493
+ {
494
+ "bindValues": [
495
+ "archived",
496
+ ],
497
+ "query": "SELECT * FROM 'person_profiles' WHERE NOT EXISTS (SELECT 1 FROM json_each("optionalTags") WHERE value = ?)",
498
+ "schema": "ReadonlyArray<person_profiles>",
499
+ }
500
+ `)
501
+ })
502
+
503
+ it('should throw error when using JSON_CONTAINS on non-JSON array column', () => {
504
+ expect(() =>
505
+ // Type system prevents this at compile time for non-array columns, but test runtime check
506
+ dump(db.todos.where({ status: { op: 'JSON_CONTAINS', value: 'active' } } as any)),
507
+ ).toThrow('JSON_CONTAINS operator can only be used on JSON array columns')
508
+ })
367
509
  })
368
510
 
369
511
  // describe('getOrCreate queries', () => {
@@ -9,7 +9,7 @@ import { astToSql } from './astToSql.ts'
9
9
  export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
10
10
  tableDef: TTableDef,
11
11
  ast: QueryBuilderAst = emptyAst(tableDef),
12
- ): QueryBuilder<TResult, TTableDef, never> => {
12
+ ): QueryBuilder<TResult, TTableDef> => {
13
13
  const api = {
14
14
  select() {
15
15
  assertSelectQueryBuilderAst(ast)
@@ -18,6 +18,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
18
18
 
19
19
  // Pluck if there's only one column selected
20
20
  if (params.length === 1) {
21
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- arguments-based overload dispatch requires manual narrowing
21
22
  const [col] = params as any as [string]
22
23
  return makeQueryBuilder(tableDef, {
23
24
  ...ast,
@@ -26,8 +27,10 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
26
27
  })
27
28
  }
28
29
 
30
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- arguments-based overload dispatch requires manual narrowing
29
31
  const columns = params as unknown as ReadonlyArray<string>
30
32
 
33
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- query builder return type depends on AST state; consumer type safety enforced by api.ts
31
34
  return makeQueryBuilder(tableDef, {
32
35
  ...ast,
33
36
  resultSchemaSingle:
@@ -44,7 +47,8 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
44
47
  const newOps = Object.entries(params)
45
48
  .filter(([, value]) => value !== undefined)
46
49
  .map<QueryBuilderAst.Where>(([col, value]) =>
47
- Predicate.hasProperty(value, 'op') && Predicate.hasProperty(value, 'value')
50
+ Predicate.hasProperty(value, 'op') === true && Predicate.hasProperty(value, 'value') === true
51
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- where clause construction; shape validated at runtime
48
52
  ? ({ col, op: value.op, value: value.value } as any)
49
53
  : { col, op: '=', value },
50
54
  )
@@ -54,6 +58,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
54
58
  case 'SelectQuery':
55
59
  case 'UpdateQuery':
56
60
  case 'DeleteQuery': {
61
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- query builder return type depends on AST state; consumer type safety enforced by api.ts
57
62
  return makeQueryBuilder(tableDef, {
58
63
  ...ast,
59
64
  where: [...ast.where, ...newOps],
@@ -74,6 +79,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
74
79
  case 'SelectQuery':
75
80
  case 'UpdateQuery':
76
81
  case 'DeleteQuery': {
82
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- query builder return type depends on AST state; consumer type safety enforced by api.ts
77
83
  return makeQueryBuilder(tableDef, {
78
84
  ...ast,
79
85
  where: [...ast.where, { col, op, value }],
@@ -91,6 +97,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
91
97
  if (arguments.length === 0 || arguments.length > 2) return invalidQueryBuilder()
92
98
 
93
99
  if (arguments.length === 1) {
100
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- arguments-based overload dispatch requires manual narrowing
94
101
  const params = arguments[0] as QueryBuilder.OrderByParams<TTableDef>
95
102
  return makeQueryBuilder(tableDef, {
96
103
  ...ast,
@@ -98,8 +105,10 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
98
105
  })
99
106
  }
100
107
 
108
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- arguments-based overload dispatch requires manual narrowing
101
109
  const [col, direction] = arguments as any as [keyof TTableDef['sqliteDef']['columns'] & string, 'asc' | 'desc']
102
110
 
111
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- query builder return type depends on AST state; consumer type safety enforced by api.ts
103
112
  return makeQueryBuilder(tableDef, {
104
113
  ...ast,
105
114
  orderBy: [...ast.orderBy, { col, direction }],
@@ -116,7 +125,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
116
125
  return makeQueryBuilder(tableDef, { ...ast, offset: Option.some(offset) })
117
126
  },
118
127
  count: () => {
119
- if (isRowQuery(ast) || ast._tag === 'InsertQuery' || ast._tag === 'UpdateQuery' || ast._tag === 'DeleteQuery')
128
+ if (isRowQuery(ast) === true || ast._tag === 'InsertQuery' || ast._tag === 'UpdateQuery' || ast._tag === 'DeleteQuery')
120
129
  return invalidQueryBuilder()
121
130
 
122
131
  return makeQueryBuilder(tableDef, {
@@ -171,6 +180,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
171
180
  insert: (values) => {
172
181
  const filteredValues = Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined))
173
182
 
183
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- query builder return type depends on AST state; consumer type safety enforced by api.ts
174
184
  return makeQueryBuilder(tableDef, {
175
185
  _tag: 'InsertQuery',
176
186
  tableDef,
@@ -185,7 +195,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
185
195
  action: 'ignore' | 'replace' | 'update',
186
196
  updateValues?: Record<string, unknown>,
187
197
  ) => {
188
- const targets = Array.isArray(targetOrTargets) ? targetOrTargets : [targetOrTargets]
198
+ const targets = Array.isArray(targetOrTargets) === true ? targetOrTargets : [targetOrTargets]
189
199
 
190
200
  assertInsertQueryBuilderAst(ast)
191
201
 
@@ -199,6 +209,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
199
209
  Match.exhaustive,
200
210
  )
201
211
 
212
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- query builder return type depends on AST state; consumer type safety enforced by api.ts
202
213
  return makeQueryBuilder(tableDef, {
203
214
  ...ast,
204
215
  onConflict,
@@ -208,6 +219,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
208
219
  returning: (...columns) => {
209
220
  assertWriteQueryBuilderAst(ast)
210
221
 
222
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- query builder return type depends on AST state; consumer type safety enforced by api.ts
211
223
  return makeQueryBuilder(tableDef, {
212
224
  ...ast,
213
225
  returning: columns,
@@ -221,6 +233,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
221
233
  // Preserve where clauses if coming from a SelectQuery
222
234
  const whereClause = ast._tag === 'SelectQuery' ? ast.where : []
223
235
 
236
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- query builder return type depends on AST state; consumer type safety enforced by api.ts
224
237
  return makeQueryBuilder(tableDef, {
225
238
  _tag: 'UpdateQuery',
226
239
  tableDef,
@@ -235,6 +248,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
235
248
  // Preserve where clauses if coming from a SelectQuery
236
249
  const whereClause = ast._tag === 'SelectQuery' ? ast.where : []
237
250
 
251
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- query builder return type depends on AST state; consumer type safety enforced by api.ts
238
252
  return makeQueryBuilder(tableDef, {
239
253
  _tag: 'DeleteQuery',
240
254
  tableDef,
@@ -248,6 +262,7 @@ export const makeQueryBuilder = <TResult, TTableDef extends TableDefBase>(
248
262
  return {
249
263
  [QueryBuilderTypeId]: QueryBuilderTypeId,
250
264
  [QueryBuilderAstSymbol]: ast,
265
+ // oxlint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- phantom type field for generic inference only
251
266
  ResultType: 'only-for-type-inference' as TResult,
252
267
  asSql: () => astToSql(ast),
253
268
  toString: () => {
@@ -277,19 +292,19 @@ const emptyAst = (tableDef: TableDefBase): QueryBuilderAst.SelectQuery => ({
277
292
 
278
293
  // Helper functions
279
294
 
280
- function assertSelectQueryBuilderAst(ast: QueryBuilderAst): asserts ast is QueryBuilderAst.SelectQuery {
295
+ const assertSelectQueryBuilderAst: (ast: QueryBuilderAst) => asserts ast is QueryBuilderAst.SelectQuery = (ast) => {
281
296
  if (ast._tag !== 'SelectQuery') {
282
297
  return shouldNeverHappen(`Expected SelectQuery but got ${ast._tag}`)
283
298
  }
284
299
  }
285
300
 
286
- function assertInsertQueryBuilderAst(ast: QueryBuilderAst): asserts ast is QueryBuilderAst.InsertQuery {
301
+ const assertInsertQueryBuilderAst: (ast: QueryBuilderAst) => asserts ast is QueryBuilderAst.InsertQuery = (ast) => {
287
302
  if (ast._tag !== 'InsertQuery') {
288
303
  return shouldNeverHappen(`Expected InsertQuery but got ${ast._tag}`)
289
304
  }
290
305
  }
291
306
 
292
- function assertWriteQueryBuilderAst(ast: QueryBuilderAst): asserts ast is QueryBuilderAst.WriteQuery {
307
+ const assertWriteQueryBuilderAst: (ast: QueryBuilderAst) => asserts ast is QueryBuilderAst.WriteQuery = (ast) => {
293
308
  if (ast._tag !== 'InsertQuery' && ast._tag !== 'UpdateQuery' && ast._tag !== 'DeleteQuery') {
294
309
  return shouldNeverHappen(`Expected WriteQuery but got ${ast._tag}`)
295
310
  }
@@ -298,7 +313,7 @@ function assertWriteQueryBuilderAst(ast: QueryBuilderAst): asserts ast is QueryB
298
313
  const isRowQuery = (ast: QueryBuilderAst): ast is QueryBuilderAst.RowQuery => ast._tag === 'RowQuery'
299
314
 
300
315
  export const invalidQueryBuilder = (msg?: string) => {
301
- return shouldNeverHappen(`Invalid query builder${msg ? `: ${msg}` : ''}`)
316
+ return shouldNeverHappen(`Invalid query builder${msg !== undefined ? `: ${msg}` : ''}`)
302
317
  }
303
318
 
304
319
  export const getResultSchema = (qb: QueryBuilder<any, any, any>): Schema.Schema<any> => {
@@ -328,7 +343,7 @@ export const getResultSchema = (qb: QueryBuilder<any, any, any>): Schema.Schema<
328
343
  case 'UpdateQuery':
329
344
  case 'DeleteQuery': {
330
345
  // For write operations with RETURNING clause, we need to return the appropriate schema
331
- if (queryAst.returning && queryAst.returning.length > 0) {
346
+ if (queryAst.returning !== undefined && queryAst.returning.length > 0) {
332
347
  // Create a schema for the returned columns
333
348
  return queryAst.tableDef.rowSchema.pipe(Schema.pick(...queryAst.returning), Schema.Array)
334
349
  }
@@ -344,8 +359,7 @@ export const getResultSchema = (qb: QueryBuilder<any, any, any>): Schema.Schema<
344
359
  Schema.headOrElse(),
345
360
  )
346
361
  }
347
- default: {
348
- casesHandled(queryAst)
349
- }
362
+ default:
363
+ return casesHandled(queryAst)
350
364
  }
351
365
  }
@@ -13,7 +13,7 @@ export const getDefaultValuesEncoded = <TTableDef extends TableDefBase>(
13
13
  ReadonlyRecord.filter((col, key) => {
14
14
  if (fallbackValues?.[key] !== undefined) return true
15
15
  if (key === 'id') return false
16
- return col!.default._tag === 'None' || SqliteDsl.isSqlDefaultValue(col!.default.value) === false
16
+ return col!.default._tag === 'None' || !SqliteDsl.isSqlDefaultValue(col!.default.value)
17
17
  }),
18
18
  ReadonlyRecord.map((column, columnName) => {
19
19
  if (fallbackValues?.[columnName] !== undefined) return fallbackValues[columnName]
@@ -39,7 +39,7 @@ export const getDefaultValuesDecoded = <TTableDef extends TableDefBase>(
39
39
  ReadonlyRecord.filter((col, key) => {
40
40
  if (fallbackValues?.[key] !== undefined) return true
41
41
  if (key === 'id') return false
42
- return col!.default._tag === 'None' || SqliteDsl.isSqlDefaultValue(col!.default.value) === false
42
+ return col!.default._tag === 'None' || !SqliteDsl.isSqlDefaultValue(col!.default.value)
43
43
  }),
44
44
  ReadonlyRecord.map((column, columnName) => {
45
45
  if (fallbackValues?.[columnName] !== undefined) return fallbackValues[columnName]