@livestore/common 0.5.0-dev.0 → 0.5.0-dev.1

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 (302) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ClientSessionLeaderThreadProxy.d.ts.map +1 -1
  3. package/dist/MaterializationJournal.d.ts +2 -2
  4. package/dist/MaterializationJournal.d.ts.map +1 -1
  5. package/dist/MaterializationJournal.js +7 -7
  6. package/dist/StateHead.d.ts +1 -1
  7. package/dist/StateHead.d.ts.map +1 -1
  8. package/dist/StateHead.js +4 -4
  9. package/dist/WorkerTransportError.d.ts.map +1 -1
  10. package/dist/__tests__/fixture.d.ts +7 -7
  11. package/dist/__tests__/fixture.js +2 -2
  12. package/dist/adapter-types.d.ts +4 -0
  13. package/dist/adapter-types.d.ts.map +1 -1
  14. package/dist/adapter-types.js +4 -4
  15. package/dist/bounded-collections.d.ts.map +1 -1
  16. package/dist/bounded-collections.js.map +1 -1
  17. package/dist/debug-info.d.ts.map +1 -1
  18. package/dist/debug-info.js +9 -8
  19. package/dist/debug-info.js.map +1 -1
  20. package/dist/defs.d.ts +4 -4
  21. package/dist/defs.js +2 -2
  22. package/dist/devtools/devtools-compatibility.test.js +1 -1
  23. package/dist/devtools/devtools-messages-client-session.d.ts +4 -4
  24. package/dist/devtools/devtools-messages-client-session.js +4 -4
  25. package/dist/devtools/devtools-messages-common.d.ts.map +1 -1
  26. package/dist/devtools/devtools-messages-common.js +1 -1
  27. package/dist/devtools/devtools-messages-leader.d.ts +9 -9
  28. package/dist/devtools/devtools-messages-leader.js +4 -4
  29. package/dist/devtools/devtools-messages.js +3 -3
  30. package/dist/devtools/devtools-sessioninfo.d.ts.map +1 -1
  31. package/dist/devtools/mod.d.ts.map +1 -1
  32. package/dist/devtools/mod.js +3 -3
  33. package/dist/errors.d.ts +4 -4
  34. package/dist/errors.d.ts.map +1 -1
  35. package/dist/errors.js +1 -1
  36. package/dist/errors.js.map +1 -1
  37. package/dist/index.js +22 -22
  38. package/dist/leader-thread/LeaderSyncProcessor.d.ts +27 -6
  39. package/dist/leader-thread/LeaderSyncProcessor.d.ts.map +1 -1
  40. package/dist/leader-thread/LeaderSyncProcessor.js +65 -55
  41. package/dist/leader-thread/LeaderSyncProcessor.js.map +1 -1
  42. package/dist/leader-thread/RejectedPushError.d.ts +9 -9
  43. package/dist/leader-thread/RejectedPushError.d.ts.map +1 -1
  44. package/dist/leader-thread/RejectedPushError.js +1 -1
  45. package/dist/leader-thread/connection.d.ts.map +1 -1
  46. package/dist/leader-thread/connection.js +2 -2
  47. package/dist/leader-thread/eventlog.d.ts +11 -1
  48. package/dist/leader-thread/eventlog.d.ts.map +1 -1
  49. package/dist/leader-thread/eventlog.js +13 -13
  50. package/dist/leader-thread/eventlog.js.map +1 -1
  51. package/dist/leader-thread/leader-worker-devtools.d.ts +1 -1
  52. package/dist/leader-thread/leader-worker-devtools.d.ts.map +1 -1
  53. package/dist/leader-thread/leader-worker-devtools.js +4 -4
  54. package/dist/leader-thread/leader-worker-devtools.js.map +1 -1
  55. package/dist/leader-thread/make-leader-thread-layer.d.ts +6 -0
  56. package/dist/leader-thread/make-leader-thread-layer.d.ts.map +1 -1
  57. package/dist/leader-thread/make-leader-thread-layer.js +43 -29
  58. package/dist/leader-thread/make-leader-thread-layer.js.map +1 -1
  59. package/dist/leader-thread/make-leader-thread-layer.test.js +2 -2
  60. package/dist/leader-thread/materialize-event.d.ts.map +1 -1
  61. package/dist/leader-thread/materialize-event.js +9 -9
  62. package/dist/leader-thread/mod.js +9 -9
  63. package/dist/leader-thread/recreate-db.d.ts +5 -1
  64. package/dist/leader-thread/recreate-db.d.ts.map +1 -1
  65. package/dist/leader-thread/recreate-db.js +21 -25
  66. package/dist/leader-thread/recreate-db.js.map +1 -1
  67. package/dist/leader-thread/shutdown-channel.js +1 -1
  68. package/dist/leader-thread/stream-events.d.ts.map +1 -1
  69. package/dist/leader-thread/stream-events.js +3 -5
  70. package/dist/leader-thread/stream-events.js.map +1 -1
  71. package/dist/leader-thread/types.d.ts +6 -4
  72. package/dist/leader-thread/types.d.ts.map +1 -1
  73. package/dist/leader-thread/types.js +5 -1
  74. package/dist/leader-thread/types.js.map +1 -1
  75. package/dist/make-client-session.d.ts +1 -1
  76. package/dist/make-client-session.d.ts.map +1 -1
  77. package/dist/make-client-session.js +2 -2
  78. package/dist/materializer-helper.d.ts.map +1 -1
  79. package/dist/materializer-helper.js +4 -4
  80. package/dist/materializer-helper.js.map +1 -1
  81. package/dist/otel.d.ts.map +1 -1
  82. package/dist/rematerialize-from-eventlog.d.ts +3 -1
  83. package/dist/rematerialize-from-eventlog.d.ts.map +1 -1
  84. package/dist/rematerialize-from-eventlog.js +16 -9
  85. package/dist/rematerialize-from-eventlog.js.map +1 -1
  86. package/dist/schema/EventDef/define.d.ts +2 -2
  87. package/dist/schema/EventDef/define.d.ts.map +1 -1
  88. package/dist/schema/EventDef/define.test.js +1 -1
  89. package/dist/schema/EventDef/deprecated.d.ts +1 -1
  90. package/dist/schema/EventDef/deprecated.d.ts.map +1 -1
  91. package/dist/schema/EventDef/deprecated.test.js +2 -2
  92. package/dist/schema/EventDef/facts.d.ts.map +1 -1
  93. package/dist/schema/EventDef/materializer.d.ts +2 -2
  94. package/dist/schema/EventDef/materializer.d.ts.map +1 -1
  95. package/dist/schema/EventDef/mod.js +5 -5
  96. package/dist/schema/EventSequenceNumber/client.d.ts +3 -8
  97. package/dist/schema/EventSequenceNumber/client.d.ts.map +1 -1
  98. package/dist/schema/EventSequenceNumber/client.js +12 -15
  99. package/dist/schema/EventSequenceNumber/client.js.map +1 -1
  100. package/dist/schema/EventSequenceNumber/mod.js +2 -2
  101. package/dist/schema/EventSequenceNumber.test.js +1 -1
  102. package/dist/schema/LiveStoreEvent/client.d.ts +8 -8
  103. package/dist/schema/LiveStoreEvent/client.d.ts.map +1 -1
  104. package/dist/schema/LiveStoreEvent/client.js +1 -1
  105. package/dist/schema/LiveStoreEvent/client.js.map +1 -1
  106. package/dist/schema/LiveStoreEvent/client.test.js +2 -2
  107. package/dist/schema/LiveStoreEvent/global.d.ts.map +1 -1
  108. package/dist/schema/LiveStoreEvent/global.js +1 -1
  109. package/dist/schema/LiveStoreEvent/input.d.ts +1 -1
  110. package/dist/schema/LiveStoreEvent/input.d.ts.map +1 -1
  111. package/dist/schema/LiveStoreEvent/mod.js +4 -4
  112. package/dist/schema/events.js +1 -1
  113. package/dist/schema/mod.js +11 -11
  114. package/dist/schema/schema.d.ts +1 -1
  115. package/dist/schema/schema.d.ts.map +1 -1
  116. package/dist/schema/schema.js +3 -3
  117. package/dist/schema/state/mod.js +2 -2
  118. package/dist/schema/state/sqlite/client-document-def.d.ts +1 -1
  119. package/dist/schema/state/sqlite/client-document-def.d.ts.map +1 -1
  120. package/dist/schema/state/sqlite/client-document-def.js +8 -8
  121. package/dist/schema/state/sqlite/client-document-def.js.map +1 -1
  122. package/dist/schema/state/sqlite/client-document-def.test.js +24 -10
  123. package/dist/schema/state/sqlite/client-document-def.test.js.map +1 -1
  124. package/dist/schema/state/sqlite/column-annotations.d.ts.map +1 -1
  125. package/dist/schema/state/sqlite/column-annotations.test.js +1 -1
  126. package/dist/schema/state/sqlite/column-def.d.ts.map +1 -1
  127. package/dist/schema/state/sqlite/column-def.js +17 -3
  128. package/dist/schema/state/sqlite/column-def.js.map +1 -1
  129. package/dist/schema/state/sqlite/column-def.test.js +60 -5
  130. package/dist/schema/state/sqlite/column-def.test.js.map +1 -1
  131. package/dist/schema/state/sqlite/column-spec.d.ts.map +1 -1
  132. package/dist/schema/state/sqlite/column-spec.js +1 -1
  133. package/dist/schema/state/sqlite/column-spec.test.js +2 -2
  134. package/dist/schema/state/sqlite/column-spec.test.js.map +1 -1
  135. package/dist/schema/state/sqlite/db-schema/ast/fingerprint-digest.d.ts +3 -0
  136. package/dist/schema/state/sqlite/db-schema/ast/fingerprint-digest.d.ts.map +1 -0
  137. package/dist/schema/state/sqlite/db-schema/ast/fingerprint-digest.js +5 -0
  138. package/dist/schema/state/sqlite/db-schema/ast/fingerprint-digest.js.map +1 -0
  139. package/dist/schema/state/sqlite/db-schema/ast/fingerprint.d.ts +5 -0
  140. package/dist/schema/state/sqlite/db-schema/ast/fingerprint.d.ts.map +1 -0
  141. package/dist/schema/state/sqlite/db-schema/ast/fingerprint.js +183 -0
  142. package/dist/schema/state/sqlite/db-schema/ast/fingerprint.js.map +1 -0
  143. package/dist/schema/state/sqlite/db-schema/ast/fingerprint.test.d.ts +2 -0
  144. package/dist/schema/state/sqlite/db-schema/ast/fingerprint.test.d.ts.map +1 -0
  145. package/dist/schema/state/sqlite/db-schema/ast/fingerprint.test.js +142 -0
  146. package/dist/schema/state/sqlite/db-schema/ast/fingerprint.test.js.map +1 -0
  147. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts +1 -6
  148. package/dist/schema/state/sqlite/db-schema/ast/sqlite.d.ts.map +1 -1
  149. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js +1 -82
  150. package/dist/schema/state/sqlite/db-schema/ast/sqlite.js.map +1 -1
  151. package/dist/schema/state/sqlite/db-schema/ast/validate.d.ts.map +1 -1
  152. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.d.ts.map +1 -1
  153. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js +2 -1
  154. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.js.map +1 -1
  155. package/dist/schema/state/sqlite/db-schema/dsl/field-defs.test.js +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/has-json-string-encoding.d.ts +3 -0
  159. package/dist/schema/state/sqlite/db-schema/has-json-string-encoding.d.ts.map +1 -0
  160. package/dist/schema/state/sqlite/db-schema/has-json-string-encoding.js +6 -0
  161. package/dist/schema/state/sqlite/db-schema/has-json-string-encoding.js.map +1 -0
  162. package/dist/schema/state/sqlite/db-schema/mod.js +2 -2
  163. package/dist/schema/state/sqlite/mod.d.ts.map +1 -1
  164. package/dist/schema/state/sqlite/mod.js +10 -9
  165. package/dist/schema/state/sqlite/mod.js.map +1 -1
  166. package/dist/schema/state/sqlite/persistence.d.ts.map +1 -1
  167. package/dist/schema/state/sqlite/query-builder/api.d.ts.map +1 -1
  168. package/dist/schema/state/sqlite/query-builder/astToSql.d.ts.map +1 -1
  169. package/dist/schema/state/sqlite/query-builder/astToSql.js +2 -7
  170. package/dist/schema/state/sqlite/query-builder/astToSql.js.map +1 -1
  171. package/dist/schema/state/sqlite/query-builder/impl.d.ts +1 -3
  172. package/dist/schema/state/sqlite/query-builder/impl.d.ts.map +1 -1
  173. package/dist/schema/state/sqlite/query-builder/impl.js +2 -2
  174. package/dist/schema/state/sqlite/query-builder/impl.js.map +1 -1
  175. package/dist/schema/state/sqlite/query-builder/impl.test.js +2 -2
  176. package/dist/schema/state/sqlite/query-builder/mod.js +2 -2
  177. package/dist/schema/state/sqlite/schema-helpers.d.ts.map +1 -1
  178. package/dist/schema/state/sqlite/schema-helpers.js +1 -1
  179. package/dist/schema/state/sqlite/schema-helpers.test.js +2 -2
  180. package/dist/schema/state/sqlite/system-tables/eventlog-tables.js +3 -3
  181. package/dist/schema/state/sqlite/system-tables/mod.js +2 -2
  182. package/dist/schema/state/sqlite/system-tables/state-tables.d.ts +60 -17
  183. package/dist/schema/state/sqlite/system-tables/state-tables.d.ts.map +1 -1
  184. package/dist/schema/state/sqlite/system-tables/state-tables.js +14 -5
  185. package/dist/schema/state/sqlite/system-tables/state-tables.js.map +1 -1
  186. package/dist/schema/state/sqlite/table-def.d.ts +1 -1
  187. package/dist/schema/state/sqlite/table-def.d.ts.map +1 -1
  188. package/dist/schema/state/sqlite/table-def.js +24 -8
  189. package/dist/schema/state/sqlite/table-def.js.map +1 -1
  190. package/dist/schema/state/sqlite/table-def.test.js +54 -1
  191. package/dist/schema/state/sqlite/table-def.test.js.map +1 -1
  192. package/dist/schema/unknown-events.d.ts.map +1 -1
  193. package/dist/schema/unknown-events.js +1 -1
  194. package/dist/schema/unknown-events.js.map +1 -1
  195. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js +26 -4
  196. package/dist/schema-management/__tests__/migrations-autoincrement-quoting.test.js.map +1 -1
  197. package/dist/schema-management/common.d.ts.map +1 -1
  198. package/dist/schema-management/common.js +2 -2
  199. package/dist/schema-management/migrations.d.ts +4 -4
  200. package/dist/schema-management/migrations.d.ts.map +1 -1
  201. package/dist/schema-management/migrations.js +9 -9
  202. package/dist/schema-management/migrations.js.map +1 -1
  203. package/dist/schema-management/validate-schema.d.ts.map +1 -1
  204. package/dist/schema-management/validate-schema.js +1 -1
  205. package/dist/session-id-symbol.d.ts.map +1 -1
  206. package/dist/sql-queries/index.js +3 -3
  207. package/dist/sql-queries/sql-queries.d.ts.map +1 -1
  208. package/dist/sql-queries/sql-queries.js +3 -3
  209. package/dist/sql-queries/sql-queries.js.map +1 -1
  210. package/dist/sql-queries/sql-query-builder.d.ts.map +1 -1
  211. package/dist/sql-queries/sql-query-builder.js +1 -1
  212. package/dist/sql-queries/types.d.ts.map +1 -1
  213. package/dist/sqlite-db-helper.d.ts +2 -2
  214. package/dist/sqlite-db-helper.d.ts.map +1 -1
  215. package/dist/sqlite-db-helper.js +2 -2
  216. package/dist/sync/ClientSessionSyncProcessor.d.ts +3 -3
  217. package/dist/sync/ClientSessionSyncProcessor.d.ts.map +1 -1
  218. package/dist/sync/ClientSessionSyncProcessor.js +7 -7
  219. package/dist/sync/errors.js +1 -1
  220. package/dist/sync/index.js +6 -6
  221. package/dist/sync/mock-sync-backend.d.ts.map +1 -1
  222. package/dist/sync/mock-sync-backend.js +4 -4
  223. package/dist/sync/mock-sync-backend.test.js +2 -2
  224. package/dist/sync/next/compact-events.d.ts.map +1 -1
  225. package/dist/sync/next/compact-events.js +3 -3
  226. package/dist/sync/next/facts.d.ts.map +1 -1
  227. package/dist/sync/next/facts.js +1 -1
  228. package/dist/sync/next/history-dag-common.d.ts +1 -1
  229. package/dist/sync/next/history-dag-common.d.ts.map +1 -1
  230. package/dist/sync/next/history-dag-common.js +1 -1
  231. package/dist/sync/next/history-dag.d.ts.map +1 -1
  232. package/dist/sync/next/history-dag.js +3 -3
  233. package/dist/sync/next/mod.js +5 -5
  234. package/dist/sync/next/rebase-events.d.ts.map +1 -1
  235. package/dist/sync/next/rebase-events.js +2 -2
  236. package/dist/sync/next/test/compact-events.calculator.test.js +4 -4
  237. package/dist/sync/next/test/compact-events.test.js +4 -4
  238. package/dist/sync/next/test/event-fixtures.d.ts +1 -1
  239. package/dist/sync/next/test/event-fixtures.d.ts.map +1 -1
  240. package/dist/sync/next/test/event-fixtures.js +5 -5
  241. package/dist/sync/next/test/history-dag-common.test.js +2 -2
  242. package/dist/sync/next/test/mod.js +1 -1
  243. package/dist/sync/sync-backend-kv.js +1 -1
  244. package/dist/sync/sync-backend.d.ts.map +1 -1
  245. package/dist/sync/sync-backend.js +1 -1
  246. package/dist/sync/sync.js +2 -2
  247. package/dist/sync/syncstate.d.ts +3 -3
  248. package/dist/sync/syncstate.d.ts.map +1 -1
  249. package/dist/sync/syncstate.js +2 -2
  250. package/dist/sync/syncstate.js.map +1 -1
  251. package/dist/sync/syncstate.test.js +3 -3
  252. package/dist/sync/transport-chunking.d.ts.map +1 -1
  253. package/dist/sync/validate-push-payload.d.ts.map +1 -1
  254. package/dist/sync/validate-push-payload.js +2 -2
  255. package/dist/testing/event-factory.d.ts.map +1 -1
  256. package/dist/testing/event-factory.js +2 -2
  257. package/dist/testing/event-factory.js.map +1 -1
  258. package/dist/testing/mod.js +1 -1
  259. package/dist/util.d.ts.map +1 -1
  260. package/dist/util.js +1 -0
  261. package/dist/util.js.map +1 -1
  262. package/dist/version.d.ts.map +1 -1
  263. package/dist/version.js.map +1 -1
  264. package/package.json +40 -39
  265. package/src/adapter-types.ts +4 -0
  266. package/src/debug-info.ts +11 -8
  267. package/src/defs.ts +2 -2
  268. package/src/leader-thread/LeaderSyncProcessor.ts +121 -97
  269. package/src/leader-thread/eventlog.ts +13 -5
  270. package/src/leader-thread/leader-worker-devtools.ts +1 -1
  271. package/src/leader-thread/make-leader-thread-layer.ts +49 -23
  272. package/src/leader-thread/recreate-db.ts +26 -25
  273. package/src/leader-thread/stream-events.ts +1 -3
  274. package/src/leader-thread/types.ts +5 -0
  275. package/src/materializer-helper.ts +1 -1
  276. package/src/rematerialize-from-eventlog.ts +28 -13
  277. package/src/schema/EventSequenceNumber/client.ts +12 -15
  278. package/src/schema/schema.ts +1 -1
  279. package/src/schema/state/sqlite/client-document-def.test.ts +23 -7
  280. package/src/schema/state/sqlite/client-document-def.ts +2 -2
  281. package/src/schema/state/sqlite/column-def.test.ts +84 -3
  282. package/src/schema/state/sqlite/column-def.ts +16 -1
  283. package/src/schema/state/sqlite/db-schema/ast/fingerprint-digest.ts +6 -0
  284. package/src/schema/state/sqlite/db-schema/ast/fingerprint.test.ts +209 -0
  285. package/src/schema/state/sqlite/db-schema/ast/fingerprint.ts +225 -0
  286. package/src/schema/state/sqlite/db-schema/ast/sqlite.ts +1 -90
  287. package/src/schema/state/sqlite/db-schema/dsl/field-defs.ts +1 -0
  288. package/src/schema/state/sqlite/db-schema/has-json-string-encoding.ts +9 -0
  289. package/src/schema/state/sqlite/mod.ts +2 -1
  290. package/src/schema/state/sqlite/query-builder/astToSql.ts +1 -8
  291. package/src/schema/state/sqlite/system-tables/state-tables.ts +13 -2
  292. package/src/schema/state/sqlite/table-def.test.ts +66 -0
  293. package/src/schema/state/sqlite/table-def.ts +21 -5
  294. package/src/schema-management/__tests__/migrations-autoincrement-quoting.test.ts +33 -2
  295. package/src/schema-management/migrations.ts +7 -6
  296. package/src/sql-queries/sql-queries.ts +1 -1
  297. package/src/util.ts +1 -0
  298. package/dist/schema/state/sqlite/db-schema/hash.d.ts +0 -2
  299. package/dist/schema/state/sqlite/db-schema/hash.d.ts.map +0 -1
  300. package/dist/schema/state/sqlite/db-schema/hash.js +0 -15
  301. package/dist/schema/state/sqlite/db-schema/hash.js.map +0 -1
  302. package/src/schema/state/sqlite/db-schema/hash.ts +0 -13
@@ -182,6 +182,54 @@ describe('table function overloads', () => {
182
182
  expect(userTable.sqliteDef.columns.email.nullable).toBe(true)
183
183
  })
184
184
 
185
+ it('keeps a date codec so the column round-trips through its encoded form', () => {
186
+ const StampSchema = Schema.Struct({
187
+ id: Schema.String.pipe(State.SQLite.withPrimaryKey),
188
+ createdAt: Schema.DateFromString,
189
+ seenAt: Schema.DateFromMillis,
190
+ bornAt: Schema.Date,
191
+ deletedAt: Schema.NullOr(Schema.DateFromString),
192
+ archivedAt: Schema.optional(Schema.DateFromString),
193
+ })
194
+
195
+ const { columns } = State.SQLite.table({ name: 'stamps', schema: StampSchema }).sqliteDef
196
+ const date = new Date('2026-01-02T03:04:05.678Z')
197
+ const roundTrip = (column: { schema: Schema.Codec<any, any> }, value: unknown) =>
198
+ Schema.decodeUnknownSync(column.schema)(Schema.encodeUnknownSync(column.schema)(value))
199
+
200
+ expect(columns.createdAt.columnType).toBe('text')
201
+ expect(Schema.encodeUnknownSync(columns.createdAt.schema)(date)).toBe(date.toISOString())
202
+ expect(roundTrip(columns.createdAt, date)).toEqual(date)
203
+
204
+ expect(columns.seenAt.columnType).toBe('integer')
205
+ expect(Schema.encodeUnknownSync(columns.seenAt.schema)(date)).toBe(date.getTime())
206
+ expect(roundTrip(columns.seenAt, date)).toEqual(date)
207
+
208
+ expect(columns.bornAt.columnType).toBe('text')
209
+ expect(Schema.encodeUnknownSync(columns.bornAt.schema)(date)).toBe(date.toISOString())
210
+ expect(roundTrip(columns.bornAt, date)).toEqual(date)
211
+
212
+ expect(columns.deletedAt.nullable).toBe(true)
213
+ expect(roundTrip(columns.deletedAt, date)).toEqual(date)
214
+ expect(roundTrip(columns.deletedAt, null)).toBeNull()
215
+
216
+ expect(columns.archivedAt.nullable).toBe(true)
217
+ expect(roundTrip(columns.archivedAt, date)).toEqual(date)
218
+ })
219
+
220
+ it('stores a codec field in its encoded form, like getColumnDefForSchema does', () => {
221
+ const CounterSchema = Schema.Struct({
222
+ id: Schema.String,
223
+ count: Schema.FiniteFromString,
224
+ })
225
+
226
+ const { columns } = State.SQLite.table({ name: 'counters', schema: CounterSchema }).sqliteDef
227
+
228
+ expect(columns.count.columnType).toBe('text')
229
+ expect(Schema.encodeUnknownSync(columns.count.schema)(42)).toBe('42')
230
+ expect(Schema.decodeSync(columns.count.schema)('42')).toBe(42)
231
+ })
232
+
185
233
  it('should handle Schema.Int as integer column', () => {
186
234
  const CounterSchema = Schema.Struct({
187
235
  id: Schema.String,
@@ -223,6 +271,24 @@ describe('table function overloads', () => {
223
271
  expect(userTable.sqliteDef.columns.age.columnType).toBe('integer')
224
272
  })
225
273
 
274
+ it('preserves Date field codecs in Schema.Class tables', () => {
275
+ class Stamp extends Schema.Class<Stamp>('Stamp')({
276
+ id: Schema.String,
277
+ createdAt: Schema.DateFromString,
278
+ seenAt: Schema.DateFromMillis,
279
+ }) {}
280
+
281
+ const { columns } = State.SQLite.table({ name: 'stamps', schema: Stamp }).sqliteDef
282
+ const date = new Date('2026-01-02T03:04:05.678Z')
283
+
284
+ expect(columns.createdAt.columnType).toBe('text')
285
+ expect(Schema.decodeSync(columns.createdAt.schema)(date.toISOString())).toEqual(date)
286
+ expect(Schema.encodeUnknownSync(columns.createdAt.schema)(date)).toBe(date.toISOString())
287
+ expect(columns.seenAt.columnType).toBe('integer')
288
+ expect(Schema.decodeSync(columns.seenAt.schema)(date.getTime())).toEqual(date)
289
+ expect(Schema.encodeUnknownSync(columns.seenAt.schema)(date)).toBe(date.getTime())
290
+ })
291
+
226
292
  it('should support schemas that transform flat columns into nested types', () => {
227
293
  const Flat = Schema.Struct({
228
294
  id: Schema.String.pipe(State.SQLite.withPrimaryKey),
@@ -265,26 +265,42 @@ export function table<
265
265
  return tableDef as any
266
266
  }
267
267
 
268
+ /**
269
+ * The encoded side of the schema decides which columns exist (a transformation may flatten a nested
270
+ * type into columns). Where the schema itself declares a property of the same name, that property's
271
+ * own AST becomes the column schema: it still carries the field's encoding, so a codec such as
272
+ * `Schema.DateFromString` or `Schema.DateFromMillis` stores its encoded form and decodes back to
273
+ * its type. Substituting the type-side AST instead would strip that encoding and leave a `Date`
274
+ * with nothing SQLite can store natively, which is how date columns ended up JSON-encoded and
275
+ * unreadable.
276
+ */
268
277
  const getSqlitePropertySignatures = (schema: Schema.Top): ReadonlyArray<SchemaAST.PropertySignature> => {
269
278
  const encodedPropertySignatures = getPropertySignatures(SchemaAST.toEncoded(schema.ast))
270
- const typePropertySignatures = getPropertySignatures(SchemaAST.toType(schema.ast))
279
+ const ownPropertySignatures = getPropertySignatures(schema.ast)
271
280
 
272
281
  return encodedPropertySignatures.map((encodedPropertySignature) => {
273
- const typePropertySignature = typePropertySignatures.find(
282
+ const ownPropertySignature = ownPropertySignatures.find(
274
283
  (propertySignature) => propertySignature.name === encodedPropertySignature.name,
275
284
  )
276
285
 
277
- if (typePropertySignature === undefined || hasLiveStoreSqliteAnnotation(encodedPropertySignature.type) === true) {
286
+ if (ownPropertySignature === undefined || hasLiveStoreSqliteAnnotation(encodedPropertySignature.type) === true) {
278
287
  return encodedPropertySignature
279
288
  }
280
289
 
281
- return new SchemaAST.PropertySignature(encodedPropertySignature.name, typePropertySignature.type)
290
+ return ownPropertySignature
282
291
  })
283
292
  }
284
293
 
285
294
  const getPropertySignatures = (ast: SchemaAST.AST): ReadonlyArray<SchemaAST.PropertySignature> => {
286
295
  if (SchemaAST.isObjects(ast) === true) return ast.propertySignatures
287
296
 
297
+ // Class declarations encode through their fields object. Inspect that first
298
+ // link before toEncoded strips the individual field codecs.
299
+ if (SchemaAST.isDeclaration(ast) === true) {
300
+ const fieldsAst = ast.encoding?.[0]?.to
301
+ if (fieldsAst !== undefined) return getPropertySignatures(fieldsAst)
302
+ }
303
+
288
304
  if (SchemaAST.isUnion(ast) === true) {
289
305
  const members = ast.types.map(getPropertySignatures)
290
306
  const [head, ...tail] = members
@@ -314,7 +330,7 @@ const getPropertySignatures = (ast: SchemaAST.AST): ReadonlyArray<SchemaAST.Prop
314
330
  const keyContext = hasOptionalMember === true ? new SchemaAST.Context(true, false) : undefined
315
331
  const union = new SchemaAST.Union(
316
332
  propertySignatures.map((memberPropertySignature) => memberPropertySignature.type),
317
- ast.mode,
333
+ ast.options,
318
334
  undefined,
319
335
  undefined,
320
336
  undefined,
@@ -2,13 +2,16 @@ import { describe, expect, it } from 'vitest'
2
2
 
3
3
  import { Effect, Option, Schema } from '@livestore/utils/effect'
4
4
 
5
+ import { fingerprint } from '../../schema/state/sqlite/db-schema/ast/fingerprint.ts'
5
6
  import { SqliteAst } from '../../schema/state/sqlite/db-schema/mod.ts'
7
+ import { schemaMetaTable } from '../../schema/state/sqlite/system-tables/state-tables.ts'
6
8
  import type { PreparedStatement, SqliteDb } from '../../sqlite-types.ts'
7
9
  import type { PreparedBindValues } from '../../util.ts'
8
10
  import { migrateTable } from '../migrations.ts'
9
11
 
10
12
  const makeStubDb = () => {
11
13
  const executed: string[] = []
14
+ const bindings: Array<PreparedBindValues | undefined> = []
12
15
 
13
16
  const db: SqliteDb = {
14
17
  _tag: 'SqliteDb',
@@ -16,8 +19,9 @@ const makeStubDb = () => {
16
19
  debug: { head: 0 as any },
17
20
  prepare: (queryStr: string): PreparedStatement => ({
18
21
  sql: queryStr,
19
- execute: (_bind: PreparedBindValues | undefined) => {
22
+ execute: (bind: PreparedBindValues | undefined) => {
20
23
  executed.push(queryStr)
24
+ bindings.push(bind)
21
25
  },
22
26
  select: <T>(_bind: PreparedBindValues | undefined) => [] as unknown as ReadonlyArray<T>,
23
27
  finalize: () => {},
@@ -32,7 +36,7 @@ const makeStubDb = () => {
32
36
  makeChangeset: () => ({ invert: () => ({ invert: () => ({}) as any, apply: () => {} }) as any, apply: () => {} }),
33
37
  }
34
38
 
35
- return { db, executed }
39
+ return { db, executed, bindings }
36
40
  }
37
41
 
38
42
  describe('migrateTable - quoting and autoincrement', () => {
@@ -85,4 +89,31 @@ describe('migrateTable - quoting and autoincrement', () => {
85
89
  expect(createStmt!).not.toContain("PRIMARY KEY ('id')")
86
90
  expect(createStmt!).not.toMatch(/'todos'|'id'|'text'/)
87
91
  })
92
+
93
+ it('persists full textual table fingerprints', () => {
94
+ const { db, bindings } = makeStubDb()
95
+ const table = SqliteAst.table(
96
+ 'todos',
97
+ [
98
+ SqliteAst.column({
99
+ name: 'id',
100
+ type: { _tag: 'text' },
101
+ nullable: false,
102
+ primaryKey: true,
103
+ autoIncrement: false,
104
+ default: Option.none(),
105
+ schema: Schema.String,
106
+ }),
107
+ ],
108
+ [],
109
+ )
110
+
111
+ migrateTable({ db, tableAst: table, behaviour: 'create-if-not-exists' }).pipe(Effect.runSync)
112
+
113
+ const schemaMetaBinding = bindings.find(
114
+ (binding) => binding !== undefined && Array.isArray(binding) === false && '$schemaHash' in binding,
115
+ )
116
+ expect(schemaMetaBinding).toMatchObject({ $schemaHash: fingerprint(table) })
117
+ expect(schemaMetaTable.sqliteDef.ast.columns.find((column) => column.name === 'schemaHash')?.type._tag).toBe('text')
118
+ })
88
119
  })
@@ -8,7 +8,7 @@
8
8
  * - EVENTLOG TABLES (NEVER modify): Changes cause data loss - need manual versioning!
9
9
  *
10
10
  * How it works:
11
- * 1. Each table's schema is hashed using SqliteAst.hash()
11
+ * 1. Each table's schema is fingerprinted
12
12
  * 2. Hashes are stored in SCHEMA_META_TABLE after successful migrations
13
13
  * 3. On app start, current schema hashes are compared with stored hashes
14
14
  * 4. Mismatches trigger migrations:
@@ -37,7 +37,8 @@ import type { MigrationsReport, MigrationsReportEntry } from '../defs.ts'
37
37
  import type { UnknownError } from '../errors.ts'
38
38
  import type { LiveStoreSchema } from '../schema/mod.ts'
39
39
  import { makeColumnSpec } from '../schema/state/sqlite/column-spec.ts'
40
- import { SqliteAst } from '../schema/state/sqlite/db-schema/mod.ts'
40
+ import { fingerprint } from '../schema/state/sqlite/db-schema/ast/fingerprint.ts'
41
+ import type { SqliteAst } from '../schema/state/sqlite/db-schema/mod.ts'
41
42
  import type { SchemaEventDefsMetaRow, SchemaMetaRow } from '../schema/state/sqlite/system-tables/state-tables.ts'
42
43
  import {
43
44
  isStateSystemTable,
@@ -114,14 +115,14 @@ export const migrateDb = ({
114
115
  ...Array.from(schema.state.sqlite.tables.values()).filter((_) => !isStateSystemTable(_.sqliteDef.name)),
115
116
  ]
116
117
 
117
- const tablesToMigrate = new Set<{ tableAst: SqliteAst.Table; schemaHash: number }>()
118
+ const tablesToMigrate = new Set<{ tableAst: SqliteAst.Table; schemaHash: string }>()
118
119
 
119
120
  const migrationsReportEntries: MigrationsReportEntry[] = []
120
121
  for (const tableDef of tableDefs) {
121
122
  const tableAst = tableDef.sqliteDef.ast
122
123
  const tableName = tableAst.name
123
124
  const dbSchemaHash = dbSchemaHashByTable[tableName]
124
- const schemaHash = SqliteAst.hash(tableAst)
125
+ const schemaHash = fingerprint(tableAst)
125
126
 
126
127
  if (schemaHash !== dbSchemaHash) {
127
128
  tablesToMigrate.add({ tableAst, schemaHash })
@@ -151,13 +152,13 @@ export const migrateDb = ({
151
152
  export const migrateTable = ({
152
153
  db,
153
154
  tableAst,
154
- schemaHash = SqliteAst.hash(tableAst),
155
+ schemaHash = fingerprint(tableAst),
155
156
  behaviour,
156
157
  skipMetaTable = false,
157
158
  }: {
158
159
  db: SqliteDb
159
160
  tableAst: SqliteAst.Table
160
- schemaHash?: number
161
+ schemaHash?: string
161
162
  behaviour: 'drop-and-recreate' | 'create-if-not-exists'
162
163
  skipMetaTable?: boolean
163
164
  }) =>
@@ -298,7 +298,7 @@ Error: ${parseErrorStr}
298
298
  Value:`,
299
299
  value,
300
300
  )
301
- // oxlint-disable-next-line eslint(no-debugger) -- intentional breakpoint for SQL decode errors
301
+ // oxlint-disable-next-line no-debugger -- intentional breakpoint for SQL decode errors
302
302
  debugger
303
303
  throw res.failure
304
304
  } else {
package/src/util.ts CHANGED
@@ -19,6 +19,7 @@ export const SqliteReal = Schema.Number
19
19
 
20
20
  export const SqlValueSchema = Schema.Union([
21
21
  Schema.String,
22
+ // @effect-diagnostics-next-line schemaNumber:off -- SQLite REAL permits Infinity/NaN; SqliteReal is the documented domain carve-out
22
23
  SqliteReal,
23
24
  Schema.Uint8Array as any as Schema.Codec<Uint8Array<ArrayBuffer>>,
24
25
  Schema.Null,
@@ -1,2 +0,0 @@
1
- export declare const hashCode: (str: string) => number;
2
- //# sourceMappingURL=hash.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../../../../src/schema/state/sqlite/db-schema/hash.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,QAAQ,GAAI,KAAK,MAAM,WAWnC,CAAA"}
@@ -1,15 +0,0 @@
1
- // Based on https://stackoverflow.com/a/7616484
2
- export const hashCode = (str) => {
3
- let hash = 0;
4
- let i;
5
- let chr;
6
- if (str.length === 0)
7
- return hash;
8
- for (i = 0; i < str.length; i++) {
9
- chr = str.charCodeAt(i);
10
- hash = (hash << 5) - hash + chr;
11
- hash = Math.trunc(hash); // Convert to 32bit integer
12
- }
13
- return hash;
14
- };
15
- //# sourceMappingURL=hash.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"hash.js","sourceRoot":"","sources":["../../../../../src/schema/state/sqlite/db-schema/hash.ts"],"names":[],"mappings":"AAAA,+CAA+C;AAC/C,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,EAAE;IACtC,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,IAAI,CAAS,CAAA;IACb,IAAI,GAAW,CAAA;IACf,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IACjC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,GAAG,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAA;QACvB,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,GAAG,CAAA;QAC/B,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA,CAAC,2BAA2B;IACrD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC,CAAA"}
@@ -1,13 +0,0 @@
1
- // Based on https://stackoverflow.com/a/7616484
2
- export const hashCode = (str: string) => {
3
- let hash = 0
4
- let i: number
5
- let chr: number
6
- if (str.length === 0) return hash
7
- for (i = 0; i < str.length; i++) {
8
- chr = str.charCodeAt(i)
9
- hash = (hash << 5) - hash + chr
10
- hash = Math.trunc(hash) // Convert to 32bit integer
11
- }
12
- return hash
13
- }