@rocicorp/zero 1.12.0-canary.3 → 1.12.0-canary.5

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 (819) hide show
  1. package/out/_virtual/{_@oxc-project_runtime@0.130.0/helpers → _@oxc-project_runtime@0.154.0/helpers/esm}/usingCtx.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.d.ts.map +1 -1
  3. package/out/analyze-query/src/analyze-cli.js +6 -4
  4. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  5. package/out/analyze-query/src/bin-transform.js +4 -3
  6. package/out/analyze-query/src/bin-transform.js.map +1 -1
  7. package/out/analyze-query/src/zql-chain.d.ts +23 -0
  8. package/out/analyze-query/src/zql-chain.d.ts.map +1 -0
  9. package/out/analyze-query/src/zql-chain.js +162 -0
  10. package/out/analyze-query/src/zql-chain.js.map +1 -0
  11. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  12. package/out/ast-to-zql/src/bin.js +4 -3
  13. package/out/ast-to-zql/src/bin.js.map +1 -1
  14. package/out/ast-to-zql/src/format.js.map +1 -1
  15. package/out/datadog/src/datadog-log-sink.js +5 -4
  16. package/out/datadog/src/datadog-log-sink.js.map +1 -1
  17. package/out/otel/src/log-options.js.map +1 -1
  18. package/out/replicache/src/btree/node.js +13 -11
  19. package/out/replicache/src/btree/node.js.map +1 -1
  20. package/out/replicache/src/btree/read.js +4 -2
  21. package/out/replicache/src/btree/read.js.map +1 -1
  22. package/out/replicache/src/btree/splice.js.map +1 -1
  23. package/out/replicache/src/btree/write.js +9 -5
  24. package/out/replicache/src/btree/write.js.map +1 -1
  25. package/out/replicache/src/config.js +1 -2
  26. package/out/replicache/src/connection-loop.js.map +1 -1
  27. package/out/replicache/src/dag/chunk.js +1 -1
  28. package/out/replicache/src/dag/gc.js.map +1 -1
  29. package/out/replicache/src/dag/lazy-store.js +8 -7
  30. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  31. package/out/replicache/src/dag/store-impl.js +1 -1
  32. package/out/replicache/src/dag/store-impl.js.map +1 -1
  33. package/out/replicache/src/dag/visitor.js.map +1 -1
  34. package/out/replicache/src/db/commit.js +5 -3
  35. package/out/replicache/src/db/commit.js.map +1 -1
  36. package/out/replicache/src/db/read.js +1 -1
  37. package/out/replicache/src/db/read.js.map +1 -1
  38. package/out/replicache/src/db/rebase.js.map +1 -1
  39. package/out/replicache/src/db/write.js +5 -2
  40. package/out/replicache/src/db/write.js.map +1 -1
  41. package/out/replicache/src/deleted-clients.js +3 -2
  42. package/out/replicache/src/deleted-clients.js.map +1 -1
  43. package/out/replicache/src/expo-sqlite.js +1 -2
  44. package/out/replicache/src/frozen-json.js +1 -1
  45. package/out/replicache/src/frozen-json.js.map +1 -1
  46. package/out/replicache/src/get-default-puller.js +1 -1
  47. package/out/replicache/src/hash.js +1 -1
  48. package/out/replicache/src/hash.js.map +1 -1
  49. package/out/replicache/src/impl.js +1 -2
  50. package/out/replicache/src/index-defs.js +5 -3
  51. package/out/replicache/src/index-defs.js.map +1 -1
  52. package/out/replicache/src/kv/expo-sqlite/store.js +1 -1
  53. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  54. package/out/replicache/src/kv/idb-store.js +1 -1
  55. package/out/replicache/src/kv/idb-store.js.map +1 -1
  56. package/out/replicache/src/kv/mem-fallback-store.js +1 -1
  57. package/out/replicache/src/kv/mem-fallback-store.js.map +1 -1
  58. package/out/replicache/src/kv/mem-store.js.map +1 -1
  59. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  60. package/out/replicache/src/kv/op-sqlite/types.js.map +1 -1
  61. package/out/replicache/src/kv/read-impl.js.map +1 -1
  62. package/out/replicache/src/kv/sqlite-store.js +1 -1
  63. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  64. package/out/replicache/src/kv/write-impl-base.js +4 -1
  65. package/out/replicache/src/kv/write-impl-base.js.map +1 -1
  66. package/out/replicache/src/kv/write-impl.js.map +1 -1
  67. package/out/replicache/src/log-options.js +2 -1
  68. package/out/replicache/src/log-options.js.map +1 -1
  69. package/out/replicache/src/make-idb-name.js +1 -1
  70. package/out/replicache/src/new-client-channel.js +9 -7
  71. package/out/replicache/src/new-client-channel.js.map +1 -1
  72. package/out/replicache/src/on-persist-channel.js +1 -1
  73. package/out/replicache/src/on-persist-channel.js.map +1 -1
  74. package/out/replicache/src/op-sqlite.js +1 -2
  75. package/out/replicache/src/pending-mutations.js +2 -1
  76. package/out/replicache/src/pending-mutations.js.map +1 -1
  77. package/out/replicache/src/persist/client-gc.js +2 -2
  78. package/out/replicache/src/persist/client-gc.js.map +1 -1
  79. package/out/replicache/src/persist/client-group-gc.js +2 -2
  80. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  81. package/out/replicache/src/persist/client-groups.js +1 -1
  82. package/out/replicache/src/persist/client-groups.js.map +1 -1
  83. package/out/replicache/src/persist/clients.js +1 -1
  84. package/out/replicache/src/persist/clients.js.map +1 -1
  85. package/out/replicache/src/persist/collect-idb-databases.js +5 -4
  86. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  87. package/out/replicache/src/persist/gather-mem-only-visitor.js.map +1 -1
  88. package/out/replicache/src/persist/gather-not-cached-visitor.js.map +1 -1
  89. package/out/replicache/src/persist/heartbeat.js +2 -2
  90. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  91. package/out/replicache/src/persist/idb-databases-store.js +1 -1
  92. package/out/replicache/src/persist/idb-databases-store.js.map +1 -1
  93. package/out/replicache/src/persist/make-client-id.js +1 -1
  94. package/out/replicache/src/persist/make-client-id.js.map +1 -1
  95. package/out/replicache/src/persist/persist.js +3 -2
  96. package/out/replicache/src/persist/persist.js.map +1 -1
  97. package/out/replicache/src/persist/refresh.js +8 -5
  98. package/out/replicache/src/persist/refresh.js.map +1 -1
  99. package/out/replicache/src/process-scheduler.js.map +1 -1
  100. package/out/replicache/src/replicache-impl.js +3 -3
  101. package/out/replicache/src/replicache-impl.js.map +1 -1
  102. package/out/replicache/src/scan-iterator.js.map +1 -1
  103. package/out/replicache/src/scan-options.js +7 -6
  104. package/out/replicache/src/scan-options.js.map +1 -1
  105. package/out/replicache/src/sqlite.js +1 -2
  106. package/out/replicache/src/subscriptions.js +1 -1
  107. package/out/replicache/src/subscriptions.js.map +1 -1
  108. package/out/replicache/src/sync/diff.js +3 -1
  109. package/out/replicache/src/sync/diff.js.map +1 -1
  110. package/out/replicache/src/sync/ids.js.map +1 -1
  111. package/out/replicache/src/sync/patch.js +2 -4
  112. package/out/replicache/src/sync/patch.js.map +1 -1
  113. package/out/replicache/src/sync/pull.js +3 -1
  114. package/out/replicache/src/sync/pull.js.map +1 -1
  115. package/out/replicache/src/sync/push.js.map +1 -1
  116. package/out/replicache/src/sync/request-id.js +1 -1
  117. package/out/replicache/src/sync/request-id.js.map +1 -1
  118. package/out/replicache/src/transactions.js +1 -1
  119. package/out/replicache/src/transactions.js.map +1 -1
  120. package/out/shared/src/arrays.js +4 -1
  121. package/out/shared/src/arrays.js.map +1 -1
  122. package/out/shared/src/asserts.js +1 -1
  123. package/out/shared/src/bigint-json.d.ts +9 -0
  124. package/out/shared/src/bigint-json.d.ts.map +1 -1
  125. package/out/shared/src/bigint-json.js +27 -2
  126. package/out/shared/src/bigint-json.js.map +1 -1
  127. package/out/shared/src/broadcast-channel.js +1 -1
  128. package/out/shared/src/btree-set.js +11 -8
  129. package/out/shared/src/btree-set.js.map +1 -1
  130. package/out/shared/src/cache.js.map +1 -1
  131. package/out/shared/src/config.js +1 -1
  132. package/out/shared/src/custom-key-map.js.map +1 -1
  133. package/out/shared/src/custom-key-set.js.map +1 -1
  134. package/out/shared/src/deep-clone.js +1 -1
  135. package/out/shared/src/document-visible.js.map +1 -1
  136. package/out/shared/src/dotenv.js.map +1 -1
  137. package/out/shared/src/error.js +1 -1
  138. package/out/shared/src/error.js.map +1 -1
  139. package/out/shared/src/hash.js +2 -2
  140. package/out/shared/src/hash.js.map +1 -1
  141. package/out/shared/src/iterables.js.map +1 -1
  142. package/out/shared/src/json-schema.js.map +1 -1
  143. package/out/shared/src/json.js.map +1 -1
  144. package/out/shared/src/logging-test-utils.js +1 -1
  145. package/out/shared/src/logging.js +1 -1
  146. package/out/shared/src/logging.js.map +1 -1
  147. package/out/shared/src/navigator.js +1 -1
  148. package/out/shared/src/options.js +9 -12
  149. package/out/shared/src/options.js.map +1 -1
  150. package/out/shared/src/promise-race.js.map +1 -1
  151. package/out/shared/src/queue.js.map +1 -1
  152. package/out/shared/src/record-proxy.js.map +1 -1
  153. package/out/shared/src/ring-buffer.js.map +1 -1
  154. package/out/shared/src/size-of-value.js +7 -10
  155. package/out/shared/src/size-of-value.js.map +1 -1
  156. package/out/shared/src/sorted-entries.js.map +1 -1
  157. package/out/shared/src/tdigest-schema.js.map +1 -1
  158. package/out/shared/src/tdigest.js.map +1 -1
  159. package/out/shared/src/valita.js +1 -1
  160. package/out/shared/src/valita.js.map +1 -1
  161. package/out/z2s/src/compiler.js +2 -4
  162. package/out/z2s/src/compiler.js.map +1 -1
  163. package/out/z2s/src/sql.js +1 -1
  164. package/out/z2s/src/sql.js.map +1 -1
  165. package/out/zero/package.js +211 -189
  166. package/out/zero/package.js.map +1 -1
  167. package/out/zero/src/adapters/drizzle.js +1 -2
  168. package/out/zero/src/adapters/kysely.js +1 -2
  169. package/out/zero/src/adapters/pg.js +1 -2
  170. package/out/zero/src/adapters/postgresjs.js +1 -2
  171. package/out/zero/src/adapters/prisma.js +1 -2
  172. package/out/zero/src/analyze.js +1 -2
  173. package/out/zero/src/change-protocol/v0.js +1 -2
  174. package/out/zero/src/expo-sqlite.js +1 -2
  175. package/out/zero/src/op-sqlite.js +1 -2
  176. package/out/zero/src/react-native.js.map +1 -1
  177. package/out/zero/src/sqlite.js +1 -2
  178. package/out/zero/src/zero-cache-dev.js +10 -9
  179. package/out/zero/src/zero-cache-dev.js.map +1 -1
  180. package/out/zero/src/zero-out.js +2 -1
  181. package/out/zero/src/zero-out.js.map +1 -1
  182. package/out/zero-cache/src/auth/auth.js +1 -1
  183. package/out/zero-cache/src/auth/auth.js.map +1 -1
  184. package/out/zero-cache/src/auth/jwt.js +4 -1
  185. package/out/zero-cache/src/auth/jwt.js.map +1 -1
  186. package/out/zero-cache/src/auth/load-permissions.js +1 -1
  187. package/out/zero-cache/src/auth/load-permissions.js.map +1 -1
  188. package/out/zero-cache/src/auth/read-authorizer.js +3 -2
  189. package/out/zero-cache/src/auth/read-authorizer.js.map +1 -1
  190. package/out/zero-cache/src/auth/write-authorizer.js +6 -17
  191. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  192. package/out/zero-cache/src/config/network.js +1 -1
  193. package/out/zero-cache/src/config/network.js.map +1 -1
  194. package/out/zero-cache/src/config/server-context.js.map +1 -1
  195. package/out/zero-cache/src/config/zero-config.d.ts +5 -0
  196. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  197. package/out/zero-cache/src/config/zero-config.js +13 -2
  198. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  199. package/out/zero-cache/src/custom/fetch.js +1 -1
  200. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  201. package/out/zero-cache/src/custom-queries/transform-query.js.map +1 -1
  202. package/out/zero-cache/src/db/create.js.map +1 -1
  203. package/out/zero-cache/src/db/index-predicate.js.map +1 -1
  204. package/out/zero-cache/src/db/lite-tables.d.ts +2 -2
  205. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  206. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  207. package/out/zero-cache/src/db/migration-lite.js +1 -1
  208. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  209. package/out/zero-cache/src/db/migration.js +5 -3
  210. package/out/zero-cache/src/db/migration.js.map +1 -1
  211. package/out/zero-cache/src/db/pg-copy-binary.js +4 -3
  212. package/out/zero-cache/src/db/pg-copy-binary.js.map +1 -1
  213. package/out/zero-cache/src/db/pg-copy.js +0 -1
  214. package/out/zero-cache/src/db/pg-copy.js.map +1 -1
  215. package/out/zero-cache/src/db/pg-to-lite.js +1 -1
  216. package/out/zero-cache/src/db/pg-to-lite.js.map +1 -1
  217. package/out/zero-cache/src/db/run-transaction.js.map +1 -1
  218. package/out/zero-cache/src/db/specs.js +7 -5
  219. package/out/zero-cache/src/db/specs.js.map +1 -1
  220. package/out/zero-cache/src/db/sqlite-corruption.js.map +1 -1
  221. package/out/zero-cache/src/db/statements.d.ts.map +1 -1
  222. package/out/zero-cache/src/db/statements.js +9 -2
  223. package/out/zero-cache/src/db/statements.js.map +1 -1
  224. package/out/zero-cache/src/db/transaction-pool.js +5 -3
  225. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  226. package/out/zero-cache/src/db/warmup.js.map +1 -1
  227. package/out/zero-cache/src/observability/events.js.map +1 -1
  228. package/out/zero-cache/src/observability/log-throttle.js.map +1 -1
  229. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  230. package/out/zero-cache/src/scripts/decommission.js +1 -1
  231. package/out/zero-cache/src/scripts/deploy-permissions.js +5 -3
  232. package/out/zero-cache/src/scripts/deploy-permissions.js.map +1 -1
  233. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  234. package/out/zero-cache/src/server/anonymous-otel-start.js +2 -1
  235. package/out/zero-cache/src/server/anonymous-otel-start.js.map +1 -1
  236. package/out/zero-cache/src/server/change-streamer.js +1 -1
  237. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  238. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  239. package/out/zero-cache/src/server/logging.js +1 -1
  240. package/out/zero-cache/src/server/logging.js.map +1 -1
  241. package/out/zero-cache/src/server/main.js.map +1 -1
  242. package/out/zero-cache/src/server/otel-log-sink.js.map +1 -1
  243. package/out/zero-cache/src/server/otel-start.js.map +1 -1
  244. package/out/zero-cache/src/server/reaper.js +2 -2
  245. package/out/zero-cache/src/server/reaper.js.map +1 -1
  246. package/out/zero-cache/src/server/replicator.js +1 -1
  247. package/out/zero-cache/src/server/replicator.js.map +1 -1
  248. package/out/zero-cache/src/server/runner/run-worker.js +2 -1
  249. package/out/zero-cache/src/server/runner/run-worker.js.map +1 -1
  250. package/out/zero-cache/src/server/runner/runtime.js +2 -1
  251. package/out/zero-cache/src/server/runner/runtime.js.map +1 -1
  252. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  253. package/out/zero-cache/src/server/shadow-syncer.js +1 -1
  254. package/out/zero-cache/src/server/shadow-syncer.js.map +1 -1
  255. package/out/zero-cache/src/server/slot-keeper.js.map +1 -1
  256. package/out/zero-cache/src/server/syncer-assigner.js.map +1 -1
  257. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  258. package/out/zero-cache/src/server/syncer.js +9 -4
  259. package/out/zero-cache/src/server/syncer.js.map +1 -1
  260. package/out/zero-cache/src/server/worker-dispatcher.js +1 -1
  261. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  262. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  263. package/out/zero-cache/src/services/analyze.js +2 -2
  264. package/out/zero-cache/src/services/analyze.js.map +1 -1
  265. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +2 -2
  266. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  267. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  268. package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -1
  269. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
  270. package/out/zero-cache/src/services/change-source/common/replica-schema.js +4 -4
  271. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  272. package/out/zero-cache/src/services/change-source/custom/change-source.js +4 -4
  273. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  274. package/out/zero-cache/src/services/change-source/pg/backfill-metadata.js.map +1 -1
  275. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +4 -4
  276. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  277. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +3 -3
  278. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -1
  279. package/out/zero-cache/src/services/change-source/pg/change-source.js +6 -8
  280. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  281. package/out/zero-cache/src/services/change-source/pg/decommission.js +2 -1
  282. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  283. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +9 -7
  284. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  285. package/out/zero-cache/src/services/change-source/pg/logical-replication/binary-reader.js.map +1 -1
  286. package/out/zero-cache/src/services/change-source/pg/logical-replication/pgoutput-parser.js.map +1 -1
  287. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +5 -4
  288. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  289. package/out/zero-cache/src/services/change-source/pg/lsn.js.map +1 -1
  290. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +2 -2
  291. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -1
  292. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js +1 -1
  293. package/out/zero-cache/src/services/change-source/pg/replication-slot-health.js.map +1 -1
  294. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +3 -3
  295. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  296. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +2 -2
  297. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  298. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +13 -12
  299. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  300. package/out/zero-cache/src/services/change-source/pg/schema/init.js +1 -1
  301. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  302. package/out/zero-cache/src/services/change-source/pg/schema/published.js +2 -2
  303. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  304. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -1
  305. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +10 -7
  306. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  307. package/out/zero-cache/src/services/change-source/pg/schema/validation.js +1 -1
  308. package/out/zero-cache/src/services/change-source/pg/schema/validation.js.map +1 -1
  309. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +1 -1
  310. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -1
  311. package/out/zero-cache/src/services/change-source/protocol/current/control.js.map +1 -1
  312. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  313. package/out/zero-cache/src/services/change-source/protocol/current/downstream.js.map +1 -1
  314. package/out/zero-cache/src/services/change-source/protocol/current/status.js.map +1 -1
  315. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  316. package/out/zero-cache/src/services/change-source/protocol/current.js +1 -1
  317. package/out/zero-cache/src/services/change-source/protocol/mod.js +1 -2
  318. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -1
  319. package/out/zero-cache/src/services/change-streamer/backfill-state.js +1 -1
  320. package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -1
  321. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  322. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  323. package/out/zero-cache/src/services/change-streamer/broadcast.js +1 -1
  324. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  325. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +1 -1
  326. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -1
  327. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +2 -3
  328. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
  329. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +3 -3
  330. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
  331. package/out/zero-cache/src/services/change-streamer/change-log-transaction-hash.js.map +1 -1
  332. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +5 -3
  333. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  334. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +21 -21
  335. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  336. package/out/zero-cache/src/services/change-streamer/change-streamer.js +1 -1
  337. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  338. package/out/zero-cache/src/services/change-streamer/drain-tracker.js.map +1 -1
  339. package/out/zero-cache/src/services/change-streamer/forwarder.js +11 -10
  340. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  341. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +1 -1
  342. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -1
  343. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +3 -3
  344. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
  345. package/out/zero-cache/src/services/change-streamer/replica-poller.js +2 -2
  346. package/out/zero-cache/src/services/change-streamer/replica-poller.js.map +1 -1
  347. package/out/zero-cache/src/services/change-streamer/restore-admission.js +12 -1
  348. package/out/zero-cache/src/services/change-streamer/restore-admission.js.map +1 -1
  349. package/out/zero-cache/src/services/change-streamer/schema/init.js +1 -1
  350. package/out/zero-cache/src/services/change-streamer/schema/init.js.map +1 -1
  351. package/out/zero-cache/src/services/change-streamer/schema/tables.js +1 -1
  352. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  353. package/out/zero-cache/src/services/change-streamer/snapshot-message.js +1 -1
  354. package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -1
  355. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  356. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +1 -1
  357. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
  358. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +2 -2
  359. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -1
  360. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-purge-scheduler.js.map +1 -1
  361. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -1
  362. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -1
  363. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +4 -6
  364. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
  365. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +3 -5
  366. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  367. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  368. package/out/zero-cache/src/services/change-streamer/storer.js +3 -3
  369. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  370. package/out/zero-cache/src/services/change-streamer/subscribe.js +1 -1
  371. package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -1
  372. package/out/zero-cache/src/services/change-streamer/subscriber.js +2 -2
  373. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  374. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +0 -1
  375. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  376. package/out/zero-cache/src/services/heapz.js +2 -1
  377. package/out/zero-cache/src/services/heapz.js.map +1 -1
  378. package/out/zero-cache/src/services/http-service.js.map +1 -1
  379. package/out/zero-cache/src/services/life-cycle.js +2 -2
  380. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  381. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js +4 -2
  382. package/out/zero-cache/src/services/limiter/sliding-window-limiter.js.map +1 -1
  383. package/out/zero-cache/src/services/litestream/commands.js +3 -3
  384. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  385. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +2 -2
  386. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
  387. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
  388. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  389. package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -1
  390. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
  391. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  392. package/out/zero-cache/src/services/mutagen/pusher.js +3 -2
  393. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  394. package/out/zero-cache/src/services/plannerz.js +6 -5
  395. package/out/zero-cache/src/services/plannerz.js.map +1 -1
  396. package/out/zero-cache/src/services/profz.js +5 -3
  397. package/out/zero-cache/src/services/profz.js.map +1 -1
  398. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
  399. package/out/zero-cache/src/services/replicator/change-log-db.js +4 -3
  400. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
  401. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +1 -1
  402. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  403. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  404. package/out/zero-cache/src/services/replicator/drain-handler.js.map +1 -1
  405. package/out/zero-cache/src/services/replicator/incremental-sync.js +4 -4
  406. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  407. package/out/zero-cache/src/services/replicator/notifier.js.map +1 -1
  408. package/out/zero-cache/src/services/replicator/replication-status.js +4 -3
  409. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  410. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  411. package/out/zero-cache/src/services/replicator/reporter/recorder.js +1 -1
  412. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  413. package/out/zero-cache/src/services/replicator/reporter/report-schema.js +1 -1
  414. package/out/zero-cache/src/services/replicator/reporter/report-schema.js.map +1 -1
  415. package/out/zero-cache/src/services/replicator/schema/backfilling.js +2 -2
  416. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
  417. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts +19 -7
  418. package/out/zero-cache/src/services/replicator/schema/change-log.d.ts.map +1 -1
  419. package/out/zero-cache/src/services/replicator/schema/change-log.js +31 -15
  420. package/out/zero-cache/src/services/replicator/schema/change-log.js.map +1 -1
  421. package/out/zero-cache/src/services/replicator/schema/column-metadata.js +1 -1
  422. package/out/zero-cache/src/services/replicator/schema/column-metadata.js.map +1 -1
  423. package/out/zero-cache/src/services/replicator/schema/replication-state.js +10 -7
  424. package/out/zero-cache/src/services/replicator/schema/replication-state.js.map +1 -1
  425. package/out/zero-cache/src/services/replicator/schema/table-metadata.js.map +1 -1
  426. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +1 -1
  427. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  428. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js +1 -1
  429. package/out/zero-cache/src/services/replicator/sqlite-change-log-purger.js.map +1 -1
  430. package/out/zero-cache/src/services/replicator/write-worker-client.js +1 -1
  431. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  432. package/out/zero-cache/src/services/replicator/write-worker.js +3 -3
  433. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  434. package/out/zero-cache/src/services/run-ast.js +1 -1
  435. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  436. package/out/zero-cache/src/services/runner.js.map +1 -1
  437. package/out/zero-cache/src/services/running-state.js.map +1 -1
  438. package/out/zero-cache/src/services/shadow-sync/shadow-sync-service.js.map +1 -1
  439. package/out/zero-cache/src/services/statz.js +4 -3
  440. package/out/zero-cache/src/services/statz.js.map +1 -1
  441. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js +2 -2
  442. package/out/zero-cache/src/services/view-syncer/active-users-gauge.js.map +1 -1
  443. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -1
  444. package/out/zero-cache/src/services/view-syncer/batch-pacer.js.map +1 -1
  445. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  446. package/out/zero-cache/src/services/view-syncer/client-handler.js +24 -3
  447. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  448. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts +15 -2
  449. package/out/zero-cache/src/services/view-syncer/client-schema.d.ts.map +1 -1
  450. package/out/zero-cache/src/services/view-syncer/client-schema.js +46 -6
  451. package/out/zero-cache/src/services/view-syncer/client-schema.js.map +1 -1
  452. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  453. package/out/zero-cache/src/services/view-syncer/cvr-purger.js +2 -2
  454. package/out/zero-cache/src/services/view-syncer/cvr-purger.js.map +1 -1
  455. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  456. package/out/zero-cache/src/services/view-syncer/cvr-store.js +11 -6
  457. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  458. package/out/zero-cache/src/services/view-syncer/cvr.js +12 -4
  459. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  460. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +1 -1
  461. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -1
  462. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  463. package/out/zero-cache/src/services/view-syncer/e2e-serving-lag.js.map +1 -1
  464. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -1
  465. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +2 -2
  466. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -1
  467. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +6 -4
  468. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  469. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  470. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +13 -10
  471. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  472. package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -1
  473. package/out/zero-cache/src/services/view-syncer/push-cost.js.map +1 -1
  474. package/out/zero-cache/src/services/view-syncer/push-cutoff.js.map +1 -1
  475. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  476. package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -1
  477. package/out/zero-cache/src/services/view-syncer/query-stats.js +1 -1
  478. package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -1
  479. package/out/zero-cache/src/services/view-syncer/row-record-cache.js +1 -1
  480. package/out/zero-cache/src/services/view-syncer/row-record-cache.js.map +1 -1
  481. package/out/zero-cache/src/services/view-syncer/schema/cvr.js +1 -1
  482. package/out/zero-cache/src/services/view-syncer/schema/types.js +1 -1
  483. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  484. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +7 -1
  485. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -1
  486. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +8 -1
  487. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  488. package/out/zero-cache/src/services/view-syncer/snapshotter.js +7 -6
  489. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  490. package/out/zero-cache/src/services/view-syncer/ttl-clock.js.map +1 -1
  491. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  492. package/out/zero-cache/src/services/view-syncer/view-syncer.js +19 -15
  493. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  494. package/out/zero-cache/src/types/error-with-level.js.map +1 -1
  495. package/out/zero-cache/src/types/lexi-version.js.map +1 -1
  496. package/out/zero-cache/src/types/lite.js +1 -1
  497. package/out/zero-cache/src/types/lite.js.map +1 -1
  498. package/out/zero-cache/src/types/pg-data-type.js +1 -1
  499. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  500. package/out/zero-cache/src/types/pg.js +6 -4
  501. package/out/zero-cache/src/types/pg.js.map +1 -1
  502. package/out/zero-cache/src/types/poke-chunk.js +2 -2
  503. package/out/zero-cache/src/types/poke-chunk.js.map +1 -1
  504. package/out/zero-cache/src/types/processes.js +1 -1
  505. package/out/zero-cache/src/types/processes.js.map +1 -1
  506. package/out/zero-cache/src/types/profiler.js +1 -1
  507. package/out/zero-cache/src/types/profiler.js.map +1 -1
  508. package/out/zero-cache/src/types/row-key.js.map +1 -1
  509. package/out/zero-cache/src/types/sql.js.map +1 -1
  510. package/out/zero-cache/src/types/streams.js +10 -9
  511. package/out/zero-cache/src/types/streams.js.map +1 -1
  512. package/out/zero-cache/src/types/strings.js.map +1 -1
  513. package/out/zero-cache/src/types/subscription.js.map +1 -1
  514. package/out/zero-cache/src/types/timeout.js +1 -1
  515. package/out/zero-cache/src/types/websocket-handoff.js +1 -1
  516. package/out/zero-cache/src/types/ws.js +1 -1
  517. package/out/zero-cache/src/workers/connection.js +5 -6
  518. package/out/zero-cache/src/workers/connection.js.map +1 -1
  519. package/out/zero-cache/src/workers/mutator.js.map +1 -1
  520. package/out/zero-cache/src/workers/replicator.js +5 -4
  521. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  522. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  523. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +9 -1
  524. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  525. package/out/zero-cache/src/workers/syncer.js +4 -4
  526. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  527. package/out/zero-client/src/client/active-clients-manager.js +1 -1
  528. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  529. package/out/zero-client/src/client/bindings.js +11 -11
  530. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  531. package/out/zero-client/src/client/connection.js +1 -1
  532. package/out/zero-client/src/client/connection.js.map +1 -1
  533. package/out/zero-client/src/client/context.js.map +1 -1
  534. package/out/zero-client/src/client/crud-impl.js +1 -1
  535. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  536. package/out/zero-client/src/client/crud.js +1 -1
  537. package/out/zero-client/src/client/crud.js.map +1 -1
  538. package/out/zero-client/src/client/custom.js +1 -1
  539. package/out/zero-client/src/client/custom.js.map +1 -1
  540. package/out/zero-client/src/client/delete-clients-manager.js.map +1 -1
  541. package/out/zero-client/src/client/enable-analytics.js +1 -1
  542. package/out/zero-client/src/client/enable-analytics.js.map +1 -1
  543. package/out/zero-client/src/client/error.js.map +1 -1
  544. package/out/zero-client/src/client/inspector/client-group.js.map +1 -1
  545. package/out/zero-client/src/client/inspector/client.js.map +1 -1
  546. package/out/zero-client/src/client/inspector/html-dialog-prompt.js +1 -1
  547. package/out/zero-client/src/client/inspector/html-dialog-prompt.js.map +1 -1
  548. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  549. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  550. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  551. package/out/zero-client/src/client/ivm-branch.js +1 -3
  552. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  553. package/out/zero-client/src/client/keys.js +2 -1
  554. package/out/zero-client/src/client/keys.js.map +1 -1
  555. package/out/zero-client/src/client/log-options.js.map +1 -1
  556. package/out/zero-client/src/client/make-replicache-mutators.js +5 -3
  557. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  558. package/out/zero-client/src/client/metrics.js +3 -5
  559. package/out/zero-client/src/client/metrics.js.map +1 -1
  560. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  561. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  562. package/out/zero-client/src/client/options.js.map +1 -1
  563. package/out/zero-client/src/client/query-manager.js +2 -2
  564. package/out/zero-client/src/client/query-manager.js.map +1 -1
  565. package/out/zero-client/src/client/reload-error-handler.js +4 -3
  566. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  567. package/out/zero-client/src/client/version.js +1 -1
  568. package/out/zero-client/src/client/zero-poke-handler.js +1 -1
  569. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  570. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  571. package/out/zero-client/src/client/zero.js +19 -11
  572. package/out/zero-client/src/client/zero.js.map +1 -1
  573. package/out/zero-client/src/mod.js +23 -23
  574. package/out/zero-client/src/util/nanoid.js.map +1 -1
  575. package/out/zero-permissions/src/permissions.js +3 -2
  576. package/out/zero-permissions/src/permissions.js.map +1 -1
  577. package/out/zero-pg/src/mod.js +9 -9
  578. package/out/zero-protocol/src/analyze-query-result.js +1 -1
  579. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  580. package/out/zero-protocol/src/application-error.js.map +1 -1
  581. package/out/zero-protocol/src/ast.js +19 -9
  582. package/out/zero-protocol/src/ast.js.map +1 -1
  583. package/out/zero-protocol/src/change-desired-queries.js.map +1 -1
  584. package/out/zero-protocol/src/client-schema.js +1 -1
  585. package/out/zero-protocol/src/client-schema.js.map +1 -1
  586. package/out/zero-protocol/src/close-connection.js +1 -1
  587. package/out/zero-protocol/src/close-connection.js.map +1 -1
  588. package/out/zero-protocol/src/connect.js +1 -1
  589. package/out/zero-protocol/src/connect.js.map +1 -1
  590. package/out/zero-protocol/src/custom-queries.js +1 -1
  591. package/out/zero-protocol/src/custom-queries.js.map +1 -1
  592. package/out/zero-protocol/src/data.js +4 -2
  593. package/out/zero-protocol/src/data.js.map +1 -1
  594. package/out/zero-protocol/src/delete-clients.js.map +1 -1
  595. package/out/zero-protocol/src/down.d.ts +27 -27
  596. package/out/zero-protocol/src/down.js.map +1 -1
  597. package/out/zero-protocol/src/error.js.map +1 -1
  598. package/out/zero-protocol/src/inspect-down.d.ts +81 -81
  599. package/out/zero-protocol/src/inspect-down.js +1 -1
  600. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  601. package/out/zero-protocol/src/inspect-up.js +1 -1
  602. package/out/zero-protocol/src/inspect-up.js.map +1 -1
  603. package/out/zero-protocol/src/mutate-server.js +1 -1
  604. package/out/zero-protocol/src/mutate-server.js.map +1 -1
  605. package/out/zero-protocol/src/mutation-id.js.map +1 -1
  606. package/out/zero-protocol/src/mutation.js +1 -1
  607. package/out/zero-protocol/src/mutation.js.map +1 -1
  608. package/out/zero-protocol/src/mutations-patch.js +1 -1
  609. package/out/zero-protocol/src/mutations-patch.js.map +1 -1
  610. package/out/zero-protocol/src/ping.js +1 -1
  611. package/out/zero-protocol/src/ping.js.map +1 -1
  612. package/out/zero-protocol/src/poke.d.ts +6 -6
  613. package/out/zero-protocol/src/poke.js +1 -1
  614. package/out/zero-protocol/src/poke.js.map +1 -1
  615. package/out/zero-protocol/src/pong.js +1 -1
  616. package/out/zero-protocol/src/pong.js.map +1 -1
  617. package/out/zero-protocol/src/primary-key.js.map +1 -1
  618. package/out/zero-protocol/src/protocol-version.js +1 -0
  619. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  620. package/out/zero-protocol/src/pull.js +1 -1
  621. package/out/zero-protocol/src/pull.js.map +1 -1
  622. package/out/zero-protocol/src/push.js +1 -1
  623. package/out/zero-protocol/src/push.js.map +1 -1
  624. package/out/zero-protocol/src/queries-patch.js +1 -1
  625. package/out/zero-protocol/src/queries-patch.js.map +1 -1
  626. package/out/zero-protocol/src/query-hash-visitor.js.map +1 -1
  627. package/out/zero-protocol/src/query-hash.js +2 -2
  628. package/out/zero-protocol/src/query-server.js +1 -1
  629. package/out/zero-protocol/src/query-server.js.map +1 -1
  630. package/out/zero-protocol/src/row-patch.js.map +1 -1
  631. package/out/zero-protocol/src/up.d.ts +60 -60
  632. package/out/zero-protocol/src/up.js.map +1 -1
  633. package/out/zero-protocol/src/update-auth.js.map +1 -1
  634. package/out/zero-protocol/src/version.js.map +1 -1
  635. package/out/zero-react/src/bindings.js +11 -11
  636. package/out/zero-react/src/mod.js +5 -5
  637. package/out/zero-react/src/use-query.js +1 -1
  638. package/out/zero-react/src/use-query.js.map +1 -1
  639. package/out/zero-react/src/zero-provider.d.ts +1 -1
  640. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  641. package/out/zero-react/src/zero-provider.js +2 -1
  642. package/out/zero-react/src/zero-provider.js.map +1 -1
  643. package/out/zero-react/src/zero.js +23 -23
  644. package/out/zero-schema/src/builder/schema-builder.js +3 -2
  645. package/out/zero-schema/src/builder/schema-builder.js.map +1 -1
  646. package/out/zero-schema/src/builder/table-builder.js +1 -1
  647. package/out/zero-schema/src/builder/table-builder.js.map +1 -1
  648. package/out/zero-schema/src/compiled-permissions.js.map +1 -1
  649. package/out/zero-schema/src/name-mapper.js +7 -5
  650. package/out/zero-schema/src/name-mapper.js.map +1 -1
  651. package/out/zero-schema/src/schema-config.js +1 -1
  652. package/out/zero-schema/src/schema-config.js.map +1 -1
  653. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  654. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  655. package/out/zero-server/src/adapters/pg.js.map +1 -1
  656. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  657. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  658. package/out/zero-server/src/closable-db-transaction.js.map +1 -1
  659. package/out/zero-server/src/custom.js +1 -1
  660. package/out/zero-server/src/custom.js.map +1 -1
  661. package/out/zero-server/src/mod.js +8 -8
  662. package/out/zero-server/src/process-mutations.js.map +1 -1
  663. package/out/zero-server/src/push-processor.js.map +1 -1
  664. package/out/zero-server/src/queries/process-queries.js +2 -1
  665. package/out/zero-server/src/queries/process-queries.js.map +1 -1
  666. package/out/zero-server/src/schema.js +7 -7
  667. package/out/zero-server/src/schema.js.map +1 -1
  668. package/out/zero-server/src/zql-database.js.map +1 -1
  669. package/out/zero-solid/src/bindings.js +11 -11
  670. package/out/zero-solid/src/mod.js +5 -5
  671. package/out/zero-solid/src/solid-view.js +1 -1
  672. package/out/zero-solid/src/solid-view.js.map +1 -1
  673. package/out/zero-solid/src/use-connection-state.js +2 -1
  674. package/out/zero-solid/src/use-connection-state.js.map +1 -1
  675. package/out/zero-solid/src/use-query.js.map +1 -1
  676. package/out/zero-solid/src/use-zero-online.js +2 -1
  677. package/out/zero-solid/src/use-zero-online.js.map +1 -1
  678. package/out/zero-solid/src/use-zero.js +3 -2
  679. package/out/zero-solid/src/use-zero.js.map +1 -1
  680. package/out/zero-solid/src/zero.js +23 -23
  681. package/out/zero-types/src/name-mapper.js.map +1 -1
  682. package/out/zql/src/builder/builder.js +15 -13
  683. package/out/zql/src/builder/builder.js.map +1 -1
  684. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -1
  685. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  686. package/out/zql/src/builder/like.js +0 -1
  687. package/out/zql/src/builder/like.js.map +1 -1
  688. package/out/zql/src/ivm/array-view.js.map +1 -1
  689. package/out/zql/src/ivm/cap.d.ts +1 -1
  690. package/out/zql/src/ivm/cap.d.ts.map +1 -1
  691. package/out/zql/src/ivm/cap.js +10 -0
  692. package/out/zql/src/ivm/cap.js.map +1 -1
  693. package/out/zql/src/ivm/constraint.js +1 -1
  694. package/out/zql/src/ivm/constraint.js.map +1 -1
  695. package/out/zql/src/ivm/data.d.ts +15 -1
  696. package/out/zql/src/ivm/data.d.ts.map +1 -1
  697. package/out/zql/src/ivm/data.js +15 -1
  698. package/out/zql/src/ivm/data.js.map +1 -1
  699. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  700. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  701. package/out/zql/src/ivm/exists.js +32 -22
  702. package/out/zql/src/ivm/exists.js.map +1 -1
  703. package/out/zql/src/ivm/fan-in.js.map +1 -1
  704. package/out/zql/src/ivm/fan-out.js.map +1 -1
  705. package/out/zql/src/ivm/filter-operators.d.ts +5 -0
  706. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  707. package/out/zql/src/ivm/filter-operators.js +1 -1
  708. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  709. package/out/zql/src/ivm/filter-push.js.map +1 -1
  710. package/out/zql/src/ivm/filter.js.map +1 -1
  711. package/out/zql/src/ivm/flipped-join.js +1 -3
  712. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  713. package/out/zql/src/ivm/join-index-tap.js.map +1 -1
  714. package/out/zql/src/ivm/join-index.js.map +1 -1
  715. package/out/zql/src/ivm/join-utils.js +11 -13
  716. package/out/zql/src/ivm/join-utils.js.map +1 -1
  717. package/out/zql/src/ivm/join.d.ts.map +1 -1
  718. package/out/zql/src/ivm/join.js +6 -3
  719. package/out/zql/src/ivm/join.js.map +1 -1
  720. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  721. package/out/zql/src/ivm/memory-source.js +27 -23
  722. package/out/zql/src/ivm/memory-source.js.map +1 -1
  723. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  724. package/out/zql/src/ivm/operator.d.ts +6 -0
  725. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  726. package/out/zql/src/ivm/operator.js +2 -2
  727. package/out/zql/src/ivm/operator.js.map +1 -1
  728. package/out/zql/src/ivm/push-accumulated.js +0 -1
  729. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  730. package/out/zql/src/ivm/skip-yields.js.map +1 -1
  731. package/out/zql/src/ivm/skip.js.map +1 -1
  732. package/out/zql/src/ivm/take-gate.js.map +1 -1
  733. package/out/zql/src/ivm/take.d.ts.map +1 -1
  734. package/out/zql/src/ivm/take.js +5 -4
  735. package/out/zql/src/ivm/take.js.map +1 -1
  736. package/out/zql/src/ivm/union-fan-in.js +1 -1
  737. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  738. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  739. package/out/zql/src/ivm/view-apply-change.js +1 -1
  740. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  741. package/out/zql/src/mutate/crud.js +3 -2
  742. package/out/zql/src/mutate/crud.js.map +1 -1
  743. package/out/zql/src/planner/planner-builder.js +1 -1
  744. package/out/zql/src/planner/planner-builder.js.map +1 -1
  745. package/out/zql/src/planner/planner-connection.js.map +1 -1
  746. package/out/zql/src/planner/planner-debug.js.map +1 -1
  747. package/out/zql/src/planner/planner-fan-in.js.map +1 -1
  748. package/out/zql/src/planner/planner-fan-out.js.map +1 -1
  749. package/out/zql/src/planner/planner-filter.js.map +1 -1
  750. package/out/zql/src/planner/planner-graph.js +1 -5
  751. package/out/zql/src/planner/planner-graph.js.map +1 -1
  752. package/out/zql/src/planner/planner-join.js +1 -1
  753. package/out/zql/src/planner/planner-join.js.map +1 -1
  754. package/out/zql/src/planner/planner-source.js.map +1 -1
  755. package/out/zql/src/planner/planner-terminus.js.map +1 -1
  756. package/out/zql/src/query/create-builder.js +2 -1
  757. package/out/zql/src/query/create-builder.js.map +1 -1
  758. package/out/zql/src/query/expression.js +1 -1
  759. package/out/zql/src/query/expression.js.map +1 -1
  760. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  761. package/out/zql/src/query/named.js +2 -1
  762. package/out/zql/src/query/named.js.map +1 -1
  763. package/out/zql/src/query/query-delegate-base.js +1 -1
  764. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  765. package/out/zql/src/query/query-impl.js +3 -2
  766. package/out/zql/src/query/query-impl.js.map +1 -1
  767. package/out/zql/src/query/query-registry.js +1 -1
  768. package/out/zql/src/query/query-registry.js.map +1 -1
  769. package/out/zql/src/query/query-transitions.js +1 -1
  770. package/out/zql/src/query/query-transitions.js.map +1 -1
  771. package/out/zql/src/query/runnable-query-impl.js +3 -2
  772. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  773. package/out/zql/src/query/static-query.js.map +1 -1
  774. package/out/zql/src/query/ttl.js +8 -8
  775. package/out/zql/src/query/ttl.js.map +1 -1
  776. package/out/zql/src/query/validate-input.js.map +1 -1
  777. package/out/zqlite/src/database-storage.d.ts +27 -8
  778. package/out/zqlite/src/database-storage.d.ts.map +1 -1
  779. package/out/zqlite/src/database-storage.js +190 -53
  780. package/out/zqlite/src/database-storage.js.map +1 -1
  781. package/out/zqlite/src/db.d.ts +47 -1
  782. package/out/zqlite/src/db.d.ts.map +1 -1
  783. package/out/zqlite/src/db.js +121 -21
  784. package/out/zqlite/src/db.js.map +1 -1
  785. package/out/zqlite/src/explain-queries.js.map +1 -1
  786. package/out/zqlite/src/fetch-sql.js.map +1 -1
  787. package/out/zqlite/src/index-usage.js.map +1 -1
  788. package/out/zqlite/src/internal/sql-inline.js.map +1 -1
  789. package/out/zqlite/src/internal/sql.js.map +1 -1
  790. package/out/zqlite/src/internal/statement-cache.d.ts +7 -0
  791. package/out/zqlite/src/internal/statement-cache.d.ts.map +1 -1
  792. package/out/zqlite/src/internal/statement-cache.js +11 -4
  793. package/out/zqlite/src/internal/statement-cache.js.map +1 -1
  794. package/out/zqlite/src/mod.js +5 -5
  795. package/out/zqlite/src/pending-delta.js.map +1 -1
  796. package/out/zqlite/src/query-builder.js +1 -1
  797. package/out/zqlite/src/query-builder.js.map +1 -1
  798. package/out/zqlite/src/query-delegate.js.map +1 -1
  799. package/out/zqlite/src/resolve-scalar-subqueries.js +2 -5
  800. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  801. package/out/zqlite/src/sqlite-cost-model.js +3 -2
  802. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  803. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  804. package/out/zqlite/src/table-source.d.ts +11 -0
  805. package/out/zqlite/src/table-source.d.ts.map +1 -1
  806. package/out/zqlite/src/table-source.js +63 -26
  807. package/out/zqlite/src/table-source.js.map +1 -1
  808. package/package.json +37 -36
  809. package/out/analyze-query/src/bin-analyze.d.ts +0 -2
  810. package/out/analyze-query/src/bin-analyze.d.ts.map +0 -1
  811. package/out/analyze-query/src/bin-analyze.js +0 -283
  812. package/out/analyze-query/src/bin-analyze.js.map +0 -1
  813. package/out/otel/src/maybe-time.d.ts +0 -3
  814. package/out/otel/src/maybe-time.d.ts.map +0 -1
  815. package/out/otel/src/maybe-time.js +0 -15
  816. package/out/otel/src/maybe-time.js.map +0 -1
  817. package/out/zero/src/analyze-query.d.ts +0 -3
  818. package/out/zero/src/analyze-query.d.ts.map +0 -1
  819. package/out/zero/src/analyze-query.js +0 -3
@@ -441,6 +441,6 @@ function dropStore(name, createDelegate, opts) {
441
441
  return Promise.resolve();
442
442
  }
443
443
  //#endregion
444
- export { SQLiteStore, clearAllNamedStoresForTesting, dropStore };
444
+ export { SQLiteStore, SQLiteStoreRead, SQLiteWrite, classifySQLiteError, clearAllNamedStoresForTesting, dropStore, safeFilename, setupDatabase };
445
445
 
446
446
  //# sourceMappingURL=sqlite-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sqlite-store.js","names":["#filename","#entry","#kind","#closed","#release","#preparedStatements","#pendingHasKeys","#pendingHasCallbacks","#scheduleLookup","#pendingGetKeys","#pendingGetCallbacks","#scheduled","#dbDelegate","#committed"],"sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"sourcesContent":["import {RWLock} from '@rocicorp/lock';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\nimport {deepFreeze} from '../frozen-json.ts';\nimport {\n StorageFailureError,\n type StorageFailureKind,\n} from '../storage-failure.ts';\nimport type {Read, Store, Write} from './store.ts';\nimport {\n maybeTransactionIsClosedRejection,\n throwIfStoreClosed,\n transactionError,\n} from './throw-if-closed.ts';\nimport {deleteSentinel, WriteImplBase} from './write-impl-base.ts';\n\n/**\n * A SQLite prepared statement.\n *\n * `SQLiteStore` prepares one statement per SQL and shares it across all\n * concurrent readers, so implementations must make each call atomic with\n * respect to other callers of the same statement: `all()` must not let another\n * `exec()`/`all()` rebind or reset the underlying statement between executing\n * it and fetching its rows. Delegates whose native API splits execute and fetch\n * into separate round trips (e.g. expo-sqlite) must serialize per statement.\n */\nexport interface PreparedStatement {\n exec(params: string[]): Promise<void>;\n all(params: string[]): Promise<unknown[][]>;\n}\n\nexport interface SQLiteDatabase {\n /**\n * Close the database connection.\n */\n close(): void;\n\n /**\n * Destroy or delete the database (e.g. delete file).\n */\n destroy(): void;\n\n /**\n * Prepare a SQL string, returning a statement you can execute.\n * E.g. `const stmt = db.prepare(\"SELECT * FROM todos WHERE id=?\");`\n */\n prepare(sql: string): PreparedStatement;\n\n // for PRAGMA statements, schema creation and transaction control.\n execSync(sql: string): void;\n}\n\nexport type CreateSQLiteDatabase = (\n filename: string,\n opts?: SQLiteStoreOptions,\n) => SQLiteDatabase;\n\n/**\n * SQLite-based implementation of the Store interface using a configurable delegate.\n * Supports shared connections between multiple store instances with the same name,\n * providing efficient resource utilization and proper transaction isolation.\n * Uses parameterized queries for safety and performance.\n */\nexport class SQLiteStore implements Store {\n readonly #filename: string;\n readonly #entry: StoreEntry;\n readonly #kind: string;\n\n #closed = false;\n\n constructor(\n name: string,\n create: CreateSQLiteDatabase,\n opts?: SQLiteStoreOptions,\n kind = 'sqlite',\n ) {\n this.#filename = resolveFilename(name, opts);\n this.#entry = getOrCreateEntry(this.#filename, create, opts);\n this.#kind = kind;\n }\n\n get kind(): string {\n return this.#kind;\n }\n\n async read(): Promise<Read> {\n throwIfStoreClosed(this);\n\n const entry = this.#entry;\n const {db, lock, preparedStatements} = entry;\n const release = await lock.read();\n\n // Start shared read transaction if this is the first reader\n // This ensures consistent reads across all concurrent readers\n try {\n if (entry.activeReaders === 0) {\n db.execSync('BEGIN');\n }\n } catch (e) {\n // The lock was acquired above; without this every later read() and\n // write() on the store waits for a release that never comes.\n release();\n throw e;\n }\n entry.activeReaders++;\n\n return new SQLiteStoreRead(() => {\n entry.activeReaders--;\n // Commit shared read transaction when last reader finishes. SQLite may\n // already have rolled the transaction back on its own (an I/O error\n // inside any reader's statement does that), in which case COMMIT\n // throws \"cannot commit - no transaction is active\". The error is\n // reported to the releasing reader, but the RWLock must be released\n // regardless or the store hangs for the rest of the process.\n try {\n if (entry.activeReaders === 0) {\n db.execSync('COMMIT');\n }\n } finally {\n release();\n }\n }, preparedStatements);\n }\n\n async write(): Promise<Write> {\n throwIfStoreClosed(this);\n\n const {lock, db, preparedStatements} = this.#entry;\n const release = await lock.write();\n\n // At this point, RWLock guarantees no active readers\n // The last reader would have already committed the shared transaction\n\n try {\n db.execSync('BEGIN IMMEDIATE');\n } catch (e) {\n // SQLITE_BUSY once busy_timeout expires, or an I/O error. The caller\n // gets the error; the write lock must not stay held.\n release();\n throw e;\n }\n\n return new SQLiteWrite(release, db, preparedStatements);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n const {lock, db} = this.#entry;\n // Wait for all readers and writers to finish.\n const writeRelease = await lock.write();\n\n // Handle reference counting for shared stores - only close database\n // when this is the last store instance using it\n decrementStoreRefCount(this.#filename, db);\n\n this.#closed = true;\n writeRelease();\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport function safeFilename(name: string): string {\n return name.replace(/[^a-zA-Z0-9]/g, '_');\n}\n\nfunction resolveFilename(name: string, opts?: SQLiteStoreOptions): string {\n const safe = safeFilename(name);\n const dir = opts?.directory;\n return dir ? `${dir}/${safe}` : safe;\n}\n\nexport type PreparedStatements = {\n has: PreparedStatement;\n get: PreparedStatement;\n hasMany: PreparedStatement;\n getMany: PreparedStatement;\n put: PreparedStatement;\n del: PreparedStatement;\n /** Multi-row INSERT with the values bound as parameters, n rows wide. */\n putN: (n: number) => PreparedStatement;\n /** Multi-key DELETE with the keys bound as parameters, n keys wide. */\n delN: (n: number) => PreparedStatement;\n};\n\n/**\n * Widest batch we bind in one statement. SQLite's SQLITE_MAX_VARIABLE_NUMBER is\n * 32766, and a put costs two parameters per row, so this is far below the cap;\n * it exists to bound how many distinct statements we prepare and cache.\n */\nconst MAX_BATCH = 128;\n\n/** `repeatList('?', 3)` -> `'?,?,?'`. */\nfunction repeatList(item: string, n: number): string {\n return `${item},`.repeat(n).slice(0, -1);\n}\n\n/**\n * Prepares (and caches) a statement of each width on demand. Callers only ever\n * ask for powers of two, so the cache holds at most log2(MAX_BATCH)+1 entries\n * however many rows a commit turns out to have.\n */\nfunction batchStatements(\n delegate: SQLiteDatabase,\n sqlFor: (n: number) => string,\n): (n: number) => PreparedStatement {\n const cache = new Map<number, PreparedStatement>();\n return (n: number) =>\n getOrInsertComputed(cache, n, () => delegate.prepare(sqlFor(n)));\n}\n\n/**\n * Runs `items` through `getStatement` in power-of-two sized batches, so any\n * length is covered by a handful of cached statement widths.\n */\nasync function execInBatches<T>(\n items: readonly T[],\n getStatement: (n: number) => PreparedStatement,\n toParams: (item: T, out: string[]) => void,\n): Promise<void> {\n for (let i = 0; i < items.length;) {\n const remaining = Math.min(MAX_BATCH, items.length - i);\n const n = 1 << (31 - Math.clz32(remaining));\n const params: string[] = [];\n for (let j = 0; j < n; j++) {\n toParams(items[i + j], params);\n }\n await getStatement(n).exec(params);\n i += n;\n }\n}\n\nexport interface SQLiteStoreOptions {\n // Common options\n busyTimeout?: number;\n journalMode?: 'WAL' | 'DELETE';\n synchronous?: 'NORMAL' | 'FULL';\n readUncommitted?: boolean;\n /** Directory in which to create the SQLite file. Defaults to the process CWD. */\n directory?: string | undefined;\n}\n\n/**\n * Replicache's rows are B-tree chunks, which `BTreeWrite` targets at 8-16KB, so\n * most of them are larger than a page and spill into overflow pages at either\n * page size. 8192 does not avoid overflow; it halves the number of pages each\n * chunk is split across.\n *\n * Measured with `replicache-perf/rn`, 4096 vs 8192 + mmap, change in time:\n *\n * | device | persist 1024x10000 | startup read (expo / op) |\n * | ---------------------- | ------------------ | ------------------------ |\n * | iOS simulator | -6% | -12% / -14% |\n * | Android emulator, 2GB | -1% to -2% | -19% / -8% |\n * | Pixel 6 | ~0% | ~-40% / ~0% |\n *\n * Split by pragma on iOS, page_size carries the write win (~6%) and mmap most\n * of the read win. Neither regressed anything on any device.\n */\nconst PAGE_SIZE = 8192;\n\n/** 256MB, matching op-sqlite's own key-value store. */\nconst MMAP_SIZE = 268435456;\n\n/**\n * Common database setup logic shared between expo-sqlite and op-sqlite implementations.\n * Configures SQLite pragmas, creates the entry table, and prepares common statements.\n */\n\nexport function setupDatabase(\n delegate: SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): PreparedStatements {\n // Configure SQLite pragmas for optimal performance.\n //\n // page_size MUST come first. SQLite silently ignores it once the database\n // has content or a journal mode has been set — no error, no warning, the\n // pragma just does nothing and you are left on the 4096 default. Verified:\n // issuing it after `journal_mode = WAL`, or after CREATE TABLE, leaves\n // `PRAGMA page_size` reporting 4096. Do not reorder these.\n delegate.execSync(`PRAGMA page_size = ${PAGE_SIZE}`);\n delegate.execSync(`PRAGMA busy_timeout = ${opts?.busyTimeout ?? 200}`);\n delegate.execSync(`PRAGMA journal_mode = '${opts?.journalMode ?? 'WAL'}'`);\n delegate.execSync(`PRAGMA synchronous = '${opts?.synchronous ?? 'NORMAL'}'`);\n delegate.execSync(\n `PRAGMA read_uncommitted = ${Boolean(opts?.readUncommitted)}`,\n );\n // Reads served from the mmap window rather than the pager account for most\n // of the startup-read win measured in the PAGE_SIZE comment.\n delegate.execSync(`PRAGMA mmap_size = ${MMAP_SIZE}`);\n\n // Create the entry table.\n //\n // This is deliberately a rowid table, not `WITHOUT ROWID`. A `WITHOUT ROWID`\n // table stores whole rows in an index B-tree, which keeps at most ~1/4 of a\n // page inline, and SQLite recommends it only for rows under ~1/20 of a page.\n // Our rows are 8-16KB chunks. As a rowid table the key gets a small separate\n // index and the values live in the table B-tree. On a Pixel 6 (5 rounds, on\n // top of the pragmas above) that cut startup read by 24% on both expo and\n // op, expo startup scan by 10%, and persist 1024x10000 by 4-7%.\n //\n // `key` needs an explicit NOT NULL: in a rowid table a non-INTEGER primary key\n // is only a UNIQUE index, and SQLite (a bug kept for compatibility) lets it\n // hold NULLs, several of them. `WITHOUT ROWID` enforced this implicitly.\n //\n // `IF NOT EXISTS` leaves an existing database's table as it was created, so\n // stores created before this change stay `WITHOUT ROWID` until recreated.\n delegate.execSync(`\n CREATE TABLE IF NOT EXISTS entry (\n key TEXT PRIMARY KEY NOT NULL,\n value TEXT NOT NULL\n )\n `);\n\n // Prepare common statements\n return {\n has: delegate.prepare(`SELECT 1 FROM entry WHERE key = ? LIMIT 1`),\n get: delegate.prepare('SELECT value FROM entry WHERE key = ?'),\n hasMany: delegate.prepare(\n `SELECT key FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n getMany: delegate.prepare(\n `SELECT key, value FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n put: delegate.prepare(\n `INSERT OR REPLACE INTO entry (key, value)\n SELECT e.value->>0, e.value->1 FROM json_each(?) e`,\n ),\n del: delegate.prepare(\n `DELETE FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n putN: batchStatements(\n delegate,\n n =>\n `INSERT OR REPLACE INTO entry (key, value) VALUES ${repeatList('(?,?)', n)}`,\n ),\n delN: batchStatements(\n delegate,\n n => `DELETE FROM entry WHERE key IN (${repeatList('?', n)})`,\n ),\n };\n}\n\n// Callbacks are stored as striped pairs: [resolve, reject, resolve, reject, ...]\nconst CB_STRIDE = 2;\nconst CB_RESOLVE = 0;\nconst CB_REJECT = 1;\n\ntype GetResolve = (v: ReadonlyJSONValue | undefined) => void;\ntype HasResolve = (v: boolean) => void;\ntype Reject = (e: unknown) => void;\n\nfunction rejectAll(callbacks: unknown[], e: unknown): void {\n for (let i = CB_REJECT; i < callbacks.length; i += CB_STRIDE) {\n (callbacks[i] as Reject)(e);\n }\n}\n\nfunction parseRawValue(raw: string | undefined): ReadonlyJSONValue | undefined {\n return raw === undefined\n ? undefined\n : deepFreeze(JSON.parse(raw) as ReadonlyJSONValue);\n}\n\nfunction resolveGet(\n resolve: GetResolve,\n reject: Reject,\n raw: string | undefined,\n): void {\n try {\n resolve(parseRawValue(raw));\n } catch (e) {\n reject(e);\n }\n}\n\nasync function flushGets(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.get.all([keys[0]])\n : await ps.getMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n resolveGet(\n callbacks[CB_RESOLVE] as GetResolve,\n callbacks[CB_REJECT] as Reject,\n rows[0]?.[0] as string | undefined,\n );\n return;\n }\n const resultMap = new Map(rows as [string, string][]);\n for (let i = 0; i < keys.length; i++) {\n resolveGet(\n callbacks[i * CB_STRIDE + CB_RESOLVE] as GetResolve,\n callbacks[i * CB_STRIDE + CB_REJECT] as Reject,\n resultMap.get(keys[i]),\n );\n }\n}\n\nasync function flushHas(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.has.all([keys[0]])\n : await ps.hasMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n (callbacks[CB_RESOLVE] as HasResolve)(rows.length > 0);\n return;\n }\n const existingKeys = new Set(rows.map(row => row[0] as string));\n for (let i = 0; i < keys.length; i++) {\n (callbacks[i * CB_STRIDE + CB_RESOLVE] as HasResolve)(\n existingKeys.has(keys[i]),\n );\n }\n}\n\nexport class SQLiteStoreRead implements Read {\n readonly #release: () => void;\n readonly #preparedStatements: PreparedStatements;\n #closed = false;\n #pendingGetKeys: string[] = [];\n #pendingGetCallbacks: unknown[] = [];\n #pendingHasKeys: string[] = [];\n #pendingHasCallbacks: unknown[] = [];\n #scheduled = false;\n\n constructor(release: () => void, preparedStatements: PreparedStatements) {\n this.#release = release;\n this.#preparedStatements = preparedStatements;\n }\n\n has(key: string): Promise<boolean> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingHasKeys.push(key);\n this.#pendingHasCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n get(key: string): Promise<ReadonlyJSONValue | undefined> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingGetKeys.push(key);\n this.#pendingGetCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n #scheduleLookup(): void {\n if (!this.#scheduled) {\n this.#scheduled = true;\n queueMicrotask(() => {\n this.#scheduled = false;\n\n const ps = this.#preparedStatements;\n const getKeys = this.#pendingGetKeys;\n this.#pendingGetKeys = [];\n const getCallbacks = this.#pendingGetCallbacks;\n this.#pendingGetCallbacks = [];\n const hasKeys = this.#pendingHasKeys;\n this.#pendingHasKeys = [];\n const hasCallbacks = this.#pendingHasCallbacks;\n this.#pendingHasCallbacks = [];\n\n if (this.#closed) {\n const e = transactionError();\n rejectAll(getCallbacks, e);\n rejectAll(hasCallbacks, e);\n return;\n }\n\n if (getKeys.length > 0) {\n void flushGets(getKeys, getCallbacks, ps);\n }\n if (hasKeys.length > 0) {\n void flushHas(hasKeys, hasCallbacks, ps);\n }\n });\n }\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n this.#release();\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport class SQLiteWrite extends WriteImplBase implements Write {\n readonly #release: () => void;\n readonly #dbDelegate: SQLiteDatabase;\n readonly #preparedStatements: PreparedStatements;\n #committed = false;\n #closed = false;\n\n constructor(\n release: () => void,\n dbDelegate: SQLiteDatabase,\n preparedStatements: PreparedStatements,\n ) {\n super(new SQLiteStoreRead(() => undefined, preparedStatements));\n this.#release = release;\n this.#dbDelegate = dbDelegate;\n this.#preparedStatements = preparedStatements;\n }\n\n async commit(): Promise<void> {\n if (this.#closed) {\n throw transactionError();\n }\n\n const deleteKeys: string[] = [];\n for (const entry of this._pending) {\n if (entry[1] === deleteSentinel) {\n deleteKeys.push(entry[0]);\n this._pending.delete(entry[0]);\n }\n }\n\n // Bind real parameters rather than serializing the whole pending set into\n // one JSON document for json_each() to parse back out. Serializing it, and\n // pushing the resulting (often megabyte-scale) string across the native\n // bridge, measured as roughly a quarter of persist on device.\n //\n // Puts and deletes go out SEQUENTIALLY, and so do the batches within each.\n // Puts and deletes used to overlap, on the grounds that they use different\n // statements over disjoint keys (deletes were removed from _pending above).\n // That is true of the happy path and not of a failing one:\n //\n // A commit is several statements and its COMMIT runs only after the last\n // one. SQLite rolls a transaction back ITSELF on an I/O error — that is what\n // `cannot rollback - no transaction is active` reports at release() — and a\n // statement reaching the connection after that runs in autocommit mode. So\n // while two statements of one commit are in flight together, a failure in\n // one leaves the other to land OUTSIDE the transaction and persist on its\n // own: the commit half-applies, durably, while the caller is told it failed.\n // For a dag write that is a head kept while the chunks it reaches are\n // deleted, or a chunk's refcount row dropped while the chunk and its\n // referrer remain — a count that no longer counts what is there, which\n // computeRefCountUpdates() accepts when it reaches zero (it rejects only a\n // negative) and #applyRefCountUpdates() then acts on by deleting a live\n // chunk, inside a transaction that commits cleanly.\n //\n // Awaiting both promises would not be enough: by the time one rejects the\n // other is already at the driver and cannot be recalled. Nothing may be in\n // flight beside a statement that can fail. Sequencing also means no batch is\n // left unawaited, which `if (putP) await putP; if (delP) await delP;` did\n // whenever the puts rejected first — an unhandled rejection on that commit.\n //\n // The batches *within* each kind could never overlap anyway: the\n // power-of-two split reuses a width when a commit is wide enough (300 rows\n // -> 128, 128, 32, 8, 4), and running two of those concurrently would have\n // two callers on one prepared statement — the rebind-during-execute hazard\n // described in kv/expo-sqlite/store.ts, which op-sqlite has no\n // per-statement lock to absorb.\n if (this._pending.size > 0) {\n await execInBatches(\n [...this._pending] as [string, ReadonlyJSONValue][],\n this.#preparedStatements.putN,\n ([key, value], out) => {\n out.push(key, JSON.stringify(value));\n },\n );\n }\n if (deleteKeys.length > 0) {\n await execInBatches(\n deleteKeys,\n this.#preparedStatements.delN,\n (key, out) => {\n out.push(key);\n },\n );\n }\n\n this.#dbDelegate.execSync('COMMIT');\n this._pending.clear();\n this.#committed = true;\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n super.release();\n let rollbackError: unknown;\n if (!this.#committed) {\n try {\n this.#dbDelegate.execSync('ROLLBACK');\n } catch (e) {\n rollbackError = e;\n }\n }\n this.#release();\n if (rollbackError !== undefined) {\n throw rollbackError;\n }\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\ntype StoreEntry = {\n readonly lock: RWLock;\n readonly db: SQLiteDatabase;\n refCount: number;\n activeReaders: number;\n preparedStatements: PreparedStatements;\n};\n\n// Global map to share database connections between multiple store instances with the same name\nconst stores = new Map<string, StoreEntry>();\n\n/**\n * Gets an existing store entry or creates a new one if it doesn't exist.\n * This implements the shared connection pattern where multiple stores with the same\n * name share the same database connection, lock, and delegate.\n */\nfunction getOrCreateEntry(\n filename: string,\n create: (filename: string, opts?: SQLiteStoreOptions) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): StoreEntry {\n const entry = stores.get(filename);\n\n if (entry) {\n entry.refCount++;\n return entry;\n }\n\n const dbDelegate = reportingStorageFailures(\n rethrowingStorageFailures(() => create(filename, opts)),\n );\n let preparedStatements: PreparedStatements;\n try {\n preparedStatements = setupDatabase(dbDelegate, opts);\n } catch (e) {\n // Not in `stores` yet, so nothing else would close this connection.\n try {\n dbDelegate.close();\n } catch {\n // The setup error is the one to report.\n }\n throw e;\n }\n\n const lock = new RWLock();\n\n const newEntry: StoreEntry = {\n lock,\n db: dbDelegate,\n refCount: 1,\n activeReaders: 0,\n preparedStatements,\n };\n stores.set(filename, newEntry);\n return newEntry;\n}\n\nconst STORAGE_FAILURE_SIGNATURES = [\n [/database or disk is full|SQLITE_FULL/, 'full'],\n [/unable to open database file|SQLITE_CANTOPEN/, 'cannot-open'],\n [/disk I\\/O error|SQLITE_IOERR/, 'io-error'],\n] as const;\n\n/**\n * The storage failure a SQLite error reports, if any. Matched on SQLite's own\n * `sqlite3_errmsg` text (or its symbolic code), which expo-sqlite, op-sqlite\n * and zero-sqlite all surface inside whatever wrapper they add.\n * `SQLITE_BUSY` is deliberately not one: it is contention, and `busy_timeout`\n * plus a retry is the right answer to it.\n */\nexport function classifySQLiteError(\n error: unknown,\n): StorageFailureKind | undefined {\n const message =\n error instanceof Error\n ? `${error.name}: ${error.message}`\n : typeof error === 'string'\n ? error\n : '';\n for (const [signature, kind] of STORAGE_FAILURE_SIGNATURES) {\n if (signature.test(message)) {\n return kind;\n }\n }\n return undefined;\n}\n\nfunction toStorageFailure(error: unknown): unknown {\n const kind = classifySQLiteError(error);\n return kind === undefined\n ? error\n : new StorageFailureError(\n kind,\n error instanceof Error ? error.message : String(error),\n {cause: error},\n );\n}\n\nfunction rethrowingStorageFailures<T>(f: () => T): T {\n try {\n return f();\n } catch (e) {\n throw toStorageFailure(e);\n }\n}\n\n/**\n * Wraps the driver so every SQLite error that reports a storage failure\n * reaches the store's callers as a {@link StorageFailureError}, whichever\n * statement or transaction step it came from.\n */\nfunction reportingStorageFailures(db: SQLiteDatabase): SQLiteDatabase {\n return {\n close: () => db.close(),\n destroy: () => db.destroy(),\n execSync: sql => rethrowingStorageFailures(() => db.execSync(sql)),\n prepare: sql => {\n const statement = rethrowingStorageFailures(() => db.prepare(sql));\n return {\n exec: async params => {\n try {\n return await statement.exec(params);\n } catch (e) {\n throw toStorageFailure(e);\n }\n },\n all: async params => {\n try {\n return await statement.all(params);\n } catch (e) {\n throw toStorageFailure(e);\n }\n },\n };\n },\n };\n}\n\n/**\n * Decrements the reference count for a shared store and cleans up resources\n * when the last reference is released.\n */\n\nfunction decrementStoreRefCount(\n filename: string,\n dbDelegate: SQLiteDatabase,\n): void {\n const entry = stores.get(filename);\n if (entry) {\n entry.refCount--;\n if (entry.refCount <= 0) {\n dbDelegate.close();\n stores.delete(filename);\n }\n }\n}\nexport function clearAllNamedStoresForTesting(): void {\n for (const entry of stores.values()) {\n entry.db.close();\n }\n stores.clear();\n}\n\nexport function dropStore(\n name: string,\n createDelegate: (\n filename: string,\n opts?: SQLiteStoreOptions,\n ) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): Promise<void> {\n const filename = resolveFilename(name, opts);\n const entry = stores.get(filename);\n if (entry) {\n try {\n entry.db.close();\n } catch {\n // Ignore close errors\n }\n stores.delete(filename);\n }\n\n // Create a temporary delegate to handle database deletion\n const tempDelegate = createDelegate(filename, opts);\n try {\n // we close the db before destroying it - this\n // caused an issue with expo-sqlite since it requires this\n tempDelegate.close();\n } catch {\n // Ignore close errors\n }\n try {\n tempDelegate.destroy();\n } catch {\n // Destroy errors shouldn't be fatal; the file may already be gone or locked\n }\n\n return Promise.resolve();\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sqlite-store.js","names":[],"sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"sourcesContent":["import {RWLock} from '@rocicorp/lock';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\nimport {deepFreeze} from '../frozen-json.ts';\nimport {\n StorageFailureError,\n type StorageFailureKind,\n} from '../storage-failure.ts';\nimport type {Read, Store, Write} from './store.ts';\nimport {\n maybeTransactionIsClosedRejection,\n throwIfStoreClosed,\n transactionError,\n} from './throw-if-closed.ts';\nimport {deleteSentinel, WriteImplBase} from './write-impl-base.ts';\n\n/**\n * A SQLite prepared statement.\n *\n * `SQLiteStore` prepares one statement per SQL and shares it across all\n * concurrent readers, so implementations must make each call atomic with\n * respect to other callers of the same statement: `all()` must not let another\n * `exec()`/`all()` rebind or reset the underlying statement between executing\n * it and fetching its rows. Delegates whose native API splits execute and fetch\n * into separate round trips (e.g. expo-sqlite) must serialize per statement.\n */\nexport interface PreparedStatement {\n exec(params: string[]): Promise<void>;\n all(params: string[]): Promise<unknown[][]>;\n}\n\nexport interface SQLiteDatabase {\n /**\n * Close the database connection.\n */\n close(): void;\n\n /**\n * Destroy or delete the database (e.g. delete file).\n */\n destroy(): void;\n\n /**\n * Prepare a SQL string, returning a statement you can execute.\n * E.g. `const stmt = db.prepare(\"SELECT * FROM todos WHERE id=?\");`\n */\n prepare(sql: string): PreparedStatement;\n\n // for PRAGMA statements, schema creation and transaction control.\n execSync(sql: string): void;\n}\n\nexport type CreateSQLiteDatabase = (\n filename: string,\n opts?: SQLiteStoreOptions,\n) => SQLiteDatabase;\n\n/**\n * SQLite-based implementation of the Store interface using a configurable delegate.\n * Supports shared connections between multiple store instances with the same name,\n * providing efficient resource utilization and proper transaction isolation.\n * Uses parameterized queries for safety and performance.\n */\nexport class SQLiteStore implements Store {\n readonly #filename: string;\n readonly #entry: StoreEntry;\n readonly #kind: string;\n\n #closed = false;\n\n constructor(\n name: string,\n create: CreateSQLiteDatabase,\n opts?: SQLiteStoreOptions,\n kind = 'sqlite',\n ) {\n this.#filename = resolveFilename(name, opts);\n this.#entry = getOrCreateEntry(this.#filename, create, opts);\n this.#kind = kind;\n }\n\n get kind(): string {\n return this.#kind;\n }\n\n async read(): Promise<Read> {\n throwIfStoreClosed(this);\n\n const entry = this.#entry;\n const {db, lock, preparedStatements} = entry;\n const release = await lock.read();\n\n // Start shared read transaction if this is the first reader\n // This ensures consistent reads across all concurrent readers\n try {\n if (entry.activeReaders === 0) {\n db.execSync('BEGIN');\n }\n } catch (e) {\n // The lock was acquired above; without this every later read() and\n // write() on the store waits for a release that never comes.\n release();\n throw e;\n }\n entry.activeReaders++;\n\n return new SQLiteStoreRead(() => {\n entry.activeReaders--;\n // Commit shared read transaction when last reader finishes. SQLite may\n // already have rolled the transaction back on its own (an I/O error\n // inside any reader's statement does that), in which case COMMIT\n // throws \"cannot commit - no transaction is active\". The error is\n // reported to the releasing reader, but the RWLock must be released\n // regardless or the store hangs for the rest of the process.\n try {\n if (entry.activeReaders === 0) {\n db.execSync('COMMIT');\n }\n } finally {\n release();\n }\n }, preparedStatements);\n }\n\n async write(): Promise<Write> {\n throwIfStoreClosed(this);\n\n const {lock, db, preparedStatements} = this.#entry;\n const release = await lock.write();\n\n // At this point, RWLock guarantees no active readers\n // The last reader would have already committed the shared transaction\n\n try {\n db.execSync('BEGIN IMMEDIATE');\n } catch (e) {\n // SQLITE_BUSY once busy_timeout expires, or an I/O error. The caller\n // gets the error; the write lock must not stay held.\n release();\n throw e;\n }\n\n return new SQLiteWrite(release, db, preparedStatements);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n const {lock, db} = this.#entry;\n // Wait for all readers and writers to finish.\n const writeRelease = await lock.write();\n\n // Handle reference counting for shared stores - only close database\n // when this is the last store instance using it\n decrementStoreRefCount(this.#filename, db);\n\n this.#closed = true;\n writeRelease();\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport function safeFilename(name: string): string {\n return name.replace(/[^a-zA-Z0-9]/g, '_');\n}\n\nfunction resolveFilename(name: string, opts?: SQLiteStoreOptions): string {\n const safe = safeFilename(name);\n const dir = opts?.directory;\n return dir ? `${dir}/${safe}` : safe;\n}\n\nexport type PreparedStatements = {\n has: PreparedStatement;\n get: PreparedStatement;\n hasMany: PreparedStatement;\n getMany: PreparedStatement;\n put: PreparedStatement;\n del: PreparedStatement;\n /** Multi-row INSERT with the values bound as parameters, n rows wide. */\n putN: (n: number) => PreparedStatement;\n /** Multi-key DELETE with the keys bound as parameters, n keys wide. */\n delN: (n: number) => PreparedStatement;\n};\n\n/**\n * Widest batch we bind in one statement. SQLite's SQLITE_MAX_VARIABLE_NUMBER is\n * 32766, and a put costs two parameters per row, so this is far below the cap;\n * it exists to bound how many distinct statements we prepare and cache.\n */\nconst MAX_BATCH = 128;\n\n/** `repeatList('?', 3)` -> `'?,?,?'`. */\nfunction repeatList(item: string, n: number): string {\n return `${item},`.repeat(n).slice(0, -1);\n}\n\n/**\n * Prepares (and caches) a statement of each width on demand. Callers only ever\n * ask for powers of two, so the cache holds at most log2(MAX_BATCH)+1 entries\n * however many rows a commit turns out to have.\n */\nfunction batchStatements(\n delegate: SQLiteDatabase,\n sqlFor: (n: number) => string,\n): (n: number) => PreparedStatement {\n const cache = new Map<number, PreparedStatement>();\n return (n: number) =>\n getOrInsertComputed(cache, n, () => delegate.prepare(sqlFor(n)));\n}\n\n/**\n * Runs `items` through `getStatement` in power-of-two sized batches, so any\n * length is covered by a handful of cached statement widths.\n */\nasync function execInBatches<T>(\n items: readonly T[],\n getStatement: (n: number) => PreparedStatement,\n toParams: (item: T, out: string[]) => void,\n): Promise<void> {\n for (let i = 0; i < items.length;) {\n const remaining = Math.min(MAX_BATCH, items.length - i);\n const n = 1 << (31 - Math.clz32(remaining));\n const params: string[] = [];\n for (let j = 0; j < n; j++) {\n toParams(items[i + j], params);\n }\n await getStatement(n).exec(params);\n i += n;\n }\n}\n\nexport interface SQLiteStoreOptions {\n // Common options\n busyTimeout?: number;\n journalMode?: 'WAL' | 'DELETE';\n synchronous?: 'NORMAL' | 'FULL';\n readUncommitted?: boolean;\n /** Directory in which to create the SQLite file. Defaults to the process CWD. */\n directory?: string | undefined;\n}\n\n/**\n * Replicache's rows are B-tree chunks, which `BTreeWrite` targets at 8-16KB, so\n * most of them are larger than a page and spill into overflow pages at either\n * page size. 8192 does not avoid overflow; it halves the number of pages each\n * chunk is split across.\n *\n * Measured with `replicache-perf/rn`, 4096 vs 8192 + mmap, change in time:\n *\n * | device | persist 1024x10000 | startup read (expo / op) |\n * | ---------------------- | ------------------ | ------------------------ |\n * | iOS simulator | -6% | -12% / -14% |\n * | Android emulator, 2GB | -1% to -2% | -19% / -8% |\n * | Pixel 6 | ~0% | ~-40% / ~0% |\n *\n * Split by pragma on iOS, page_size carries the write win (~6%) and mmap most\n * of the read win. Neither regressed anything on any device.\n */\nconst PAGE_SIZE = 8192;\n\n/** 256MB, matching op-sqlite's own key-value store. */\nconst MMAP_SIZE = 268435456;\n\n/**\n * Common database setup logic shared between expo-sqlite and op-sqlite implementations.\n * Configures SQLite pragmas, creates the entry table, and prepares common statements.\n */\n\nexport function setupDatabase(\n delegate: SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): PreparedStatements {\n // Configure SQLite pragmas for optimal performance.\n //\n // page_size MUST come first. SQLite silently ignores it once the database\n // has content or a journal mode has been set — no error, no warning, the\n // pragma just does nothing and you are left on the 4096 default. Verified:\n // issuing it after `journal_mode = WAL`, or after CREATE TABLE, leaves\n // `PRAGMA page_size` reporting 4096. Do not reorder these.\n delegate.execSync(`PRAGMA page_size = ${PAGE_SIZE}`);\n delegate.execSync(`PRAGMA busy_timeout = ${opts?.busyTimeout ?? 200}`);\n delegate.execSync(`PRAGMA journal_mode = '${opts?.journalMode ?? 'WAL'}'`);\n delegate.execSync(`PRAGMA synchronous = '${opts?.synchronous ?? 'NORMAL'}'`);\n delegate.execSync(\n `PRAGMA read_uncommitted = ${Boolean(opts?.readUncommitted)}`,\n );\n // Reads served from the mmap window rather than the pager account for most\n // of the startup-read win measured in the PAGE_SIZE comment.\n delegate.execSync(`PRAGMA mmap_size = ${MMAP_SIZE}`);\n\n // Create the entry table.\n //\n // This is deliberately a rowid table, not `WITHOUT ROWID`. A `WITHOUT ROWID`\n // table stores whole rows in an index B-tree, which keeps at most ~1/4 of a\n // page inline, and SQLite recommends it only for rows under ~1/20 of a page.\n // Our rows are 8-16KB chunks. As a rowid table the key gets a small separate\n // index and the values live in the table B-tree. On a Pixel 6 (5 rounds, on\n // top of the pragmas above) that cut startup read by 24% on both expo and\n // op, expo startup scan by 10%, and persist 1024x10000 by 4-7%.\n //\n // `key` needs an explicit NOT NULL: in a rowid table a non-INTEGER primary key\n // is only a UNIQUE index, and SQLite (a bug kept for compatibility) lets it\n // hold NULLs, several of them. `WITHOUT ROWID` enforced this implicitly.\n //\n // `IF NOT EXISTS` leaves an existing database's table as it was created, so\n // stores created before this change stay `WITHOUT ROWID` until recreated.\n delegate.execSync(`\n CREATE TABLE IF NOT EXISTS entry (\n key TEXT PRIMARY KEY NOT NULL,\n value TEXT NOT NULL\n )\n `);\n\n // Prepare common statements\n return {\n has: delegate.prepare(`SELECT 1 FROM entry WHERE key = ? LIMIT 1`),\n get: delegate.prepare('SELECT value FROM entry WHERE key = ?'),\n hasMany: delegate.prepare(\n `SELECT key FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n getMany: delegate.prepare(\n `SELECT key, value FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n put: delegate.prepare(\n `INSERT OR REPLACE INTO entry (key, value)\n SELECT e.value->>0, e.value->1 FROM json_each(?) e`,\n ),\n del: delegate.prepare(\n `DELETE FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n putN: batchStatements(\n delegate,\n n =>\n `INSERT OR REPLACE INTO entry (key, value) VALUES ${repeatList('(?,?)', n)}`,\n ),\n delN: batchStatements(\n delegate,\n n => `DELETE FROM entry WHERE key IN (${repeatList('?', n)})`,\n ),\n };\n}\n\n// Callbacks are stored as striped pairs: [resolve, reject, resolve, reject, ...]\nconst CB_STRIDE = 2;\nconst CB_RESOLVE = 0;\nconst CB_REJECT = 1;\n\ntype GetResolve = (v: ReadonlyJSONValue | undefined) => void;\ntype HasResolve = (v: boolean) => void;\ntype Reject = (e: unknown) => void;\n\nfunction rejectAll(callbacks: unknown[], e: unknown): void {\n for (let i = CB_REJECT; i < callbacks.length; i += CB_STRIDE) {\n (callbacks[i] as Reject)(e);\n }\n}\n\nfunction parseRawValue(raw: string | undefined): ReadonlyJSONValue | undefined {\n return raw === undefined\n ? undefined\n : deepFreeze(JSON.parse(raw) as ReadonlyJSONValue);\n}\n\nfunction resolveGet(\n resolve: GetResolve,\n reject: Reject,\n raw: string | undefined,\n): void {\n try {\n resolve(parseRawValue(raw));\n } catch (e) {\n reject(e);\n }\n}\n\nasync function flushGets(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.get.all([keys[0]])\n : await ps.getMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n resolveGet(\n callbacks[CB_RESOLVE] as GetResolve,\n callbacks[CB_REJECT] as Reject,\n rows[0]?.[0] as string | undefined,\n );\n return;\n }\n const resultMap = new Map(rows as [string, string][]);\n for (let i = 0; i < keys.length; i++) {\n resolveGet(\n callbacks[i * CB_STRIDE + CB_RESOLVE] as GetResolve,\n callbacks[i * CB_STRIDE + CB_REJECT] as Reject,\n resultMap.get(keys[i]),\n );\n }\n}\n\nasync function flushHas(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.has.all([keys[0]])\n : await ps.hasMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n (callbacks[CB_RESOLVE] as HasResolve)(rows.length > 0);\n return;\n }\n const existingKeys = new Set(rows.map(row => row[0] as string));\n for (let i = 0; i < keys.length; i++) {\n (callbacks[i * CB_STRIDE + CB_RESOLVE] as HasResolve)(\n existingKeys.has(keys[i]),\n );\n }\n}\n\nexport class SQLiteStoreRead implements Read {\n readonly #release: () => void;\n readonly #preparedStatements: PreparedStatements;\n #closed = false;\n #pendingGetKeys: string[] = [];\n #pendingGetCallbacks: unknown[] = [];\n #pendingHasKeys: string[] = [];\n #pendingHasCallbacks: unknown[] = [];\n #scheduled = false;\n\n constructor(release: () => void, preparedStatements: PreparedStatements) {\n this.#release = release;\n this.#preparedStatements = preparedStatements;\n }\n\n has(key: string): Promise<boolean> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingHasKeys.push(key);\n this.#pendingHasCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n get(key: string): Promise<ReadonlyJSONValue | undefined> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingGetKeys.push(key);\n this.#pendingGetCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n #scheduleLookup(): void {\n if (!this.#scheduled) {\n this.#scheduled = true;\n queueMicrotask(() => {\n this.#scheduled = false;\n\n const ps = this.#preparedStatements;\n const getKeys = this.#pendingGetKeys;\n this.#pendingGetKeys = [];\n const getCallbacks = this.#pendingGetCallbacks;\n this.#pendingGetCallbacks = [];\n const hasKeys = this.#pendingHasKeys;\n this.#pendingHasKeys = [];\n const hasCallbacks = this.#pendingHasCallbacks;\n this.#pendingHasCallbacks = [];\n\n if (this.#closed) {\n const e = transactionError();\n rejectAll(getCallbacks, e);\n rejectAll(hasCallbacks, e);\n return;\n }\n\n if (getKeys.length > 0) {\n void flushGets(getKeys, getCallbacks, ps);\n }\n if (hasKeys.length > 0) {\n void flushHas(hasKeys, hasCallbacks, ps);\n }\n });\n }\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n this.#release();\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport class SQLiteWrite extends WriteImplBase implements Write {\n readonly #release: () => void;\n readonly #dbDelegate: SQLiteDatabase;\n readonly #preparedStatements: PreparedStatements;\n #committed = false;\n #closed = false;\n\n constructor(\n release: () => void,\n dbDelegate: SQLiteDatabase,\n preparedStatements: PreparedStatements,\n ) {\n super(new SQLiteStoreRead(() => undefined, preparedStatements));\n this.#release = release;\n this.#dbDelegate = dbDelegate;\n this.#preparedStatements = preparedStatements;\n }\n\n async commit(): Promise<void> {\n if (this.#closed) {\n throw transactionError();\n }\n\n const deleteKeys: string[] = [];\n for (const entry of this._pending) {\n if (entry[1] === deleteSentinel) {\n deleteKeys.push(entry[0]);\n this._pending.delete(entry[0]);\n }\n }\n\n // Bind real parameters rather than serializing the whole pending set into\n // one JSON document for json_each() to parse back out. Serializing it, and\n // pushing the resulting (often megabyte-scale) string across the native\n // bridge, measured as roughly a quarter of persist on device.\n //\n // Puts and deletes go out SEQUENTIALLY, and so do the batches within each.\n // Puts and deletes used to overlap, on the grounds that they use different\n // statements over disjoint keys (deletes were removed from _pending above).\n // That is true of the happy path and not of a failing one:\n //\n // A commit is several statements and its COMMIT runs only after the last\n // one. SQLite rolls a transaction back ITSELF on an I/O error — that is what\n // `cannot rollback - no transaction is active` reports at release() — and a\n // statement reaching the connection after that runs in autocommit mode. So\n // while two statements of one commit are in flight together, a failure in\n // one leaves the other to land OUTSIDE the transaction and persist on its\n // own: the commit half-applies, durably, while the caller is told it failed.\n // For a dag write that is a head kept while the chunks it reaches are\n // deleted, or a chunk's refcount row dropped while the chunk and its\n // referrer remain — a count that no longer counts what is there, which\n // computeRefCountUpdates() accepts when it reaches zero (it rejects only a\n // negative) and #applyRefCountUpdates() then acts on by deleting a live\n // chunk, inside a transaction that commits cleanly.\n //\n // Awaiting both promises would not be enough: by the time one rejects the\n // other is already at the driver and cannot be recalled. Nothing may be in\n // flight beside a statement that can fail. Sequencing also means no batch is\n // left unawaited, which `if (putP) await putP; if (delP) await delP;` did\n // whenever the puts rejected first — an unhandled rejection on that commit.\n //\n // The batches *within* each kind could never overlap anyway: the\n // power-of-two split reuses a width when a commit is wide enough (300 rows\n // -> 128, 128, 32, 8, 4), and running two of those concurrently would have\n // two callers on one prepared statement — the rebind-during-execute hazard\n // described in kv/expo-sqlite/store.ts, which op-sqlite has no\n // per-statement lock to absorb.\n if (this._pending.size > 0) {\n await execInBatches(\n [...this._pending] as [string, ReadonlyJSONValue][],\n this.#preparedStatements.putN,\n ([key, value], out) => {\n out.push(key, JSON.stringify(value));\n },\n );\n }\n if (deleteKeys.length > 0) {\n await execInBatches(\n deleteKeys,\n this.#preparedStatements.delN,\n (key, out) => {\n out.push(key);\n },\n );\n }\n\n this.#dbDelegate.execSync('COMMIT');\n this._pending.clear();\n this.#committed = true;\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n super.release();\n let rollbackError: unknown;\n if (!this.#committed) {\n try {\n this.#dbDelegate.execSync('ROLLBACK');\n } catch (e) {\n rollbackError = e;\n }\n }\n this.#release();\n if (rollbackError !== undefined) {\n throw rollbackError;\n }\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\ntype StoreEntry = {\n readonly lock: RWLock;\n readonly db: SQLiteDatabase;\n refCount: number;\n activeReaders: number;\n preparedStatements: PreparedStatements;\n};\n\n// Global map to share database connections between multiple store instances with the same name\nconst stores = new Map<string, StoreEntry>();\n\n/**\n * Gets an existing store entry or creates a new one if it doesn't exist.\n * This implements the shared connection pattern where multiple stores with the same\n * name share the same database connection, lock, and delegate.\n */\nfunction getOrCreateEntry(\n filename: string,\n create: (filename: string, opts?: SQLiteStoreOptions) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): StoreEntry {\n const entry = stores.get(filename);\n\n if (entry) {\n entry.refCount++;\n return entry;\n }\n\n const dbDelegate = reportingStorageFailures(\n rethrowingStorageFailures(() => create(filename, opts)),\n );\n let preparedStatements: PreparedStatements;\n try {\n preparedStatements = setupDatabase(dbDelegate, opts);\n } catch (e) {\n // Not in `stores` yet, so nothing else would close this connection.\n try {\n dbDelegate.close();\n } catch {\n // The setup error is the one to report.\n }\n throw e;\n }\n\n const lock = new RWLock();\n\n const newEntry: StoreEntry = {\n lock,\n db: dbDelegate,\n refCount: 1,\n activeReaders: 0,\n preparedStatements,\n };\n stores.set(filename, newEntry);\n return newEntry;\n}\n\nconst STORAGE_FAILURE_SIGNATURES = [\n [/database or disk is full|SQLITE_FULL/, 'full'],\n [/unable to open database file|SQLITE_CANTOPEN/, 'cannot-open'],\n [/disk I\\/O error|SQLITE_IOERR/, 'io-error'],\n] as const;\n\n/**\n * The storage failure a SQLite error reports, if any. Matched on SQLite's own\n * `sqlite3_errmsg` text (or its symbolic code), which expo-sqlite, op-sqlite\n * and zero-sqlite all surface inside whatever wrapper they add.\n * `SQLITE_BUSY` is deliberately not one: it is contention, and `busy_timeout`\n * plus a retry is the right answer to it.\n */\nexport function classifySQLiteError(\n error: unknown,\n): StorageFailureKind | undefined {\n const message =\n error instanceof Error\n ? `${error.name}: ${error.message}`\n : typeof error === 'string'\n ? error\n : '';\n for (const [signature, kind] of STORAGE_FAILURE_SIGNATURES) {\n if (signature.test(message)) {\n return kind;\n }\n }\n return undefined;\n}\n\nfunction toStorageFailure(error: unknown): unknown {\n const kind = classifySQLiteError(error);\n return kind === undefined\n ? error\n : new StorageFailureError(\n kind,\n error instanceof Error ? error.message : String(error),\n {cause: error},\n );\n}\n\nfunction rethrowingStorageFailures<T>(f: () => T): T {\n try {\n return f();\n } catch (e) {\n throw toStorageFailure(e);\n }\n}\n\n/**\n * Wraps the driver so every SQLite error that reports a storage failure\n * reaches the store's callers as a {@link StorageFailureError}, whichever\n * statement or transaction step it came from.\n */\nfunction reportingStorageFailures(db: SQLiteDatabase): SQLiteDatabase {\n return {\n close: () => db.close(),\n destroy: () => db.destroy(),\n execSync: sql => rethrowingStorageFailures(() => db.execSync(sql)),\n prepare: sql => {\n const statement = rethrowingStorageFailures(() => db.prepare(sql));\n return {\n exec: async params => {\n try {\n return await statement.exec(params);\n } catch (e) {\n throw toStorageFailure(e);\n }\n },\n all: async params => {\n try {\n return await statement.all(params);\n } catch (e) {\n throw toStorageFailure(e);\n }\n },\n };\n },\n };\n}\n\n/**\n * Decrements the reference count for a shared store and cleans up resources\n * when the last reference is released.\n */\n\nfunction decrementStoreRefCount(\n filename: string,\n dbDelegate: SQLiteDatabase,\n): void {\n const entry = stores.get(filename);\n if (entry) {\n entry.refCount--;\n if (entry.refCount <= 0) {\n dbDelegate.close();\n stores.delete(filename);\n }\n }\n}\nexport function clearAllNamedStoresForTesting(): void {\n for (const entry of stores.values()) {\n entry.db.close();\n }\n stores.clear();\n}\n\nexport function dropStore(\n name: string,\n createDelegate: (\n filename: string,\n opts?: SQLiteStoreOptions,\n ) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): Promise<void> {\n const filename = resolveFilename(name, opts);\n const entry = stores.get(filename);\n if (entry) {\n try {\n entry.db.close();\n } catch {\n // Ignore close errors\n }\n stores.delete(filename);\n }\n\n // Create a temporary delegate to handle database deletion\n const tempDelegate = createDelegate(filename, opts);\n try {\n // we close the db before destroying it - this\n // caused an issue with expo-sqlite since it requires this\n tempDelegate.close();\n } catch {\n // Ignore close errors\n }\n try {\n tempDelegate.destroy();\n } catch {\n // Destroy errors shouldn't be fatal; the file may already be gone or locked\n }\n\n return Promise.resolve();\n}\n"],"mappings":"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"}
@@ -22,7 +22,10 @@ var WriteImplBase = class {
22
22
  const v = this._pending.get(key);
23
23
  switch (v) {
24
24
  case deleteSentinel: return;
25
- case void 0: return deepFreezeAllowUndefined(await this.#read.get(key));
25
+ case void 0: {
26
+ const v = await this.#read.get(key);
27
+ return deepFreezeAllowUndefined(v);
28
+ }
26
29
  default: return v;
27
30
  }
28
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"write-impl-base.js","names":["#read"],"sources":["../../../../../replicache/src/kv/write-impl-base.ts"],"sourcesContent":["import type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {\n promiseFalse,\n promiseTrue,\n promiseVoid,\n} from '../../../shared/src/resolved-promises.ts';\nimport {\n type FrozenJSONValue,\n deepFreeze,\n deepFreezeAllowUndefined,\n} from '../frozen-json.ts';\nimport type {Read} from './store.ts';\nimport {\n maybeTransactionIsClosedRejection,\n transactionIsClosedRejection,\n} from './throw-if-closed.ts';\n\nexport const deleteSentinel = Symbol();\ntype DeleteSentinel = typeof deleteSentinel;\n\nexport class WriteImplBase {\n protected readonly _pending: Map<string, FrozenJSONValue | DeleteSentinel> =\n new Map();\n readonly #read: Read;\n\n constructor(read: Read) {\n this.#read = read;\n }\n\n has(key: string): Promise<boolean> {\n if (this.#read.closed) {\n return transactionIsClosedRejection();\n }\n switch (this._pending.get(key)) {\n case undefined:\n return this.#read.has(key);\n case deleteSentinel:\n return promiseFalse;\n default:\n return promiseTrue;\n }\n }\n\n async get(key: string): Promise<FrozenJSONValue | undefined> {\n if (this.#read.closed) {\n return transactionIsClosedRejection();\n }\n const v = this._pending.get(key);\n switch (v) {\n case deleteSentinel:\n return undefined;\n case undefined: {\n const v = await this.#read.get(key);\n return deepFreezeAllowUndefined(v);\n }\n default:\n return v;\n }\n }\n\n put(key: string, value: ReadonlyJSONValue) {\n return (\n maybeTransactionIsClosedRejection(this.#read) ??\n (this._pending.set(key, deepFreeze(value)), promiseVoid)\n );\n }\n\n del(key: string): Promise<void> {\n return (\n maybeTransactionIsClosedRejection(this.#read) ??\n (this._pending.set(key, deleteSentinel), promiseVoid)\n );\n }\n\n release(): void {\n this.#read.release();\n }\n\n get closed(): boolean {\n return this.#read.closed;\n }\n}\n"],"mappings":";;;;AAiBA,IAAa,iBAAiB,OAAO;AAGrC,IAAa,gBAAb,MAA2B;CACzB,2BACE,IAAI,IAAI;CACV;CAEA,YAAY,MAAY;EACtB,KAAKA,QAAQ;CACf;CAEA,IAAI,KAA+B;EACjC,IAAI,KAAKA,MAAM,QACb,OAAO,6BAA6B;EAEtC,QAAQ,KAAK,SAAS,IAAI,GAAG,GAA7B;GACE,KAAK,KAAA,GACH,OAAO,KAAKA,MAAM,IAAI,GAAG;GAC3B,KAAK,gBACH,OAAO;GACT,SACE,OAAO;EACX;CACF;CAEA,MAAM,IAAI,KAAmD;EAC3D,IAAI,KAAKA,MAAM,QACb,OAAO,6BAA6B;EAEtC,MAAM,IAAI,KAAK,SAAS,IAAI,GAAG;EAC/B,QAAQ,GAAR;GACE,KAAK,gBACH;GACF,KAAK,KAAA,GAEH,OAAO,yBAAyB,MADhB,KAAKA,MAAM,IAAI,GAAG,CACD;GAEnC,SACE,OAAO;EACX;CACF;CAEA,IAAI,KAAa,OAA0B;EACzC,OACE,kCAAkC,KAAKA,KAAK,MAC3C,KAAK,SAAS,IAAI,KAAK,WAAW,KAAK,CAAC,GAAG;CAEhD;CAEA,IAAI,KAA4B;EAC9B,OACE,kCAAkC,KAAKA,KAAK,MAC3C,KAAK,SAAS,IAAI,KAAK,cAAc,GAAG;CAE7C;CAEA,UAAgB;EACd,KAAKA,MAAM,QAAQ;CACrB;CAEA,IAAI,SAAkB;EACpB,OAAO,KAAKA,MAAM;CACpB;AACF"}
1
+ {"version":3,"file":"write-impl-base.js","names":[],"sources":["../../../../../replicache/src/kv/write-impl-base.ts"],"sourcesContent":["import type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {\n promiseFalse,\n promiseTrue,\n promiseVoid,\n} from '../../../shared/src/resolved-promises.ts';\nimport {\n type FrozenJSONValue,\n deepFreeze,\n deepFreezeAllowUndefined,\n} from '../frozen-json.ts';\nimport type {Read} from './store.ts';\nimport {\n maybeTransactionIsClosedRejection,\n transactionIsClosedRejection,\n} from './throw-if-closed.ts';\n\nexport const deleteSentinel = Symbol();\ntype DeleteSentinel = typeof deleteSentinel;\n\nexport class WriteImplBase {\n protected readonly _pending: Map<string, FrozenJSONValue | DeleteSentinel> =\n new Map();\n readonly #read: Read;\n\n constructor(read: Read) {\n this.#read = read;\n }\n\n has(key: string): Promise<boolean> {\n if (this.#read.closed) {\n return transactionIsClosedRejection();\n }\n switch (this._pending.get(key)) {\n case undefined:\n return this.#read.has(key);\n case deleteSentinel:\n return promiseFalse;\n default:\n return promiseTrue;\n }\n }\n\n async get(key: string): Promise<FrozenJSONValue | undefined> {\n if (this.#read.closed) {\n return transactionIsClosedRejection();\n }\n const v = this._pending.get(key);\n switch (v) {\n case deleteSentinel:\n return undefined;\n case undefined: {\n const v = await this.#read.get(key);\n return deepFreezeAllowUndefined(v);\n }\n default:\n return v;\n }\n }\n\n put(key: string, value: ReadonlyJSONValue) {\n return (\n maybeTransactionIsClosedRejection(this.#read) ??\n (this._pending.set(key, deepFreeze(value)), promiseVoid)\n );\n }\n\n del(key: string): Promise<void> {\n return (\n maybeTransactionIsClosedRejection(this.#read) ??\n (this._pending.set(key, deleteSentinel), promiseVoid)\n );\n }\n\n release(): void {\n this.#read.release();\n }\n\n get closed(): boolean {\n return this.#read.closed;\n }\n}\n"],"mappings":";;;;AAiBA,IAAa,iBAAiB,OAAO;AAGrC,IAAa,gBAAb,MAA2B;CACzB,2BACE,IAAI,IAAI;CACV;CAEA,YAAY,MAAY;EACtB,KAAK,QAAQ;CACf;CAEA,IAAI,KAA+B;EACjC,IAAI,KAAK,MAAM,QACb,OAAO,6BAA6B;EAEtC,QAAQ,KAAK,SAAS,IAAI,GAAG,GAA7B;GACE,KAAK,KAAA,GACH,OAAO,KAAK,MAAM,IAAI,GAAG;GAC3B,KAAK,gBACH,OAAO;GACT,SACE,OAAO;EACX;CACF;CAEA,MAAM,IAAI,KAAmD;EAC3D,IAAI,KAAK,MAAM,QACb,OAAO,6BAA6B;EAEtC,MAAM,IAAI,KAAK,SAAS,IAAI,GAAG;EAC/B,QAAQ,GAAR;GACE,KAAK,gBACH;GACF,KAAK,KAAA,GAAW;IACd,MAAM,IAAI,MAAM,KAAK,MAAM,IAAI,GAAG;IAClC,OAAO,yBAAyB,CAAC;GACnC;GACA,SACE,OAAO;EACX;CACF;CAEA,IAAI,KAAa,OAA0B;EACzC,OACE,kCAAkC,KAAK,KAAK,MAC3C,KAAK,SAAS,IAAI,KAAK,WAAW,KAAK,CAAC,GAAG;CAEhD;CAEA,IAAI,KAA4B;EAC9B,OACE,kCAAkC,KAAK,KAAK,MAC3C,KAAK,SAAS,IAAI,KAAK,cAAc,GAAG;CAE7C;CAEA,UAAgB;EACd,KAAK,MAAM,QAAQ;CACrB;CAEA,IAAI,SAAkB;EACpB,OAAO,KAAK,MAAM;CACpB;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"write-impl.js","names":["#map"],"sources":["../../../../../replicache/src/kv/write-impl.ts"],"sourcesContent":["import {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {ReadImpl} from './read-impl.ts';\nimport type {Write} from './store.ts';\nimport {transactionIsClosedRejection} from './throw-if-closed.ts';\nimport {deleteSentinel, WriteImplBase} from './write-impl-base.ts';\n\nexport class WriteImpl extends WriteImplBase implements Write {\n readonly #map: Map<string, FrozenJSONValue>;\n\n constructor(map: Map<string, FrozenJSONValue>, release: () => void) {\n super(new ReadImpl(map, release));\n this.#map = map;\n }\n\n commit(): Promise<void> {\n if (this.closed) {\n return transactionIsClosedRejection();\n }\n\n // HOT. Do not allocate entry tuple and destructure.\n this._pending.forEach((value, key) => {\n if (value === deleteSentinel) {\n this.#map.delete(key);\n } else {\n this.#map.set(key, value);\n }\n });\n this._pending.clear();\n this.release();\n return promiseVoid;\n }\n}\n"],"mappings":";;;;;AAOA,IAAa,YAAb,cAA+B,cAA+B;CAC5D;CAEA,YAAY,KAAmC,SAAqB;EAClE,MAAM,IAAI,SAAS,KAAK,OAAO,CAAC;EAChC,KAAKA,OAAO;CACd;CAEA,SAAwB;EACtB,IAAI,KAAK,QACP,OAAO,6BAA6B;EAItC,KAAK,SAAS,SAAS,OAAO,QAAQ;GACpC,IAAI,UAAU,gBACZ,KAAKA,KAAK,OAAO,GAAG;QAEpB,KAAKA,KAAK,IAAI,KAAK,KAAK;EAE5B,CAAC;EACD,KAAK,SAAS,MAAM;EACpB,KAAK,QAAQ;EACb,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"write-impl.js","names":[],"sources":["../../../../../replicache/src/kv/write-impl.ts"],"sourcesContent":["import {promiseVoid} from '../../../shared/src/resolved-promises.ts';\nimport type {FrozenJSONValue} from '../frozen-json.ts';\nimport {ReadImpl} from './read-impl.ts';\nimport type {Write} from './store.ts';\nimport {transactionIsClosedRejection} from './throw-if-closed.ts';\nimport {deleteSentinel, WriteImplBase} from './write-impl-base.ts';\n\nexport class WriteImpl extends WriteImplBase implements Write {\n readonly #map: Map<string, FrozenJSONValue>;\n\n constructor(map: Map<string, FrozenJSONValue>, release: () => void) {\n super(new ReadImpl(map, release));\n this.#map = map;\n }\n\n commit(): Promise<void> {\n if (this.closed) {\n return transactionIsClosedRejection();\n }\n\n // HOT. Do not allocate entry tuple and destructure.\n this._pending.forEach((value, key) => {\n if (value === deleteSentinel) {\n this.#map.delete(key);\n } else {\n this.#map.set(key, value);\n }\n });\n this._pending.clear();\n this.release();\n return promiseVoid;\n }\n}\n"],"mappings":";;;;;AAOA,IAAa,YAAb,cAA+B,cAA+B;CAC5D;CAEA,YAAY,KAAmC,SAAqB;EAClE,MAAM,IAAI,SAAS,KAAK,OAAO,CAAC;EAChC,KAAK,OAAO;CACd;CAEA,SAAwB;EACtB,IAAI,KAAK,QACP,OAAO,6BAA6B;EAItC,KAAK,SAAS,SAAS,OAAO,QAAQ;GACpC,IAAI,UAAU,gBACZ,KAAK,KAAK,OAAO,GAAG;QAEpB,KAAK,KAAK,IAAI,KAAK,KAAK;EAE5B,CAAC;EACD,KAAK,SAAS,MAAM;EACpB,KAAK,QAAQ;EACb,OAAO;CACT;AACF"}
@@ -8,7 +8,8 @@ import { LogContext, TeeLogSink, consoleLogSink } from "@rocicorp/logger";
8
8
  * @returns A LogContext instance configured with the provided options.
9
9
  */
10
10
  function createLogContext(logLevel = "info", logSinks = [consoleLogSink], context) {
11
- return new LogContext(logLevel, context, logSinks.length === 1 ? logSinks[0] : new TeeLogSink(logSinks));
11
+ const logSink = logSinks.length === 1 ? logSinks[0] : new TeeLogSink(logSinks);
12
+ return new LogContext(logLevel, context, logSink);
12
13
  }
13
14
  //#endregion
14
15
  export { createLogContext };
@@ -1 +1 @@
1
- {"version":3,"file":"log-options.js","names":[],"sources":["../../../../replicache/src/log-options.ts"],"sourcesContent":["import {\n consoleLogSink,\n LogContext,\n TeeLogSink,\n type Context,\n type LogLevel,\n type LogSink,\n} from '@rocicorp/logger';\n\n/**\n * Creates a LogContext\n * @param logLevel The log level to use. Default is `'info'`.\n * @param logSinks Destination for logs. Default is `[consoleLogSink]`.\n * @param context Optional: Additional information that can be associated with logs.\n * @returns A LogContext instance configured with the provided options.\n */\nexport function createLogContext(\n logLevel: LogLevel = 'info',\n logSinks: LogSink[] = [consoleLogSink],\n context?: Context,\n): LogContext {\n const logSink =\n logSinks.length === 1 ? logSinks[0] : new TeeLogSink(logSinks);\n return new LogContext(logLevel, context, logSink);\n}\n"],"mappings":";;;;;;;;;AAgBA,SAAgB,iBACd,WAAqB,QACrB,WAAsB,CAAC,cAAc,GACrC,SACY;CAGZ,OAAO,IAAI,WAAW,UAAU,SAD9B,SAAS,WAAW,IAAI,SAAS,KAAK,IAAI,WAAW,QAAQ,CACf;AAClD"}
1
+ {"version":3,"file":"log-options.js","names":[],"sources":["../../../../replicache/src/log-options.ts"],"sourcesContent":["import {\n consoleLogSink,\n LogContext,\n TeeLogSink,\n type Context,\n type LogLevel,\n type LogSink,\n} from '@rocicorp/logger';\n\n/**\n * Creates a LogContext\n * @param logLevel The log level to use. Default is `'info'`.\n * @param logSinks Destination for logs. Default is `[consoleLogSink]`.\n * @param context Optional: Additional information that can be associated with logs.\n * @returns A LogContext instance configured with the provided options.\n */\nexport function createLogContext(\n logLevel: LogLevel = 'info',\n logSinks: LogSink[] = [consoleLogSink],\n context?: Context,\n): LogContext {\n const logSink =\n logSinks.length === 1 ? logSinks[0] : new TeeLogSink(logSinks);\n return new LogContext(logLevel, context, logSink);\n}\n"],"mappings":";;;;;;;;;AAgBA,SAAgB,iBACd,WAAqB,QACrB,WAAsB,CAAC,cAAc,GACrC,SACY;CACZ,MAAM,UACJ,SAAS,WAAW,IAAI,SAAS,KAAK,IAAI,WAAW,QAAQ;CAC/D,OAAO,IAAI,WAAW,UAAU,SAAS,OAAO;AAClD"}
@@ -13,6 +13,6 @@ function makeIDBNameInternal(name, schemaVersion, formatVersion) {
13
13
  return schemaVersion ? `${n}:${schemaVersion}` : n;
14
14
  }
15
15
  //#endregion
16
- export { makeIDBName };
16
+ export { makeIDBName, makeIDBNameInternal as makeIDBNameForTesting };
17
17
 
18
18
  //# sourceMappingURL=make-idb-name.js.map
@@ -1,6 +1,6 @@
1
1
  import { withRead } from "./with-transactions.js";
2
2
  import { getClientGroup } from "./persist/client-groups.js";
3
- import { bc } from "../../shared/src/broadcast-channel.js";
3
+ import { BroadcastChannel as bc } from "../../shared/src/broadcast-channel.js";
4
4
  //#region ../replicache/src/new-client-channel.ts
5
5
  function makeChannelNameV0(replicacheName) {
6
6
  return `replicache-new-client-group:${replicacheName}`;
@@ -27,17 +27,19 @@ function initNewClientChannel(replicacheName, idbName, signal, clientGroupID, is
27
27
  const { data } = e;
28
28
  if (isNewClientChannelMessageV1(data)) {
29
29
  const { clientGroupID: newClientGroupID, idbName: newClientIDBName } = data;
30
- if (newClientGroupID !== clientGroupID) if (newClientIDBName === idbName) {
31
- if (await withRead(perdag, async (perdagRead) => await getClientGroup(newClientGroupID, perdagRead) !== void 0)) onUpdateNeeded();
32
- } else {
33
- onUpdateNeeded();
34
- return;
30
+ if (newClientGroupID !== clientGroupID) {
31
+ if (newClientIDBName === idbName) {
32
+ if (await withRead(perdag, async (perdagRead) => await getClientGroup(newClientGroupID, perdagRead) !== void 0)) onUpdateNeeded();
33
+ } else {
34
+ onUpdateNeeded();
35
+ return;
36
+ }
35
37
  }
36
38
  }
37
39
  };
38
40
  signal.addEventListener("abort", () => channelV1.close(), { once: true });
39
41
  }
40
42
  //#endregion
41
- export { initNewClientChannel };
43
+ export { initNewClientChannel, makeChannelNameV0 as makeChannelNameV0ForTesting, makeChannelNameV1 as makeChannelNameV1ForTesting };
42
44
 
43
45
  //# sourceMappingURL=new-client-channel.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"new-client-channel.js","names":[],"sources":["../../../../replicache/src/new-client-channel.ts"],"sourcesContent":["import {BroadcastChannel} from '../../shared/src/broadcast-channel.ts';\nimport type {Read, Store} from './dag/store.ts';\nimport {getClientGroup} from './persist/client-groups.ts';\nimport {withRead} from './with-transactions.ts';\n\n// Older clients (<= replicache@13.0.1), listened on this channel name\n// and *asserted* that the messages received were an array containing exactly\n// one string.\nfunction makeChannelNameV0(replicacheName: string): string {\n return `replicache-new-client-group:${replicacheName}`;\n}\n\n// This channel name was introduced when we first needed to change the message\n// format. The design of the messages sent on this channel allows for\n// the message content to be extended in the future in a way that is\n// forward and backwards compatible. The message format can be extended\n// by adding new *optional* fields.\nfunction makeChannelNameV1(replicacheName: string): string {\n return `replicache-new-client-group-v1:${replicacheName}`;\n}\n\nexport {\n makeChannelNameV0 as makeChannelNameV0ForTesting,\n makeChannelNameV1 as makeChannelNameV1ForTesting,\n};\n\n// This message type can be extended with optional properties.\ntype NewClientChannelMessageV1 = {clientGroupID: string; idbName: string};\n\nfunction isNewClientChannelMessageV1(\n message: unknown,\n): message is NewClientChannelMessageV1 {\n return (\n typeof message === 'object' &&\n typeof (message as {clientGroupID: unknown}).clientGroupID === 'string' &&\n typeof (message as {idbName: unknown}).idbName === 'string'\n );\n}\n\nexport function initNewClientChannel(\n replicacheName: string,\n idbName: string,\n signal: AbortSignal,\n clientGroupID: string,\n isNewClientGroup: boolean,\n onUpdateNeeded: () => void,\n perdag: Store,\n) {\n if (signal.aborted) {\n return;\n }\n\n const channelV1 = new BroadcastChannel(makeChannelNameV1(replicacheName));\n if (isNewClientGroup) {\n channelV1.postMessage({clientGroupID, idbName});\n // Send expected format to V0 channel for old clients.\n const channelV0 = new BroadcastChannel(makeChannelNameV0(replicacheName));\n channelV0.postMessage([clientGroupID]);\n channelV0.close();\n }\n\n channelV1.onmessage = async (e: MessageEvent) => {\n const {data} = e;\n if (isNewClientChannelMessageV1(data)) {\n const {clientGroupID: newClientGroupID, idbName: newClientIDBName} = data;\n if (newClientGroupID !== clientGroupID) {\n if (newClientIDBName === idbName) {\n // Check if this client can see the new client's newClientGroupID in its\n // perdag. It should be able to if the clients share persistent\n // storage. However, with `ReplicacheOption.kvStore`\n // or a store that failed to open (the instance then runs in memory)\n // clients may not actually share persistent storage. If storage is not shared, then there is no point\n // in updating, since clients cannot sync locally. If clients do update\n // in this case, they can continually cause each other to update, since\n // on each update the clients get assigned a new client group.\n const updateNeeded = await withRead(\n perdag,\n async (perdagRead: Read) =>\n (await getClientGroup(newClientGroupID, perdagRead)) !==\n undefined,\n );\n if (updateNeeded) {\n onUpdateNeeded();\n }\n } else {\n // Idb name is different, indicating new schema or format version.\n // Update to get assigned to newClientIDBName, and hopefully\n // newClientGroupID.\n // If storage is not actually shared (i.e. due to\n // `ReplicacheOption.kvStore`\n // or a store that failed to open) the new client will not\n // get assigned to newClientGroupID, but should get the\n // newClientIDBName.\n // Note: we don't try to read from newClientIDBName to see\n // if this client shares storage with the new client, because\n // the newClientIDBName may have a format version this client\n // cannot read.\n onUpdateNeeded();\n return;\n }\n }\n }\n };\n\n signal.addEventListener('abort', () => channelV1.close(), {once: true});\n}\n"],"mappings":";;;;AAQA,SAAS,kBAAkB,gBAAgC;CACzD,OAAO,+BAA+B;AACxC;AAOA,SAAS,kBAAkB,gBAAgC;CACzD,OAAO,kCAAkC;AAC3C;AAUA,SAAS,4BACP,SACsC;CACtC,OACE,OAAO,YAAY,YACnB,OAAQ,QAAqC,kBAAkB,YAC/D,OAAQ,QAA+B,YAAY;AAEvD;AAEA,SAAgB,qBACd,gBACA,SACA,QACA,eACA,kBACA,gBACA,QACA;CACA,IAAI,OAAO,SACT;CAGF,MAAM,YAAY,IAAI,GAAiB,kBAAkB,cAAc,CAAC;CACxE,IAAI,kBAAkB;EACpB,UAAU,YAAY;GAAC;GAAe;EAAO,CAAC;EAE9C,MAAM,YAAY,IAAI,GAAiB,kBAAkB,cAAc,CAAC;EACxE,UAAU,YAAY,CAAC,aAAa,CAAC;EACrC,UAAU,MAAM;CAClB;CAEA,UAAU,YAAY,OAAO,MAAoB;EAC/C,MAAM,EAAC,SAAQ;EACf,IAAI,4BAA4B,IAAI,GAAG;GACrC,MAAM,EAAC,eAAe,kBAAkB,SAAS,qBAAoB;GACrE,IAAI,qBAAqB,eACvB,IAAI,qBAAqB;QAenB,MANuB,SACzB,QACA,OAAO,eACJ,MAAM,eAAe,kBAAkB,UAAU,MAClD,KAAA,CACJ,GAEE,eAAe;GAAA,OAEZ;IAaL,eAAe;IACf;GACF;EAEJ;CACF;CAEA,OAAO,iBAAiB,eAAe,UAAU,MAAM,GAAG,EAAC,MAAM,KAAI,CAAC;AACxE"}
1
+ {"version":3,"file":"new-client-channel.js","names":["BroadcastChannel"],"sources":["../../../../replicache/src/new-client-channel.ts"],"sourcesContent":["import {BroadcastChannel} from '../../shared/src/broadcast-channel.ts';\nimport type {Read, Store} from './dag/store.ts';\nimport {getClientGroup} from './persist/client-groups.ts';\nimport {withRead} from './with-transactions.ts';\n\n// Older clients (<= replicache@13.0.1), listened on this channel name\n// and *asserted* that the messages received were an array containing exactly\n// one string.\nfunction makeChannelNameV0(replicacheName: string): string {\n return `replicache-new-client-group:${replicacheName}`;\n}\n\n// This channel name was introduced when we first needed to change the message\n// format. The design of the messages sent on this channel allows for\n// the message content to be extended in the future in a way that is\n// forward and backwards compatible. The message format can be extended\n// by adding new *optional* fields.\nfunction makeChannelNameV1(replicacheName: string): string {\n return `replicache-new-client-group-v1:${replicacheName}`;\n}\n\nexport {\n makeChannelNameV0 as makeChannelNameV0ForTesting,\n makeChannelNameV1 as makeChannelNameV1ForTesting,\n};\n\n// This message type can be extended with optional properties.\ntype NewClientChannelMessageV1 = {clientGroupID: string; idbName: string};\n\nfunction isNewClientChannelMessageV1(\n message: unknown,\n): message is NewClientChannelMessageV1 {\n return (\n typeof message === 'object' &&\n typeof (message as {clientGroupID: unknown}).clientGroupID === 'string' &&\n typeof (message as {idbName: unknown}).idbName === 'string'\n );\n}\n\nexport function initNewClientChannel(\n replicacheName: string,\n idbName: string,\n signal: AbortSignal,\n clientGroupID: string,\n isNewClientGroup: boolean,\n onUpdateNeeded: () => void,\n perdag: Store,\n) {\n if (signal.aborted) {\n return;\n }\n\n const channelV1 = new BroadcastChannel(makeChannelNameV1(replicacheName));\n if (isNewClientGroup) {\n channelV1.postMessage({clientGroupID, idbName});\n // Send expected format to V0 channel for old clients.\n const channelV0 = new BroadcastChannel(makeChannelNameV0(replicacheName));\n channelV0.postMessage([clientGroupID]);\n channelV0.close();\n }\n\n channelV1.onmessage = async (e: MessageEvent) => {\n const {data} = e;\n if (isNewClientChannelMessageV1(data)) {\n const {clientGroupID: newClientGroupID, idbName: newClientIDBName} = data;\n if (newClientGroupID !== clientGroupID) {\n if (newClientIDBName === idbName) {\n // Check if this client can see the new client's newClientGroupID in its\n // perdag. It should be able to if the clients share persistent\n // storage. However, with `ReplicacheOption.kvStore`\n // or a store that failed to open (the instance then runs in memory)\n // clients may not actually share persistent storage. If storage is not shared, then there is no point\n // in updating, since clients cannot sync locally. If clients do update\n // in this case, they can continually cause each other to update, since\n // on each update the clients get assigned a new client group.\n const updateNeeded = await withRead(\n perdag,\n async (perdagRead: Read) =>\n (await getClientGroup(newClientGroupID, perdagRead)) !==\n undefined,\n );\n if (updateNeeded) {\n onUpdateNeeded();\n }\n } else {\n // Idb name is different, indicating new schema or format version.\n // Update to get assigned to newClientIDBName, and hopefully\n // newClientGroupID.\n // If storage is not actually shared (i.e. due to\n // `ReplicacheOption.kvStore`\n // or a store that failed to open) the new client will not\n // get assigned to newClientGroupID, but should get the\n // newClientIDBName.\n // Note: we don't try to read from newClientIDBName to see\n // if this client shares storage with the new client, because\n // the newClientIDBName may have a format version this client\n // cannot read.\n onUpdateNeeded();\n return;\n }\n }\n }\n };\n\n signal.addEventListener('abort', () => channelV1.close(), {once: true});\n}\n"],"mappings":";;;;AAQA,SAAS,kBAAkB,gBAAgC;CACzD,OAAO,+BAA+B;AACxC;AAOA,SAAS,kBAAkB,gBAAgC;CACzD,OAAO,kCAAkC;AAC3C;AAUA,SAAS,4BACP,SACsC;CACtC,OACE,OAAO,YAAY,YACnB,OAAQ,QAAqC,kBAAkB,YAC/D,OAAQ,QAA+B,YAAY;AAEvD;AAEA,SAAgB,qBACd,gBACA,SACA,QACA,eACA,kBACA,gBACA,QACA;CACA,IAAI,OAAO,SACT;CAGF,MAAM,YAAY,IAAIA,GAAiB,kBAAkB,cAAc,CAAC;CACxE,IAAI,kBAAkB;EACpB,UAAU,YAAY;GAAC;GAAe;EAAO,CAAC;EAE9C,MAAM,YAAY,IAAIA,GAAiB,kBAAkB,cAAc,CAAC;EACxE,UAAU,YAAY,CAAC,aAAa,CAAC;EACrC,UAAU,MAAM;CAClB;CAEA,UAAU,YAAY,OAAO,MAAoB;EAC/C,MAAM,EAAC,SAAQ;EACf,IAAI,4BAA4B,IAAI,GAAG;GACrC,MAAM,EAAC,eAAe,kBAAkB,SAAS,qBAAoB;GACrE,IAAI,qBAAqB,eAAe;IACtC,IAAI,qBAAqB,SAenB;SAAA,MANuB,SACzB,QACA,OAAO,eACJ,MAAM,eAAe,kBAAkB,UAAU,MAClD,KAAA,CACJ,GAEE,eAAe;IAAA,OAEZ;KAaL,eAAe;KACf;IACF;GACF;EACF;CACF;CAEA,OAAO,iBAAiB,eAAe,UAAU,MAAM,GAAG,EAAC,MAAM,KAAI,CAAC;AACxE"}
@@ -1,5 +1,5 @@
1
1
  import { assertObject, assertString } from "../../shared/src/asserts.js";
2
- import { bc } from "../../shared/src/broadcast-channel.js";
2
+ import { BroadcastChannel as bc } from "../../shared/src/broadcast-channel.js";
3
3
  //#region ../replicache/src/on-persist-channel.ts
4
4
  function makeChannelName(replicacheName) {
5
5
  return `replicache-on-persist:${replicacheName}`;
@@ -1 +1 @@
1
- {"version":3,"file":"on-persist-channel.js","names":[],"sources":["../../../../replicache/src/on-persist-channel.ts"],"sourcesContent":["import {assertObject, assertString} from '../../shared/src/asserts.ts';\nimport {BroadcastChannel} from '../../shared/src/broadcast-channel.ts';\nimport type {ClientGroupID, ClientID} from './sync/ids.ts';\n\nfunction makeChannelName(replicacheName: string): string {\n return `replicache-on-persist:${replicacheName}`;\n}\n\nexport type PersistInfo = {\n clientGroupID: ClientGroupID;\n clientID: ClientID;\n};\n\nexport type OnPersist = (persistInfo: PersistInfo) => void;\n\ntype HandlePersist = OnPersist;\n\nfunction assertPersistInfo(value: unknown): asserts value is PersistInfo {\n assertObject(value);\n assertString(value.clientGroupID);\n assertString(value.clientID);\n}\n\nexport function initOnPersistChannel(\n replicacheName: string,\n signal: AbortSignal,\n handlePersist: HandlePersist,\n): OnPersist {\n if (signal.aborted) {\n return () => undefined;\n }\n const channel = new BroadcastChannel(makeChannelName(replicacheName));\n\n channel.onmessage = e => {\n const {data} = e;\n assertPersistInfo(data);\n handlePersist({\n clientGroupID: data.clientGroupID,\n clientID: data.clientID,\n });\n };\n\n signal.addEventListener('abort', () => channel.close(), {once: true});\n\n return (persistInfo: PersistInfo) => {\n if (signal.aborted) {\n return;\n }\n channel.postMessage(persistInfo);\n handlePersist(persistInfo);\n };\n}\n"],"mappings":";;;AAIA,SAAS,gBAAgB,gBAAgC;CACvD,OAAO,yBAAyB;AAClC;AAWA,SAAS,kBAAkB,OAA8C;CACvE,aAAa,KAAK;CAClB,aAAa,MAAM,aAAa;CAChC,aAAa,MAAM,QAAQ;AAC7B;AAEA,SAAgB,qBACd,gBACA,QACA,eACW;CACX,IAAI,OAAO,SACT,aAAa,KAAA;CAEf,MAAM,UAAU,IAAI,GAAiB,gBAAgB,cAAc,CAAC;CAEpE,QAAQ,aAAY,MAAK;EACvB,MAAM,EAAC,SAAQ;EACf,kBAAkB,IAAI;EACtB,cAAc;GACZ,eAAe,KAAK;GACpB,UAAU,KAAK;EACjB,CAAC;CACH;CAEA,OAAO,iBAAiB,eAAe,QAAQ,MAAM,GAAG,EAAC,MAAM,KAAI,CAAC;CAEpE,QAAQ,gBAA6B;EACnC,IAAI,OAAO,SACT;EAEF,QAAQ,YAAY,WAAW;EAC/B,cAAc,WAAW;CAC3B;AACF"}
1
+ {"version":3,"file":"on-persist-channel.js","names":["BroadcastChannel"],"sources":["../../../../replicache/src/on-persist-channel.ts"],"sourcesContent":["import {assertObject, assertString} from '../../shared/src/asserts.ts';\nimport {BroadcastChannel} from '../../shared/src/broadcast-channel.ts';\nimport type {ClientGroupID, ClientID} from './sync/ids.ts';\n\nfunction makeChannelName(replicacheName: string): string {\n return `replicache-on-persist:${replicacheName}`;\n}\n\nexport type PersistInfo = {\n clientGroupID: ClientGroupID;\n clientID: ClientID;\n};\n\nexport type OnPersist = (persistInfo: PersistInfo) => void;\n\ntype HandlePersist = OnPersist;\n\nfunction assertPersistInfo(value: unknown): asserts value is PersistInfo {\n assertObject(value);\n assertString(value.clientGroupID);\n assertString(value.clientID);\n}\n\nexport function initOnPersistChannel(\n replicacheName: string,\n signal: AbortSignal,\n handlePersist: HandlePersist,\n): OnPersist {\n if (signal.aborted) {\n return () => undefined;\n }\n const channel = new BroadcastChannel(makeChannelName(replicacheName));\n\n channel.onmessage = e => {\n const {data} = e;\n assertPersistInfo(data);\n handlePersist({\n clientGroupID: data.clientGroupID,\n clientID: data.clientID,\n });\n };\n\n signal.addEventListener('abort', () => channel.close(), {once: true});\n\n return (persistInfo: PersistInfo) => {\n if (signal.aborted) {\n return;\n }\n channel.postMessage(persistInfo);\n handlePersist(persistInfo);\n };\n}\n"],"mappings":";;;AAIA,SAAS,gBAAgB,gBAAgC;CACvD,OAAO,yBAAyB;AAClC;AAWA,SAAS,kBAAkB,OAA8C;CACvE,aAAa,KAAK;CAClB,aAAa,MAAM,aAAa;CAChC,aAAa,MAAM,QAAQ;AAC7B;AAEA,SAAgB,qBACd,gBACA,QACA,eACW;CACX,IAAI,OAAO,SACT,aAAa,KAAA;CAEf,MAAM,UAAU,IAAIA,GAAiB,gBAAgB,cAAc,CAAC;CAEpE,QAAQ,aAAY,MAAK;EACvB,MAAM,EAAC,SAAQ;EACf,kBAAkB,IAAI;EACtB,cAAc;GACZ,eAAe,KAAK;GACpB,UAAU,KAAK;EACjB,CAAC;CACH;CAEA,OAAO,iBAAiB,eAAe,QAAQ,MAAM,GAAG,EAAC,MAAM,KAAI,CAAC;CAEpE,QAAQ,gBAA6B;EACnC,IAAI,OAAO,SACT;EAEF,QAAQ,YAAY,WAAW;EAC/B,cAAc,WAAW;CAC3B;AACF"}
@@ -1,2 +1 @@
1
- import "./kv/op-sqlite/store.js";
2
- export {};
1
+ export { opSQLiteStoreProvider } from "./kv/op-sqlite/store.js";
@@ -5,7 +5,8 @@ import { DEFAULT_HEAD_NAME, localMutationsDD31 } from "./db/commit.js";
5
5
  * This returns the pending changes with the oldest mutations first.
6
6
  */
7
7
  async function pendingMutationsForAPI(dagRead) {
8
- return (await localMutationsDD31(await mustGetHeadHash(DEFAULT_HEAD_NAME, dagRead), dagRead)).map((p) => ({
8
+ const mainHeadHash = await mustGetHeadHash(DEFAULT_HEAD_NAME, dagRead);
9
+ return (await localMutationsDD31(mainHeadHash, dagRead)).map((p) => ({
9
10
  id: p.meta.mutationID,
10
11
  name: p.meta.mutatorName,
11
12
  args: p.meta.mutatorArgsJSON,
@@ -1 +1 @@
1
- {"version":3,"file":"pending-mutations.js","names":[],"sources":["../../../../replicache/src/pending-mutations.ts"],"sourcesContent":["import type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {mustGetHeadHash, type Read} from './dag/store.ts';\nimport {DEFAULT_HEAD_NAME, localMutationsDD31} from './db/commit.ts';\nimport type {ClientID} from './sync/ids.ts';\n\nexport type PendingMutation = {\n readonly name: string;\n readonly id: number;\n readonly args: ReadonlyJSONValue;\n readonly clientID: ClientID;\n};\n\n/**\n * This returns the pending changes with the oldest mutations first.\n */\nexport async function pendingMutationsForAPI(\n dagRead: Read,\n): Promise<readonly PendingMutation[]> {\n const mainHeadHash = await mustGetHeadHash(DEFAULT_HEAD_NAME, dagRead);\n const pending = await localMutationsDD31(mainHeadHash, dagRead);\n return pending\n .map(p => ({\n id: p.meta.mutationID,\n name: p.meta.mutatorName,\n args: p.meta.mutatorArgsJSON,\n clientID: p.meta.clientID,\n }))\n .reverse();\n}\n"],"mappings":";;;;;;AAeA,eAAsB,uBACpB,SACqC;CAGrC,QAAO,MADe,mBAAmB,MADd,gBAAgB,mBAAmB,OAAO,GACd,OAAO,GAE3D,KAAI,OAAM;EACT,IAAI,EAAE,KAAK;EACX,MAAM,EAAE,KAAK;EACb,MAAM,EAAE,KAAK;EACb,UAAU,EAAE,KAAK;CACnB,EAAE,EACD,QAAQ;AACb"}
1
+ {"version":3,"file":"pending-mutations.js","names":[],"sources":["../../../../replicache/src/pending-mutations.ts"],"sourcesContent":["import type {ReadonlyJSONValue} from '../../shared/src/json.ts';\nimport {mustGetHeadHash, type Read} from './dag/store.ts';\nimport {DEFAULT_HEAD_NAME, localMutationsDD31} from './db/commit.ts';\nimport type {ClientID} from './sync/ids.ts';\n\nexport type PendingMutation = {\n readonly name: string;\n readonly id: number;\n readonly args: ReadonlyJSONValue;\n readonly clientID: ClientID;\n};\n\n/**\n * This returns the pending changes with the oldest mutations first.\n */\nexport async function pendingMutationsForAPI(\n dagRead: Read,\n): Promise<readonly PendingMutation[]> {\n const mainHeadHash = await mustGetHeadHash(DEFAULT_HEAD_NAME, dagRead);\n const pending = await localMutationsDD31(mainHeadHash, dagRead);\n return pending\n .map(p => ({\n id: p.meta.mutationID,\n name: p.meta.mutatorName,\n args: p.meta.mutatorArgsJSON,\n clientID: p.meta.clientID,\n }))\n .reverse();\n}\n"],"mappings":";;;;;;AAeA,eAAsB,uBACpB,SACqC;CACrC,MAAM,eAAe,MAAM,gBAAgB,mBAAmB,OAAO;CAErE,QAAO,MADe,mBAAmB,cAAc,OAAO,EAAA,CAE3D,KAAI,OAAM;EACT,IAAI,EAAE,KAAK;EACX,MAAM,EAAE,KAAK;EACb,MAAM,EAAE,KAAK;EACb,UAAU,EAAE,KAAK;CACnB,EAAE,CAAC,CACF,QAAQ;AACb"}
@@ -8,11 +8,11 @@ import { addDeletedClients } from "../deleted-clients.js";
8
8
  * This means that this is the maximum time a tab can be in the background
9
9
  * (frozen) and still be able to sync when it comes back to the foreground.
10
10
  */
11
- var CLIENT_MAX_INACTIVE_TIME = 1440 * 60 * 1e3;
11
+ var CLIENT_MAX_INACTIVE_TIME = 864e5;
12
12
  /**
13
13
  * How frequently to try to garbage collect clients.
14
14
  */
15
- var GC_INTERVAL = 300 * 1e3;
15
+ var GC_INTERVAL = 3e5;
16
16
  var latestGCUpdate;
17
17
  function initClientGC(clientID, dagStore, clientMaxInactiveTime, gcInterval, onClientsDeleted, lc, signal, onStorageFailure) {
18
18
  initBgIntervalProcess("ClientGC", () => {
@@ -1 +1 @@
1
- {"version":3,"file":"client-gc.js","names":[],"sources":["../../../../../replicache/src/persist/client-gc.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport type {Store} from '../dag/store.ts';\nimport {\n addDeletedClients,\n type WritableDeletedClients,\n} from '../deleted-clients.ts';\nimport type {StorageFailureError} from '../storage-failure.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport type {Client, OnClientsDeleted} from './clients.ts';\nimport {type ClientMap, getClients, setClients} from './clients.ts';\n\n/**\n * The maximum time a client can be inactive before it is garbage collected.\n * This means that this is the maximum time a tab can be in the background\n * (frozen) and still be able to sync when it comes back to the foreground.\n */\nexport const CLIENT_MAX_INACTIVE_TIME = 24 * 60 * 60 * 1000; // 24 hours\n\n/**\n * How frequently to try to garbage collect clients.\n */\nexport const GC_INTERVAL = 5 * 60 * 1000; // 5 minutes\n\nlet latestGCUpdate: Promise<ClientMap> | undefined;\nexport function getLatestGCUpdate(): Promise<ClientMap> | undefined {\n return latestGCUpdate;\n}\n\nexport function initClientGC(\n clientID: ClientID,\n dagStore: Store,\n clientMaxInactiveTime: number,\n gcInterval: number,\n onClientsDeleted: OnClientsDeleted,\n lc: LogContext,\n signal: AbortSignal,\n onStorageFailure?: ((failure: StorageFailureError) => void) | undefined,\n): void {\n initBgIntervalProcess(\n 'ClientGC',\n () => {\n latestGCUpdate = gcClients(\n clientID,\n dagStore,\n clientMaxInactiveTime,\n onClientsDeleted,\n );\n return latestGCUpdate;\n },\n () => gcInterval,\n lc,\n signal,\n onStorageFailure,\n );\n}\n\nfunction gcClients(\n clientID: ClientID,\n dagStore: Store,\n clientMaxInactiveTime: number,\n onClientsDeleted: OnClientsDeleted,\n): Promise<ClientMap> {\n return withWrite(dagStore, async dagWrite => {\n const now = Date.now();\n const clients = await getClients(dagWrite);\n const deletedClients: WritableDeletedClients = [];\n const newClients: Map<ClientID, Client> = new Map();\n for (const [id, client] of clients) {\n if (\n id === clientID /* never collect ourself */ ||\n now - client.heartbeatTimestampMs <= clientMaxInactiveTime\n ) {\n newClients.set(id, client);\n } else {\n deletedClients.push({\n clientGroupID: client.clientGroupID,\n clientID: id,\n });\n }\n }\n\n if (newClients.size === clients.size) {\n return clients;\n }\n await setClients(newClients, dagWrite);\n const normalizedDeletedClients = await addDeletedClients(\n dagWrite,\n deletedClients,\n );\n await onClientsDeleted(normalizedDeletedClients);\n return newClients;\n });\n}\n"],"mappings":";;;;;;;;;;AAkBA,IAAa,2BAA2B,OAAU,KAAK;;;;AAKvD,IAAa,cAAc,MAAS;AAEpC,IAAI;AAKJ,SAAgB,aACd,UACA,UACA,uBACA,YACA,kBACA,IACA,QACA,kBACM;CACN,sBACE,kBACM;EACJ,iBAAiB,UACf,UACA,UACA,uBACA,gBACF;EACA,OAAO;CACT,SACM,YACN,IACA,QACA,gBACF;AACF;AAEA,SAAS,UACP,UACA,UACA,uBACA,kBACoB;CACpB,OAAO,UAAU,UAAU,OAAM,aAAY;EAC3C,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,UAAU,MAAM,WAAW,QAAQ;EACzC,MAAM,iBAAyC,CAAC;EAChD,MAAM,6BAAoC,IAAI,IAAI;EAClD,KAAK,MAAM,CAAC,IAAI,WAAW,SACzB,IACE,OAAO,YACP,MAAM,OAAO,wBAAwB,uBAErC,WAAW,IAAI,IAAI,MAAM;OAEzB,eAAe,KAAK;GAClB,eAAe,OAAO;GACtB,UAAU;EACZ,CAAC;EAIL,IAAI,WAAW,SAAS,QAAQ,MAC9B,OAAO;EAET,MAAM,WAAW,YAAY,QAAQ;EAKrC,MAAM,iBAAiB,MAJgB,kBACrC,UACA,cACF,CAC+C;EAC/C,OAAO;CACT,CAAC;AACH"}
1
+ {"version":3,"file":"client-gc.js","names":[],"sources":["../../../../../replicache/src/persist/client-gc.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport type {Store} from '../dag/store.ts';\nimport {\n addDeletedClients,\n type WritableDeletedClients,\n} from '../deleted-clients.ts';\nimport type {StorageFailureError} from '../storage-failure.ts';\nimport type {ClientID} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport type {Client, OnClientsDeleted} from './clients.ts';\nimport {type ClientMap, getClients, setClients} from './clients.ts';\n\n/**\n * The maximum time a client can be inactive before it is garbage collected.\n * This means that this is the maximum time a tab can be in the background\n * (frozen) and still be able to sync when it comes back to the foreground.\n */\nexport const CLIENT_MAX_INACTIVE_TIME = 24 * 60 * 60 * 1000; // 24 hours\n\n/**\n * How frequently to try to garbage collect clients.\n */\nexport const GC_INTERVAL = 5 * 60 * 1000; // 5 minutes\n\nlet latestGCUpdate: Promise<ClientMap> | undefined;\nexport function getLatestGCUpdate(): Promise<ClientMap> | undefined {\n return latestGCUpdate;\n}\n\nexport function initClientGC(\n clientID: ClientID,\n dagStore: Store,\n clientMaxInactiveTime: number,\n gcInterval: number,\n onClientsDeleted: OnClientsDeleted,\n lc: LogContext,\n signal: AbortSignal,\n onStorageFailure?: ((failure: StorageFailureError) => void) | undefined,\n): void {\n initBgIntervalProcess(\n 'ClientGC',\n () => {\n latestGCUpdate = gcClients(\n clientID,\n dagStore,\n clientMaxInactiveTime,\n onClientsDeleted,\n );\n return latestGCUpdate;\n },\n () => gcInterval,\n lc,\n signal,\n onStorageFailure,\n );\n}\n\nfunction gcClients(\n clientID: ClientID,\n dagStore: Store,\n clientMaxInactiveTime: number,\n onClientsDeleted: OnClientsDeleted,\n): Promise<ClientMap> {\n return withWrite(dagStore, async dagWrite => {\n const now = Date.now();\n const clients = await getClients(dagWrite);\n const deletedClients: WritableDeletedClients = [];\n const newClients: Map<ClientID, Client> = new Map();\n for (const [id, client] of clients) {\n if (\n id === clientID /* never collect ourself */ ||\n now - client.heartbeatTimestampMs <= clientMaxInactiveTime\n ) {\n newClients.set(id, client);\n } else {\n deletedClients.push({\n clientGroupID: client.clientGroupID,\n clientID: id,\n });\n }\n }\n\n if (newClients.size === clients.size) {\n return clients;\n }\n await setClients(newClients, dagWrite);\n const normalizedDeletedClients = await addDeletedClients(\n dagWrite,\n deletedClients,\n );\n await onClientsDeleted(normalizedDeletedClients);\n return newClients;\n });\n}\n"],"mappings":";;;;;;;;;;AAkBA,IAAa,2BAA2B;;;;AAKxC,IAAa,cAAc;AAE3B,IAAI;AAKJ,SAAgB,aACd,UACA,UACA,uBACA,YACA,kBACA,IACA,QACA,kBACM;CACN,sBACE,kBACM;EACJ,iBAAiB,UACf,UACA,UACA,uBACA,gBACF;EACA,OAAO;CACT,SACM,YACN,IACA,QACA,gBACF;AACF;AAEA,SAAS,UACP,UACA,UACA,uBACA,kBACoB;CACpB,OAAO,UAAU,UAAU,OAAM,aAAY;EAC3C,MAAM,MAAM,KAAK,IAAI;EACrB,MAAM,UAAU,MAAM,WAAW,QAAQ;EACzC,MAAM,iBAAyC,CAAC;EAChD,MAAM,6BAAoC,IAAI,IAAI;EAClD,KAAK,MAAM,CAAC,IAAI,WAAW,SACzB,IACE,OAAO,YACP,MAAM,OAAO,wBAAwB,uBAErC,WAAW,IAAI,IAAI,MAAM;OAEzB,eAAe,KAAK;GAClB,eAAe,OAAO;GACtB,UAAU;EACZ,CAAC;EAIL,IAAI,WAAW,SAAS,QAAQ,MAC9B,OAAO;EAET,MAAM,WAAW,YAAY,QAAQ;EAKrC,MAAM,iBAAiB,MAJgB,kBACrC,UACA,cACF,CAC+C;EAC/C,OAAO;CACT,CAAC;AACH"}
@@ -3,7 +3,7 @@ import { withWrite } from "../with-transactions.js";
3
3
  import { clientGroupHasPendingMutations, getClientGroups, setClientGroups } from "./client-groups.js";
4
4
  import { getClients } from "./clients.js";
5
5
  //#region ../replicache/src/persist/client-group-gc.ts
6
- var GC_INTERVAL_MS = 300 * 1e3;
6
+ var GC_INTERVAL_MS = 3e5;
7
7
  var latestGCUpdate;
8
8
  function initClientGroupGC(dagStore, enableMutationRecovery, lc, signal, onStorageFailure) {
9
9
  initBgIntervalProcess("ClientGroupGC", () => {
@@ -31,6 +31,6 @@ function gcClientGroups(dagStore, enableMutationRecovery) {
31
31
  });
32
32
  }
33
33
  //#endregion
34
- export { initClientGroupGC };
34
+ export { gcClientGroups, initClientGroupGC };
35
35
 
36
36
  //# sourceMappingURL=client-group-gc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-group-gc.js","names":[],"sources":["../../../../../replicache/src/persist/client-group-gc.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport type {Store} from '../dag/store.ts';\nimport type {StorageFailureError} from '../storage-failure.ts';\nimport type {ClientGroupID} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n setClientGroups,\n type ClientGroupMap,\n} from './client-groups.ts';\nimport {getClients} from './clients.ts';\n\nconst GC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n\nlet latestGCUpdate: Promise<ClientGroupMap> | undefined;\nexport function getLatestGCUpdate(): Promise<ClientGroupMap> | undefined {\n return latestGCUpdate;\n}\n\nexport function initClientGroupGC(\n dagStore: Store,\n enableMutationRecovery: boolean,\n lc: LogContext,\n signal: AbortSignal,\n onStorageFailure?: ((failure: StorageFailureError) => void) | undefined,\n): void {\n initBgIntervalProcess(\n 'ClientGroupGC',\n () => {\n latestGCUpdate = gcClientGroups(dagStore, enableMutationRecovery);\n return latestGCUpdate;\n },\n () => GC_INTERVAL_MS,\n lc,\n signal,\n onStorageFailure,\n );\n}\n\n/**\n * This removes client groups that have no clients and no pending mutations.\n * If {@link enableMutationRecovery} is true, it will keep client groups with\n * pending mutations. If it is false, it will remove client groups even when they\n * have pending mutations.\n */\nexport function gcClientGroups(\n dagStore: Store,\n enableMutationRecovery: boolean,\n): Promise<ClientGroupMap> {\n return withWrite(dagStore, async tx => {\n const clients = await getClients(tx);\n const clientGroupIDs = new Set();\n for (const client of clients.values()) {\n clientGroupIDs.add(client.clientGroupID);\n }\n const clientGroups = new Map();\n const removeClientGroups: Set<ClientGroupID> = new Set();\n for (const [clientGroupID, clientGroup] of await getClientGroups(tx)) {\n if (\n clientGroupIDs.has(clientGroupID) ||\n (enableMutationRecovery && clientGroupHasPendingMutations(clientGroup))\n ) {\n clientGroups.set(clientGroupID, clientGroup);\n } else {\n removeClientGroups.add(clientGroupID);\n }\n }\n await setClientGroups(clientGroups, tx);\n // Client group GC doesn't delete individual clients, so we don't call onClientsDeleted\n return clientGroups;\n });\n}\n"],"mappings":";;;;;AAcA,IAAM,iBAAiB,MAAS;AAEhC,IAAI;AAKJ,SAAgB,kBACd,UACA,wBACA,IACA,QACA,kBACM;CACN,sBACE,uBACM;EACJ,iBAAiB,eAAe,UAAU,sBAAsB;EAChE,OAAO;CACT,SACM,gBACN,IACA,QACA,gBACF;AACF;;;;;;;AAQA,SAAgB,eACd,UACA,wBACyB;CACzB,OAAO,UAAU,UAAU,OAAM,OAAM;EACrC,MAAM,UAAU,MAAM,WAAW,EAAE;EACnC,MAAM,iCAAiB,IAAI,IAAI;EAC/B,KAAK,MAAM,UAAU,QAAQ,OAAO,GAClC,eAAe,IAAI,OAAO,aAAa;EAEzC,MAAM,+BAAe,IAAI,IAAI;EAC7B,MAAM,qCAAyC,IAAI,IAAI;EACvD,KAAK,MAAM,CAAC,eAAe,gBAAgB,MAAM,gBAAgB,EAAE,GACjE,IACE,eAAe,IAAI,aAAa,KAC/B,0BAA0B,+BAA+B,WAAW,GAErE,aAAa,IAAI,eAAe,WAAW;OAE3C,mBAAmB,IAAI,aAAa;EAGxC,MAAM,gBAAgB,cAAc,EAAE;EAEtC,OAAO;CACT,CAAC;AACH"}
1
+ {"version":3,"file":"client-group-gc.js","names":[],"sources":["../../../../../replicache/src/persist/client-group-gc.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {initBgIntervalProcess} from '../bg-interval.ts';\nimport type {Store} from '../dag/store.ts';\nimport type {StorageFailureError} from '../storage-failure.ts';\nimport type {ClientGroupID} from '../sync/ids.ts';\nimport {withWrite} from '../with-transactions.ts';\nimport {\n clientGroupHasPendingMutations,\n getClientGroups,\n setClientGroups,\n type ClientGroupMap,\n} from './client-groups.ts';\nimport {getClients} from './clients.ts';\n\nconst GC_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes\n\nlet latestGCUpdate: Promise<ClientGroupMap> | undefined;\nexport function getLatestGCUpdate(): Promise<ClientGroupMap> | undefined {\n return latestGCUpdate;\n}\n\nexport function initClientGroupGC(\n dagStore: Store,\n enableMutationRecovery: boolean,\n lc: LogContext,\n signal: AbortSignal,\n onStorageFailure?: ((failure: StorageFailureError) => void) | undefined,\n): void {\n initBgIntervalProcess(\n 'ClientGroupGC',\n () => {\n latestGCUpdate = gcClientGroups(dagStore, enableMutationRecovery);\n return latestGCUpdate;\n },\n () => GC_INTERVAL_MS,\n lc,\n signal,\n onStorageFailure,\n );\n}\n\n/**\n * This removes client groups that have no clients and no pending mutations.\n * If {@link enableMutationRecovery} is true, it will keep client groups with\n * pending mutations. If it is false, it will remove client groups even when they\n * have pending mutations.\n */\nexport function gcClientGroups(\n dagStore: Store,\n enableMutationRecovery: boolean,\n): Promise<ClientGroupMap> {\n return withWrite(dagStore, async tx => {\n const clients = await getClients(tx);\n const clientGroupIDs = new Set();\n for (const client of clients.values()) {\n clientGroupIDs.add(client.clientGroupID);\n }\n const clientGroups = new Map();\n const removeClientGroups: Set<ClientGroupID> = new Set();\n for (const [clientGroupID, clientGroup] of await getClientGroups(tx)) {\n if (\n clientGroupIDs.has(clientGroupID) ||\n (enableMutationRecovery && clientGroupHasPendingMutations(clientGroup))\n ) {\n clientGroups.set(clientGroupID, clientGroup);\n } else {\n removeClientGroups.add(clientGroupID);\n }\n }\n await setClientGroups(clientGroups, tx);\n // Client group GC doesn't delete individual clients, so we don't call onClientsDeleted\n return clientGroups;\n });\n}\n"],"mappings":";;;;;AAcA,IAAM,iBAAiB;AAEvB,IAAI;AAKJ,SAAgB,kBACd,UACA,wBACA,IACA,QACA,kBACM;CACN,sBACE,uBACM;EACJ,iBAAiB,eAAe,UAAU,sBAAsB;EAChE,OAAO;CACT,SACM,gBACN,IACA,QACA,gBACF;AACF;;;;;;;AAQA,SAAgB,eACd,UACA,wBACyB;CACzB,OAAO,UAAU,UAAU,OAAM,OAAM;EACrC,MAAM,UAAU,MAAM,WAAW,EAAE;EACnC,MAAM,iCAAiB,IAAI,IAAI;EAC/B,KAAK,MAAM,UAAU,QAAQ,OAAO,GAClC,eAAe,IAAI,OAAO,aAAa;EAEzC,MAAM,+BAAe,IAAI,IAAI;EAC7B,MAAM,qCAAyC,IAAI,IAAI;EACvD,KAAK,MAAM,CAAC,eAAe,gBAAgB,MAAM,gBAAgB,EAAE,GACjE,IACE,eAAe,IAAI,aAAa,KAC/B,0BAA0B,+BAA+B,WAAW,GAErE,aAAa,IAAI,eAAe,WAAW;OAE3C,mBAAmB,IAAI,aAAa;EAGxC,MAAM,gBAAgB,cAAc,EAAE;EAEtC,OAAO;CACT,CAAC;AACH"}
@@ -145,6 +145,6 @@ async function disableClientGroup(clientGroupID, dagWrite) {
145
145
  }, dagWrite);
146
146
  }
147
147
  //#endregion
148
- export { clientGroupHasPendingMutations, disableClientGroup, getClientGroup, getClientGroups, mutatorNamesEqual, setClientGroup, setClientGroups };
148
+ export { CLIENT_GROUPS_HEAD_NAME, clientGroupHasPendingMutations, disableClientGroup, getClientGroup, getClientGroups, mutatorNamesEqual, setClientGroup, setClientGroups };
149
149
 
150
150
  //# sourceMappingURL=client-groups.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"client-groups.js","names":[],"sources":["../../../../../replicache/src/persist/client-groups.ts"],"sourcesContent":["import {assert, assertObject} from '../../../shared/src/asserts.ts';\nimport * as valita from '../../../shared/src/valita.ts';\nimport {toRefs} from '../dag/chunk.ts';\nimport type {Read, Write} from '../dag/store.ts';\nimport {deepFreeze, type FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, hashSchema} from '../hash.ts';\nimport {indexDefinitionsEqual, indexDefinitionsSchema} from '../index-defs.ts';\nimport type {ClientGroupID} from '../sync/ids.ts';\n\nexport type ClientGroupMap = ReadonlyMap<ClientGroupID, ClientGroup>;\n\nconst clientGroupSchema = valita.readonlyObject({\n /**\n * The hash of the commit in the perdag last persisted to this client group.\n * Should only be updated by clients assigned to this client group.\n */\n headHash: hashSchema,\n\n /**\n * Set of mutator names common to all clients assigned to this client group.\n */\n mutatorNames: valita.readonlyArray(valita.string()),\n\n /**\n * Index definitions common to all clients assigned to this client group.\n */\n indexes: indexDefinitionsSchema,\n\n /**\n * The highest mutation ID of every client assigned to this client group.\n * Should only be updated by clients assigned to this client group. Read by\n * other clients to determine if there are unacknowledged pending mutations\n * for them to try to recover. This is redundant with information in the\n * commit graph at `headHash`, but allows other clients to determine if there\n * are unacknowledged pending mutations without having to load the commit\n * graph.\n */\n mutationIDs: valita.readonlyRecord(valita.number()),\n\n /**\n * The highest lastMutationID received from the server for every client\n * assigned to this client group.\n *\n * Should be updated by the clients assigned to this client group whenever\n * they persist to this client group. Read by other clients to determine if\n * there are unacknowledged pending mutations for them to recover and\n * *updated* by other clients upon successfully recovering pending mutations\n * to avoid redundant pushes of pending mutations.\n *\n * Note: This will be the same as the `lastMutationIDs` of the base snapshot\n * of the client group's commit graph when written by clients assigned to this\n * client group. However, when written by another client recovering mutations\n * it may be different because the other client does not update the commit\n * graph.\n */\n lastServerAckdMutationIDs: valita.record(valita.number()),\n\n /**\n * If the server deletes this client group it can signal that the client group\n * was deleted. If that happens we mark this client group as disabled so that\n * we do not use it again when creating new clients.\n */\n disabled: valita.boolean(),\n});\n\nexport type ClientGroup = valita.Infer<typeof clientGroupSchema>;\n\nexport const CLIENT_GROUPS_HEAD_NAME = 'client-groups';\n\nfunction assertClientGroup(value: unknown): asserts value is ClientGroup {\n valita.assert(value, clientGroupSchema);\n}\n\nfunction chunkDataToClientGroupMap(chunkData: unknown): ClientGroupMap {\n assertObject(chunkData);\n const clientGroups = new Map<ClientGroupID, ClientGroup>();\n for (const [key, value] of Object.entries(chunkData)) {\n if (value !== undefined) {\n assertClientGroup(value);\n clientGroups.set(key, value);\n }\n }\n return clientGroups;\n}\n\nfunction clientGroupMapToChunkData(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): FrozenJSONValue {\n const chunkData: {[id: ClientGroupID]: ClientGroup} = {};\n for (const [clientGroupID, clientGroup] of clientGroups.entries()) {\n dagWrite.assertValidHash(clientGroup.headHash);\n chunkData[clientGroupID] = {\n ...clientGroup,\n mutatorNames: [...clientGroup.mutatorNames.values()],\n };\n }\n return deepFreeze(chunkData);\n}\n\nasync function getClientGroupsAtHash(\n hash: Hash,\n dagRead: Read,\n): Promise<ClientGroupMap> {\n const chunk = await dagRead.getChunk(hash);\n return chunkDataToClientGroupMap(chunk?.data);\n}\n\nexport async function getClientGroups(dagRead: Read): Promise<ClientGroupMap> {\n const hash = await dagRead.getHead(CLIENT_GROUPS_HEAD_NAME);\n if (!hash) {\n return new Map();\n }\n return getClientGroupsAtHash(hash, dagRead);\n}\n\nexport async function setClientGroups(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n for (const [clientGroupID, clientGroup] of clientGroups) {\n const currClientGroup = currClientGroups.get(clientGroupID);\n validateClientGroupUpdate(clientGroup, currClientGroup);\n }\n return setValidatedClientGroups(clientGroups, dagWrite);\n}\n\nexport async function setClientGroup(\n clientGroupID: ClientGroupID,\n clientGroup: ClientGroup,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n const currClientGroup = currClientGroups.get(clientGroupID);\n validateClientGroupUpdate(clientGroup, currClientGroup);\n const newClientGroups = new Map(currClientGroups);\n newClientGroups.set(clientGroupID, clientGroup);\n return setValidatedClientGroups(newClientGroups, dagWrite);\n}\n\nexport async function deleteClientGroup(\n clientGroupID: ClientGroupID,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n if (!currClientGroups.has(clientGroupID)) {\n return currClientGroups;\n }\n const newClientGroups = new Map(currClientGroups.entries());\n newClientGroups.delete(clientGroupID);\n return setValidatedClientGroups(newClientGroups, dagWrite);\n}\n\nfunction validateClientGroupUpdate(\n clientGroup: ClientGroup,\n currClientGroup: ClientGroup | undefined,\n) {\n const mutatorNamesSet = new Set(clientGroup.mutatorNames);\n assert(\n mutatorNamesSet.size === clientGroup.mutatorNames.length,\n \"A client group's mutatorNames must be a set.\",\n );\n if (currClientGroup !== undefined) {\n assert(\n indexDefinitionsEqual(currClientGroup.indexes, clientGroup.indexes),\n \"A client group's index definitions must never change.\",\n );\n assert(\n mutatorNamesEqual(mutatorNamesSet, currClientGroup.mutatorNames),\n \"A client group's mutatorNames must never change.\",\n );\n }\n}\n\nasync function setValidatedClientGroups(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const chunkData = clientGroupMapToChunkData(clientGroups, dagWrite);\n const refs: Set<Hash> = new Set();\n for (const clientGroup of clientGroups.values()) {\n refs.add(clientGroup.headHash);\n }\n const chunk = dagWrite.createChunk(chunkData, toRefs(refs));\n await dagWrite.putChunk(chunk);\n await dagWrite.setHead(CLIENT_GROUPS_HEAD_NAME, chunk.hash);\n return clientGroups;\n}\n\nexport function mutatorNamesEqual(\n mutatorNamesSet: ReadonlySet<string>,\n mutatorNames: readonly string[],\n): boolean {\n if (mutatorNames.length !== mutatorNamesSet.size) {\n return false;\n }\n for (const mutatorName of mutatorNames) {\n if (!mutatorNamesSet.has(mutatorName)) {\n return false;\n }\n }\n return true;\n}\n\nexport async function getClientGroup(\n id: ClientGroupID,\n dagRead: Read,\n): Promise<ClientGroup | undefined> {\n const clientGroups = await getClientGroups(dagRead);\n return clientGroups.get(id);\n}\n\nexport function clientGroupHasPendingMutations(clientGroup: ClientGroup) {\n for (const [clientID, mutationID] of Object.entries(\n clientGroup.mutationIDs,\n )) {\n const lastServerAckdMutationID =\n clientGroup.lastServerAckdMutationIDs[clientID];\n if (\n (lastServerAckdMutationID === undefined && mutationID !== 0) ||\n lastServerAckdMutationID < mutationID\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Marks a client group as disabled. This can happen if the server deletes the\n * client group (servers should not delete clients or client groups but it often\n * happens in practice when developing).\n *\n * A disabled client group prevents pulls and pushes from happening.\n */\nexport async function disableClientGroup(\n clientGroupID: string,\n dagWrite: Write,\n): Promise<void> {\n const clientGroup = await getClientGroup(clientGroupID, dagWrite);\n if (!clientGroup) {\n // No client group matching in the database, so nothing to do.\n return;\n }\n const disabledClientGroup = {\n ...clientGroup,\n disabled: true,\n };\n await setClientGroup(clientGroupID, disabledClientGroup, dagWrite);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client-groups.js","names":["valita.readonlyObject","valita.readonlyArray","valita","valita.readonlyRecord"],"sources":["../../../../../replicache/src/persist/client-groups.ts"],"sourcesContent":["import {assert, assertObject} from '../../../shared/src/asserts.ts';\nimport * as valita from '../../../shared/src/valita.ts';\nimport {toRefs} from '../dag/chunk.ts';\nimport type {Read, Write} from '../dag/store.ts';\nimport {deepFreeze, type FrozenJSONValue} from '../frozen-json.ts';\nimport {type Hash, hashSchema} from '../hash.ts';\nimport {indexDefinitionsEqual, indexDefinitionsSchema} from '../index-defs.ts';\nimport type {ClientGroupID} from '../sync/ids.ts';\n\nexport type ClientGroupMap = ReadonlyMap<ClientGroupID, ClientGroup>;\n\nconst clientGroupSchema = valita.readonlyObject({\n /**\n * The hash of the commit in the perdag last persisted to this client group.\n * Should only be updated by clients assigned to this client group.\n */\n headHash: hashSchema,\n\n /**\n * Set of mutator names common to all clients assigned to this client group.\n */\n mutatorNames: valita.readonlyArray(valita.string()),\n\n /**\n * Index definitions common to all clients assigned to this client group.\n */\n indexes: indexDefinitionsSchema,\n\n /**\n * The highest mutation ID of every client assigned to this client group.\n * Should only be updated by clients assigned to this client group. Read by\n * other clients to determine if there are unacknowledged pending mutations\n * for them to try to recover. This is redundant with information in the\n * commit graph at `headHash`, but allows other clients to determine if there\n * are unacknowledged pending mutations without having to load the commit\n * graph.\n */\n mutationIDs: valita.readonlyRecord(valita.number()),\n\n /**\n * The highest lastMutationID received from the server for every client\n * assigned to this client group.\n *\n * Should be updated by the clients assigned to this client group whenever\n * they persist to this client group. Read by other clients to determine if\n * there are unacknowledged pending mutations for them to recover and\n * *updated* by other clients upon successfully recovering pending mutations\n * to avoid redundant pushes of pending mutations.\n *\n * Note: This will be the same as the `lastMutationIDs` of the base snapshot\n * of the client group's commit graph when written by clients assigned to this\n * client group. However, when written by another client recovering mutations\n * it may be different because the other client does not update the commit\n * graph.\n */\n lastServerAckdMutationIDs: valita.record(valita.number()),\n\n /**\n * If the server deletes this client group it can signal that the client group\n * was deleted. If that happens we mark this client group as disabled so that\n * we do not use it again when creating new clients.\n */\n disabled: valita.boolean(),\n});\n\nexport type ClientGroup = valita.Infer<typeof clientGroupSchema>;\n\nexport const CLIENT_GROUPS_HEAD_NAME = 'client-groups';\n\nfunction assertClientGroup(value: unknown): asserts value is ClientGroup {\n valita.assert(value, clientGroupSchema);\n}\n\nfunction chunkDataToClientGroupMap(chunkData: unknown): ClientGroupMap {\n assertObject(chunkData);\n const clientGroups = new Map<ClientGroupID, ClientGroup>();\n for (const [key, value] of Object.entries(chunkData)) {\n if (value !== undefined) {\n assertClientGroup(value);\n clientGroups.set(key, value);\n }\n }\n return clientGroups;\n}\n\nfunction clientGroupMapToChunkData(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): FrozenJSONValue {\n const chunkData: {[id: ClientGroupID]: ClientGroup} = {};\n for (const [clientGroupID, clientGroup] of clientGroups.entries()) {\n dagWrite.assertValidHash(clientGroup.headHash);\n chunkData[clientGroupID] = {\n ...clientGroup,\n mutatorNames: [...clientGroup.mutatorNames.values()],\n };\n }\n return deepFreeze(chunkData);\n}\n\nasync function getClientGroupsAtHash(\n hash: Hash,\n dagRead: Read,\n): Promise<ClientGroupMap> {\n const chunk = await dagRead.getChunk(hash);\n return chunkDataToClientGroupMap(chunk?.data);\n}\n\nexport async function getClientGroups(dagRead: Read): Promise<ClientGroupMap> {\n const hash = await dagRead.getHead(CLIENT_GROUPS_HEAD_NAME);\n if (!hash) {\n return new Map();\n }\n return getClientGroupsAtHash(hash, dagRead);\n}\n\nexport async function setClientGroups(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n for (const [clientGroupID, clientGroup] of clientGroups) {\n const currClientGroup = currClientGroups.get(clientGroupID);\n validateClientGroupUpdate(clientGroup, currClientGroup);\n }\n return setValidatedClientGroups(clientGroups, dagWrite);\n}\n\nexport async function setClientGroup(\n clientGroupID: ClientGroupID,\n clientGroup: ClientGroup,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n const currClientGroup = currClientGroups.get(clientGroupID);\n validateClientGroupUpdate(clientGroup, currClientGroup);\n const newClientGroups = new Map(currClientGroups);\n newClientGroups.set(clientGroupID, clientGroup);\n return setValidatedClientGroups(newClientGroups, dagWrite);\n}\n\nexport async function deleteClientGroup(\n clientGroupID: ClientGroupID,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const currClientGroups = await getClientGroups(dagWrite);\n if (!currClientGroups.has(clientGroupID)) {\n return currClientGroups;\n }\n const newClientGroups = new Map(currClientGroups.entries());\n newClientGroups.delete(clientGroupID);\n return setValidatedClientGroups(newClientGroups, dagWrite);\n}\n\nfunction validateClientGroupUpdate(\n clientGroup: ClientGroup,\n currClientGroup: ClientGroup | undefined,\n) {\n const mutatorNamesSet = new Set(clientGroup.mutatorNames);\n assert(\n mutatorNamesSet.size === clientGroup.mutatorNames.length,\n \"A client group's mutatorNames must be a set.\",\n );\n if (currClientGroup !== undefined) {\n assert(\n indexDefinitionsEqual(currClientGroup.indexes, clientGroup.indexes),\n \"A client group's index definitions must never change.\",\n );\n assert(\n mutatorNamesEqual(mutatorNamesSet, currClientGroup.mutatorNames),\n \"A client group's mutatorNames must never change.\",\n );\n }\n}\n\nasync function setValidatedClientGroups(\n clientGroups: ClientGroupMap,\n dagWrite: Write,\n): Promise<ClientGroupMap> {\n const chunkData = clientGroupMapToChunkData(clientGroups, dagWrite);\n const refs: Set<Hash> = new Set();\n for (const clientGroup of clientGroups.values()) {\n refs.add(clientGroup.headHash);\n }\n const chunk = dagWrite.createChunk(chunkData, toRefs(refs));\n await dagWrite.putChunk(chunk);\n await dagWrite.setHead(CLIENT_GROUPS_HEAD_NAME, chunk.hash);\n return clientGroups;\n}\n\nexport function mutatorNamesEqual(\n mutatorNamesSet: ReadonlySet<string>,\n mutatorNames: readonly string[],\n): boolean {\n if (mutatorNames.length !== mutatorNamesSet.size) {\n return false;\n }\n for (const mutatorName of mutatorNames) {\n if (!mutatorNamesSet.has(mutatorName)) {\n return false;\n }\n }\n return true;\n}\n\nexport async function getClientGroup(\n id: ClientGroupID,\n dagRead: Read,\n): Promise<ClientGroup | undefined> {\n const clientGroups = await getClientGroups(dagRead);\n return clientGroups.get(id);\n}\n\nexport function clientGroupHasPendingMutations(clientGroup: ClientGroup) {\n for (const [clientID, mutationID] of Object.entries(\n clientGroup.mutationIDs,\n )) {\n const lastServerAckdMutationID =\n clientGroup.lastServerAckdMutationIDs[clientID];\n if (\n (lastServerAckdMutationID === undefined && mutationID !== 0) ||\n lastServerAckdMutationID < mutationID\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Marks a client group as disabled. This can happen if the server deletes the\n * client group (servers should not delete clients or client groups but it often\n * happens in practice when developing).\n *\n * A disabled client group prevents pulls and pushes from happening.\n */\nexport async function disableClientGroup(\n clientGroupID: string,\n dagWrite: Write,\n): Promise<void> {\n const clientGroup = await getClientGroup(clientGroupID, dagWrite);\n if (!clientGroup) {\n // No client group matching in the database, so nothing to do.\n return;\n }\n const disabledClientGroup = {\n ...clientGroup,\n disabled: true,\n };\n await setClientGroup(clientGroupID, disabledClientGroup, dagWrite);\n}\n"],"mappings":"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"}
@@ -257,6 +257,6 @@ async function setClients(clients, dagWrite) {
257
257
  return chunk.hash;
258
258
  }
259
259
  //#endregion
260
- export { ClientStateNotFoundError, assertClientV6, assertHasClientState, getClient, getClientGroupForClient, getClientGroupIDForClient, getClients, hasClientState, initClientV6, mustGetClient, setClient, setClients };
260
+ export { CLIENTS_HEAD_NAME, ClientStateNotFoundError, assertClientV6, assertHasClientState, findMatchingClient, getClient, getClientGroupForClient, getClientGroupIDForClient, getClients, hasClientState, initClientV6, mustGetClient, setClient, setClients };
261
261
 
262
262
  //# sourceMappingURL=clients.js.map