@rocicorp/zero 1.11.0-canary.3 → 1.11.0-canary.30

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 (988) hide show
  1. package/out/analyze-query/src/analyze-cli.js +4 -4
  2. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  3. package/out/analyze-query/src/bin-analyze.js +13 -12
  4. package/out/analyze-query/src/bin-analyze.js.map +1 -1
  5. package/out/analyze-query/src/bin-transform.js +1 -1
  6. package/out/analyze-query/src/bin-transform.js.map +1 -1
  7. package/out/ast-to-zql/src/ast-to-zql.d.ts +8 -1
  8. package/out/ast-to-zql/src/ast-to-zql.d.ts.map +1 -1
  9. package/out/ast-to-zql/src/ast-to-zql.js +25 -25
  10. package/out/ast-to-zql/src/ast-to-zql.js.map +1 -1
  11. package/out/otel/src/log-options.d.ts +16 -0
  12. package/out/otel/src/log-options.d.ts.map +1 -1
  13. package/out/otel/src/log-options.js +60 -6
  14. package/out/otel/src/log-options.js.map +1 -1
  15. package/out/replicache/src/bg-interval.d.ts +2 -1
  16. package/out/replicache/src/bg-interval.d.ts.map +1 -1
  17. package/out/replicache/src/bg-interval.js +13 -4
  18. package/out/replicache/src/bg-interval.js.map +1 -1
  19. package/out/replicache/src/btree/node.d.ts.map +1 -1
  20. package/out/replicache/src/btree/node.js +3 -6
  21. package/out/replicache/src/btree/node.js.map +1 -1
  22. package/out/replicache/src/dag/gc.d.ts.map +1 -1
  23. package/out/replicache/src/dag/gc.js +8 -11
  24. package/out/replicache/src/dag/gc.js.map +1 -1
  25. package/out/replicache/src/dag/invalid-ref-count-error.d.ts +12 -0
  26. package/out/replicache/src/dag/invalid-ref-count-error.d.ts.map +1 -0
  27. package/out/replicache/src/dag/invalid-ref-count-error.js +19 -0
  28. package/out/replicache/src/dag/invalid-ref-count-error.js.map +1 -0
  29. package/out/replicache/src/dag/lazy-store.d.ts +17 -4
  30. package/out/replicache/src/dag/lazy-store.d.ts.map +1 -1
  31. package/out/replicache/src/dag/lazy-store.js +35 -9
  32. package/out/replicache/src/dag/lazy-store.js.map +1 -1
  33. package/out/replicache/src/dag/store-impl.d.ts +12 -2
  34. package/out/replicache/src/dag/store-impl.d.ts.map +1 -1
  35. package/out/replicache/src/dag/store-impl.js +29 -9
  36. package/out/replicache/src/dag/store-impl.js.map +1 -1
  37. package/out/replicache/src/db/rebase.d.ts +14 -2
  38. package/out/replicache/src/db/rebase.d.ts.map +1 -1
  39. package/out/replicache/src/db/rebase.js +27 -5
  40. package/out/replicache/src/db/rebase.js.map +1 -1
  41. package/out/replicache/src/get-kv-store-provider.d.ts +1 -1
  42. package/out/replicache/src/get-kv-store-provider.d.ts.map +1 -1
  43. package/out/replicache/src/get-kv-store-provider.js +4 -4
  44. package/out/replicache/src/get-kv-store-provider.js.map +1 -1
  45. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  46. package/out/replicache/src/kv/expo-sqlite/store.js +28 -5
  47. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  48. package/out/replicache/src/kv/idb-store.d.ts +18 -0
  49. package/out/replicache/src/kv/idb-store.d.ts.map +1 -1
  50. package/out/replicache/src/kv/idb-store.js +30 -2
  51. package/out/replicache/src/kv/idb-store.js.map +1 -1
  52. package/out/replicache/src/kv/mem-fallback-store.d.ts +54 -0
  53. package/out/replicache/src/kv/mem-fallback-store.d.ts.map +1 -0
  54. package/out/replicache/src/kv/mem-fallback-store.js +177 -0
  55. package/out/replicache/src/kv/mem-fallback-store.js.map +1 -0
  56. package/out/replicache/src/kv/mem-store.d.ts +1 -0
  57. package/out/replicache/src/kv/mem-store.d.ts.map +1 -1
  58. package/out/replicache/src/kv/mem-store.js +3 -0
  59. package/out/replicache/src/kv/mem-store.js.map +1 -1
  60. package/out/replicache/src/kv/op-sqlite/store.d.ts.map +1 -1
  61. package/out/replicache/src/kv/op-sqlite/store.js +4 -4
  62. package/out/replicache/src/kv/op-sqlite/store.js.map +1 -1
  63. package/out/replicache/src/kv/sqlite-store.d.ts +22 -1
  64. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  65. package/out/replicache/src/kv/sqlite-store.js +159 -14
  66. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  67. package/out/replicache/src/kv/store.d.ts +6 -0
  68. package/out/replicache/src/kv/store.d.ts.map +1 -1
  69. package/out/replicache/src/new-client-channel.js.map +1 -1
  70. package/out/replicache/src/persist/client-gc.d.ts +2 -1
  71. package/out/replicache/src/persist/client-gc.d.ts.map +1 -1
  72. package/out/replicache/src/persist/client-gc.js +2 -2
  73. package/out/replicache/src/persist/client-gc.js.map +1 -1
  74. package/out/replicache/src/persist/client-group-gc.d.ts +2 -1
  75. package/out/replicache/src/persist/client-group-gc.d.ts.map +1 -1
  76. package/out/replicache/src/persist/client-group-gc.js +2 -2
  77. package/out/replicache/src/persist/client-group-gc.js.map +1 -1
  78. package/out/replicache/src/persist/clients.d.ts +2 -2
  79. package/out/replicache/src/persist/collect-idb-databases.d.ts +28 -8
  80. package/out/replicache/src/persist/collect-idb-databases.d.ts.map +1 -1
  81. package/out/replicache/src/persist/collect-idb-databases.js +97 -20
  82. package/out/replicache/src/persist/collect-idb-databases.js.map +1 -1
  83. package/out/replicache/src/persist/heartbeat.d.ts +2 -1
  84. package/out/replicache/src/persist/heartbeat.d.ts.map +1 -1
  85. package/out/replicache/src/persist/heartbeat.js +2 -2
  86. package/out/replicache/src/persist/heartbeat.js.map +1 -1
  87. package/out/replicache/src/persist/idb-databases-store-db-name.js +1 -1
  88. package/out/replicache/src/persist/idb-databases-store-db-name.js.map +1 -1
  89. package/out/replicache/src/persist/persist.d.ts.map +1 -1
  90. package/out/replicache/src/persist/persist.js +18 -8
  91. package/out/replicache/src/persist/persist.js.map +1 -1
  92. package/out/replicache/src/persist/refresh.js +6 -2
  93. package/out/replicache/src/persist/refresh.js.map +1 -1
  94. package/out/replicache/src/replicache-impl.d.ts +85 -3
  95. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  96. package/out/replicache/src/replicache-impl.js +321 -29
  97. package/out/replicache/src/replicache-impl.js.map +1 -1
  98. package/out/replicache/src/replicache-options.d.ts +9 -0
  99. package/out/replicache/src/replicache-options.d.ts.map +1 -1
  100. package/out/replicache/src/storage-failure.d.ts +37 -0
  101. package/out/replicache/src/storage-failure.d.ts.map +1 -0
  102. package/out/replicache/src/storage-failure.js +31 -0
  103. package/out/replicache/src/storage-failure.js.map +1 -0
  104. package/out/shared/src/arrays.d.ts +5 -0
  105. package/out/shared/src/arrays.d.ts.map +1 -1
  106. package/out/shared/src/arrays.js +8 -10
  107. package/out/shared/src/arrays.js.map +1 -1
  108. package/out/shared/src/base64.d.ts +19 -0
  109. package/out/shared/src/base64.d.ts.map +1 -0
  110. package/out/shared/src/base64.js +40 -0
  111. package/out/shared/src/base64.js.map +1 -0
  112. package/out/shared/src/centroid.d.ts +0 -1
  113. package/out/shared/src/centroid.d.ts.map +1 -1
  114. package/out/shared/src/centroid.js +1 -4
  115. package/out/shared/src/centroid.js.map +1 -1
  116. package/out/shared/src/error.d.ts +11 -0
  117. package/out/shared/src/error.d.ts.map +1 -1
  118. package/out/shared/src/error.js +35 -1
  119. package/out/shared/src/error.js.map +1 -1
  120. package/out/shared/src/hash.d.ts.map +1 -1
  121. package/out/shared/src/hash.js +32 -7
  122. package/out/shared/src/hash.js.map +1 -1
  123. package/out/shared/src/iterables.d.ts +2 -0
  124. package/out/shared/src/iterables.d.ts.map +1 -1
  125. package/out/shared/src/iterables.js +18 -1
  126. package/out/shared/src/iterables.js.map +1 -1
  127. package/out/shared/src/map.d.ts +24 -2
  128. package/out/shared/src/map.d.ts.map +1 -1
  129. package/out/shared/src/map.js +18 -4
  130. package/out/shared/src/map.js.map +1 -1
  131. package/out/shared/src/options-types.d.ts +2 -2
  132. package/out/shared/src/options.d.ts.map +1 -1
  133. package/out/shared/src/options.js +10 -7
  134. package/out/shared/src/options.js.map +1 -1
  135. package/out/shared/src/packed-boolean-map.d.ts +7 -0
  136. package/out/shared/src/packed-boolean-map.d.ts.map +1 -0
  137. package/out/shared/src/packed-boolean-map.js +36 -0
  138. package/out/shared/src/packed-boolean-map.js.map +1 -0
  139. package/out/shared/src/random-values.js +12 -1
  140. package/out/shared/src/random-values.js.map +1 -1
  141. package/out/shared/src/ring-buffer.d.ts +10 -0
  142. package/out/shared/src/ring-buffer.d.ts.map +1 -1
  143. package/out/shared/src/ring-buffer.js +26 -1
  144. package/out/shared/src/ring-buffer.js.map +1 -1
  145. package/out/shared/src/tdigest.d.ts +10 -0
  146. package/out/shared/src/tdigest.d.ts.map +1 -1
  147. package/out/shared/src/tdigest.js +147 -63
  148. package/out/shared/src/tdigest.js.map +1 -1
  149. package/out/shared/src/valita.d.ts +13 -0
  150. package/out/shared/src/valita.d.ts.map +1 -1
  151. package/out/shared/src/valita.js +23 -23
  152. package/out/shared/src/valita.js.map +1 -1
  153. package/out/z2s/src/compiler.d.ts.map +1 -1
  154. package/out/z2s/src/compiler.js +10 -6
  155. package/out/z2s/src/compiler.js.map +1 -1
  156. package/out/z2s/src/sql.d.ts.map +1 -1
  157. package/out/z2s/src/sql.js +9 -5
  158. package/out/z2s/src/sql.js.map +1 -1
  159. package/out/zero/package.js +4 -5
  160. package/out/zero/package.js.map +1 -1
  161. package/out/zero/src/zero.js +2 -1
  162. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  163. package/out/zero-cache/src/auth/write-authorizer.js +1 -0
  164. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  165. package/out/zero-cache/src/config/normalize.d.ts +1 -0
  166. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  167. package/out/zero-cache/src/config/normalize.js +46 -4
  168. package/out/zero-cache/src/config/normalize.js.map +1 -1
  169. package/out/zero-cache/src/config/zero-config.d.ts +116 -24
  170. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  171. package/out/zero-cache/src/config/zero-config.js +409 -157
  172. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  173. package/out/zero-cache/src/custom/fetch.d.ts +1 -1
  174. package/out/zero-cache/src/custom/fetch.d.ts.map +1 -1
  175. package/out/zero-cache/src/custom/fetch.js.map +1 -1
  176. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  177. package/out/zero-cache/src/db/lite-tables.js +3 -4
  178. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  179. package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
  180. package/out/zero-cache/src/db/migration-lite.js +5 -3
  181. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  182. package/out/zero-cache/src/observability/events.js +4 -1
  183. package/out/zero-cache/src/observability/events.js.map +1 -1
  184. package/out/zero-cache/src/observability/log-throttle.d.ts +44 -0
  185. package/out/zero-cache/src/observability/log-throttle.d.ts.map +1 -0
  186. package/out/zero-cache/src/observability/log-throttle.js +76 -0
  187. package/out/zero-cache/src/observability/log-throttle.js.map +1 -0
  188. package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
  189. package/out/zero-cache/src/observability/metrics.js +2 -7
  190. package/out/zero-cache/src/observability/metrics.js.map +1 -1
  191. package/out/zero-cache/src/scripts/permissions.d.ts +16 -0
  192. package/out/zero-cache/src/scripts/permissions.d.ts.map +1 -1
  193. package/out/zero-cache/src/scripts/permissions.js +3 -3
  194. package/out/zero-cache/src/scripts/permissions.js.map +1 -1
  195. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  196. package/out/zero-cache/src/server/change-streamer.js +117 -77
  197. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  198. package/out/zero-cache/src/server/inspector-delegate.d.ts +28 -2
  199. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  200. package/out/zero-cache/src/server/inspector-delegate.js +37 -17
  201. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  202. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  203. package/out/zero-cache/src/server/main.js +8 -1
  204. package/out/zero-cache/src/server/main.js.map +1 -1
  205. package/out/zero-cache/src/server/mutator.js +2 -0
  206. package/out/zero-cache/src/server/mutator.js.map +1 -1
  207. package/out/zero-cache/src/server/reaper.d.ts.map +1 -1
  208. package/out/zero-cache/src/server/reaper.js +2 -0
  209. package/out/zero-cache/src/server/reaper.js.map +1 -1
  210. package/out/zero-cache/src/server/replicator.d.ts +3 -0
  211. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  212. package/out/zero-cache/src/server/replicator.js +95 -21
  213. package/out/zero-cache/src/server/replicator.js.map +1 -1
  214. package/out/zero-cache/src/server/runner/zero-dispatcher.d.ts.map +1 -1
  215. package/out/zero-cache/src/server/runner/zero-dispatcher.js +5 -0
  216. package/out/zero-cache/src/server/runner/zero-dispatcher.js.map +1 -1
  217. package/out/zero-cache/src/server/syncer-assigner.d.ts +14 -0
  218. package/out/zero-cache/src/server/syncer-assigner.d.ts.map +1 -0
  219. package/out/zero-cache/src/server/syncer-assigner.js +72 -0
  220. package/out/zero-cache/src/server/syncer-assigner.js.map +1 -0
  221. package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
  222. package/out/zero-cache/src/server/syncer.js +24 -2
  223. package/out/zero-cache/src/server/syncer.js.map +1 -1
  224. package/out/zero-cache/src/server/worker-dispatcher.d.ts.map +1 -1
  225. package/out/zero-cache/src/server/worker-dispatcher.js +21 -4
  226. package/out/zero-cache/src/server/worker-dispatcher.js.map +1 -1
  227. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  228. package/out/zero-cache/src/services/analyze.js +11 -9
  229. package/out/zero-cache/src/services/analyze.js.map +1 -1
  230. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
  231. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +198 -24
  232. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  233. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.d.ts.map +1 -1
  234. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js +16 -4
  235. package/out/zero-cache/src/services/change-source/common/change-stream-multiplexer.js.map +1 -1
  236. package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts +24 -0
  237. package/out/zero-cache/src/services/change-source/common/init-cleanup.d.ts.map +1 -0
  238. package/out/zero-cache/src/services/change-source/common/init-cleanup.js +44 -0
  239. package/out/zero-cache/src/services/change-source/common/init-cleanup.js.map +1 -0
  240. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts +92 -2
  241. package/out/zero-cache/src/services/change-source/common/replica-restore.d.ts.map +1 -1
  242. package/out/zero-cache/src/services/change-source/common/replica-restore.js +27 -10
  243. package/out/zero-cache/src/services/change-source/common/replica-restore.js.map +1 -1
  244. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts +1 -0
  245. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  246. package/out/zero-cache/src/services/change-source/common/replica-schema.js +15 -3
  247. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  248. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts +1 -1
  249. package/out/zero-cache/src/services/change-source/custom/change-source.d.ts.map +1 -1
  250. package/out/zero-cache/src/services/change-source/custom/change-source.js +7 -3
  251. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  252. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +9 -0
  253. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
  254. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +206 -19
  255. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  256. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts +55 -0
  257. package/out/zero-cache/src/services/change-source/pg/change-source-init.d.ts.map +1 -0
  258. package/out/zero-cache/src/services/change-source/pg/change-source-init.js +305 -0
  259. package/out/zero-cache/src/services/change-source/pg/change-source-init.js.map +1 -0
  260. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +31 -18
  261. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  262. package/out/zero-cache/src/services/change-source/pg/change-source.js +130 -157
  263. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  264. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +38 -2
  265. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  266. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +55 -21
  267. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  268. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +31 -0
  269. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
  270. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +102 -3
  271. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  272. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts +16 -0
  273. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.d.ts.map +1 -0
  274. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js +113 -0
  275. package/out/zero-cache/src/services/change-source/pg/replication-slot-cleanup-monitor.js.map +1 -0
  276. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +40 -15
  277. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  278. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +149 -27
  279. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  280. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -1
  281. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +68 -8
  282. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -1
  283. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  284. package/out/zero-cache/src/services/change-source/pg/schema/init.js +32 -1
  285. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  286. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts +10 -0
  287. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.d.ts.map +1 -0
  288. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js +6 -0
  289. package/out/zero-cache/src/services/change-source/pg/schema/replica-stage-enum.js.map +1 -0
  290. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +50 -27
  291. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  292. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +98 -34
  293. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  294. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts +68 -0
  295. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.d.ts.map +1 -0
  296. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js +141 -0
  297. package/out/zero-cache/src/services/change-source/protocol/backfill-progress.js.map +1 -0
  298. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +45 -2
  299. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  300. package/out/zero-cache/src/services/change-source/protocol/current/data.js +10 -1
  301. package/out/zero-cache/src/services/change-source/protocol/current/data.js.map +1 -1
  302. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +60 -3
  303. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  304. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts +23 -10
  305. package/out/zero-cache/src/services/change-source/protocol/current/upstream.d.ts.map +1 -1
  306. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js +17 -7
  307. package/out/zero-cache/src/services/change-source/protocol/current/upstream.js.map +1 -1
  308. package/out/zero-cache/src/services/change-source/protocol/current.js +2 -1
  309. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts +72 -0
  310. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.d.ts.map +1 -0
  311. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js +162 -0
  312. package/out/zero-cache/src/services/change-streamer/backfill-state-tracker.js.map +1 -0
  313. package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts +50 -0
  314. package/out/zero-cache/src/services/change-streamer/backfill-state.d.ts.map +1 -0
  315. package/out/zero-cache/src/services/change-streamer/backfill-state.js +268 -0
  316. package/out/zero-cache/src/services/change-streamer/backfill-state.js.map +1 -0
  317. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  318. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +6 -17
  319. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  320. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  321. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +59 -8
  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.d.ts +8 -2
  324. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  325. package/out/zero-cache/src/services/change-streamer/broadcast.js +24 -4
  326. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  327. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts +19 -4
  328. package/out/zero-cache/src/services/change-streamer/change-log-codec.d.ts.map +1 -1
  329. package/out/zero-cache/src/services/change-streamer/change-log-codec.js +28 -8
  330. package/out/zero-cache/src/services/change-streamer/change-log-codec.js.map +1 -1
  331. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts +17 -0
  332. package/out/zero-cache/src/services/change-streamer/change-log-initializer.d.ts.map +1 -1
  333. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js +44 -1
  334. package/out/zero-cache/src/services/change-streamer/change-log-initializer.js.map +1 -1
  335. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +48 -6
  336. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  337. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +131 -34
  338. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  339. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +22 -0
  340. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  341. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +149 -78
  342. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  343. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +38 -16
  344. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  345. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  346. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts +3 -3
  347. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  348. package/out/zero-cache/src/services/change-streamer/forwarder.js +36 -5
  349. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  350. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts +53 -0
  351. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.d.ts.map +1 -0
  352. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js +156 -0
  353. package/out/zero-cache/src/services/change-streamer/litestream-sync-requester.js.map +1 -0
  354. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts +58 -1
  355. package/out/zero-cache/src/services/change-streamer/schema/tables.d.ts.map +1 -1
  356. package/out/zero-cache/src/services/change-streamer/schema/tables.js +107 -16
  357. package/out/zero-cache/src/services/change-streamer/schema/tables.js.map +1 -1
  358. package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts +92 -0
  359. package/out/zero-cache/src/services/change-streamer/snapshot-message.d.ts.map +1 -0
  360. package/out/zero-cache/src/services/change-streamer/snapshot-message.js +42 -0
  361. package/out/zero-cache/src/services/change-streamer/snapshot-message.js.map +1 -0
  362. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +1 -1
  363. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -1
  364. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +16 -4
  365. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  366. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts +7 -69
  367. package/out/zero-cache/src/services/change-streamer/snapshot.d.ts.map +1 -1
  368. package/out/zero-cache/src/services/change-streamer/snapshot.js +21 -53
  369. package/out/zero-cache/src/services/change-streamer/snapshot.js.map +1 -1
  370. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts +6 -1
  371. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.d.ts.map +1 -1
  372. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js +27 -9
  373. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-writer.js.map +1 -1
  374. package/out/zero-cache/src/services/change-streamer/storer.d.ts +95 -4
  375. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  376. package/out/zero-cache/src/services/change-streamer/storer.js +293 -83
  377. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  378. package/out/zero-cache/src/services/change-streamer/subscribe.d.ts +446 -0
  379. package/out/zero-cache/src/services/change-streamer/subscribe.d.ts.map +1 -0
  380. package/out/zero-cache/src/services/change-streamer/subscribe.js +76 -0
  381. package/out/zero-cache/src/services/change-streamer/subscribe.js.map +1 -0
  382. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +58 -1
  383. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  384. package/out/zero-cache/src/services/change-streamer/subscriber.js +123 -12
  385. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  386. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts +19 -1
  387. package/out/zero-cache/src/services/change-streamer/upstream-acker.d.ts.map +1 -1
  388. package/out/zero-cache/src/services/change-streamer/upstream-acker.js +23 -3
  389. package/out/zero-cache/src/services/change-streamer/upstream-acker.js.map +1 -1
  390. package/out/zero-cache/src/services/http-service.d.ts +1 -1
  391. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  392. package/out/zero-cache/src/services/http-service.js.map +1 -1
  393. package/out/zero-cache/src/services/life-cycle.d.ts.map +1 -1
  394. package/out/zero-cache/src/services/life-cycle.js +3 -1
  395. package/out/zero-cache/src/services/life-cycle.js.map +1 -1
  396. package/out/zero-cache/src/services/litestream/commands.d.ts +34 -3
  397. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  398. package/out/zero-cache/src/services/litestream/commands.js +121 -35
  399. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  400. package/out/zero-cache/src/services/litestream/config-v5.yml +6 -7
  401. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +51 -24
  402. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -1
  403. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +10 -2
  404. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +1 -1
  405. package/out/zero-cache/src/services/litestream/litestream-controller.js +3 -2
  406. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -1
  407. package/out/zero-cache/src/services/litestream/restore-progress.d.ts +24 -0
  408. package/out/zero-cache/src/services/litestream/restore-progress.d.ts.map +1 -0
  409. package/out/zero-cache/src/services/litestream/restore-progress.js +91 -0
  410. package/out/zero-cache/src/services/litestream/restore-progress.js.map +1 -0
  411. package/out/zero-cache/src/services/litestream/wal-keeper.d.ts +33 -0
  412. package/out/zero-cache/src/services/litestream/wal-keeper.d.ts.map +1 -0
  413. package/out/zero-cache/src/services/litestream/wal-keeper.js +49 -0
  414. package/out/zero-cache/src/services/litestream/wal-keeper.js.map +1 -0
  415. package/out/zero-cache/src/services/mutagen/mutagen.d.ts +5 -0
  416. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  417. package/out/zero-cache/src/services/mutagen/mutagen.js +7 -1
  418. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  419. package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
  420. package/out/zero-cache/src/services/mutagen/pusher.js +5 -6
  421. package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
  422. package/out/zero-cache/src/services/plannerz.d.ts +163 -0
  423. package/out/zero-cache/src/services/plannerz.d.ts.map +1 -0
  424. package/out/zero-cache/src/services/plannerz.js +267 -0
  425. package/out/zero-cache/src/services/plannerz.js.map +1 -0
  426. package/out/zero-cache/src/services/profz.d.ts +30 -0
  427. package/out/zero-cache/src/services/profz.d.ts.map +1 -0
  428. package/out/zero-cache/src/services/profz.js +163 -0
  429. package/out/zero-cache/src/services/profz.js.map +1 -0
  430. package/out/zero-cache/src/services/replicator/change-log-cookies.js +1 -1
  431. package/out/zero-cache/src/services/replicator/change-log-cookies.js.map +1 -1
  432. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts +5 -4
  433. package/out/zero-cache/src/services/replicator/change-log-stream-writer.d.ts.map +1 -1
  434. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js +20 -22
  435. package/out/zero-cache/src/services/replicator/change-log-stream-writer.js.map +1 -1
  436. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  437. package/out/zero-cache/src/services/replicator/change-processor.js +51 -5
  438. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  439. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +5 -2
  440. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  441. package/out/zero-cache/src/services/replicator/incremental-sync.js +54 -21
  442. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  443. package/out/zero-cache/src/services/replicator/replication-status.d.ts +35 -1
  444. package/out/zero-cache/src/services/replicator/replication-status.d.ts.map +1 -1
  445. package/out/zero-cache/src/services/replicator/replication-status.js +99 -8
  446. package/out/zero-cache/src/services/replicator/replication-status.js.map +1 -1
  447. package/out/zero-cache/src/services/replicator/replicator.d.ts +4 -1
  448. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
  449. package/out/zero-cache/src/services/replicator/replicator.js +5 -2
  450. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  451. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts +16 -1
  452. package/out/zero-cache/src/services/replicator/reporter/recorder.d.ts.map +1 -1
  453. package/out/zero-cache/src/services/replicator/reporter/recorder.js +70 -27
  454. package/out/zero-cache/src/services/replicator/reporter/recorder.js.map +1 -1
  455. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts +23 -2
  456. package/out/zero-cache/src/services/replicator/schema/backfilling.d.ts.map +1 -1
  457. package/out/zero-cache/src/services/replicator/schema/backfilling.js +54 -2
  458. package/out/zero-cache/src/services/replicator/schema/backfilling.js.map +1 -1
  459. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +6 -6
  460. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -1
  461. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +19 -14
  462. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  463. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +11 -3
  464. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
  465. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  466. package/out/zero-cache/src/services/replicator/write-worker.js +5 -1
  467. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  468. package/out/zero-cache/src/services/run-ast.d.ts +12 -0
  469. package/out/zero-cache/src/services/run-ast.d.ts.map +1 -1
  470. package/out/zero-cache/src/services/run-ast.js +42 -22
  471. package/out/zero-cache/src/services/run-ast.js.map +1 -1
  472. package/out/zero-cache/src/services/runner.d.ts +2 -1
  473. package/out/zero-cache/src/services/runner.d.ts.map +1 -1
  474. package/out/zero-cache/src/services/runner.js +10 -2
  475. package/out/zero-cache/src/services/runner.js.map +1 -1
  476. package/out/zero-cache/src/services/statz.d.ts +4 -0
  477. package/out/zero-cache/src/services/statz.d.ts.map +1 -1
  478. package/out/zero-cache/src/services/statz.js +22 -5
  479. package/out/zero-cache/src/services/statz.js.map +1 -1
  480. package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts +26 -0
  481. package/out/zero-cache/src/services/view-syncer/advancement-metrics.d.ts.map +1 -0
  482. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js +26 -0
  483. package/out/zero-cache/src/services/view-syncer/advancement-metrics.js.map +1 -0
  484. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +19 -2
  485. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  486. package/out/zero-cache/src/services/view-syncer/client-handler.js +42 -30
  487. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  488. package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts +2 -1
  489. package/out/zero-cache/src/services/view-syncer/connection-context-manager.d.ts.map +1 -1
  490. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js +1 -0
  491. package/out/zero-cache/src/services/view-syncer/connection-context-manager.js.map +1 -1
  492. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts +27 -2
  493. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  494. package/out/zero-cache/src/services/view-syncer/cvr-store.js +85 -20
  495. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  496. package/out/zero-cache/src/services/view-syncer/cvr.d.ts +76 -11
  497. package/out/zero-cache/src/services/view-syncer/cvr.d.ts.map +1 -1
  498. package/out/zero-cache/src/services/view-syncer/cvr.js +214 -40
  499. package/out/zero-cache/src/services/view-syncer/cvr.js.map +1 -1
  500. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts +81 -0
  501. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.d.ts.map +1 -0
  502. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js +128 -0
  503. package/out/zero-cache/src/services/view-syncer/deferred-writes-budget.js.map +1 -0
  504. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts +14 -1
  505. package/out/zero-cache/src/services/view-syncer/drain-coordinator.d.ts.map +1 -1
  506. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js +30 -4
  507. package/out/zero-cache/src/services/view-syncer/drain-coordinator.js.map +1 -1
  508. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts +33 -0
  509. package/out/zero-cache/src/services/view-syncer/hydration-budget.d.ts.map +1 -0
  510. package/out/zero-cache/src/services/view-syncer/hydration-budget.js +62 -0
  511. package/out/zero-cache/src/services/view-syncer/hydration-budget.js.map +1 -0
  512. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts +51 -0
  513. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.d.ts.map +1 -0
  514. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js +78 -0
  515. package/out/zero-cache/src/services/view-syncer/hydration-circuit-breaker.js.map +1 -0
  516. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts +10 -1
  517. package/out/zero-cache/src/services/view-syncer/inspect-handler.d.ts.map +1 -1
  518. package/out/zero-cache/src/services/view-syncer/inspect-handler.js +18 -4
  519. package/out/zero-cache/src/services/view-syncer/inspect-handler.js.map +1 -1
  520. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts +80 -6
  521. package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
  522. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +658 -195
  523. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  524. package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts +7 -0
  525. package/out/zero-cache/src/services/view-syncer/plan-warnings.d.ts.map +1 -0
  526. package/out/zero-cache/src/services/view-syncer/plan-warnings.js +35 -0
  527. package/out/zero-cache/src/services/view-syncer/plan-warnings.js.map +1 -0
  528. package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -1
  529. package/out/zero-cache/src/services/view-syncer/query-covering.js +2 -6
  530. package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
  531. package/out/zero-cache/src/services/view-syncer/query-shape.d.ts +25 -0
  532. package/out/zero-cache/src/services/view-syncer/query-shape.d.ts.map +1 -0
  533. package/out/zero-cache/src/services/view-syncer/query-shape.js +28 -0
  534. package/out/zero-cache/src/services/view-syncer/query-shape.js.map +1 -0
  535. package/out/zero-cache/src/services/view-syncer/query-stats.d.ts +82 -0
  536. package/out/zero-cache/src/services/view-syncer/query-stats.d.ts.map +1 -0
  537. package/out/zero-cache/src/services/view-syncer/query-stats.js +214 -0
  538. package/out/zero-cache/src/services/view-syncer/query-stats.js.map +1 -0
  539. package/out/zero-cache/src/services/view-syncer/schema/types.d.ts +2 -107
  540. package/out/zero-cache/src/services/view-syncer/schema/types.d.ts.map +1 -1
  541. package/out/zero-cache/src/services/view-syncer/schema/types.js +5 -20
  542. package/out/zero-cache/src/services/view-syncer/schema/types.js.map +1 -1
  543. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts +95 -0
  544. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts.map +1 -0
  545. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +155 -0
  546. package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -0
  547. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +96 -14
  548. package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
  549. package/out/zero-cache/src/services/view-syncer/snapshotter.js +214 -30
  550. package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
  551. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +11 -1
  552. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  553. package/out/zero-cache/src/services/view-syncer/view-syncer.js +566 -107
  554. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  555. package/out/zero-cache/src/types/pg-data-type.d.ts +2 -0
  556. package/out/zero-cache/src/types/pg-data-type.d.ts.map +1 -1
  557. package/out/zero-cache/src/types/pg-data-type.js +6 -1
  558. package/out/zero-cache/src/types/pg-data-type.js.map +1 -1
  559. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  560. package/out/zero-cache/src/types/pg.js +1 -1
  561. package/out/zero-cache/src/types/pg.js.map +1 -1
  562. package/out/zero-cache/src/types/processes.d.ts +39 -0
  563. package/out/zero-cache/src/types/processes.d.ts.map +1 -1
  564. package/out/zero-cache/src/types/processes.js +43 -4
  565. package/out/zero-cache/src/types/processes.js.map +1 -1
  566. package/out/zero-cache/src/types/profiler.d.ts +12 -0
  567. package/out/zero-cache/src/types/profiler.d.ts.map +1 -1
  568. package/out/zero-cache/src/types/profiler.js +46 -1
  569. package/out/zero-cache/src/types/profiler.js.map +1 -1
  570. package/out/zero-cache/src/types/row-key.d.ts +1 -15
  571. package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
  572. package/out/zero-cache/src/types/row-key.js +6 -8
  573. package/out/zero-cache/src/types/row-key.js.map +1 -1
  574. package/out/zero-cache/src/types/streams.d.ts +49 -7
  575. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  576. package/out/zero-cache/src/types/streams.js +190 -58
  577. package/out/zero-cache/src/types/streams.js.map +1 -1
  578. package/out/zero-cache/src/types/subscription.d.ts +15 -0
  579. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  580. package/out/zero-cache/src/types/subscription.js +123 -31
  581. package/out/zero-cache/src/types/subscription.js.map +1 -1
  582. package/out/zero-cache/src/types/websocket-handoff.d.ts +1 -1
  583. package/out/zero-cache/src/types/websocket-handoff.d.ts.map +1 -1
  584. package/out/zero-cache/src/types/websocket-handoff.js +18 -9
  585. package/out/zero-cache/src/types/websocket-handoff.js.map +1 -1
  586. package/out/zero-cache/src/types/ws.d.ts.map +1 -1
  587. package/out/zero-cache/src/types/ws.js +1 -16
  588. package/out/zero-cache/src/types/ws.js.map +1 -1
  589. package/out/zero-cache/src/workers/connect-params.d.ts +18 -1
  590. package/out/zero-cache/src/workers/connect-params.d.ts.map +1 -1
  591. package/out/zero-cache/src/workers/connect-params.js +25 -1
  592. package/out/zero-cache/src/workers/connect-params.js.map +1 -1
  593. package/out/zero-cache/src/workers/connection.d.ts +4 -4
  594. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  595. package/out/zero-cache/src/workers/connection.js +8 -8
  596. package/out/zero-cache/src/workers/connection.js.map +1 -1
  597. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  598. package/out/zero-cache/src/workers/replicator.js +15 -7
  599. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  600. package/out/zero-cache/src/workers/syncer-ws-message-handler.d.ts.map +1 -1
  601. package/out/zero-cache/src/workers/syncer-ws-message-handler.js +21 -16
  602. package/out/zero-cache/src/workers/syncer-ws-message-handler.js.map +1 -1
  603. package/out/zero-cache/src/workers/syncer.d.ts.map +1 -1
  604. package/out/zero-cache/src/workers/syncer.js +55 -4
  605. package/out/zero-cache/src/workers/syncer.js.map +1 -1
  606. package/out/zero-client/src/client/active-clients-manager.d.ts.map +1 -1
  607. package/out/zero-client/src/client/active-clients-manager.js +4 -1
  608. package/out/zero-client/src/client/active-clients-manager.js.map +1 -1
  609. package/out/zero-client/src/client/connection-manager.d.ts +20 -0
  610. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  611. package/out/zero-client/src/client/connection-manager.js +77 -13
  612. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  613. package/out/zero-client/src/client/connection-status-enum.d.ts +2 -0
  614. package/out/zero-client/src/client/connection-status-enum.d.ts.map +1 -1
  615. package/out/zero-client/src/client/connection-status-enum.js +3 -1
  616. package/out/zero-client/src/client/connection-status-enum.js.map +1 -1
  617. package/out/zero-client/src/client/connection.d.ts +6 -0
  618. package/out/zero-client/src/client/connection.d.ts.map +1 -1
  619. package/out/zero-client/src/client/connection.js +2 -1
  620. package/out/zero-client/src/client/connection.js.map +1 -1
  621. package/out/zero-client/src/client/context.d.ts +23 -13
  622. package/out/zero-client/src/client/context.d.ts.map +1 -1
  623. package/out/zero-client/src/client/context.js +94 -26
  624. package/out/zero-client/src/client/context.js.map +1 -1
  625. package/out/zero-client/src/client/crud-impl.d.ts.map +1 -1
  626. package/out/zero-client/src/client/crud-impl.js +1 -1
  627. package/out/zero-client/src/client/crud-impl.js.map +1 -1
  628. package/out/zero-client/src/client/custom.d.ts +5 -4
  629. package/out/zero-client/src/client/custom.d.ts.map +1 -1
  630. package/out/zero-client/src/client/custom.js +3 -3
  631. package/out/zero-client/src/client/custom.js.map +1 -1
  632. package/out/zero-client/src/client/inspector/lazy-inspector.js +1 -1
  633. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  634. package/out/zero-client/src/client/ivm-branch.js +20 -3
  635. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  636. package/out/zero-client/src/client/keys.d.ts.map +1 -1
  637. package/out/zero-client/src/client/keys.js +19 -1
  638. package/out/zero-client/src/client/keys.js.map +1 -1
  639. package/out/zero-client/src/client/make-mutate-property.d.ts +1 -1
  640. package/out/zero-client/src/client/make-mutate-property.d.ts.map +1 -1
  641. package/out/zero-client/src/client/make-mutate-property.js.map +1 -1
  642. package/out/zero-client/src/client/make-replicache-mutators.d.ts.map +1 -1
  643. package/out/zero-client/src/client/make-replicache-mutators.js +1 -1
  644. package/out/zero-client/src/client/make-replicache-mutators.js.map +1 -1
  645. package/out/zero-client/src/client/mutation-tracker.d.ts +2 -2
  646. package/out/zero-client/src/client/mutation-tracker.d.ts.map +1 -1
  647. package/out/zero-client/src/client/mutation-tracker.js +3 -7
  648. package/out/zero-client/src/client/mutation-tracker.js.map +1 -1
  649. package/out/zero-client/src/client/mutator-proxy.d.ts.map +1 -1
  650. package/out/zero-client/src/client/mutator-proxy.js +9 -4
  651. package/out/zero-client/src/client/mutator-proxy.js.map +1 -1
  652. package/out/zero-client/src/client/options.d.ts +33 -0
  653. package/out/zero-client/src/client/options.d.ts.map +1 -1
  654. package/out/zero-client/src/client/options.js.map +1 -1
  655. package/out/zero-client/src/client/query-manager.d.ts +9 -0
  656. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  657. package/out/zero-client/src/client/query-manager.js +47 -14
  658. package/out/zero-client/src/client/query-manager.js.map +1 -1
  659. package/out/zero-client/src/client/reload-error-handler.js +2 -2
  660. package/out/zero-client/src/client/reload-error-handler.js.map +1 -1
  661. package/out/zero-client/src/client/version.js +1 -1
  662. package/out/zero-client/src/client/zero-rep.d.ts +1 -0
  663. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  664. package/out/zero-client/src/client/zero-rep.js +8 -6
  665. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  666. package/out/zero-client/src/client/zero.d.ts +18 -3
  667. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  668. package/out/zero-client/src/client/zero.js +89 -50
  669. package/out/zero-client/src/client/zero.js.map +1 -1
  670. package/out/zero-client/src/mod.d.ts +2 -1
  671. package/out/zero-client/src/mod.d.ts.map +1 -1
  672. package/out/zero-client/src/mod.js +1 -0
  673. package/out/zero-client/src/types/query-result.d.ts +2 -0
  674. package/out/zero-client/src/types/query-result.d.ts.map +1 -1
  675. package/out/zero-events/src/status.d.ts +64 -0
  676. package/out/zero-events/src/status.d.ts.map +1 -1
  677. package/out/zero-protocol/src/analyze-query-result.d.ts +5 -0
  678. package/out/zero-protocol/src/analyze-query-result.d.ts.map +1 -1
  679. package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
  680. package/out/zero-protocol/src/ast.d.ts.map +1 -1
  681. package/out/zero-protocol/src/ast.js +13 -14
  682. package/out/zero-protocol/src/ast.js.map +1 -1
  683. package/out/zero-protocol/src/connect.d.ts +16 -0
  684. package/out/zero-protocol/src/connect.d.ts.map +1 -1
  685. package/out/zero-protocol/src/connect.js +27 -1
  686. package/out/zero-protocol/src/connect.js.map +1 -1
  687. package/out/zero-protocol/src/down.d.ts +31 -31
  688. package/out/zero-protocol/src/feature-flag-enum.d.ts +14 -0
  689. package/out/zero-protocol/src/feature-flag-enum.d.ts.map +1 -0
  690. package/out/zero-protocol/src/feature-flag-enum.js +10 -0
  691. package/out/zero-protocol/src/feature-flag-enum.js.map +1 -0
  692. package/out/zero-protocol/src/feature-flag.d.ts +5 -0
  693. package/out/zero-protocol/src/feature-flag.d.ts.map +1 -0
  694. package/out/zero-protocol/src/inspect-down.d.ts +16 -16
  695. package/out/zero-protocol/src/poke.d.ts +17 -10
  696. package/out/zero-protocol/src/poke.d.ts.map +1 -1
  697. package/out/zero-protocol/src/poke.js +3 -2
  698. package/out/zero-protocol/src/poke.js.map +1 -1
  699. package/out/zero-protocol/src/protocol-version.d.ts +30 -6
  700. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  701. package/out/zero-protocol/src/protocol-version.js +1 -1
  702. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  703. package/out/zero-protocol/src/query-hash.d.ts.map +1 -1
  704. package/out/zero-protocol/src/query-hash.js +2 -5
  705. package/out/zero-protocol/src/query-hash.js.map +1 -1
  706. package/out/zero-react/src/use-query.d.ts +5 -4
  707. package/out/zero-react/src/use-query.d.ts.map +1 -1
  708. package/out/zero-react/src/use-query.js +74 -7
  709. package/out/zero-react/src/use-query.js.map +1 -1
  710. package/out/zero-react/src/zero.js +1 -0
  711. package/out/zero-server/src/adapters/drizzle.d.ts +2 -1
  712. package/out/zero-server/src/adapters/drizzle.d.ts.map +1 -1
  713. package/out/zero-server/src/adapters/drizzle.js +12 -7
  714. package/out/zero-server/src/adapters/drizzle.js.map +1 -1
  715. package/out/zero-server/src/adapters/kysely.d.ts +2 -1
  716. package/out/zero-server/src/adapters/kysely.d.ts.map +1 -1
  717. package/out/zero-server/src/adapters/kysely.js +8 -2
  718. package/out/zero-server/src/adapters/kysely.js.map +1 -1
  719. package/out/zero-server/src/adapters/pg.d.ts +5 -1
  720. package/out/zero-server/src/adapters/pg.d.ts.map +1 -1
  721. package/out/zero-server/src/adapters/pg.js +28 -7
  722. package/out/zero-server/src/adapters/pg.js.map +1 -1
  723. package/out/zero-server/src/adapters/postgresjs.d.ts +1 -0
  724. package/out/zero-server/src/adapters/postgresjs.d.ts.map +1 -1
  725. package/out/zero-server/src/adapters/postgresjs.js +7 -1
  726. package/out/zero-server/src/adapters/postgresjs.js.map +1 -1
  727. package/out/zero-server/src/adapters/prisma.d.ts +3 -2
  728. package/out/zero-server/src/adapters/prisma.d.ts.map +1 -1
  729. package/out/zero-server/src/adapters/prisma.js +8 -2
  730. package/out/zero-server/src/adapters/prisma.js.map +1 -1
  731. package/out/zero-server/src/closable-db-transaction.d.ts +16 -0
  732. package/out/zero-server/src/closable-db-transaction.d.ts.map +1 -0
  733. package/out/zero-server/src/closable-db-transaction.js +31 -0
  734. package/out/zero-server/src/closable-db-transaction.js.map +1 -0
  735. package/out/zero-server/src/custom.d.ts +10 -4
  736. package/out/zero-server/src/custom.d.ts.map +1 -1
  737. package/out/zero-server/src/custom.js +15 -6
  738. package/out/zero-server/src/custom.js.map +1 -1
  739. package/out/zero-server/src/logging.d.ts +2 -2
  740. package/out/zero-server/src/logging.d.ts.map +1 -1
  741. package/out/zero-server/src/logging.js +2 -2
  742. package/out/zero-server/src/logging.js.map +1 -1
  743. package/out/zero-server/src/mod.d.ts +1 -1
  744. package/out/zero-server/src/mod.d.ts.map +1 -1
  745. package/out/zero-server/src/pg-query-executor.d.ts +2 -2
  746. package/out/zero-server/src/pg-query-executor.d.ts.map +1 -1
  747. package/out/zero-server/src/pg-query-executor.js +2 -2
  748. package/out/zero-server/src/pg-query-executor.js.map +1 -1
  749. package/out/zero-server/src/process-mutations.d.ts +25 -4
  750. package/out/zero-server/src/process-mutations.d.ts.map +1 -1
  751. package/out/zero-server/src/process-mutations.js +103 -52
  752. package/out/zero-server/src/process-mutations.js.map +1 -1
  753. package/out/zero-server/src/push-processor.d.ts +8 -2
  754. package/out/zero-server/src/push-processor.d.ts.map +1 -1
  755. package/out/zero-server/src/push-processor.js +12 -4
  756. package/out/zero-server/src/push-processor.js.map +1 -1
  757. package/out/zero-server/src/schema.d.ts +2 -2
  758. package/out/zero-server/src/schema.d.ts.map +1 -1
  759. package/out/zero-server/src/schema.js +3 -2
  760. package/out/zero-server/src/schema.js.map +1 -1
  761. package/out/zero-server/src/zql-database.d.ts +21 -1
  762. package/out/zero-server/src/zql-database.d.ts.map +1 -1
  763. package/out/zero-server/src/zql-database.js +61 -24
  764. package/out/zero-server/src/zql-database.js.map +1 -1
  765. package/out/zero-solid/src/solid-view.d.ts +17 -0
  766. package/out/zero-solid/src/solid-view.d.ts.map +1 -1
  767. package/out/zero-solid/src/solid-view.js +39 -1
  768. package/out/zero-solid/src/solid-view.js.map +1 -1
  769. package/out/zero-solid/src/use-query.js +1 -2
  770. package/out/zero-solid/src/use-query.js.map +1 -1
  771. package/out/zero-solid/src/zero.js +1 -0
  772. package/out/zql/src/builder/builder.d.ts +25 -0
  773. package/out/zql/src/builder/builder.d.ts.map +1 -1
  774. package/out/zql/src/builder/builder.js +69 -19
  775. package/out/zql/src/builder/builder.js.map +1 -1
  776. package/out/zql/src/builder/correlated-predicate-pushdown.d.ts +43 -0
  777. package/out/zql/src/builder/correlated-predicate-pushdown.d.ts.map +1 -0
  778. package/out/zql/src/builder/correlated-predicate-pushdown.js +161 -0
  779. package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -0
  780. package/out/zql/src/builder/debug-delegate.d.ts +6 -1
  781. package/out/zql/src/builder/debug-delegate.d.ts.map +1 -1
  782. package/out/zql/src/builder/debug-delegate.js +14 -2
  783. package/out/zql/src/builder/debug-delegate.js.map +1 -1
  784. package/out/zql/src/ivm/array-view.d.ts +29 -0
  785. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  786. package/out/zql/src/ivm/array-view.js +74 -16
  787. package/out/zql/src/ivm/array-view.js.map +1 -1
  788. package/out/zql/src/ivm/cap.d.ts +2 -1
  789. package/out/zql/src/ivm/cap.d.ts.map +1 -1
  790. package/out/zql/src/ivm/cap.js +14 -4
  791. package/out/zql/src/ivm/cap.js.map +1 -1
  792. package/out/zql/src/ivm/data.d.ts.map +1 -1
  793. package/out/zql/src/ivm/data.js +8 -3
  794. package/out/zql/src/ivm/data.js.map +1 -1
  795. package/out/zql/src/ivm/deferred-input.d.ts.map +1 -1
  796. package/out/zql/src/ivm/deferred-input.js +3 -2
  797. package/out/zql/src/ivm/deferred-input.js.map +1 -1
  798. package/out/zql/src/ivm/exists.d.ts +3 -1
  799. package/out/zql/src/ivm/exists.d.ts.map +1 -1
  800. package/out/zql/src/ivm/exists.js +4 -1
  801. package/out/zql/src/ivm/exists.js.map +1 -1
  802. package/out/zql/src/ivm/fan-in.d.ts +6 -2
  803. package/out/zql/src/ivm/fan-in.d.ts.map +1 -1
  804. package/out/zql/src/ivm/fan-in.js +13 -5
  805. package/out/zql/src/ivm/fan-in.js.map +1 -1
  806. package/out/zql/src/ivm/fan-out.d.ts +5 -2
  807. package/out/zql/src/ivm/fan-out.d.ts.map +1 -1
  808. package/out/zql/src/ivm/fan-out.js +4 -0
  809. package/out/zql/src/ivm/fan-out.js.map +1 -1
  810. package/out/zql/src/ivm/filter-operators.d.ts +8 -6
  811. package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
  812. package/out/zql/src/ivm/filter-operators.js +19 -8
  813. package/out/zql/src/ivm/filter-operators.js.map +1 -1
  814. package/out/zql/src/ivm/filter.d.ts +5 -2
  815. package/out/zql/src/ivm/filter.d.ts.map +1 -1
  816. package/out/zql/src/ivm/filter.js +10 -4
  817. package/out/zql/src/ivm/filter.js.map +1 -1
  818. package/out/zql/src/ivm/flipped-join.d.ts +9 -4
  819. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  820. package/out/zql/src/ivm/flipped-join.js +97 -46
  821. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  822. package/out/zql/src/ivm/join-index-tap.d.ts +33 -0
  823. package/out/zql/src/ivm/join-index-tap.d.ts.map +1 -0
  824. package/out/zql/src/ivm/join-index-tap.js +70 -0
  825. package/out/zql/src/ivm/join-index-tap.js.map +1 -0
  826. package/out/zql/src/ivm/join-index.d.ts +35 -0
  827. package/out/zql/src/ivm/join-index.d.ts.map +1 -0
  828. package/out/zql/src/ivm/join-index.js +141 -0
  829. package/out/zql/src/ivm/join-index.js.map +1 -0
  830. package/out/zql/src/ivm/join-utils.d.ts +6 -0
  831. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  832. package/out/zql/src/ivm/join-utils.js +22 -1
  833. package/out/zql/src/ivm/join-utils.js.map +1 -1
  834. package/out/zql/src/ivm/join.d.ts +19 -1
  835. package/out/zql/src/ivm/join.d.ts.map +1 -1
  836. package/out/zql/src/ivm/join.js +107 -15
  837. package/out/zql/src/ivm/join.js.map +1 -1
  838. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts +1 -1
  839. package/out/zql/src/ivm/maybe-split-and-push-edit-change.d.ts.map +1 -1
  840. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js +6 -4
  841. package/out/zql/src/ivm/maybe-split-and-push-edit-change.js.map +1 -1
  842. package/out/zql/src/ivm/memory-source.d.ts +29 -4
  843. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  844. package/out/zql/src/ivm/memory-source.js +70 -34
  845. package/out/zql/src/ivm/memory-source.js.map +1 -1
  846. package/out/zql/src/ivm/memory-storage.d.ts +1 -0
  847. package/out/zql/src/ivm/memory-storage.d.ts.map +1 -1
  848. package/out/zql/src/ivm/memory-storage.js +1 -0
  849. package/out/zql/src/ivm/memory-storage.js.map +1 -1
  850. package/out/zql/src/ivm/operator.d.ts +45 -10
  851. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  852. package/out/zql/src/ivm/operator.js +9 -4
  853. package/out/zql/src/ivm/operator.js.map +1 -1
  854. package/out/zql/src/ivm/push-accumulated.d.ts +6 -1
  855. package/out/zql/src/ivm/push-accumulated.d.ts.map +1 -1
  856. package/out/zql/src/ivm/push-accumulated.js +47 -2
  857. package/out/zql/src/ivm/push-accumulated.js.map +1 -1
  858. package/out/zql/src/ivm/skip.d.ts +2 -1
  859. package/out/zql/src/ivm/skip.d.ts.map +1 -1
  860. package/out/zql/src/ivm/skip.js +3 -0
  861. package/out/zql/src/ivm/skip.js.map +1 -1
  862. package/out/zql/src/ivm/take-gate.d.ts +57 -0
  863. package/out/zql/src/ivm/take-gate.d.ts.map +1 -0
  864. package/out/zql/src/ivm/take-gate.js +106 -0
  865. package/out/zql/src/ivm/take-gate.js.map +1 -0
  866. package/out/zql/src/ivm/take.d.ts +8 -2
  867. package/out/zql/src/ivm/take.d.ts.map +1 -1
  868. package/out/zql/src/ivm/take.js +139 -103
  869. package/out/zql/src/ivm/take.js.map +1 -1
  870. package/out/zql/src/ivm/union-fan-in.d.ts +3 -0
  871. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  872. package/out/zql/src/ivm/union-fan-in.js +63 -10
  873. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  874. package/out/zql/src/ivm/union-fan-out.d.ts +2 -1
  875. package/out/zql/src/ivm/union-fan-out.d.ts.map +1 -1
  876. package/out/zql/src/ivm/union-fan-out.js +5 -0
  877. package/out/zql/src/ivm/union-fan-out.js.map +1 -1
  878. package/out/zql/src/ivm/view-apply-change.d.ts.map +1 -1
  879. package/out/zql/src/ivm/view-apply-change.js +9 -4
  880. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  881. package/out/zql/src/ivm/view.d.ts +15 -0
  882. package/out/zql/src/ivm/view.d.ts.map +1 -1
  883. package/out/zql/src/mutate/custom.d.ts +53 -1
  884. package/out/zql/src/mutate/custom.d.ts.map +1 -1
  885. package/out/zql/src/mutate/custom.js.map +1 -1
  886. package/out/zql/src/mutate/mutator-registry.d.ts.map +1 -1
  887. package/out/zql/src/mutate/mutator-registry.js +3 -3
  888. package/out/zql/src/mutate/mutator-registry.js.map +1 -1
  889. package/out/zql/src/mutate/mutator.d.ts +2 -2
  890. package/out/zql/src/mutate/mutator.d.ts.map +1 -1
  891. package/out/zql/src/mutate/mutator.js.map +1 -1
  892. package/out/zql/src/planner/planner-builder.d.ts +23 -3
  893. package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
  894. package/out/zql/src/planner/planner-builder.js +34 -17
  895. package/out/zql/src/planner/planner-builder.js.map +1 -1
  896. package/out/zql/src/planner/planner-connection.d.ts +58 -2
  897. package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
  898. package/out/zql/src/planner/planner-connection.js +103 -11
  899. package/out/zql/src/planner/planner-connection.js.map +1 -1
  900. package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
  901. package/out/zql/src/planner/planner-debug.js +3 -8
  902. package/out/zql/src/planner/planner-debug.js.map +1 -1
  903. package/out/zql/src/planner/planner-graph.d.ts +10 -0
  904. package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
  905. package/out/zql/src/planner/planner-graph.js +14 -0
  906. package/out/zql/src/planner/planner-graph.js.map +1 -1
  907. package/out/zql/src/planner/planner-source.d.ts +2 -2
  908. package/out/zql/src/planner/planner-source.d.ts.map +1 -1
  909. package/out/zql/src/planner/planner-source.js +2 -2
  910. package/out/zql/src/planner/planner-source.js.map +1 -1
  911. package/out/zql/src/planner/planner-warnings.d.ts +83 -0
  912. package/out/zql/src/planner/planner-warnings.d.ts.map +1 -0
  913. package/out/zql/src/planner/planner-warnings.js +78 -0
  914. package/out/zql/src/planner/planner-warnings.js.map +1 -0
  915. package/out/zql/src/query/measure-push-operator.d.ts +9 -2
  916. package/out/zql/src/query/measure-push-operator.d.ts.map +1 -1
  917. package/out/zql/src/query/measure-push-operator.js +52 -5
  918. package/out/zql/src/query/measure-push-operator.js.map +1 -1
  919. package/out/zql/src/query/query-delegate-base.d.ts +4 -2
  920. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  921. package/out/zql/src/query/query-delegate-base.js +66 -33
  922. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  923. package/out/zql/src/query/query-delegate.d.ts +33 -6
  924. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  925. package/out/zql/src/query/query-impl.d.ts +1 -0
  926. package/out/zql/src/query/query-impl.d.ts.map +1 -1
  927. package/out/zql/src/query/query-impl.js +6 -23
  928. package/out/zql/src/query/query-impl.js.map +1 -1
  929. package/out/zql/src/query/query-transitions.d.ts.map +1 -1
  930. package/out/zql/src/query/query-transitions.js +2 -5
  931. package/out/zql/src/query/query-transitions.js.map +1 -1
  932. package/out/zql/src/query/query.d.ts +22 -5
  933. package/out/zql/src/query/query.d.ts.map +1 -1
  934. package/out/zql/src/query/runnable-query-impl.d.ts +1 -0
  935. package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
  936. package/out/zql/src/query/runnable-query-impl.js +2 -10
  937. package/out/zql/src/query/runnable-query-impl.js.map +1 -1
  938. package/out/zql/src/query/typed-view.d.ts +1 -1
  939. package/out/zql/src/query/typed-view.d.ts.map +1 -1
  940. package/out/zqlite/src/database-storage.d.ts.map +1 -1
  941. package/out/zqlite/src/database-storage.js +9 -1
  942. package/out/zqlite/src/database-storage.js.map +1 -1
  943. package/out/zqlite/src/db.d.ts +9 -1
  944. package/out/zqlite/src/db.d.ts.map +1 -1
  945. package/out/zqlite/src/db.js +62 -30
  946. package/out/zqlite/src/db.js.map +1 -1
  947. package/out/zqlite/src/index-usage.d.ts +19 -0
  948. package/out/zqlite/src/index-usage.d.ts.map +1 -0
  949. package/out/zqlite/src/index-usage.js +49 -0
  950. package/out/zqlite/src/index-usage.js.map +1 -0
  951. package/out/zqlite/src/pending-delta.d.ts +110 -0
  952. package/out/zqlite/src/pending-delta.d.ts.map +1 -0
  953. package/out/zqlite/src/pending-delta.js +278 -0
  954. package/out/zqlite/src/pending-delta.js.map +1 -0
  955. package/out/zqlite/src/query-builder.d.ts +17 -1
  956. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  957. package/out/zqlite/src/query-builder.js +111 -20
  958. package/out/zqlite/src/query-builder.js.map +1 -1
  959. package/out/zqlite/src/query-delegate.d.ts.map +1 -1
  960. package/out/zqlite/src/query-delegate.js +10 -7
  961. package/out/zqlite/src/query-delegate.js.map +1 -1
  962. package/out/zqlite/src/resolve-scalar-subqueries.d.ts +3 -1
  963. package/out/zqlite/src/resolve-scalar-subqueries.d.ts.map +1 -1
  964. package/out/zqlite/src/resolve-scalar-subqueries.js +1 -0
  965. package/out/zqlite/src/resolve-scalar-subqueries.js.map +1 -1
  966. package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
  967. package/out/zqlite/src/sqlite-cost-model.js +52 -1
  968. package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
  969. package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
  970. package/out/zqlite/src/sqlite-stat-fanout.js +5 -31
  971. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  972. package/out/zqlite/src/sqlite-stat4-sample.d.ts +37 -0
  973. package/out/zqlite/src/sqlite-stat4-sample.d.ts.map +1 -0
  974. package/out/zqlite/src/sqlite-stat4-sample.js +61 -0
  975. package/out/zqlite/src/sqlite-stat4-sample.js.map +1 -0
  976. package/out/zqlite/src/table-source.d.ts +87 -2
  977. package/out/zqlite/src/table-source.d.ts.map +1 -1
  978. package/out/zqlite/src/table-source.js +276 -42
  979. package/out/zqlite/src/table-source.js.map +1 -1
  980. package/package.json +4 -5
  981. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts +0 -25
  982. package/out/replicache/src/kv/idb-store-with-mem-fallback.d.ts.map +0 -1
  983. package/out/replicache/src/kv/idb-store-with-mem-fallback.js +0 -83
  984. package/out/replicache/src/kv/idb-store-with-mem-fallback.js.map +0 -1
  985. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +0 -29
  986. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +0 -1
  987. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +0 -154
  988. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"life-cycle.js","names":["#lc","#userFacing","#all","#exitImpl","#start","#ready","#startDrain","#kill","#exit","#drainStart","#runningState","#onExit","#initializing","#nextID","#stopInterval","#lastHeartbeat","#checkIntervalTimer","#checkStopInterval","#checkImmediateTimer"],"sources":["../../../../../zero-cache/src/services/life-cycle.ts"],"sourcesContent":["import type {IncomingHttpHeaders} from 'node:http';\nimport {pid} from 'node:process';\nimport type {EventEmitter} from 'stream';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {logLastChanceSQLiteCorruptionDiagnostics} from '../db/sqlite-corruption.ts';\nimport {\n getOrCreateHistogram,\n LONG_DURATION_HISTOGRAM_BOUNDARIES_S,\n} from '../observability/metrics.ts';\nimport {ConfigurationError} from '../types/configuration-error.ts';\nimport {\n singleProcessMode,\n type Subprocess,\n type Worker,\n} from '../types/processes.ts';\nimport {RunningState} from './running-state.ts';\nimport type {SingletonService} from './service.ts';\n\n/**\n * * `user-facing` workers serve external requests and are the first to\n * receive a `SIGTERM` or `SIGINT` signal for graceful shutdown.\n *\n * * `supporting` workers support `user-facing` workers and are sent\n * the `SIGTERM` signal only after all `user-facing` workers have\n * exited.\n *\n * For other kill signals, such as `SIGQUIT` and `SIGABRT`, all workers\n * are stopped without draining. `SIGQUIT` is used to represent an\n * intentional shutdown (for which draining is not beneficial), whereas\n * `SIGABRT` is used for unexpected process exits.\n */\nexport type WorkerType = 'user-facing' | 'supporting';\n\nexport const GRACEFUL_SHUTDOWN = ['SIGTERM', 'SIGINT'] as const;\nexport const FORCEFUL_SHUTDOWN = ['SIGQUIT', 'SIGABRT'] as const;\n\ntype GracefulShutdownSignal = (typeof GRACEFUL_SHUTDOWN)[number];\n\nfunction workerStartupMetricName(name: string) {\n if (name === 'zero-cache') {\n return undefined;\n }\n if (name.startsWith('change-streamer')) {\n return 'change_streamer';\n }\n if (name.startsWith('replicator')) {\n if (name.includes('(backup)')) {\n return 'backup_replicator';\n }\n if (name.includes('(serving-copy)')) {\n return 'serving_copy_replicator';\n }\n if (name.includes('(serving)')) {\n return 'serving_replicator';\n }\n return 'other';\n }\n if (name.startsWith('syncer')) {\n return 'syncer';\n }\n if (name.startsWith('reaper')) {\n return 'reaper';\n }\n if (name.startsWith('shadow-syncer')) {\n return 'shadow_syncer';\n }\n if (name.startsWith('mutator')) {\n return 'mutator';\n }\n return 'other';\n}\n\nfunction startupDuration() {\n return getOrCreateHistogram('server', 'startup_duration', {\n description: 'Duration from starting zero-cache to its ready signal.',\n unit: 's',\n bucketBoundaries: LONG_DURATION_HISTOGRAM_BOUNDARIES_S,\n });\n}\n\nexport function recordStartupDurationMs(durationMs: number) {\n startupDuration().recordMs(durationMs, {component: 'dispatcher'});\n}\n\nfunction workerStartupDuration() {\n return getOrCreateHistogram('server', 'worker_startup_duration', {\n description: 'Duration from starting a worker to its ready signal.',\n unit: 's',\n bucketBoundaries: LONG_DURATION_HISTOGRAM_BOUNDARIES_S,\n });\n}\n\n// An internal error code used to indicate that a message has already been\n// logged at level ERRROR. When a process exits with this error code, the\n// parent process logs the exit at level WARN instead of ERROR.\nexport const UNHANDLED_EXCEPTION_ERROR_CODE = 13;\n\n// An internal error code used to indicate that the server should exit\n// without draining (e.g. due to a supporting worker get a signal to shut\n// down), but the exit is otherwise intentional.\nexport const INTENTIONAL_SHUTDOWN_ERROR_CODE = 14;\n\n/**\n * Handles readiness, termination signals, and coordination of graceful\n * shutdown.\n */\nexport class ProcessManager {\n readonly #lc: LogContext;\n readonly #userFacing = new Set<Subprocess>();\n readonly #all = new Set<Subprocess>();\n readonly #exitImpl: (code: number) => never;\n readonly #start = Date.now();\n readonly #ready: Promise<void>[] = [];\n\n #runningState = new RunningState('process-manager');\n #drainStart = 0;\n\n constructor(lc: LogContext, proc: EventEmitter) {\n this.#lc = lc.withContext('component', 'process-manager');\n\n // Propagate `SIGTERM` and `SIGINT` to all user-facing workers,\n // initiating a graceful shutdown. The parent process will\n // exit once all user-facing workers have exited ...\n for (const signal of GRACEFUL_SHUTDOWN) {\n proc.on(signal, () => this.#startDrain(signal));\n }\n\n // ... which will result in sending `SIGTERM` to the remaining workers.\n proc.on('exit', code =>\n this.#kill(\n this.#all,\n code === 0\n ? 'SIGTERM' // graceful, drained shutdown\n : code === INTENTIONAL_SHUTDOWN_ERROR_CODE\n ? 'SIGQUIT' // intentional abort without drain\n : 'SIGABRT', // unintentional shutdown, alertable error\n ),\n );\n\n // For other (catchable) kill signals, exit with a non-zero error code\n // to send a `SIGQUIT` (intentional shutdown) or `SIGABRT` (unexpected\n // shutdown) to all workers. For these signals, workers are stopped\n // immediately without draining, since there is no merit to slowly draining\n // when supporting workers have stopped.\n //\n // The logic for handling these signals is in `runUntilKilled()`.\n for (const signal of FORCEFUL_SHUTDOWN) {\n proc.on(signal, () =>\n this.#exit(signal === 'SIGQUIT' ? INTENTIONAL_SHUTDOWN_ERROR_CODE : -1),\n );\n }\n\n this.#exitImpl = (code: number) => {\n if (singleProcessMode()) {\n return proc.emit('exit', code) as never; // For unit / integration tests.\n }\n process.exit(code);\n };\n }\n\n async shutdown() {\n if (this.#drainStart === 0) {\n this.#startDrain(GRACEFUL_SHUTDOWN[0]);\n }\n await this.#runningState.stopped();\n }\n\n #exit(code: number) {\n this.#lc.info?.('exiting with code', code);\n this.#runningState.stop(this.#lc);\n void this.#lc.flush().finally(() => this.#exitImpl(code));\n }\n\n #startDrain(signal: GracefulShutdownSignal) {\n if (this.#all.size === 0) {\n // Shutdown if a signal is received before any subprocesses are added.\n this.#lc.info?.(`exiting on ${signal}`);\n this.#exit(0);\n }\n this.#lc.info?.(`initiating drain (${signal})`);\n this.#drainStart = Date.now();\n if (this.#userFacing.size) {\n this.#kill(this.#userFacing, signal);\n } else {\n this.#kill(this.#all, signal);\n }\n }\n\n addSubprocess(proc: Subprocess, type: WorkerType, name: string) {\n if (type === 'user-facing') {\n this.#userFacing.add(proc);\n }\n this.#all.add(proc);\n\n let isOpen = true;\n proc.on('close', (code, signal) => {\n isOpen = false;\n this.#onExit(code, signal, null, type, name, proc);\n });\n\n // As per https://nodejs.org/api/child_process.html#event-error\n // 'error' events can happen when sending a message to a child process\n // fails. This is not really an error when the server is shutting down,\n // so log any post-close errors at 'warn'.\n proc.on('error', err =>\n this.#lc[!isOpen || this.#drainStart > 0 ? 'warn' : 'error']?.(\n `error from ${name} ${proc.pid}`,\n err,\n ),\n );\n }\n\n readonly #initializing = new Map<number, string>();\n #nextID = 0;\n\n addWorker(worker: Worker, type: WorkerType, name: string): Worker {\n this.addSubprocess(worker, type, name);\n\n const start = performance.now();\n const id = ++this.#nextID;\n this.#initializing.set(id, name);\n const {promise, resolve} = resolver();\n this.#ready.push(promise);\n\n worker.onceMessageType('ready', () => {\n const elapsed = performance.now() - start;\n const workerMetricName = workerStartupMetricName(name);\n if (workerMetricName) {\n workerStartupDuration().recordMs(elapsed, {\n worker: workerMetricName,\n type,\n });\n }\n this.#lc.debug?.(`${name} ready (${Date.now() - this.#start} ms)`);\n this.#initializing.delete(id);\n resolve();\n });\n\n return worker;\n }\n\n initializing(): string[] {\n return [...this.#initializing.values()];\n }\n\n async allWorkersReady() {\n await Promise.all(this.#ready);\n }\n\n logErrorAndExit(err: unknown, name: string) {\n // only accessible by the main (i.e. user-facing) process.\n this.#onExit(-1, null, err, 'user-facing', name, undefined);\n }\n\n #onExit(\n code: number,\n sig: NodeJS.Signals | null,\n err: unknown,\n type: WorkerType,\n name: string,\n worker: Subprocess | undefined,\n ) {\n // Remove the worker from maps to avoid attempting to send more signals to it.\n if (worker) {\n this.#userFacing.delete(worker);\n this.#all.delete(worker);\n }\n\n const pid = worker?.pid ?? process.pid;\n\n if (type === 'supporting') {\n // Supporting workers like the replication-manager shut down without a\n // drain signal when receiving protocol-specific instructions (like auto\n // reset). In this case, a special error code is used to signal that the\n // server should be shut down without draining, but it is otherwise not\n // considered an unexpected/alertable error.\n if (code === 0 && (this.#drainStart === 0 || this.#userFacing.size > 0)) {\n code = INTENTIONAL_SHUTDOWN_ERROR_CODE;\n }\n const log =\n code === 0 || code === INTENTIONAL_SHUTDOWN_ERROR_CODE\n ? 'info'\n : 'warn';\n this.#lc[log]?.(`${name} (${pid}) exited with code (${code})`, err ?? '');\n return this.#exit(code);\n }\n\n const log =\n code === 0 || code === INTENTIONAL_SHUTDOWN_ERROR_CODE\n ? 'info'\n : this.#drainStart > 0 || code === UNHANDLED_EXCEPTION_ERROR_CODE\n ? 'warn'\n : 'error';\n this.#lc[log]?.(\n sig\n ? `${name} (${pid}) killed with (${sig})`\n : `${name} (${pid}) exited with code (${code})`,\n err ?? '',\n );\n\n // user-facing workers exited or finished draining.\n if (this.#userFacing.size === 0) {\n this.#lc.info?.(\n this.#drainStart\n ? `all user-facing workers drained (${\n Date.now() - this.#drainStart\n } ms)`\n : `all user-facing workers exited`,\n );\n return this.#exit(this.#drainStart ? 0 : code || -1);\n }\n\n if (this.#drainStart === 0) {\n // If a user-facing worker exits without receiving a drain signal,\n // shutdown the server.\n return this.#exit(code || -1);\n }\n\n return undefined;\n }\n\n #kill(workers: Iterable<Subprocess>, signal: NodeJS.Signals) {\n for (const worker of workers) {\n try {\n worker.kill(signal);\n } catch (e) {\n this.#lc.error?.(e);\n }\n }\n }\n}\n\n/**\n * Runs the specified services, stopping them on `SIGTERM` or `SIGINT` with\n * an optional {@link SingletonService.drain drain()}, or stopping them\n * without draining for `SIGQUIT`.\n *\n * @returns a Promise that resolves/rejects when any of the services stops/throws.\n */\n\nexport async function runUntilKilled(\n lc: LogContext,\n parent: EventEmitter,\n ...services: SingletonService[]\n): Promise<void> {\n if (services.length === 0) {\n return;\n }\n for (const signal of [...GRACEFUL_SHUTDOWN, ...FORCEFUL_SHUTDOWN]) {\n parent.once(signal, () => {\n const GRACEFUL_SIGNALS = GRACEFUL_SHUTDOWN as readonly NodeJS.Signals[];\n\n services.forEach(async svc => {\n if (GRACEFUL_SIGNALS.includes(signal) && svc.drain) {\n lc.info?.(`draining ${svc.constructor.name} ${svc.id} (${signal})`);\n await svc.drain();\n }\n lc.info?.(`stopping ${svc.constructor.name} ${svc.id} (${signal})`);\n await svc.stop();\n });\n });\n }\n\n try {\n // Run all services and resolve when any of them stops.\n const svc = await Promise.race(\n services.map(svc => svc.run().then(() => svc)),\n );\n lc.info?.(`${svc.constructor.name} (${svc.id}) stopped`);\n } catch (e) {\n lc.error?.(`exiting on error`, e);\n throw e;\n }\n}\n\nexport async function exitAfter(\n logContext: LogContext | (() => LogContext),\n run: () => Promise<void>,\n) {\n const getLogContext =\n typeof logContext === 'function' ? logContext : () => logContext;\n try {\n await run();\n const lc = getLogContext();\n lc.info?.(`pid ${pid} exiting normally`);\n process.exit(0);\n } catch (e) {\n const lc = getLogContext();\n if (e instanceof ConfigurationError) {\n lc.error?.(`exiting with configuration error: ${String(e)}`, e);\n process.exit(0);\n }\n lc.error?.(`exiting with error: ${String(e)}`, e);\n try {\n logLastChanceSQLiteCorruptionDiagnostics(lc, e);\n } catch (diagnosticError) {\n lc.error?.('SQLite corruption last-chance diagnostic failed', {\n error: diagnosticError,\n });\n }\n try {\n await lc.flush();\n } finally {\n process.exit(-1);\n }\n }\n}\n\n/**\n * The HeartbeatMonitor monitors the cadence heartbeats (e.g. \"/keepalive\"\n * health checks made to HttpServices) that signal that the server\n * should continue processing requests. When a configurable `stopInterval`\n * elapses without receiving these heartbeats, the monitor initiates a\n * graceful shutdown of the server. This works with common load balancing\n * frameworks such as AWS Elastic Load Balancing.\n *\n * The HeartbeatMonitor is **opt-in** in that it only kicks in after it\n * starts receiving keepalives.\n */\nexport class HeartbeatMonitor {\n readonly #stopInterval: number;\n\n #lc: LogContext;\n #checkIntervalTimer: NodeJS.Timeout | undefined;\n #checkImmediateTimer: NodeJS.Immediate | undefined;\n #lastHeartbeat = 0;\n\n constructor(lc: LogContext, stopInterval: number) {\n this.#lc = lc;\n this.#stopInterval = stopInterval;\n }\n\n onHeartbeat(reqHeaders: IncomingHttpHeaders) {\n this.#lastHeartbeat = Date.now();\n if (this.#checkIntervalTimer === undefined) {\n this.#lc.info?.(\n `starting heartbeat monitor at ${\n this.#stopInterval / 1000\n } second interval`,\n reqHeaders,\n );\n // e.g. check every 5 seconds to see if it's been over 20 seconds\n // since the last heartbeat.\n this.#checkIntervalTimer = setInterval(\n this.#checkStopInterval,\n this.#stopInterval / 4,\n );\n }\n }\n\n #checkStopInterval = () => {\n // In the Node.js event loop, timers like setInterval and setTimeout\n // run *before* I/O events coming from network sockets or file reads/writes.\n // When this process gets starved of CPU resources for long periods of time,\n // for example when other processes are monopolizing all available cores,\n // pathological behavior can emerge:\n // - keepalive network request comes in, but is queued in Node internals waiting\n // for time on the event loop\n // - CPU is starved/monopolized by other processes for longer than the time\n // configured via this.#stopInterval\n // - When CPU becomes available and the event loop wakes up, this stop interval\n // check is run *before* the keepalive request is processed. The value of\n // this.#lastHeartbeat is now very stale, and erroneously triggers a shutdown\n // even though keepalive requests were about to be processed and update\n // this.#lastHeartbeat. Downtime ensues.\n //\n // To avoid this, we push the check out to a phase of the event loop *after*\n // I/O events are processed, using setImmediate():\n // https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout\n //\n // This ensures we see a value for this.#lastHeartbeat that reflects\n // any keepalive requests that came in during the current event loop turn.\n this.#checkImmediateTimer = setImmediate(() => {\n this.#checkImmediateTimer = undefined;\n const timeSinceLastHeartbeat = Date.now() - this.#lastHeartbeat;\n if (timeSinceLastHeartbeat >= this.#stopInterval) {\n this.#lc.info?.(\n `last heartbeat received ${\n timeSinceLastHeartbeat / 1000\n } seconds ago. draining.`,\n );\n process.kill(process.pid, GRACEFUL_SHUTDOWN[0]);\n }\n });\n };\n\n stop() {\n clearTimeout(this.#checkIntervalTimer);\n if (this.#checkImmediateTimer) {\n clearImmediate(this.#checkImmediateTimer);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"life-cycle.js","names":["#lc","#userFacing","#all","#exitImpl","#start","#ready","#startDrain","#kill","#exit","#drainStart","#runningState","#onExit","#initializing","#nextID","#stopInterval","#lastHeartbeat","#checkIntervalTimer","#checkStopInterval","#checkImmediateTimer"],"sources":["../../../../../zero-cache/src/services/life-cycle.ts"],"sourcesContent":["import type {IncomingHttpHeaders} from 'node:http';\nimport {pid} from 'node:process';\nimport type {EventEmitter} from 'stream';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {AbortError} from '../../../shared/src/abort-error.ts';\nimport {logLastChanceSQLiteCorruptionDiagnostics} from '../db/sqlite-corruption.ts';\nimport {\n getOrCreateHistogram,\n LONG_DURATION_HISTOGRAM_BOUNDARIES_S,\n} from '../observability/metrics.ts';\nimport {ConfigurationError} from '../types/configuration-error.ts';\nimport {\n singleProcessMode,\n type Subprocess,\n type Worker,\n} from '../types/processes.ts';\nimport {RunningState} from './running-state.ts';\nimport type {SingletonService} from './service.ts';\n\n/**\n * * `user-facing` workers serve external requests and are the first to\n * receive a `SIGTERM` or `SIGINT` signal for graceful shutdown.\n *\n * * `supporting` workers support `user-facing` workers and are sent\n * the `SIGTERM` signal only after all `user-facing` workers have\n * exited.\n *\n * For other kill signals, such as `SIGQUIT` and `SIGABRT`, all workers\n * are stopped without draining. `SIGQUIT` is used to represent an\n * intentional shutdown (for which draining is not beneficial), whereas\n * `SIGABRT` is used for unexpected process exits.\n */\nexport type WorkerType = 'user-facing' | 'supporting';\n\nexport const GRACEFUL_SHUTDOWN = ['SIGTERM', 'SIGINT'] as const;\nexport const FORCEFUL_SHUTDOWN = ['SIGQUIT', 'SIGABRT'] as const;\n\ntype GracefulShutdownSignal = (typeof GRACEFUL_SHUTDOWN)[number];\n\nfunction workerStartupMetricName(name: string) {\n if (name === 'zero-cache') {\n return undefined;\n }\n if (name.startsWith('change-streamer')) {\n return 'change_streamer';\n }\n if (name.startsWith('replicator')) {\n if (name.includes('(backup)')) {\n return 'backup_replicator';\n }\n if (name.includes('(serving-copy)')) {\n return 'serving_copy_replicator';\n }\n if (name.includes('(serving)')) {\n return 'serving_replicator';\n }\n return 'other';\n }\n if (name.startsWith('syncer')) {\n return 'syncer';\n }\n if (name.startsWith('reaper')) {\n return 'reaper';\n }\n if (name.startsWith('shadow-syncer')) {\n return 'shadow_syncer';\n }\n if (name.startsWith('mutator')) {\n return 'mutator';\n }\n return 'other';\n}\n\nfunction startupDuration() {\n return getOrCreateHistogram('server', 'startup_duration', {\n description: 'Duration from starting zero-cache to its ready signal.',\n unit: 's',\n bucketBoundaries: LONG_DURATION_HISTOGRAM_BOUNDARIES_S,\n });\n}\n\nexport function recordStartupDurationMs(durationMs: number) {\n startupDuration().recordMs(durationMs, {component: 'dispatcher'});\n}\n\nfunction workerStartupDuration() {\n return getOrCreateHistogram('server', 'worker_startup_duration', {\n description: 'Duration from starting a worker to its ready signal.',\n unit: 's',\n bucketBoundaries: LONG_DURATION_HISTOGRAM_BOUNDARIES_S,\n });\n}\n\n// An internal error code used to indicate that a message has already been\n// logged at level ERRROR. When a process exits with this error code, the\n// parent process logs the exit at level WARN instead of ERROR.\nexport const UNHANDLED_EXCEPTION_ERROR_CODE = 13;\n\n// An internal error code used to indicate that the server should exit\n// without draining (e.g. due to a supporting worker get a signal to shut\n// down), but the exit is otherwise intentional.\nexport const INTENTIONAL_SHUTDOWN_ERROR_CODE = 14;\n\n/**\n * Handles readiness, termination signals, and coordination of graceful\n * shutdown.\n */\nexport class ProcessManager {\n readonly #lc: LogContext;\n readonly #userFacing = new Set<Subprocess>();\n readonly #all = new Set<Subprocess>();\n readonly #exitImpl: (code: number) => never;\n readonly #start = Date.now();\n readonly #ready: Promise<void>[] = [];\n\n #runningState = new RunningState('process-manager');\n #drainStart = 0;\n\n constructor(lc: LogContext, proc: EventEmitter) {\n this.#lc = lc.withContext('component', 'process-manager');\n\n // Propagate `SIGTERM` and `SIGINT` to all user-facing workers,\n // initiating a graceful shutdown. The parent process will\n // exit once all user-facing workers have exited ...\n for (const signal of GRACEFUL_SHUTDOWN) {\n proc.on(signal, () => this.#startDrain(signal));\n }\n\n // ... which will result in sending `SIGTERM` to the remaining workers.\n proc.on('exit', code =>\n this.#kill(\n this.#all,\n code === 0\n ? 'SIGTERM' // graceful, drained shutdown\n : code === INTENTIONAL_SHUTDOWN_ERROR_CODE\n ? 'SIGQUIT' // intentional abort without drain\n : 'SIGABRT', // unintentional shutdown, alertable error\n ),\n );\n\n // For other (catchable) kill signals, exit with a non-zero error code\n // to send a `SIGQUIT` (intentional shutdown) or `SIGABRT` (unexpected\n // shutdown) to all workers. For these signals, workers are stopped\n // immediately without draining, since there is no merit to slowly draining\n // when supporting workers have stopped.\n //\n // The logic for handling these signals is in `runUntilKilled()`.\n for (const signal of FORCEFUL_SHUTDOWN) {\n proc.on(signal, () =>\n this.#exit(signal === 'SIGQUIT' ? INTENTIONAL_SHUTDOWN_ERROR_CODE : -1),\n );\n }\n\n this.#exitImpl = (code: number) => {\n if (singleProcessMode()) {\n return proc.emit('exit', code) as never; // For unit / integration tests.\n }\n process.exit(code);\n };\n }\n\n async shutdown() {\n if (this.#drainStart === 0) {\n this.#startDrain(GRACEFUL_SHUTDOWN[0]);\n }\n await this.#runningState.stopped();\n }\n\n #exit(code: number) {\n this.#lc.info?.('exiting with code', code);\n this.#runningState.stop(this.#lc);\n void this.#lc.flush().finally(() => this.#exitImpl(code));\n }\n\n #startDrain(signal: GracefulShutdownSignal) {\n if (this.#all.size === 0) {\n // Shutdown if a signal is received before any subprocesses are added.\n this.#lc.info?.(`exiting on ${signal}`);\n this.#exit(0);\n }\n this.#lc.info?.(`initiating drain (${signal})`);\n this.#drainStart = Date.now();\n if (this.#userFacing.size) {\n this.#kill(this.#userFacing, signal);\n } else {\n this.#kill(this.#all, signal);\n }\n }\n\n addSubprocess(proc: Subprocess, type: WorkerType, name: string) {\n if (type === 'user-facing') {\n this.#userFacing.add(proc);\n }\n this.#all.add(proc);\n\n let isOpen = true;\n proc.on('close', (code, signal) => {\n isOpen = false;\n this.#onExit(code, signal, null, type, name, proc);\n });\n\n // As per https://nodejs.org/api/child_process.html#event-error\n // 'error' events can happen when sending a message to a child process\n // fails. This is not really an error when the server is shutting down,\n // so log any post-close errors at 'warn'.\n proc.on('error', err =>\n this.#lc[!isOpen || this.#drainStart > 0 ? 'warn' : 'error']?.(\n `error from ${name} ${proc.pid}`,\n err,\n ),\n );\n }\n\n readonly #initializing = new Map<number, string>();\n #nextID = 0;\n\n addWorker(worker: Worker, type: WorkerType, name: string): Worker {\n this.addSubprocess(worker, type, name);\n\n const start = performance.now();\n const id = ++this.#nextID;\n this.#initializing.set(id, name);\n const {promise, resolve} = resolver();\n this.#ready.push(promise);\n\n worker.onceMessageType('ready', () => {\n const elapsed = performance.now() - start;\n const workerMetricName = workerStartupMetricName(name);\n if (workerMetricName) {\n workerStartupDuration().recordMs(elapsed, {\n worker: workerMetricName,\n type,\n });\n }\n this.#lc.debug?.(`${name} ready (${Date.now() - this.#start} ms)`);\n this.#initializing.delete(id);\n resolve();\n });\n\n return worker;\n }\n\n initializing(): string[] {\n return [...this.#initializing.values()];\n }\n\n async allWorkersReady() {\n await Promise.all(this.#ready);\n }\n\n logErrorAndExit(err: unknown, name: string) {\n // only accessible by the main (i.e. user-facing) process.\n this.#onExit(-1, null, err, 'user-facing', name, undefined);\n }\n\n #onExit(\n code: number,\n sig: NodeJS.Signals | null,\n err: unknown,\n type: WorkerType,\n name: string,\n worker: Subprocess | undefined,\n ) {\n // Remove the worker from maps to avoid attempting to send more signals to it.\n if (worker) {\n this.#userFacing.delete(worker);\n this.#all.delete(worker);\n }\n\n const pid = worker?.pid ?? process.pid;\n\n if (type === 'supporting') {\n // Supporting workers like the replication-manager shut down without a\n // drain signal when receiving protocol-specific instructions (like auto\n // reset). In this case, a special error code is used to signal that the\n // server should be shut down without draining, but it is otherwise not\n // considered an unexpected/alertable error.\n if (code === 0 && (this.#drainStart === 0 || this.#userFacing.size > 0)) {\n code = INTENTIONAL_SHUTDOWN_ERROR_CODE;\n }\n const log =\n code === 0 || code === INTENTIONAL_SHUTDOWN_ERROR_CODE\n ? 'info'\n : 'warn';\n this.#lc[log]?.(`${name} (${pid}) exited with code (${code})`, err ?? '');\n return this.#exit(code);\n }\n\n const log =\n code === 0 || code === INTENTIONAL_SHUTDOWN_ERROR_CODE\n ? 'info'\n : this.#drainStart > 0 || code === UNHANDLED_EXCEPTION_ERROR_CODE\n ? 'warn'\n : 'error';\n this.#lc[log]?.(\n sig\n ? `${name} (${pid}) killed with (${sig})`\n : `${name} (${pid}) exited with code (${code})`,\n err ?? '',\n );\n\n // user-facing workers exited or finished draining.\n if (this.#userFacing.size === 0) {\n this.#lc.info?.(\n this.#drainStart\n ? `all user-facing workers drained (${\n Date.now() - this.#drainStart\n } ms)`\n : `all user-facing workers exited`,\n );\n return this.#exit(this.#drainStart ? 0 : code || -1);\n }\n\n if (this.#drainStart === 0) {\n // If a user-facing worker exits without receiving a drain signal,\n // shutdown the server.\n return this.#exit(code || -1);\n }\n\n return undefined;\n }\n\n #kill(workers: Iterable<Subprocess>, signal: NodeJS.Signals) {\n for (const worker of workers) {\n try {\n worker.kill(signal);\n } catch (e) {\n this.#lc.error?.(e);\n }\n }\n }\n}\n\n/**\n * Runs the specified services, stopping them on `SIGTERM` or `SIGINT` with\n * an optional {@link SingletonService.drain drain()}, or stopping them\n * without draining for `SIGQUIT`.\n *\n * @returns a Promise that resolves/rejects when any of the services stops/throws.\n */\n\nexport async function runUntilKilled(\n lc: LogContext,\n parent: EventEmitter,\n ...services: SingletonService[]\n): Promise<void> {\n if (services.length === 0) {\n return;\n }\n for (const signal of [...GRACEFUL_SHUTDOWN, ...FORCEFUL_SHUTDOWN]) {\n parent.once(signal, () => {\n const GRACEFUL_SIGNALS = GRACEFUL_SHUTDOWN as readonly NodeJS.Signals[];\n\n services.forEach(async svc => {\n if (GRACEFUL_SIGNALS.includes(signal) && svc.drain) {\n lc.info?.(`draining ${svc.constructor.name} ${svc.id} (${signal})`);\n await svc.drain();\n }\n lc.info?.(`stopping ${svc.constructor.name} ${svc.id} (${signal})`);\n await svc.stop();\n });\n });\n }\n\n try {\n // Run all services and resolve when any of them stops.\n const svc = await Promise.race(\n services.map(svc => svc.run().then(() => svc)),\n );\n lc.info?.(`${svc.constructor.name} (${svc.id}) stopped`);\n } catch (e) {\n lc.error?.(`exiting on error`, e);\n throw e;\n }\n}\n\nexport async function exitAfter(\n logContext: LogContext | (() => LogContext),\n run: () => Promise<void>,\n) {\n const getLogContext =\n typeof logContext === 'function' ? logContext : () => logContext;\n try {\n await run();\n const lc = getLogContext();\n lc.info?.(`pid ${pid} exiting normally`);\n process.exit(0);\n } catch (e) {\n const lc = getLogContext();\n if (e instanceof ConfigurationError) {\n lc.error?.(`exiting with configuration error: ${String(e)}`, e);\n process.exit(0);\n }\n if (e instanceof AbortError) {\n // AbortErrors signal an intentional shutdown or restart (e.g. SIGTERM\n // during a rollout, AutoResetSignal) and should not raise alerts. The\n // non-zero exit code is kept so that restart semantics are unchanged.\n lc.warn?.(`exiting after abort: ${String(e)}`, e);\n } else {\n lc.error?.(`exiting with error: ${String(e)}`, e);\n }\n try {\n logLastChanceSQLiteCorruptionDiagnostics(lc, e);\n } catch (diagnosticError) {\n lc.error?.('SQLite corruption last-chance diagnostic failed', {\n error: diagnosticError,\n });\n }\n try {\n await lc.flush();\n } finally {\n process.exit(-1);\n }\n }\n}\n\n/**\n * The HeartbeatMonitor monitors the cadence heartbeats (e.g. \"/keepalive\"\n * health checks made to HttpServices) that signal that the server\n * should continue processing requests. When a configurable `stopInterval`\n * elapses without receiving these heartbeats, the monitor initiates a\n * graceful shutdown of the server. This works with common load balancing\n * frameworks such as AWS Elastic Load Balancing.\n *\n * The HeartbeatMonitor is **opt-in** in that it only kicks in after it\n * starts receiving keepalives.\n */\nexport class HeartbeatMonitor {\n readonly #stopInterval: number;\n\n #lc: LogContext;\n #checkIntervalTimer: NodeJS.Timeout | undefined;\n #checkImmediateTimer: NodeJS.Immediate | undefined;\n #lastHeartbeat = 0;\n\n constructor(lc: LogContext, stopInterval: number) {\n this.#lc = lc;\n this.#stopInterval = stopInterval;\n }\n\n onHeartbeat(reqHeaders: IncomingHttpHeaders) {\n this.#lastHeartbeat = Date.now();\n if (this.#checkIntervalTimer === undefined) {\n this.#lc.info?.(\n `starting heartbeat monitor at ${\n this.#stopInterval / 1000\n } second interval`,\n reqHeaders,\n );\n // e.g. check every 5 seconds to see if it's been over 20 seconds\n // since the last heartbeat.\n this.#checkIntervalTimer = setInterval(\n this.#checkStopInterval,\n this.#stopInterval / 4,\n );\n }\n }\n\n #checkStopInterval = () => {\n // In the Node.js event loop, timers like setInterval and setTimeout\n // run *before* I/O events coming from network sockets or file reads/writes.\n // When this process gets starved of CPU resources for long periods of time,\n // for example when other processes are monopolizing all available cores,\n // pathological behavior can emerge:\n // - keepalive network request comes in, but is queued in Node internals waiting\n // for time on the event loop\n // - CPU is starved/monopolized by other processes for longer than the time\n // configured via this.#stopInterval\n // - When CPU becomes available and the event loop wakes up, this stop interval\n // check is run *before* the keepalive request is processed. The value of\n // this.#lastHeartbeat is now very stale, and erroneously triggers a shutdown\n // even though keepalive requests were about to be processed and update\n // this.#lastHeartbeat. Downtime ensues.\n //\n // To avoid this, we push the check out to a phase of the event loop *after*\n // I/O events are processed, using setImmediate():\n // https://nodejs.org/en/learn/asynchronous-work/event-loop-timers-and-nexttick#setimmediate-vs-settimeout\n //\n // This ensures we see a value for this.#lastHeartbeat that reflects\n // any keepalive requests that came in during the current event loop turn.\n this.#checkImmediateTimer = setImmediate(() => {\n this.#checkImmediateTimer = undefined;\n const timeSinceLastHeartbeat = Date.now() - this.#lastHeartbeat;\n if (timeSinceLastHeartbeat >= this.#stopInterval) {\n this.#lc.info?.(\n `last heartbeat received ${\n timeSinceLastHeartbeat / 1000\n } seconds ago. draining.`,\n );\n process.kill(process.pid, GRACEFUL_SHUTDOWN[0]);\n }\n });\n };\n\n stop() {\n clearTimeout(this.#checkIntervalTimer);\n if (this.#checkImmediateTimer) {\n clearImmediate(this.#checkImmediateTimer);\n }\n }\n}\n"],"mappings":"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"}
@@ -1,22 +1,54 @@
1
1
  import type { ChildProcess } from 'node:child_process';
2
2
  import type { LogContext } from '@rocicorp/logger';
3
3
  import { type LitestreamConfig } from '../../config/normalize.ts';
4
+ import type { InitCleanup } from '../change-source/common/init-cleanup.ts';
4
5
  import { type LitestreamRole } from './metrics.ts';
6
+ import { WalKeeper } from './wal-keeper.ts';
5
7
  export type ReplicaConstraints = {
6
8
  replicaVersion: string;
7
9
  minWatermark: string;
8
10
  };
9
11
  export type RestoreResult = 'success' | 'no_backup' | 'invalid_replica' | 'error';
10
- type RestoreAttempt = {
12
+ export type RestoreAttempt = {
11
13
  restored: boolean;
12
14
  backupURL: string | undefined;
13
15
  result: RestoreResult;
16
+ /**
17
+ * Set when the restore prepared the backup to continue from the restored
18
+ * replica (see {@link ForkBackup}). It is registered with the
19
+ * `cleanup`; otherwise the caller must release it (see {@link WalKeeper}).
20
+ */
21
+ walKeeper?: WalKeeper | undefined;
22
+ };
23
+ /**
24
+ * Prepares the restored replica to continue from a forked backup
25
+ * instead of starting a new lineage with a full snapshot.
26
+ */
27
+ export type ForkBackup = {
28
+ /** The restore also forks its backup into the (new, empty) one at this URL. */
29
+ url: string;
30
+ /**
31
+ * Registers the resulting {@link WalKeeper} to be released if the
32
+ * initialization fails.
33
+ */
34
+ cleanup?: InitCleanup | undefined;
14
35
  };
15
36
  export declare class BackupNotFoundException extends Error {
16
37
  static readonly name = "BackupNotFoundException";
17
38
  constructor(backupURL: string | undefined);
18
39
  }
19
- export declare function tryRestore(lc: LogContext, config: LitestreamConfig, replicaFile: string, replicaConstraints: ReplicaConstraints | undefined, role: LitestreamRole): Promise<RestoreAttempt>;
40
+ /**
41
+ * The effective backup URL: the `backupURLOverride` if present (i.e. a
42
+ * lineage-specific location computed by the replication-manager), or
43
+ * otherwise the configured `backupURL`.
44
+ */
45
+ export declare function backupDestinationURL(config: Pick<LitestreamConfig, 'backupURL' | 'backupURLOverride'>): string | undefined;
46
+ export declare function tryRestore(lc: LogContext, config: LitestreamConfig, replicaFile: string, replicaConstraints: ReplicaConstraints | undefined, role: LitestreamRole, signal?: AbortSignal, forkBackup?: ForkBackup | undefined): Promise<RestoreAttempt>;
47
+ /**
48
+ * Deletes litestream's local state for `replicaFile` (its default meta
49
+ * directory, `.<file>-litestream`).
50
+ */
51
+ export declare function deleteLitestreamMetaDir(replicaFile: string): void;
20
52
  export declare function startReplicaBackupProcess(lc: LogContext, config: LitestreamConfig, replicaFile: string): ChildProcess;
21
53
  /**
22
54
  * Returns the time of the most recent object (snapshot or WAL segment)
@@ -41,5 +73,4 @@ export declare function getLastBackupTime(lc: LogContext, config: LitestreamConf
41
73
  * Exported for testing.
42
74
  */
43
75
  export declare function parseBackupCreatedTimes(lc: LogContext, command: 'snapshots' | 'wal', output: string): Date[];
44
- export {};
45
76
  //# sourceMappingURL=commands.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/litestream/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAIrD,OAAO,KAAK,EAAC,UAAU,EAAW,MAAM,kBAAkB,CAAC;AAI3D,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAUhE,OAAO,EAWL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,WAAW,GACX,iBAAiB,GACjB,OAAO,CAAC;AAEZ,KAAK,cAAc,GAAG;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,aAAa,CAAC;CACvB,CAAC;AAKF,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,MAAM,CAAC,QAAQ,CAAC,IAAI,6BAA6B;IAEjD,YAAY,SAAS,EAAE,MAAM,GAAG,SAAS,EAExC;CACF;AAyFD,wBAAsB,UAAU,CAC9B,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,EAClD,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,cAAc,CAAC,CAwIzB;AAoGD,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,GAClB,YAAY,CAuCd;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAgBf;AA8DD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,WAAW,GAAG,KAAK,EAC5B,MAAM,EAAE,MAAM,GACb,IAAI,EAAE,CAoBR"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../../../../../zero-cache/src/services/litestream/commands.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,oBAAoB,CAAC;AAIrD,OAAO,KAAK,EAAC,UAAU,EAAW,MAAM,kBAAkB,CAAC;AAK3D,OAAO,EAAC,KAAK,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAOhE,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,yCAAyC,CAAC;AAIzE,OAAO,EAWL,KAAK,cAAc,EACpB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAE1C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,aAAa,GACrB,SAAS,GACT,WAAW,GACX,iBAAiB,GACjB,OAAO,CAAC;AAEZ,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,EAAE,aAAa,CAAC;IACtB;;;;OAIG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CACnC,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CACnC,CAAC;AAKF,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,MAAM,CAAC,QAAQ,CAAC,IAAI,6BAA6B;IAEjD,YAAY,SAAS,EAAE,MAAM,GAAG,SAAS,EAExC;CACF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,GAAG,mBAAmB,CAAC,GAChE,MAAM,GAAG,SAAS,CAEpB;AA6FD,wBAAsB,UAAU,CAC9B,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,EACnB,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,EAClD,IAAI,EAAE,cAAc,EACpB,MAAM,CAAC,EAAE,WAAW,EACpB,UAAU,CAAC,EAAE,UAAU,GAAG,SAAS,GAClC,OAAO,CAAC,cAAc,CAAC,CA2BzB;AAiOD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,MAAM,QAK1D;AA2GD,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,GAClB,YAAY,CAuCd;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,gBAAgB,EACxB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAgBf;AA8DD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,WAAW,GAAG,KAAK,EAC5B,MAAM,EAAE,MAAM,GACb,IAAI,EAAE,CAoBR"}
@@ -1,3 +1,4 @@
1
+ import { parse, valita_exports } from "../../../../shared/src/valita.js";
1
2
  import { must } from "../../../../shared/src/must.js";
2
3
  import "../../config/normalize.js";
3
4
  import { Database } from "../../../../zqlite/src/db.js";
@@ -8,6 +9,7 @@ import { deleteLiteDB } from "../../db/delete-lite-db.js";
8
9
  import { deleteChangeLogDB } from "../replicator/change-log-db.js";
9
10
  import { litestreamSocketPath } from "./litestream-controller.js";
10
11
  import { litestreamBackupListDuration, litestreamBackupMetricAttrs, litestreamBackupProcessDuration, litestreamBackupProcessMetricAttrs, litestreamBackupProcessRuns, litestreamRestoreAttempts, litestreamRestoreMetricAttrs, litestreamRestoreProcessDuration, litestreamRestoreValidationDuration, litestreamRestoredDbBytes } from "./metrics.js";
12
+ import { WalKeeper } from "./wal-keeper.js";
11
13
  import { resolver } from "@rocicorp/resolver";
12
14
  import { spawn } from "node:child_process";
13
15
  import { existsSync, readdirSync, rmSync, statSync } from "node:fs";
@@ -15,27 +17,35 @@ import { basename, dirname, join } from "node:path";
15
17
  //#region ../zero-cache/src/services/litestream/commands.ts
16
18
  var MAX_LOGGED_RESTORE_DIRECTORY_ENTRIES = 100;
17
19
  var LEGACY_RESTORE_WAL_INDEX = /^[0-9a-f]{8}$/;
20
+ /**
21
+ * The effective backup URL: the `backupURLOverride` if present (i.e. a
22
+ * lineage-specific location computed by the replication-manager), or
23
+ * otherwise the configured `backupURL`.
24
+ */
25
+ function backupDestinationURL(config) {
26
+ return config.backupURLOverride ?? config.backupURL;
27
+ }
18
28
  function getLitestream(mode, config, replicaFile, logLevelOverride) {
19
- const { executable, executableV5, restoreUsingV5, backupUsingV5, backupURL, logLevel, configPath, configPathV5, endpoint, region, port, checkpointThresholdMB, minCheckpointPageCount = checkpointThresholdMB * 256, maxCheckpointPageCount = minCheckpointPageCount * 10, forceCheckpointThresholdMB, incrementalBackupIntervalMinutes, incrementalBackupIntervalSeconds, snapshotBackupIntervalHours, snapshotBackupIntervalHoursV5, multipartConcurrency, multipartSize } = config;
29
+ const { executable, executableV5, restoreUsingV5, backupUsingV5, logLevel, configPath, configPathV5, endpoint, region, port, checkpointThresholdMB, minCheckpointPageCount = checkpointThresholdMB * 256, maxCheckpointPageCount = minCheckpointPageCount * 10, forceCheckpointThresholdMB, incrementalBackupIntervalMinutes, incrementalBackupIntervalSeconds, snapshotBackupIntervalHours, multipartConcurrency, multipartSize } = config;
20
30
  const v5 = mode === "restore" && restoreUsingV5 || mode === "replicate" && backupUsingV5;
21
31
  const litestream = (v5 ? executableV5 : executable) ?? must(executable, `Missing --litestream-executable`);
22
32
  const litestreamConfig = v5 ? configPathV5 : configPath;
23
- const snapshotIntervalHours = v5 ? snapshotBackupIntervalHoursV5 : snapshotBackupIntervalHours;
24
33
  const truncatePageN = forceCheckpointThresholdMB ? -1 : 121359;
25
34
  return {
26
35
  litestream,
27
36
  env: {
28
37
  ...process.env,
29
38
  ["ZERO_REPLICA_FILE"]: replicaFile,
30
- ["ZERO_LITESTREAM_BACKUP_URL"]: must(backupURL),
39
+ ["ZERO_LITESTREAM_BACKUP_URL"]: must(backupDestinationURL(config)),
31
40
  ["ZERO_LITESTREAM_MIN_CHECKPOINT_PAGE_COUNT"]: String(minCheckpointPageCount),
32
41
  ["ZERO_LITESTREAM_MAX_CHECKPOINT_PAGE_COUNT"]: String(maxCheckpointPageCount),
33
42
  ["ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES"]: String(incrementalBackupIntervalMinutes),
34
43
  ["ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_SECONDS"]: String(incrementalBackupIntervalSeconds),
44
+ ["ZERO_LITESTREAM_MONITOR_INTERVAL_SECONDS"]: String(2 * incrementalBackupIntervalSeconds),
35
45
  ["ZERO_LITESTREAM_TRUNCATE_PAGE_N"]: String(truncatePageN),
36
46
  ["ZERO_LITESTREAM_LOG_LEVEL"]: logLevelOverride ?? logLevel,
37
- ["ZERO_LITESTREAM_SNAPSHOT_BACKUP_INTERVAL_HOURS"]: String(snapshotIntervalHours),
38
- ["ZERO_LITESTREAM_SNAPSHOT_RETENTION_INTERVAL_HOURS"]: String(snapshotIntervalHours + 6),
47
+ ["ZERO_LITESTREAM_SNAPSHOT_BACKUP_INTERVAL_HOURS"]: String(snapshotBackupIntervalHours),
48
+ ["ZERO_LITESTREAM_SNAPSHOT_RETENTION_INTERVAL_HOURS"]: String(snapshotBackupIntervalHours + 6),
39
49
  ["ZERO_LITESTREAM_MULTIPART_CONCURRENCY"]: String(multipartConcurrency),
40
50
  ["ZERO_LITESTREAM_MULTIPART_SIZE"]: String(multipartSize),
41
51
  ["ZERO_LOG_FORMAT"]: "json",
@@ -43,18 +53,30 @@ function getLitestream(mode, config, replicaFile, logLevelOverride) {
43
53
  ["LITESTREAM_CONFIG"]: litestreamConfig,
44
54
  ["LITESTREAM_PORT"]: String(port),
45
55
  ...endpoint ? { ["ZERO_LITESTREAM_ENDPOINT"]: endpoint } : {},
46
- ...region ? { ["ZERO_LITESTREAM_REGION"]: region } : {}
56
+ ...region ? { ["ZERO_LITESTREAM_REGION"]: region } : {},
57
+ ["LITESTREAM_COMPACTION_SERIALIZE"]: "false"
47
58
  }
48
59
  };
49
60
  }
50
- async function tryRestore(lc, config, replicaFile, replicaConstraints, role) {
51
- const { backupURL } = config;
61
+ async function tryRestore(lc, config, replicaFile, replicaConstraints, role, signal, forkBackup) {
62
+ const { reusedExisting, ...attempt } = await restoreOnce(lc, config, replicaFile, replicaConstraints, role, signal, forkBackup);
63
+ if (attempt.result === "invalid_replica" && reusedExisting) {
64
+ const { reusedExisting: _, ...retry } = await restoreOnce(lc, config, replicaFile, replicaConstraints, role, signal, forkBackup);
65
+ return retry;
66
+ }
67
+ return attempt;
68
+ }
69
+ async function restoreOnce(lc, config, replicaFile, replicaConstraints, role, signal, forkBackup) {
70
+ const { backupUsingV5 } = config;
71
+ const backupURL = backupDestinationURL(config);
72
+ if (forkBackup && !backupUsingV5) forkBackup = void 0;
52
73
  const attrs = litestreamRestoreMetricAttrs(config, role, backupURL);
53
74
  let result = "error";
54
75
  try {
55
76
  logRestoreDirectoryContents(lc, replicaFile, "before-temp-cleanup");
56
77
  deleteRestoreTempFiles(replicaFile);
57
- const replicaExistedBeforeRestore = existsSync(replicaFile);
78
+ const reusedExisting = existsSync(replicaFile);
79
+ if (!reusedExisting) deleteLitestreamMetaDir(replicaFile);
58
80
  const { litestream, env } = getLitestream("restore", config, replicaFile, "debug");
59
81
  const { restoreParallelism: parallelism, multipartConcurrency, multipartSize } = config;
60
82
  lc.info?.(`starting litestream restore`, {
@@ -68,6 +90,11 @@ async function tryRestore(lc, config, replicaFile, replicaConstraints, role) {
68
90
  "-if-replica-exists",
69
91
  "-parallelism",
70
92
  String(parallelism),
93
+ ...forkBackup ? [
94
+ "-json",
95
+ "-fork-to-url",
96
+ forkBackup.url
97
+ ] : [],
71
98
  replicaFile
72
99
  ], {
73
100
  env,
@@ -76,7 +103,8 @@ async function tryRestore(lc, config, replicaFile, replicaConstraints, role) {
76
103
  "pipe",
77
104
  "pipe"
78
105
  ],
79
- windowsHide: true
106
+ windowsHide: true,
107
+ signal
80
108
  });
81
109
  let stdout = "";
82
110
  let stderr = "";
@@ -117,39 +145,53 @@ async function tryRestore(lc, config, replicaFile, replicaConstraints, role) {
117
145
  return {
118
146
  restored: false,
119
147
  backupURL,
120
- result
148
+ result,
149
+ reusedExisting
121
150
  };
122
151
  }
123
152
  const validationStart = performance.now();
124
- const valid = replicaIsValid(lc, replicaFile, replicaConstraints);
153
+ let db = openValidReplica(lc, replicaFile, replicaConstraints);
125
154
  litestreamRestoreValidationDuration().recordMs(performance.now() - validationStart, {
126
155
  ...attrs,
127
- result: valid ? "success" : "invalid_replica"
156
+ result: db ? "success" : "invalid_replica"
128
157
  });
129
- if (!valid) {
158
+ if (!db) {
130
159
  result = "invalid_replica";
131
160
  lc.info?.(`Deleting local replica and retrying restore`);
132
161
  deleteLiteDB(replicaFile);
133
162
  deleteChangeLogDB(replicaFile);
163
+ deleteLitestreamMetaDir(replicaFile);
134
164
  return {
135
165
  restored: false,
136
166
  backupURL,
137
- result
167
+ result,
168
+ reusedExisting
138
169
  };
139
170
  }
140
- result = "success";
141
- if (!replicaExistedBeforeRestore) {
142
- deleteChangeLogDB(replicaFile);
143
- litestreamRestoredDbBytes().add(statSync(replicaFile).size, {
144
- ...attrs,
145
- result: "success"
146
- });
171
+ try {
172
+ result = "success";
173
+ if (!reusedExisting) {
174
+ deleteChangeLogDB(replicaFile);
175
+ litestreamRestoredDbBytes().add(statSync(replicaFile).size, {
176
+ ...attrs,
177
+ result: "success"
178
+ });
179
+ }
180
+ const walKeeper = forkBackup && !reusedExisting && restoreForked(lc, stdout) ? new WalKeeper(lc, db) : void 0;
181
+ if (walKeeper) {
182
+ db = void 0;
183
+ forkBackup?.cleanup?.onFailure("WAL keeper", () => walKeeper.release("initialization failed"));
184
+ }
185
+ return {
186
+ restored: true,
187
+ backupURL,
188
+ result,
189
+ reusedExisting,
190
+ walKeeper
191
+ };
192
+ } finally {
193
+ db?.close();
147
194
  }
148
- return {
149
- restored: true,
150
- backupURL,
151
- result
152
- };
153
195
  } finally {
154
196
  try {
155
197
  deleteRestoreTempFiles(replicaFile);
@@ -166,6 +208,43 @@ async function tryRestore(lc, config, replicaFile, replicaConstraints, role) {
166
208
  });
167
209
  }
168
210
  }
211
+ var restoreForkOutputSchema = valita_exports.object({ fork: valita_exports.object({
212
+ forked: valita_exports.boolean(),
213
+ txid: valita_exports.string().optional()
214
+ }).optional() });
215
+ /**
216
+ * Whether the `-json` output of `litestream restore -fork-to-url` reports that
217
+ * the backup was forked. A restore plan that cannot fork the backup is
218
+ * restored without forking.
219
+ */
220
+ function restoreForked(lc, stdout) {
221
+ const start = stdout.indexOf("{");
222
+ const end = stdout.lastIndexOf("}");
223
+ if (start < 0 || end < start) {
224
+ lc.warn?.("litestream restore did not report a fork result");
225
+ return false;
226
+ }
227
+ let output;
228
+ try {
229
+ output = parse(JSON.parse(stdout.slice(start, end + 1)), restoreForkOutputSchema, "passthrough");
230
+ } catch (e) {
231
+ lc.warn?.("unable to parse the litestream restore result", e);
232
+ return false;
233
+ }
234
+ const forked = output.fork?.forked === true;
235
+ lc.info?.(forked ? `forked the backup at txid ${output.fork?.txid}` : "restored without forking the backup");
236
+ return forked;
237
+ }
238
+ /**
239
+ * Deletes litestream's local state for `replicaFile` (its default meta
240
+ * directory, `.<file>-litestream`).
241
+ */
242
+ function deleteLitestreamMetaDir(replicaFile) {
243
+ rmSync(join(dirname(replicaFile), `.${basename(replicaFile)}-litestream`), {
244
+ recursive: true,
245
+ force: true
246
+ });
247
+ }
169
248
  function deleteRestoreTempFiles(replicaFile) {
170
249
  const temporaryReplicaFile = `${replicaFile}.tmp`;
171
250
  deleteLiteDB(temporaryReplicaFile);
@@ -203,7 +282,12 @@ function logRestoreDirectoryContents(lc, replicaFile, phase) {
203
282
  });
204
283
  }
205
284
  }
206
- function replicaIsValid(lc, replica, constraints) {
285
+ /**
286
+ * Opens and validates the restored replica. Returns the open connection if
287
+ * the replica is valid, which the caller must close; otherwise closes it and
288
+ * returns `undefined`.
289
+ */
290
+ function openValidReplica(lc, replica, constraints) {
207
291
  let db;
208
292
  try {
209
293
  db = new Database(lc, replica);
@@ -211,19 +295,21 @@ function replicaIsValid(lc, replica, constraints) {
211
295
  if (constraints) {
212
296
  if (replicaVersion !== constraints.replicaVersion) {
213
297
  lc.warn?.(`Local replica version ${replicaVersion} does not match expected replicaVersion ${constraints.replicaVersion}`, constraints);
214
- return false;
298
+ return;
215
299
  }
216
300
  if (watermark < constraints.minWatermark) {
217
301
  lc.warn?.(`Local replica watermark ${watermark} is earlier than minWatermark ${constraints.minWatermark}`);
218
- return false;
302
+ return;
219
303
  }
220
304
  lc.info?.(`Local replica at version ${replicaVersion} and watermark ${watermark} is compatible`, constraints);
221
305
  }
222
- return true;
306
+ const valid = db;
307
+ db = void 0;
308
+ return valid;
223
309
  } catch (e) {
224
310
  if (isSQLiteCorruption(e)) logSQLiteCorruptionDiagnostics(lc, "restored replica", replica, e);
225
311
  lc.error?.("Error while validating restored replica", e);
226
- return false;
312
+ return;
227
313
  } finally {
228
314
  db?.close();
229
315
  }
@@ -231,7 +317,7 @@ function replicaIsValid(lc, replica, constraints) {
231
317
  function startReplicaBackupProcess(lc, config, replicaFile) {
232
318
  const { litestream, env } = getLitestream("replicate", config, replicaFile);
233
319
  const attrs = litestreamBackupProcessMetricAttrs(config);
234
- lc.info?.(`starting litestream backup to ${config.backupURL}`);
320
+ lc.info?.(`starting litestream backup to ${backupDestinationURL(config)}`);
235
321
  const start = performance.now();
236
322
  const proc = spawn(litestream, ["replicate"], {
237
323
  env,
@@ -284,7 +370,7 @@ var wsRe = /\s+/;
284
370
  async function getLastBackupTime(lc, config, replicaFile) {
285
371
  const [snapshots, wal] = await Promise.all([listBackupCreatedTimes(lc, config, replicaFile, "snapshots"), listBackupCreatedTimes(lc, config, replicaFile, "wal")]);
286
372
  const times = [...snapshots, ...wal];
287
- if (times.length === 0) throw new Error(`no snapshots or WAL segments listed at ${config.backupURL}`);
373
+ if (times.length === 0) throw new Error(`no snapshots or WAL segments listed at ${backupDestinationURL(config)}`);
288
374
  return new Date(Math.max(...times.map((time) => time.getTime())));
289
375
  }
290
376
  /**
@@ -362,6 +448,6 @@ function parseBackupCreatedTimes(lc, command, output) {
362
448
  return times;
363
449
  }
364
450
  //#endregion
365
- export { getLastBackupTime, startReplicaBackupProcess, tryRestore };
451
+ export { backupDestinationURL, deleteLitestreamMetaDir, getLastBackupTime, startReplicaBackupProcess, tryRestore };
366
452
 
367
453
  //# sourceMappingURL=commands.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"commands.js","names":[],"sources":["../../../../../../zero-cache/src/services/litestream/commands.ts"],"sourcesContent":["import type {ChildProcess} from 'node:child_process';\nimport {spawn} from 'node:child_process';\nimport {existsSync, readdirSync, rmSync, statSync} from 'node:fs';\nimport {basename, dirname, join} from 'node:path';\nimport type {LogContext, LogLevel} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {must} from '../../../../shared/src/must.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {type LitestreamConfig} from '../../config/normalize.ts';\nimport {deleteLiteDB} from '../../db/delete-lite-db.ts';\nimport {\n isSQLiteCorruption,\n logSQLiteCorruptionDiagnostics,\n} from '../../db/sqlite-corruption.ts';\nimport {StatementRunner} from '../../db/statements.ts';\nimport {deleteChangeLogDB} from '../replicator/change-log-db.ts';\nimport {getSubscriptionState} from '../replicator/schema/replication-state.ts';\nimport {litestreamSocketPath} from './litestream-controller.ts';\nimport {\n litestreamBackupListDuration,\n litestreamBackupMetricAttrs,\n litestreamBackupProcessDuration,\n litestreamBackupProcessMetricAttrs,\n litestreamBackupProcessRuns,\n litestreamRestoreAttempts,\n litestreamRestoredDbBytes,\n litestreamRestoreMetricAttrs,\n litestreamRestoreProcessDuration,\n litestreamRestoreValidationDuration,\n type LitestreamRole,\n} from './metrics.ts';\n\nexport type ReplicaConstraints = {\n replicaVersion: string;\n minWatermark: string;\n};\n\nexport type RestoreResult =\n | 'success'\n | 'no_backup'\n | 'invalid_replica'\n | 'error';\n\ntype RestoreAttempt = {\n restored: boolean;\n backupURL: string | undefined;\n result: RestoreResult;\n};\n\nconst MAX_LOGGED_RESTORE_DIRECTORY_ENTRIES = 100;\nconst LEGACY_RESTORE_WAL_INDEX = /^[0-9a-f]{8}$/;\n\nexport class BackupNotFoundException extends Error {\n static readonly name = 'BackupNotFoundException';\n\n constructor(backupURL: string | undefined) {\n super(`backup not found at ${backupURL}`);\n }\n}\n\nfunction getLitestream(\n mode: 'restore' | 'replicate',\n config: LitestreamConfig,\n replicaFile: string,\n logLevelOverride?: LogLevel,\n): {\n litestream: string;\n env: NodeJS.ProcessEnv;\n} {\n const {\n executable,\n executableV5,\n restoreUsingV5,\n backupUsingV5,\n backupURL,\n logLevel,\n configPath,\n configPathV5,\n endpoint,\n region,\n port,\n checkpointThresholdMB,\n // Note: This assumes the default page size. If page size configuration\n // is added, this computation must be adjusted accordingly.\n minCheckpointPageCount = checkpointThresholdMB * 256, // 1 MiB / 4 KiB page size\n maxCheckpointPageCount = minCheckpointPageCount * 10,\n forceCheckpointThresholdMB,\n incrementalBackupIntervalMinutes,\n incrementalBackupIntervalSeconds,\n snapshotBackupIntervalHours,\n snapshotBackupIntervalHoursV5,\n multipartConcurrency,\n multipartSize,\n } = config;\n\n const v5 =\n (mode === 'restore' && restoreUsingV5) ||\n (mode === 'replicate' && backupUsingV5);\n const litestream =\n (v5 ? executableV5 : executable) ??\n must(executable, `Missing --litestream-executable`);\n const litestreamConfig = v5 ? configPathV5 : configPath;\n const snapshotIntervalHours = v5\n ? snapshotBackupIntervalHoursV5\n : snapshotBackupIntervalHours;\n // Disable truncate-page-n if forced checkpoints are enabled,\n // and otherwise use litestream's default.\n const truncatePageN = forceCheckpointThresholdMB ? -1 : 121359;\n\n return {\n litestream,\n env: {\n ...process.env,\n ['ZERO_REPLICA_FILE']: replicaFile,\n ['ZERO_LITESTREAM_BACKUP_URL']: must(backupURL),\n ['ZERO_LITESTREAM_MIN_CHECKPOINT_PAGE_COUNT']: String(\n minCheckpointPageCount,\n ),\n ['ZERO_LITESTREAM_MAX_CHECKPOINT_PAGE_COUNT']: String(\n maxCheckpointPageCount,\n ),\n ['ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES']: String(\n incrementalBackupIntervalMinutes, // v3 only\n ),\n ['ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_SECONDS']: String(\n incrementalBackupIntervalSeconds, // v5 only\n ),\n ['ZERO_LITESTREAM_TRUNCATE_PAGE_N']: String(truncatePageN),\n ['ZERO_LITESTREAM_LOG_LEVEL']: logLevelOverride ?? logLevel,\n ['ZERO_LITESTREAM_SNAPSHOT_BACKUP_INTERVAL_HOURS']: String(\n snapshotIntervalHours,\n ),\n ['ZERO_LITESTREAM_SNAPSHOT_RETENTION_INTERVAL_HOURS']: String(\n snapshotIntervalHours + 6, // delete old snapshots after 6 hours\n ),\n ['ZERO_LITESTREAM_MULTIPART_CONCURRENCY']: String(multipartConcurrency),\n ['ZERO_LITESTREAM_MULTIPART_SIZE']: String(multipartSize),\n ['ZERO_LOG_FORMAT']: 'json',\n ['ZERO_LITESTREAM_SOCKET_PATH']: litestreamSocketPath(replicaFile),\n ['LITESTREAM_CONFIG']: litestreamConfig,\n ['LITESTREAM_PORT']: String(port),\n ...(endpoint ? {['ZERO_LITESTREAM_ENDPOINT']: endpoint} : {}),\n ...(region ? {['ZERO_LITESTREAM_REGION']: region} : {}),\n },\n };\n}\n\nexport async function tryRestore(\n lc: LogContext,\n config: LitestreamConfig,\n replicaFile: string,\n replicaConstraints: ReplicaConstraints | undefined,\n role: LitestreamRole,\n): Promise<RestoreAttempt> {\n const {backupURL} = config;\n const attrs = litestreamRestoreMetricAttrs(config, role, backupURL);\n let result: RestoreResult = 'error';\n try {\n logRestoreDirectoryContents(lc, replicaFile, 'before-temp-cleanup');\n deleteRestoreTempFiles(replicaFile);\n const replicaExistedBeforeRestore = existsSync(replicaFile);\n const {litestream, env} = getLitestream(\n 'restore',\n config,\n replicaFile,\n 'debug', // Include all output from `litestream restore`, as it's minimal.\n );\n const {\n restoreParallelism: parallelism,\n multipartConcurrency,\n multipartSize,\n } = config;\n lc.info?.(`starting litestream restore`, {\n restoreParallelism: parallelism,\n multipartConcurrency,\n multipartSize,\n });\n // Pipe (rather than inherit) litestream's stdout/stderr so that its own\n // `\"level\":\"ERROR\"` output on a failed restore does not go straight to the\n // pod's stdout — where a log-scraper alert would page on it — before our\n // code has decided how to handle the failure. The captured output is\n // included in the thrown error so the caller can log the final outcome.\n // See INC-961.\n const proc = spawn(\n litestream,\n [\n 'restore',\n '-if-db-not-exists',\n '-if-replica-exists',\n '-parallelism',\n String(parallelism),\n replicaFile,\n ],\n {env, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true},\n );\n let stdout = '';\n let stderr = '';\n proc.stdout.setEncoding('utf-8');\n proc.stderr.setEncoding('utf-8');\n // Output AND capture the stdout and stderr, the former for observability\n // and the latter for error handling.\n proc.stdout.on('data', chunk => {\n process.stdout.write(chunk);\n stdout += chunk;\n });\n proc.stderr.on('data', chunk => {\n process.stderr.write(chunk);\n stderr += chunk;\n });\n const {promise, resolve, reject} = resolver();\n proc.on('error', reject);\n proc.on('close', (code, signal) => {\n if (signal) {\n reject(`litestream killed with ${signal}`);\n } else if (code !== 0) {\n reject(`litestream exited with code ${code}`);\n } else {\n resolve();\n }\n });\n const processStart = performance.now();\n try {\n await promise;\n litestreamRestoreProcessDuration().recordMs(\n performance.now() - processStart,\n {...attrs, result: 'success'},\n );\n } catch (e) {\n litestreamRestoreProcessDuration().recordMs(\n performance.now() - processStart,\n {...attrs, result: 'error'},\n );\n const output = [stdout, stderr]\n .map(value => value.trim())\n .filter(Boolean)\n .join('\\n');\n throw new Error(output ? `${String(e)}\\n${output}` : String(e), {\n cause: e,\n });\n }\n if (!existsSync(replicaFile)) {\n result = 'no_backup';\n return {restored: false, backupURL, result};\n }\n const validationStart = performance.now();\n const valid = replicaIsValid(lc, replicaFile, replicaConstraints);\n litestreamRestoreValidationDuration().recordMs(\n performance.now() - validationStart,\n {...attrs, result: valid ? 'success' : 'invalid_replica'},\n );\n if (!valid) {\n result = 'invalid_replica';\n lc.info?.(`Deleting local replica and retrying restore`);\n deleteLiteDB(replicaFile);\n deleteChangeLogDB(replicaFile);\n return {restored: false, backupURL, result};\n }\n result = 'success';\n if (!replicaExistedBeforeRestore) {\n // This restore materialized the replica file, so any change log beside it\n // was written against a replica that is no longer there. Reconciliation\n // is the safety net — a restored replica keeps its replicaVersion, so the\n // rule falls through to truncate-or-reseed on head mismatch — but the log\n // is a cache and deleting it here is the explicit statement that nothing\n // in it survives the restore.\n //\n // A restore that reuses an existing replica (`-if-db-not-exists` made it\n // a no-op) deliberately keeps the log: that is the process-restart path,\n // where the log is still the one written beside this exact replica and\n // discarding it would cost every reconnecting subscriber a `too-old`.\n deleteChangeLogDB(replicaFile);\n litestreamRestoredDbBytes().add(statSync(replicaFile).size, {\n ...attrs,\n result: 'success',\n });\n }\n return {restored: true, backupURL, result};\n } finally {\n try {\n deleteRestoreTempFiles(replicaFile);\n } catch (e) {\n lc.warn?.('Unable to clean up Litestream restore temporary files', {\n replicaFile,\n error: String(e),\n });\n }\n logRestoreDirectoryContents(lc, replicaFile, 'after-temp-cleanup');\n litestreamRestoreAttempts().add(1, {...attrs, result});\n }\n}\n\nfunction deleteRestoreTempFiles(replicaFile: string) {\n const temporaryReplicaFile = `${replicaFile}.tmp`;\n deleteLiteDB(temporaryReplicaFile);\n\n // rocicorp/litestream zero@v0.0.9 downloads WAL indexes in parallel to\n // `<output>.tmp-<8 lowercase hex digits>-wal` before applying them.\n const directory = dirname(temporaryReplicaFile);\n if (!existsSync(directory)) {\n return;\n }\n const prefix = `${basename(temporaryReplicaFile)}-`;\n for (const name of readdirSync(directory)) {\n if (!name.startsWith(prefix) || !name.endsWith('-wal')) {\n continue;\n }\n const index = name.slice(prefix.length, -'-wal'.length);\n if (LEGACY_RESTORE_WAL_INDEX.test(index)) {\n rmSync(join(directory, name), {force: true});\n }\n }\n}\n\nfunction logRestoreDirectoryContents(\n lc: LogContext,\n replicaFile: string,\n phase: 'before-temp-cleanup' | 'after-temp-cleanup',\n) {\n const directory = dirname(replicaFile);\n try {\n const names = readdirSync(directory).sort();\n const entries = names\n .slice(0, MAX_LOGGED_RESTORE_DIRECTORY_ENTRIES)\n .map(name => ({name, sizeBytes: statSync(join(directory, name)).size}));\n lc.info?.('Litestream restore directory contents', {\n phase,\n directory,\n replicaFile,\n entryCount: names.length,\n omittedEntryCount: Math.max(\n 0,\n names.length - MAX_LOGGED_RESTORE_DIRECTORY_ENTRIES,\n ),\n entries,\n });\n } catch (e) {\n lc.warn?.('Unable to inspect Litestream restore directory', {\n phase,\n directory,\n replicaFile,\n error: String(e),\n });\n }\n}\n\nfunction replicaIsValid(\n lc: LogContext,\n replica: string,\n constraints: ReplicaConstraints | undefined,\n) {\n let db: Database | undefined;\n try {\n // Note: Open the database and read the subscription state as a\n // sanity / corruption check, even if there are no constraints.\n db = new Database(lc, replica);\n const {replicaVersion, watermark} = getSubscriptionState(\n new StatementRunner(db),\n );\n if (constraints) {\n if (replicaVersion !== constraints.replicaVersion) {\n lc.warn?.(\n `Local replica version ${replicaVersion} does not match expected replicaVersion ${constraints.replicaVersion}`,\n constraints,\n );\n return false;\n }\n if (watermark < constraints.minWatermark) {\n lc.warn?.(\n `Local replica watermark ${watermark} is earlier than minWatermark ${constraints.minWatermark}`,\n );\n return false;\n }\n lc.info?.(\n `Local replica at version ${replicaVersion} and watermark ${watermark} is compatible`,\n constraints,\n );\n }\n return true;\n } catch (e) {\n if (isSQLiteCorruption(e)) {\n logSQLiteCorruptionDiagnostics(lc, 'restored replica', replica, e);\n }\n lc.error?.('Error while validating restored replica', e);\n return false;\n } finally {\n db?.close();\n }\n}\n\nexport function startReplicaBackupProcess(\n lc: LogContext,\n config: LitestreamConfig,\n replicaFile: string,\n): ChildProcess {\n const {litestream, env} = getLitestream('replicate', config, replicaFile);\n const attrs = litestreamBackupProcessMetricAttrs(config);\n lc.info?.(`starting litestream backup to ${config.backupURL}`);\n const start = performance.now();\n const proc = spawn(litestream, ['replicate'], {\n env,\n stdio: 'inherit',\n windowsHide: true,\n });\n let recorded = false;\n const record = (result: 'success' | 'error' | 'stopped') => {\n if (recorded) {\n return;\n }\n recorded = true;\n const labels = {...attrs, result};\n litestreamBackupProcessRuns().add(1, labels);\n litestreamBackupProcessDuration().recordMs(\n performance.now() - start,\n labels,\n );\n };\n proc.on('error', e => {\n lc.warn?.(`litestream backup process error`, e);\n record('error');\n });\n proc.on('close', (code, signal) => {\n if (signal) {\n lc.info?.(`litestream backup process stopped`, {signal});\n record('stopped');\n } else if (code === 0) {\n record('success');\n } else {\n lc.warn?.(`litestream backup process exited with code ${code}`);\n record('error');\n }\n });\n return proc;\n}\n\n// Listing the backup state requires a few S3 LIST requests, which should\n// normally complete well within this timeout.\nconst LIST_BACKUP_TIMEOUT_MS = 30_000;\n\nconst wsRe = /\\s+/;\n\n/**\n * Returns the time of the most recent object (snapshot or WAL segment)\n * actually uploaded to the backup replica destination, as listed by the\n * bundled litestream CLI (`litestream snapshots` / `litestream wal`).\n *\n * This queries the replica destination (e.g. S3) directly, and thus serves\n * as a source of truth for backup durability. This is in contrast to the\n * `litestream_replica_progress` metric, which is exported when litestream\n * *believes* an upload has succeeded, and has been observed to advance even\n * when nothing is actually written to the destination.\n *\n * Rejects if the backup state cannot be determined (spawn error, non-zero\n * exit, timeout, or empty/unparseable listing).\n */\nexport async function getLastBackupTime(\n lc: LogContext,\n config: LitestreamConfig,\n replicaFile: string,\n): Promise<Date> {\n const [snapshots, wal] = await Promise.all([\n listBackupCreatedTimes(lc, config, replicaFile, 'snapshots'),\n listBackupCreatedTimes(lc, config, replicaFile, 'wal'),\n ]);\n const times = [...snapshots, ...wal];\n if (times.length === 0) {\n // Note: the litestream CLI exits with code 0 and logs listing errors\n // (e.g. S3 failures) to stderr, so an empty listing cannot be\n // distinguished from a failed one. Since a valid backup always contains\n // at least one snapshot, an empty listing is treated as a failure.\n throw new Error(\n `no snapshots or WAL segments listed at ${config.backupURL}`,\n );\n }\n return new Date(Math.max(...times.map(time => time.getTime())));\n}\n\n/**\n * Runs `litestream <snapshots|wal> <replica-file>` with the same config /\n * environment used by the `litestream replicate` process (so that the\n * backupURL, endpoint, region, and credentials are identical), and parses\n * the `created` column (RFC3339) of the tab-formatted output, e.g.:\n *\n * ```\n * replica generation index size created\n * s3 1862f44967b3863f 0 4546445 2026-06-10T01:11:32Z\n * ```\n */\nasync function listBackupCreatedTimes(\n lc: LogContext,\n config: LitestreamConfig,\n replicaFile: string,\n command: 'snapshots' | 'wal',\n): Promise<Date[]> {\n const start = performance.now();\n let result: 'success' | 'empty' | 'timeout' | 'error' = 'error';\n const {litestream, env} = getLitestream('replicate', config, replicaFile);\n const proc = spawn(litestream, [command, replicaFile], {\n env,\n stdio: ['ignore', 'pipe', 'inherit'],\n windowsHide: true,\n });\n const {promise, resolve, reject} = resolver<string>();\n let stdout = '';\n proc.stdout.setEncoding('utf-8');\n proc.stdout.on('data', chunk => (stdout += chunk));\n proc.on('error', reject);\n proc.on('close', (code, signal) => {\n if (signal) {\n reject(new Error(`litestream ${command} killed with ${signal}`));\n } else if (code !== 0) {\n reject(new Error(`litestream ${command} exited with code ${code}`));\n } else {\n resolve(stdout);\n }\n });\n const timeout = setTimeout(() => {\n result = 'timeout';\n reject(new Error(`timed out listing backup state (litestream ${command})`));\n proc.kill('SIGKILL');\n }, LIST_BACKUP_TIMEOUT_MS);\n\n try {\n const output = await promise;\n const times = parseBackupCreatedTimes(lc, command, output);\n result = times.length ? 'success' : 'empty';\n return times;\n } finally {\n clearTimeout(timeout);\n litestreamBackupListDuration().recordMs(performance.now() - start, {\n ...litestreamBackupMetricAttrs(config),\n command,\n result,\n });\n }\n}\n\n/**\n * Parses the `created` column (the last, RFC3339-formatted column) from the\n * tab-formatted output of `litestream snapshots` / `litestream wal`. The\n * header row and any unparseable lines are skipped.\n *\n * Exported for testing.\n */\nexport function parseBackupCreatedTimes(\n lc: LogContext,\n command: 'snapshots' | 'wal',\n output: string,\n): Date[] {\n const times: Date[] = [];\n for (const line of output.split('\\n')) {\n const cols = line.trim().split(wsRe);\n const created = cols.at(-1);\n if (\n cols.length < 2 ||\n created === undefined ||\n created === 'created' /* header row */\n ) {\n continue;\n }\n const time = new Date(created);\n if (Number.isNaN(time.getTime())) {\n lc.warn?.(`unexpected line in litestream ${command} output: ${line}`);\n continue;\n }\n times.push(time);\n }\n return times;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"commands.js","names":[],"sources":["../../../../../../zero-cache/src/services/litestream/commands.ts"],"sourcesContent":["import type {ChildProcess} from 'node:child_process';\nimport {spawn} from 'node:child_process';\nimport {existsSync, readdirSync, rmSync, statSync} from 'node:fs';\nimport {basename, dirname, join} from 'node:path';\nimport type {LogContext, LogLevel} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {must} from '../../../../shared/src/must.ts';\nimport * as v from '../../../../shared/src/valita.ts';\nimport {Database} from '../../../../zqlite/src/db.ts';\nimport {type LitestreamConfig} from '../../config/normalize.ts';\nimport {deleteLiteDB} from '../../db/delete-lite-db.ts';\nimport {\n isSQLiteCorruption,\n logSQLiteCorruptionDiagnostics,\n} from '../../db/sqlite-corruption.ts';\nimport {StatementRunner} from '../../db/statements.ts';\nimport type {InitCleanup} from '../change-source/common/init-cleanup.ts';\nimport {deleteChangeLogDB} from '../replicator/change-log-db.ts';\nimport {getSubscriptionState} from '../replicator/schema/replication-state.ts';\nimport {litestreamSocketPath} from './litestream-controller.ts';\nimport {\n litestreamBackupListDuration,\n litestreamBackupMetricAttrs,\n litestreamBackupProcessDuration,\n litestreamBackupProcessMetricAttrs,\n litestreamBackupProcessRuns,\n litestreamRestoreAttempts,\n litestreamRestoredDbBytes,\n litestreamRestoreMetricAttrs,\n litestreamRestoreProcessDuration,\n litestreamRestoreValidationDuration,\n type LitestreamRole,\n} from './metrics.ts';\nimport {WalKeeper} from './wal-keeper.ts';\n\nexport type ReplicaConstraints = {\n replicaVersion: string;\n minWatermark: string;\n};\n\nexport type RestoreResult =\n | 'success'\n | 'no_backup'\n | 'invalid_replica'\n | 'error';\n\nexport type RestoreAttempt = {\n restored: boolean;\n backupURL: string | undefined;\n result: RestoreResult;\n /**\n * Set when the restore prepared the backup to continue from the restored\n * replica (see {@link ForkBackup}). It is registered with the\n * `cleanup`; otherwise the caller must release it (see {@link WalKeeper}).\n */\n walKeeper?: WalKeeper | undefined;\n};\n\n/**\n * Prepares the restored replica to continue from a forked backup\n * instead of starting a new lineage with a full snapshot.\n */\nexport type ForkBackup = {\n /** The restore also forks its backup into the (new, empty) one at this URL. */\n url: string;\n /**\n * Registers the resulting {@link WalKeeper} to be released if the\n * initialization fails.\n */\n cleanup?: InitCleanup | undefined;\n};\n\nconst MAX_LOGGED_RESTORE_DIRECTORY_ENTRIES = 100;\nconst LEGACY_RESTORE_WAL_INDEX = /^[0-9a-f]{8}$/;\n\nexport class BackupNotFoundException extends Error {\n static readonly name = 'BackupNotFoundException';\n\n constructor(backupURL: string | undefined) {\n super(`backup not found at ${backupURL}`);\n }\n}\n\n/**\n * The effective backup URL: the `backupURLOverride` if present (i.e. a\n * lineage-specific location computed by the replication-manager), or\n * otherwise the configured `backupURL`.\n */\nexport function backupDestinationURL(\n config: Pick<LitestreamConfig, 'backupURL' | 'backupURLOverride'>,\n): string | undefined {\n return config.backupURLOverride ?? config.backupURL;\n}\n\nfunction getLitestream(\n mode: 'restore' | 'replicate',\n config: LitestreamConfig,\n replicaFile: string,\n logLevelOverride?: LogLevel,\n): {\n litestream: string;\n env: NodeJS.ProcessEnv;\n} {\n const {\n executable,\n executableV5,\n restoreUsingV5,\n backupUsingV5,\n logLevel,\n configPath,\n configPathV5,\n endpoint,\n region,\n port,\n checkpointThresholdMB,\n // Note: This assumes the default page size. If page size configuration\n // is added, this computation must be adjusted accordingly.\n minCheckpointPageCount = checkpointThresholdMB * 256, // 1 MiB / 4 KiB page size\n maxCheckpointPageCount = minCheckpointPageCount * 10,\n forceCheckpointThresholdMB,\n incrementalBackupIntervalMinutes,\n incrementalBackupIntervalSeconds,\n snapshotBackupIntervalHours,\n multipartConcurrency,\n multipartSize,\n } = config;\n\n const v5 =\n (mode === 'restore' && restoreUsingV5) ||\n (mode === 'replicate' && backupUsingV5);\n const litestream =\n (v5 ? executableV5 : executable) ??\n must(executable, `Missing --litestream-executable`);\n const litestreamConfig = v5 ? configPathV5 : configPath;\n // Disable truncate-page-n if forced checkpoints are enabled,\n // and otherwise use litestream's default.\n const truncatePageN = forceCheckpointThresholdMB ? -1 : 121359;\n\n return {\n litestream,\n env: {\n ...process.env,\n ['ZERO_REPLICA_FILE']: replicaFile,\n ['ZERO_LITESTREAM_BACKUP_URL']: must(backupDestinationURL(config)),\n ['ZERO_LITESTREAM_MIN_CHECKPOINT_PAGE_COUNT']: String(\n minCheckpointPageCount,\n ),\n ['ZERO_LITESTREAM_MAX_CHECKPOINT_PAGE_COUNT']: String(\n maxCheckpointPageCount,\n ),\n ['ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_MINUTES']: String(\n incrementalBackupIntervalMinutes, // v3 only\n ),\n ['ZERO_LITESTREAM_INCREMENTAL_BACKUP_INTERVAL_SECONDS']: String(\n incrementalBackupIntervalSeconds, // v5 only\n ),\n // v5 only\n ['ZERO_LITESTREAM_MONITOR_INTERVAL_SECONDS']: String(\n // zero-cache explicitly requests syncs at the incremental backup\n // interval (see LitestreamSyncRequester), and litestream's\n // own monitor becomes a backstop that keeps backups and checkpoints\n // progressing if zero-cache stops requesting them.\n 2 * incrementalBackupIntervalSeconds,\n ),\n ['ZERO_LITESTREAM_TRUNCATE_PAGE_N']: String(truncatePageN),\n ['ZERO_LITESTREAM_LOG_LEVEL']: logLevelOverride ?? logLevel,\n ['ZERO_LITESTREAM_SNAPSHOT_BACKUP_INTERVAL_HOURS']: String(\n snapshotBackupIntervalHours,\n ),\n ['ZERO_LITESTREAM_SNAPSHOT_RETENTION_INTERVAL_HOURS']: String(\n snapshotBackupIntervalHours + 6, // delete old snapshots after 6 hours\n ),\n ['ZERO_LITESTREAM_MULTIPART_CONCURRENCY']: String(multipartConcurrency),\n ['ZERO_LITESTREAM_MULTIPART_SIZE']: String(multipartSize),\n ['ZERO_LOG_FORMAT']: 'json',\n ['ZERO_LITESTREAM_SOCKET_PATH']: litestreamSocketPath(replicaFile),\n ['LITESTREAM_CONFIG']: litestreamConfig,\n ['LITESTREAM_PORT']: String(port),\n ...(endpoint ? {['ZERO_LITESTREAM_ENDPOINT']: endpoint} : {}),\n ...(region ? {['ZERO_LITESTREAM_REGION']: region} : {}),\n ['LITESTREAM_COMPACTION_SERIALIZE']: 'false',\n },\n };\n}\n\nexport async function tryRestore(\n lc: LogContext,\n config: LitestreamConfig,\n replicaFile: string,\n replicaConstraints: ReplicaConstraints | undefined,\n role: LitestreamRole,\n signal?: AbortSignal,\n forkBackup?: ForkBackup | undefined,\n): Promise<RestoreAttempt> {\n const {reusedExisting, ...attempt} = await restoreOnce(\n lc,\n config,\n replicaFile,\n replicaConstraints,\n role,\n signal,\n forkBackup,\n );\n if (attempt.result === 'invalid_replica' && reusedExisting) {\n // `-if-db-not-exists` made the restore a no-op, and the existing local\n // replica (e.g. one left behind by a failed initial sync of a different\n // generation) was invalid. It has since been deleted, so restore from the\n // backup.\n const {reusedExisting: _, ...retry} = await restoreOnce(\n lc,\n config,\n replicaFile,\n replicaConstraints,\n role,\n signal,\n forkBackup,\n );\n return retry;\n }\n return attempt;\n}\n\nasync function restoreOnce(\n lc: LogContext,\n config: LitestreamConfig,\n replicaFile: string,\n replicaConstraints: ReplicaConstraints | undefined,\n role: LitestreamRole,\n signal: AbortSignal | undefined,\n forkBackup: ForkBackup | undefined,\n): Promise<RestoreAttempt & {reusedExisting: boolean}> {\n const {backupUsingV5} = config;\n const backupURL = backupDestinationURL(config);\n if (forkBackup && !backupUsingV5) {\n forkBackup = undefined; // Ignore forkBackup if v5 is disabled.\n }\n const attrs = litestreamRestoreMetricAttrs(config, role, backupURL);\n let result: RestoreResult = 'error';\n try {\n logRestoreDirectoryContents(lc, replicaFile, 'before-temp-cleanup');\n deleteRestoreTempFiles(replicaFile);\n const reusedExisting = existsSync(replicaFile);\n if (!reusedExisting) {\n // The local litestream state (and anything recorded in it) describes a\n // replica that no longer exists. Discard it before the restore, which\n // (if it seeds a backup) writes the state that belongs to the new one.\n deleteLitestreamMetaDir(replicaFile);\n }\n const {litestream, env} = getLitestream(\n 'restore',\n config,\n replicaFile,\n 'debug', // Include all output from `litestream restore`, as it's minimal.\n );\n const {\n restoreParallelism: parallelism,\n multipartConcurrency,\n multipartSize,\n } = config;\n lc.info?.(`starting litestream restore`, {\n restoreParallelism: parallelism,\n multipartConcurrency,\n multipartSize,\n });\n // Pipe (rather than inherit) litestream's stdout/stderr so that its own\n // `\"level\":\"ERROR\"` output on a failed restore does not go straight to the\n // pod's stdout — where a log-scraper alert would page on it — before our\n // code has decided how to handle the failure. The captured output is\n // included in the thrown error so the caller can log the final outcome.\n // See INC-961.\n const proc = spawn(\n litestream,\n [\n 'restore',\n '-if-db-not-exists',\n '-if-replica-exists',\n '-parallelism',\n String(parallelism),\n ...(forkBackup ? ['-json', '-fork-to-url', forkBackup.url] : []),\n replicaFile,\n ],\n {env, stdio: ['ignore', 'pipe', 'pipe'], windowsHide: true, signal},\n );\n let stdout = '';\n let stderr = '';\n proc.stdout.setEncoding('utf-8');\n proc.stderr.setEncoding('utf-8');\n // Output AND capture the stdout and stderr, the former for observability\n // and the latter for error handling.\n proc.stdout.on('data', chunk => {\n process.stdout.write(chunk);\n stdout += chunk;\n });\n proc.stderr.on('data', chunk => {\n process.stderr.write(chunk);\n stderr += chunk;\n });\n const {promise, resolve, reject} = resolver();\n proc.on('error', reject);\n proc.on('close', (code, signal) => {\n if (signal) {\n reject(`litestream killed with ${signal}`);\n } else if (code !== 0) {\n reject(`litestream exited with code ${code}`);\n } else {\n resolve();\n }\n });\n const processStart = performance.now();\n try {\n await promise;\n litestreamRestoreProcessDuration().recordMs(\n performance.now() - processStart,\n {...attrs, result: 'success'},\n );\n } catch (e) {\n litestreamRestoreProcessDuration().recordMs(\n performance.now() - processStart,\n {...attrs, result: 'error'},\n );\n const output = [stdout, stderr]\n .map(value => value.trim())\n .filter(Boolean)\n .join('\\n');\n throw new Error(output ? `${String(e)}\\n${output}` : String(e), {\n cause: e,\n });\n }\n if (!existsSync(replicaFile)) {\n result = 'no_backup';\n return {restored: false, backupURL, result, reusedExisting};\n }\n const validationStart = performance.now();\n // On success, the validating connection is kept open until it is either\n // closed or kept as the WalKeeper. Until then, its close would be the last\n // one, which checkpoints and deletes the WAL that a seeded backup continues\n // from.\n let db = openValidReplica(lc, replicaFile, replicaConstraints);\n litestreamRestoreValidationDuration().recordMs(\n performance.now() - validationStart,\n {...attrs, result: db ? 'success' : 'invalid_replica'},\n );\n if (!db) {\n result = 'invalid_replica';\n lc.info?.(`Deleting local replica and retrying restore`);\n deleteLiteDB(replicaFile);\n deleteChangeLogDB(replicaFile);\n // The litestream state (e.g. the anchor of a seeded backup) describes\n // the deleted replica.\n deleteLitestreamMetaDir(replicaFile);\n return {restored: false, backupURL, result, reusedExisting};\n }\n try {\n result = 'success';\n if (!reusedExisting) {\n // This restore materialized the replica file, so any change log beside it\n // was written against a replica that is no longer there. Reconciliation\n // is the safety net — a restored replica keeps its replicaVersion, so the\n // rule falls through to truncate-or-reseed on head mismatch — but the log\n // is a cache and deleting it here is the explicit statement that nothing\n // in it survives the restore.\n //\n // A restore that reuses an existing replica (`-if-db-not-exists` made it\n // a no-op) deliberately keeps the log: that is the process-restart path,\n // where the log is still the one written beside this exact replica and\n // discarding it would cost every reconnecting subscriber a `too-old`.\n deleteChangeLogDB(replicaFile);\n litestreamRestoredDbBytes().add(statSync(replicaFile).size, {\n ...attrs,\n result: 'success',\n });\n }\n // Last, so that nothing can fail after the WalKeeper is created.\n const walKeeper =\n forkBackup && !reusedExisting && restoreForked(lc, stdout)\n ? new WalKeeper(lc, db)\n : undefined;\n if (walKeeper) {\n db = undefined; // now owned by the WalKeeper\n forkBackup?.cleanup?.onFailure('WAL keeper', () =>\n walKeeper.release('initialization failed'),\n );\n }\n return {restored: true, backupURL, result, reusedExisting, walKeeper};\n } finally {\n db?.close();\n }\n } finally {\n try {\n deleteRestoreTempFiles(replicaFile);\n } catch (e) {\n lc.warn?.('Unable to clean up Litestream restore temporary files', {\n replicaFile,\n error: String(e),\n });\n }\n logRestoreDirectoryContents(lc, replicaFile, 'after-temp-cleanup');\n litestreamRestoreAttempts().add(1, {...attrs, result});\n }\n}\n\n// From litestream/cmd/litestream/restore.go\nconst restoreForkOutputSchema = v.object({\n fork: v\n .object({\n forked: v.boolean(),\n txid: v.string().optional(),\n })\n .optional(),\n});\n\ntype RestoreForkOutput = v.Infer<typeof restoreForkOutputSchema>;\n\n/**\n * Whether the `-json` output of `litestream restore -fork-to-url` reports that\n * the backup was forked. A restore plan that cannot fork the backup is\n * restored without forking.\n */\nfunction restoreForked(lc: LogContext, stdout: string): boolean {\n const start = stdout.indexOf('{');\n const end = stdout.lastIndexOf('}');\n if (start < 0 || end < start) {\n lc.warn?.('litestream restore did not report a fork result');\n return false;\n }\n let output: RestoreForkOutput;\n try {\n output = v.parse(\n JSON.parse(stdout.slice(start, end + 1)),\n restoreForkOutputSchema,\n 'passthrough',\n );\n } catch (e) {\n lc.warn?.('unable to parse the litestream restore result', e);\n return false;\n }\n const forked = output.fork?.forked === true;\n lc.info?.(\n forked\n ? `forked the backup at txid ${output.fork?.txid}`\n : 'restored without forking the backup',\n );\n return forked;\n}\n\n/**\n * Deletes litestream's local state for `replicaFile` (its default meta\n * directory, `.<file>-litestream`).\n */\nexport function deleteLitestreamMetaDir(replicaFile: string) {\n rmSync(join(dirname(replicaFile), `.${basename(replicaFile)}-litestream`), {\n recursive: true,\n force: true,\n });\n}\n\nfunction deleteRestoreTempFiles(replicaFile: string) {\n const temporaryReplicaFile = `${replicaFile}.tmp`;\n deleteLiteDB(temporaryReplicaFile);\n\n // rocicorp/litestream zero@v0.0.9 downloads WAL indexes in parallel to\n // `<output>.tmp-<8 lowercase hex digits>-wal` before applying them.\n const directory = dirname(temporaryReplicaFile);\n if (!existsSync(directory)) {\n return;\n }\n const prefix = `${basename(temporaryReplicaFile)}-`;\n for (const name of readdirSync(directory)) {\n if (!name.startsWith(prefix) || !name.endsWith('-wal')) {\n continue;\n }\n const index = name.slice(prefix.length, -'-wal'.length);\n if (LEGACY_RESTORE_WAL_INDEX.test(index)) {\n rmSync(join(directory, name), {force: true});\n }\n }\n}\n\nfunction logRestoreDirectoryContents(\n lc: LogContext,\n replicaFile: string,\n phase: 'before-temp-cleanup' | 'after-temp-cleanup',\n) {\n const directory = dirname(replicaFile);\n try {\n const names = readdirSync(directory).sort();\n const entries = names\n .slice(0, MAX_LOGGED_RESTORE_DIRECTORY_ENTRIES)\n .map(name => ({name, sizeBytes: statSync(join(directory, name)).size}));\n lc.info?.('Litestream restore directory contents', {\n phase,\n directory,\n replicaFile,\n entryCount: names.length,\n omittedEntryCount: Math.max(\n 0,\n names.length - MAX_LOGGED_RESTORE_DIRECTORY_ENTRIES,\n ),\n entries,\n });\n } catch (e) {\n lc.warn?.('Unable to inspect Litestream restore directory', {\n phase,\n directory,\n replicaFile,\n error: String(e),\n });\n }\n}\n\n/**\n * Opens and validates the restored replica. Returns the open connection if\n * the replica is valid, which the caller must close; otherwise closes it and\n * returns `undefined`.\n */\nfunction openValidReplica(\n lc: LogContext,\n replica: string,\n constraints: ReplicaConstraints | undefined,\n): Database | undefined {\n let db: Database | undefined;\n try {\n // Note: Open the database and read the subscription state as a\n // sanity / corruption check, even if there are no constraints.\n db = new Database(lc, replica);\n const {replicaVersion, watermark} = getSubscriptionState(\n new StatementRunner(db),\n );\n if (constraints) {\n if (replicaVersion !== constraints.replicaVersion) {\n lc.warn?.(\n `Local replica version ${replicaVersion} does not match expected replicaVersion ${constraints.replicaVersion}`,\n constraints,\n );\n return undefined;\n }\n if (watermark < constraints.minWatermark) {\n lc.warn?.(\n `Local replica watermark ${watermark} is earlier than minWatermark ${constraints.minWatermark}`,\n );\n return undefined;\n }\n lc.info?.(\n `Local replica at version ${replicaVersion} and watermark ${watermark} is compatible`,\n constraints,\n );\n }\n const valid = db;\n db = undefined; // owned by the caller\n return valid;\n } catch (e) {\n if (isSQLiteCorruption(e)) {\n logSQLiteCorruptionDiagnostics(lc, 'restored replica', replica, e);\n }\n lc.error?.('Error while validating restored replica', e);\n return undefined;\n } finally {\n db?.close();\n }\n}\n\nexport function startReplicaBackupProcess(\n lc: LogContext,\n config: LitestreamConfig,\n replicaFile: string,\n): ChildProcess {\n const {litestream, env} = getLitestream('replicate', config, replicaFile);\n const attrs = litestreamBackupProcessMetricAttrs(config);\n lc.info?.(`starting litestream backup to ${backupDestinationURL(config)}`);\n const start = performance.now();\n const proc = spawn(litestream, ['replicate'], {\n env,\n stdio: 'inherit',\n windowsHide: true,\n });\n let recorded = false;\n const record = (result: 'success' | 'error' | 'stopped') => {\n if (recorded) {\n return;\n }\n recorded = true;\n const labels = {...attrs, result};\n litestreamBackupProcessRuns().add(1, labels);\n litestreamBackupProcessDuration().recordMs(\n performance.now() - start,\n labels,\n );\n };\n proc.on('error', e => {\n lc.warn?.(`litestream backup process error`, e);\n record('error');\n });\n proc.on('close', (code, signal) => {\n if (signal) {\n lc.info?.(`litestream backup process stopped`, {signal});\n record('stopped');\n } else if (code === 0) {\n record('success');\n } else {\n lc.warn?.(`litestream backup process exited with code ${code}`);\n record('error');\n }\n });\n return proc;\n}\n\n// Listing the backup state requires a few S3 LIST requests, which should\n// normally complete well within this timeout.\nconst LIST_BACKUP_TIMEOUT_MS = 30_000;\n\nconst wsRe = /\\s+/;\n\n/**\n * Returns the time of the most recent object (snapshot or WAL segment)\n * actually uploaded to the backup replica destination, as listed by the\n * bundled litestream CLI (`litestream snapshots` / `litestream wal`).\n *\n * This queries the replica destination (e.g. S3) directly, and thus serves\n * as a source of truth for backup durability. This is in contrast to the\n * `litestream_replica_progress` metric, which is exported when litestream\n * *believes* an upload has succeeded, and has been observed to advance even\n * when nothing is actually written to the destination.\n *\n * Rejects if the backup state cannot be determined (spawn error, non-zero\n * exit, timeout, or empty/unparseable listing).\n */\nexport async function getLastBackupTime(\n lc: LogContext,\n config: LitestreamConfig,\n replicaFile: string,\n): Promise<Date> {\n const [snapshots, wal] = await Promise.all([\n listBackupCreatedTimes(lc, config, replicaFile, 'snapshots'),\n listBackupCreatedTimes(lc, config, replicaFile, 'wal'),\n ]);\n const times = [...snapshots, ...wal];\n if (times.length === 0) {\n // Note: the litestream CLI exits with code 0 and logs listing errors\n // (e.g. S3 failures) to stderr, so an empty listing cannot be\n // distinguished from a failed one. Since a valid backup always contains\n // at least one snapshot, an empty listing is treated as a failure.\n throw new Error(\n `no snapshots or WAL segments listed at ${backupDestinationURL(config)}`,\n );\n }\n return new Date(Math.max(...times.map(time => time.getTime())));\n}\n\n/**\n * Runs `litestream <snapshots|wal> <replica-file>` with the same config /\n * environment used by the `litestream replicate` process (so that the\n * backupURL, endpoint, region, and credentials are identical), and parses\n * the `created` column (RFC3339) of the tab-formatted output, e.g.:\n *\n * ```\n * replica generation index size created\n * s3 1862f44967b3863f 0 4546445 2026-06-10T01:11:32Z\n * ```\n */\nasync function listBackupCreatedTimes(\n lc: LogContext,\n config: LitestreamConfig,\n replicaFile: string,\n command: 'snapshots' | 'wal',\n): Promise<Date[]> {\n const start = performance.now();\n let result: 'success' | 'empty' | 'timeout' | 'error' = 'error';\n const {litestream, env} = getLitestream('replicate', config, replicaFile);\n const proc = spawn(litestream, [command, replicaFile], {\n env,\n stdio: ['ignore', 'pipe', 'inherit'],\n windowsHide: true,\n });\n const {promise, resolve, reject} = resolver<string>();\n let stdout = '';\n proc.stdout.setEncoding('utf-8');\n proc.stdout.on('data', chunk => (stdout += chunk));\n proc.on('error', reject);\n proc.on('close', (code, signal) => {\n if (signal) {\n reject(new Error(`litestream ${command} killed with ${signal}`));\n } else if (code !== 0) {\n reject(new Error(`litestream ${command} exited with code ${code}`));\n } else {\n resolve(stdout);\n }\n });\n const timeout = setTimeout(() => {\n result = 'timeout';\n reject(new Error(`timed out listing backup state (litestream ${command})`));\n proc.kill('SIGKILL');\n }, LIST_BACKUP_TIMEOUT_MS);\n\n try {\n const output = await promise;\n const times = parseBackupCreatedTimes(lc, command, output);\n result = times.length ? 'success' : 'empty';\n return times;\n } finally {\n clearTimeout(timeout);\n litestreamBackupListDuration().recordMs(performance.now() - start, {\n ...litestreamBackupMetricAttrs(config),\n command,\n result,\n });\n }\n}\n\n/**\n * Parses the `created` column (the last, RFC3339-formatted column) from the\n * tab-formatted output of `litestream snapshots` / `litestream wal`. The\n * header row and any unparseable lines are skipped.\n *\n * Exported for testing.\n */\nexport function parseBackupCreatedTimes(\n lc: LogContext,\n command: 'snapshots' | 'wal',\n output: string,\n): Date[] {\n const times: Date[] = [];\n for (const line of output.split('\\n')) {\n const cols = line.trim().split(wsRe);\n const created = cols.at(-1);\n if (\n cols.length < 2 ||\n created === undefined ||\n created === 'created' /* header row */\n ) {\n continue;\n }\n const time = new Date(created);\n if (Number.isNaN(time.getTime())) {\n lc.warn?.(`unexpected line in litestream ${command} output: ${line}`);\n continue;\n }\n times.push(time);\n }\n return times;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAwEA,IAAM,uCAAuC;AAC7C,IAAM,2BAA2B;;;;;;AAejC,SAAgB,qBACd,QACoB;CACpB,OAAO,OAAO,qBAAqB,OAAO;AAC5C;AAEA,SAAS,cACP,MACA,QACA,aACA,kBAIA;CACA,MAAM,EACJ,YACA,cACA,gBACA,eACA,UACA,YACA,cACA,UACA,QACA,MACA,uBAGA,yBAAyB,wBAAwB,KACjD,yBAAyB,yBAAyB,IAClD,4BACA,kCACA,kCACA,6BACA,sBACA,kBACE;CAEJ,MAAM,KACH,SAAS,aAAa,kBACtB,SAAS,eAAe;CAC3B,MAAM,cACH,KAAK,eAAe,eACrB,KAAK,YAAY,iCAAiC;CACpD,MAAM,mBAAmB,KAAK,eAAe;CAG7C,MAAM,gBAAgB,6BAA6B,KAAK;CAExD,OAAO;EACL;EACA,KAAK;GACH,GAAG,QAAQ;IACV,sBAAsB;IACtB,+BAA+B,KAAK,qBAAqB,MAAM,CAAC;IAChE,8CAA8C,OAC7C,sBACF;IACC,8CAA8C,OAC7C,sBACF;IACC,wDAAwD,OACvD,gCACF;IACC,wDAAwD,OACvD,gCACF;IAEC,6CAA6C,OAK5C,IAAI,gCACN;IACC,oCAAoC,OAAO,aAAa;IACxD,8BAA8B,oBAAoB;IAClD,mDAAmD,OAClD,2BACF;IACC,sDAAsD,OACrD,8BAA8B,CAChC;IACC,0CAA0C,OAAO,oBAAoB;IACrE,mCAAmC,OAAO,aAAa;IACvD,oBAAoB;IACpB,gCAAgC,qBAAqB,WAAW;IAChE,sBAAsB;IACtB,oBAAoB,OAAO,IAAI;GAChC,GAAI,WAAW,GAAE,6BAA6B,SAAQ,IAAI,CAAC;GAC3D,GAAI,SAAS,GAAE,2BAA2B,OAAM,IAAI,CAAC;IACpD,oCAAoC;EACvC;CACF;AACF;AAEA,eAAsB,WACpB,IACA,QACA,aACA,oBACA,MACA,QACA,YACyB;CACzB,MAAM,EAAC,gBAAgB,GAAG,YAAW,MAAM,YACzC,IACA,QACA,aACA,oBACA,MACA,QACA,UACF;CACA,IAAI,QAAQ,WAAW,qBAAqB,gBAAgB;EAK1D,MAAM,EAAC,gBAAgB,GAAG,GAAG,UAAS,MAAM,YAC1C,IACA,QACA,aACA,oBACA,MACA,QACA,UACF;EACA,OAAO;CACT;CACA,OAAO;AACT;AAEA,eAAe,YACb,IACA,QACA,aACA,oBACA,MACA,QACA,YACqD;CACrD,MAAM,EAAC,kBAAiB;CACxB,MAAM,YAAY,qBAAqB,MAAM;CAC7C,IAAI,cAAc,CAAC,eACjB,aAAa,KAAA;CAEf,MAAM,QAAQ,6BAA6B,QAAQ,MAAM,SAAS;CAClE,IAAI,SAAwB;CAC5B,IAAI;EACF,4BAA4B,IAAI,aAAa,qBAAqB;EAClE,uBAAuB,WAAW;EAClC,MAAM,iBAAiB,WAAW,WAAW;EAC7C,IAAI,CAAC,gBAIH,wBAAwB,WAAW;EAErC,MAAM,EAAC,YAAY,QAAO,cACxB,WACA,QACA,aACA,OACF;EACA,MAAM,EACJ,oBAAoB,aACpB,sBACA,kBACE;EACJ,GAAG,OAAO,+BAA+B;GACvC,oBAAoB;GACpB;GACA;EACF,CAAC;EAOD,MAAM,OAAO,MACX,YACA;GACE;GACA;GACA;GACA;GACA,OAAO,WAAW;GAClB,GAAI,aAAa;IAAC;IAAS;IAAgB,WAAW;GAAG,IAAI,CAAC;GAC9D;EACF,GACA;GAAC;GAAK,OAAO;IAAC;IAAU;IAAQ;GAAM;GAAG,aAAa;GAAM;EAAM,CACpE;EACA,IAAI,SAAS;EACb,IAAI,SAAS;EACb,KAAK,OAAO,YAAY,OAAO;EAC/B,KAAK,OAAO,YAAY,OAAO;EAG/B,KAAK,OAAO,GAAG,SAAQ,UAAS;GAC9B,QAAQ,OAAO,MAAM,KAAK;GAC1B,UAAU;EACZ,CAAC;EACD,KAAK,OAAO,GAAG,SAAQ,UAAS;GAC9B,QAAQ,OAAO,MAAM,KAAK;GAC1B,UAAU;EACZ,CAAC;EACD,MAAM,EAAC,SAAS,SAAS,WAAU,SAAS;EAC5C,KAAK,GAAG,SAAS,MAAM;EACvB,KAAK,GAAG,UAAU,MAAM,WAAW;GACjC,IAAI,QACF,OAAO,0BAA0B,QAAQ;QACpC,IAAI,SAAS,GAClB,OAAO,+BAA+B,MAAM;QAE5C,QAAQ;EAEZ,CAAC;EACD,MAAM,eAAe,YAAY,IAAI;EACrC,IAAI;GACF,MAAM;GACN,iCAAiC,EAAE,SACjC,YAAY,IAAI,IAAI,cACpB;IAAC,GAAG;IAAO,QAAQ;GAAS,CAC9B;EACF,SAAS,GAAG;GACV,iCAAiC,EAAE,SACjC,YAAY,IAAI,IAAI,cACpB;IAAC,GAAG;IAAO,QAAQ;GAAO,CAC5B;GACA,MAAM,SAAS,CAAC,QAAQ,MAAM,EAC3B,KAAI,UAAS,MAAM,KAAK,CAAC,EACzB,OAAO,OAAO,EACd,KAAK,IAAI;GACZ,MAAM,IAAI,MAAM,SAAS,GAAG,OAAO,CAAC,EAAE,IAAI,WAAW,OAAO,CAAC,GAAG,EAC9D,OAAO,EACT,CAAC;EACH;EACA,IAAI,CAAC,WAAW,WAAW,GAAG;GAC5B,SAAS;GACT,OAAO;IAAC,UAAU;IAAO;IAAW;IAAQ;GAAc;EAC5D;EACA,MAAM,kBAAkB,YAAY,IAAI;EAKxC,IAAI,KAAK,iBAAiB,IAAI,aAAa,kBAAkB;EAC7D,oCAAoC,EAAE,SACpC,YAAY,IAAI,IAAI,iBACpB;GAAC,GAAG;GAAO,QAAQ,KAAK,YAAY;EAAiB,CACvD;EACA,IAAI,CAAC,IAAI;GACP,SAAS;GACT,GAAG,OAAO,6CAA6C;GACvD,aAAa,WAAW;GACxB,kBAAkB,WAAW;GAG7B,wBAAwB,WAAW;GACnC,OAAO;IAAC,UAAU;IAAO;IAAW;IAAQ;GAAc;EAC5D;EACA,IAAI;GACF,SAAS;GACT,IAAI,CAAC,gBAAgB;IAYnB,kBAAkB,WAAW;IAC7B,0BAA0B,EAAE,IAAI,SAAS,WAAW,EAAE,MAAM;KAC1D,GAAG;KACH,QAAQ;IACV,CAAC;GACH;GAEA,MAAM,YACJ,cAAc,CAAC,kBAAkB,cAAc,IAAI,MAAM,IACrD,IAAI,UAAU,IAAI,EAAE,IACpB,KAAA;GACN,IAAI,WAAW;IACb,KAAK,KAAA;IACL,YAAY,SAAS,UAAU,oBAC7B,UAAU,QAAQ,uBAAuB,CAC3C;GACF;GACA,OAAO;IAAC,UAAU;IAAM;IAAW;IAAQ;IAAgB;GAAS;EACtE,UAAU;GACR,IAAI,MAAM;EACZ;CACF,UAAU;EACR,IAAI;GACF,uBAAuB,WAAW;EACpC,SAAS,GAAG;GACV,GAAG,OAAO,yDAAyD;IACjE;IACA,OAAO,OAAO,CAAC;GACjB,CAAC;EACH;EACA,4BAA4B,IAAI,aAAa,oBAAoB;EACjE,0BAA0B,EAAE,IAAI,GAAG;GAAC,GAAG;GAAO;EAAM,CAAC;CACvD;AACF;AAGA,IAAM,0BAA0B,eAAE,OAAO,EACvC,MAAM,eACH,OAAO;CACN,QAAQ,eAAE,QAAQ;CAClB,MAAM,eAAE,OAAO,EAAE,SAAS;AAC5B,CAAC,EACA,SAAS,EACd,CAAC;;;;;;AASD,SAAS,cAAc,IAAgB,QAAyB;CAC9D,MAAM,QAAQ,OAAO,QAAQ,GAAG;CAChC,MAAM,MAAM,OAAO,YAAY,GAAG;CAClC,IAAI,QAAQ,KAAK,MAAM,OAAO;EAC5B,GAAG,OAAO,iDAAiD;EAC3D,OAAO;CACT;CACA,IAAI;CACJ,IAAI;EACF,SAAS,MACP,KAAK,MAAM,OAAO,MAAM,OAAO,MAAM,CAAC,CAAC,GACvC,yBACA,aACF;CACF,SAAS,GAAG;EACV,GAAG,OAAO,iDAAiD,CAAC;EAC5D,OAAO;CACT;CACA,MAAM,SAAS,OAAO,MAAM,WAAW;CACvC,GAAG,OACD,SACI,6BAA6B,OAAO,MAAM,SAC1C,qCACN;CACA,OAAO;AACT;;;;;AAMA,SAAgB,wBAAwB,aAAqB;CAC3D,OAAO,KAAK,QAAQ,WAAW,GAAG,IAAI,SAAS,WAAW,EAAE,YAAY,GAAG;EACzE,WAAW;EACX,OAAO;CACT,CAAC;AACH;AAEA,SAAS,uBAAuB,aAAqB;CACnD,MAAM,uBAAuB,GAAG,YAAY;CAC5C,aAAa,oBAAoB;CAIjC,MAAM,YAAY,QAAQ,oBAAoB;CAC9C,IAAI,CAAC,WAAW,SAAS,GACvB;CAEF,MAAM,SAAS,GAAG,SAAS,oBAAoB,EAAE;CACjD,KAAK,MAAM,QAAQ,YAAY,SAAS,GAAG;EACzC,IAAI,CAAC,KAAK,WAAW,MAAM,KAAK,CAAC,KAAK,SAAS,MAAM,GACnD;EAEF,MAAM,QAAQ,KAAK,MAAM,OAAO,QAAQ,EAAc;EACtD,IAAI,yBAAyB,KAAK,KAAK,GACrC,OAAO,KAAK,WAAW,IAAI,GAAG,EAAC,OAAO,KAAI,CAAC;CAE/C;AACF;AAEA,SAAS,4BACP,IACA,aACA,OACA;CACA,MAAM,YAAY,QAAQ,WAAW;CACrC,IAAI;EACF,MAAM,QAAQ,YAAY,SAAS,EAAE,KAAK;EAC1C,MAAM,UAAU,MACb,MAAM,GAAG,oCAAoC,EAC7C,KAAI,UAAS;GAAC;GAAM,WAAW,SAAS,KAAK,WAAW,IAAI,CAAC,EAAE;EAAI,EAAE;EACxE,GAAG,OAAO,yCAAyC;GACjD;GACA;GACA;GACA,YAAY,MAAM;GAClB,mBAAmB,KAAK,IACtB,GACA,MAAM,SAAS,oCACjB;GACA;EACF,CAAC;CACH,SAAS,GAAG;EACV,GAAG,OAAO,kDAAkD;GAC1D;GACA;GACA;GACA,OAAO,OAAO,CAAC;EACjB,CAAC;CACH;AACF;;;;;;AAOA,SAAS,iBACP,IACA,SACA,aACsB;CACtB,IAAI;CACJ,IAAI;EAGF,KAAK,IAAI,SAAS,IAAI,OAAO;EAC7B,MAAM,EAAC,gBAAgB,cAAa,qBAClC,IAAI,gBAAgB,EAAE,CACxB;EACA,IAAI,aAAa;GACf,IAAI,mBAAmB,YAAY,gBAAgB;IACjD,GAAG,OACD,yBAAyB,eAAe,0CAA0C,YAAY,kBAC9F,WACF;IACA;GACF;GACA,IAAI,YAAY,YAAY,cAAc;IACxC,GAAG,OACD,2BAA2B,UAAU,gCAAgC,YAAY,cACnF;IACA;GACF;GACA,GAAG,OACD,4BAA4B,eAAe,iBAAiB,UAAU,iBACtE,WACF;EACF;EACA,MAAM,QAAQ;EACd,KAAK,KAAA;EACL,OAAO;CACT,SAAS,GAAG;EACV,IAAI,mBAAmB,CAAC,GACtB,+BAA+B,IAAI,oBAAoB,SAAS,CAAC;EAEnE,GAAG,QAAQ,2CAA2C,CAAC;EACvD;CACF,UAAU;EACR,IAAI,MAAM;CACZ;AACF;AAEA,SAAgB,0BACd,IACA,QACA,aACc;CACd,MAAM,EAAC,YAAY,QAAO,cAAc,aAAa,QAAQ,WAAW;CACxE,MAAM,QAAQ,mCAAmC,MAAM;CACvD,GAAG,OAAO,iCAAiC,qBAAqB,MAAM,GAAG;CACzE,MAAM,QAAQ,YAAY,IAAI;CAC9B,MAAM,OAAO,MAAM,YAAY,CAAC,WAAW,GAAG;EAC5C;EACA,OAAO;EACP,aAAa;CACf,CAAC;CACD,IAAI,WAAW;CACf,MAAM,UAAU,WAA4C;EAC1D,IAAI,UACF;EAEF,WAAW;EACX,MAAM,SAAS;GAAC,GAAG;GAAO;EAAM;EAChC,4BAA4B,EAAE,IAAI,GAAG,MAAM;EAC3C,gCAAgC,EAAE,SAChC,YAAY,IAAI,IAAI,OACpB,MACF;CACF;CACA,KAAK,GAAG,UAAS,MAAK;EACpB,GAAG,OAAO,mCAAmC,CAAC;EAC9C,OAAO,OAAO;CAChB,CAAC;CACD,KAAK,GAAG,UAAU,MAAM,WAAW;EACjC,IAAI,QAAQ;GACV,GAAG,OAAO,qCAAqC,EAAC,OAAM,CAAC;GACvD,OAAO,SAAS;EAClB,OAAO,IAAI,SAAS,GAClB,OAAO,SAAS;OACX;GACL,GAAG,OAAO,8CAA8C,MAAM;GAC9D,OAAO,OAAO;EAChB;CACF,CAAC;CACD,OAAO;AACT;AAIA,IAAM,yBAAyB;AAE/B,IAAM,OAAO;;;;;;;;;;;;;;;AAgBb,eAAsB,kBACpB,IACA,QACA,aACe;CACf,MAAM,CAAC,WAAW,OAAO,MAAM,QAAQ,IAAI,CACzC,uBAAuB,IAAI,QAAQ,aAAa,WAAW,GAC3D,uBAAuB,IAAI,QAAQ,aAAa,KAAK,CACvD,CAAC;CACD,MAAM,QAAQ,CAAC,GAAG,WAAW,GAAG,GAAG;CACnC,IAAI,MAAM,WAAW,GAKnB,MAAM,IAAI,MACR,0CAA0C,qBAAqB,MAAM,GACvE;CAEF,OAAO,IAAI,KAAK,KAAK,IAAI,GAAG,MAAM,KAAI,SAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC;AAChE;;;;;;;;;;;;AAaA,eAAe,uBACb,IACA,QACA,aACA,SACiB;CACjB,MAAM,QAAQ,YAAY,IAAI;CAC9B,IAAI,SAAoD;CACxD,MAAM,EAAC,YAAY,QAAO,cAAc,aAAa,QAAQ,WAAW;CACxE,MAAM,OAAO,MAAM,YAAY,CAAC,SAAS,WAAW,GAAG;EACrD;EACA,OAAO;GAAC;GAAU;GAAQ;EAAS;EACnC,aAAa;CACf,CAAC;CACD,MAAM,EAAC,SAAS,SAAS,WAAU,SAAiB;CACpD,IAAI,SAAS;CACb,KAAK,OAAO,YAAY,OAAO;CAC/B,KAAK,OAAO,GAAG,SAAQ,UAAU,UAAU,KAAM;CACjD,KAAK,GAAG,SAAS,MAAM;CACvB,KAAK,GAAG,UAAU,MAAM,WAAW;EACjC,IAAI,QACF,uBAAO,IAAI,MAAM,cAAc,QAAQ,eAAe,QAAQ,CAAC;OAC1D,IAAI,SAAS,GAClB,uBAAO,IAAI,MAAM,cAAc,QAAQ,oBAAoB,MAAM,CAAC;OAElE,QAAQ,MAAM;CAElB,CAAC;CACD,MAAM,UAAU,iBAAiB;EAC/B,SAAS;EACT,uBAAO,IAAI,MAAM,8CAA8C,QAAQ,EAAE,CAAC;EAC1E,KAAK,KAAK,SAAS;CACrB,GAAG,sBAAsB;CAEzB,IAAI;EAEF,MAAM,QAAQ,wBAAwB,IAAI,SAAS,MAD9B,OACoC;EACzD,SAAS,MAAM,SAAS,YAAY;EACpC,OAAO;CACT,UAAU;EACR,aAAa,OAAO;EACpB,6BAA6B,EAAE,SAAS,YAAY,IAAI,IAAI,OAAO;GACjE,GAAG,4BAA4B,MAAM;GACrC;GACA;EACF,CAAC;CACH;AACF;;;;;;;;AASA,SAAgB,wBACd,IACA,SACA,QACQ;CACR,MAAM,QAAgB,CAAC;CACvB,KAAK,MAAM,QAAQ,OAAO,MAAM,IAAI,GAAG;EACrC,MAAM,OAAO,KAAK,KAAK,EAAE,MAAM,IAAI;EACnC,MAAM,UAAU,KAAK,GAAG,EAAE;EAC1B,IACE,KAAK,SAAS,KACd,YAAY,KAAA,KACZ,YAAY,WAEZ;EAEF,MAAM,OAAO,IAAI,KAAK,OAAO;EAC7B,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAAG;GAChC,GAAG,OAAO,iCAAiC,QAAQ,WAAW,MAAM;GACpE;EACF;EACA,MAAM,KAAK,IAAI;CACjB;CACA,OAAO;AACT"}