@rocicorp/zero 1.10.0-canary.2 → 1.10.0-canary.20

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 (372) hide show
  1. package/out/replicache/src/btree/read.d.ts +12 -1
  2. package/out/replicache/src/btree/read.d.ts.map +1 -1
  3. package/out/replicache/src/btree/read.js +24 -12
  4. package/out/replicache/src/btree/read.js.map +1 -1
  5. package/out/replicache/src/db/write.js.map +1 -1
  6. package/out/replicache/src/kv/expo-sqlite/store.d.ts.map +1 -1
  7. package/out/replicache/src/kv/expo-sqlite/store.js +27 -4
  8. package/out/replicache/src/kv/expo-sqlite/store.js.map +1 -1
  9. package/out/replicache/src/kv/sqlite-store.d.ts +11 -0
  10. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  11. package/out/replicache/src/kv/sqlite-store.js +43 -3
  12. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  13. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  14. package/out/replicache/src/replicache-impl.js +10 -0
  15. package/out/replicache/src/replicache-impl.js.map +1 -1
  16. package/out/replicache/src/transactions.d.ts.map +1 -1
  17. package/out/replicache/src/transactions.js +1 -1
  18. package/out/replicache/src/transactions.js.map +1 -1
  19. package/out/shared/src/error.d.ts +11 -0
  20. package/out/shared/src/error.d.ts.map +1 -1
  21. package/out/shared/src/error.js +35 -1
  22. package/out/shared/src/error.js.map +1 -1
  23. package/out/shared/src/hash.d.ts.map +1 -1
  24. package/out/shared/src/hash.js +105 -10
  25. package/out/shared/src/hash.js.map +1 -1
  26. package/out/shared/src/map.d.ts +7 -2
  27. package/out/shared/src/map.d.ts.map +1 -1
  28. package/out/shared/src/map.js.map +1 -1
  29. package/out/shared/src/promise-race.d.ts +14 -0
  30. package/out/shared/src/promise-race.d.ts.map +1 -1
  31. package/out/shared/src/promise-race.js +32 -1
  32. package/out/shared/src/promise-race.js.map +1 -1
  33. package/out/zero/package.js +2 -3
  34. package/out/zero/package.js.map +1 -1
  35. package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
  36. package/out/zero-cache/src/auth/write-authorizer.js +8 -1
  37. package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
  38. package/out/zero-cache/src/config/normalize.d.ts +5 -0
  39. package/out/zero-cache/src/config/normalize.d.ts.map +1 -1
  40. package/out/zero-cache/src/config/normalize.js +16 -3
  41. package/out/zero-cache/src/config/normalize.js.map +1 -1
  42. package/out/zero-cache/src/config/zero-config.d.ts +36 -7
  43. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  44. package/out/zero-cache/src/config/zero-config.js +121 -46
  45. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  46. package/out/zero-cache/src/db/create.d.ts.map +1 -1
  47. package/out/zero-cache/src/db/create.js +4 -1
  48. package/out/zero-cache/src/db/create.js.map +1 -1
  49. package/out/zero-cache/src/db/index-predicate.d.ts +4 -0
  50. package/out/zero-cache/src/db/index-predicate.d.ts.map +1 -0
  51. package/out/zero-cache/src/db/index-predicate.js +46 -0
  52. package/out/zero-cache/src/db/index-predicate.js.map +1 -0
  53. package/out/zero-cache/src/db/lite-tables.d.ts +10 -2
  54. package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
  55. package/out/zero-cache/src/db/lite-tables.js +8 -3
  56. package/out/zero-cache/src/db/lite-tables.js.map +1 -1
  57. package/out/zero-cache/src/db/migration-lite.d.ts.map +1 -1
  58. package/out/zero-cache/src/db/migration-lite.js +5 -3
  59. package/out/zero-cache/src/db/migration-lite.js.map +1 -1
  60. package/out/zero-cache/src/db/specs.d.ts +41 -0
  61. package/out/zero-cache/src/db/specs.d.ts.map +1 -1
  62. package/out/zero-cache/src/db/specs.js +30 -3
  63. package/out/zero-cache/src/db/specs.js.map +1 -1
  64. package/out/zero-cache/src/db/transaction-pool.d.ts +5 -15
  65. package/out/zero-cache/src/db/transaction-pool.d.ts.map +1 -1
  66. package/out/zero-cache/src/db/transaction-pool.js +39 -73
  67. package/out/zero-cache/src/db/transaction-pool.js.map +1 -1
  68. package/out/zero-cache/src/server/change-streamer.d.ts.map +1 -1
  69. package/out/zero-cache/src/server/change-streamer.js +27 -15
  70. package/out/zero-cache/src/server/change-streamer.js.map +1 -1
  71. package/out/zero-cache/src/server/main.d.ts.map +1 -1
  72. package/out/zero-cache/src/server/main.js +4 -4
  73. package/out/zero-cache/src/server/main.js.map +1 -1
  74. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  75. package/out/zero-cache/src/server/replicator.js +8 -4
  76. package/out/zero-cache/src/server/replicator.js.map +1 -1
  77. package/out/zero-cache/src/server/runner/run-worker.js +1 -1
  78. package/out/zero-cache/src/server/worker-urls.d.ts +0 -1
  79. package/out/zero-cache/src/server/worker-urls.d.ts.map +1 -1
  80. package/out/zero-cache/src/server/worker-urls.js +1 -2
  81. package/out/zero-cache/src/server/worker-urls.js.map +1 -1
  82. package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
  83. package/out/zero-cache/src/services/analyze.js +1 -0
  84. package/out/zero-cache/src/services/analyze.js.map +1 -1
  85. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts +12 -2
  86. package/out/zero-cache/src/services/change-source/common/backfill-manager.d.ts.map +1 -1
  87. package/out/zero-cache/src/services/change-source/common/backfill-manager.js +14 -3
  88. package/out/zero-cache/src/services/change-source/common/backfill-manager.js.map +1 -1
  89. package/out/zero-cache/src/services/change-source/common/replica-schema.d.ts.map +1 -1
  90. package/out/zero-cache/src/services/change-source/common/replica-schema.js +10 -41
  91. package/out/zero-cache/src/services/change-source/common/replica-schema.js.map +1 -1
  92. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts +3 -2
  93. package/out/zero-cache/src/services/change-source/pg/backfill-stream.d.ts.map +1 -1
  94. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +42 -16
  95. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  96. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts +34 -2
  97. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  98. package/out/zero-cache/src/services/change-source/pg/change-source.js +47 -19
  99. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  100. package/out/zero-cache/src/services/change-source/pg/decommission.d.ts.map +1 -1
  101. package/out/zero-cache/src/services/change-source/pg/decommission.js +20 -11
  102. package/out/zero-cache/src/services/change-source/pg/decommission.js.map +1 -1
  103. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +1 -0
  104. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  105. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +58 -80
  106. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  107. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts +20 -0
  108. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.d.ts.map +1 -1
  109. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js +43 -3
  110. package/out/zero-cache/src/services/change-source/pg/logical-replication/stream.js.map +1 -1
  111. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +24 -1
  112. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -1
  113. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +35 -5
  114. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -1
  115. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts +1513 -229
  116. package/out/zero-cache/src/services/change-source/pg/schema/ddl.d.ts.map +1 -1
  117. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js +2 -2
  118. package/out/zero-cache/src/services/change-source/pg/schema/ddl.js.map +1 -1
  119. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts +19 -0
  120. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.d.ts.map +1 -0
  121. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js +581 -0
  122. package/out/zero-cache/src/services/change-source/pg/schema/index-predicate.js.map +1 -0
  123. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts +1 -1
  124. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  125. package/out/zero-cache/src/services/change-source/pg/schema/init.js +15 -6
  126. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  127. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts +137 -20
  128. package/out/zero-cache/src/services/change-source/pg/schema/published.d.ts.map +1 -1
  129. package/out/zero-cache/src/services/change-source/pg/schema/published.js +82 -13
  130. package/out/zero-cache/src/services/change-source/pg/schema/published.js.map +1 -1
  131. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +128 -21
  132. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  133. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +5 -5
  134. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  135. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts +2 -0
  136. package/out/zero-cache/src/services/change-source/protocol/current/data.d.ts.map +1 -1
  137. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts +3 -0
  138. package/out/zero-cache/src/services/change-source/protocol/current/downstream.d.ts.map +1 -1
  139. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts +1 -4
  140. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.d.ts.map +1 -1
  141. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js +11 -18
  142. package/out/zero-cache/src/services/change-streamer/backup-cleanup-monitor-factory.js.map +1 -1
  143. package/out/zero-cache/src/services/change-streamer/backup-monitor.d.ts.map +1 -1
  144. package/out/zero-cache/src/services/change-streamer/backup-monitor.js +8 -3
  145. package/out/zero-cache/src/services/change-streamer/backup-monitor.js.map +1 -1
  146. package/out/zero-cache/src/services/change-streamer/broadcast.d.ts.map +1 -1
  147. package/out/zero-cache/src/services/change-streamer/broadcast.js +4 -1
  148. package/out/zero-cache/src/services/change-streamer/broadcast.js.map +1 -1
  149. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts +34 -0
  150. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.d.ts.map +1 -0
  151. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js +67 -0
  152. package/out/zero-cache/src/services/change-streamer/change-log-compare-digest.js.map +1 -0
  153. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +2 -4
  154. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  155. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +8 -2
  156. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  157. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +34 -13
  158. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  159. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +165 -36
  160. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  161. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +4 -9
  162. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  163. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  164. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  165. package/out/zero-cache/src/services/change-streamer/forwarder.js +12 -3
  166. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  167. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.d.ts.map +1 -1
  168. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js +11 -5
  169. package/out/zero-cache/src/services/change-streamer/litestream3-prometheus-poller.js.map +1 -1
  170. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts +11 -0
  171. package/out/zero-cache/src/services/change-streamer/shard-sampling.d.ts.map +1 -0
  172. package/out/zero-cache/src/services/change-streamer/shard-sampling.js +19 -0
  173. package/out/zero-cache/src/services/change-streamer/shard-sampling.js.map +1 -0
  174. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts +11 -1
  175. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.d.ts.map +1 -1
  176. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js +38 -5
  177. package/out/zero-cache/src/services/change-streamer/snapshot-reservations.js.map +1 -1
  178. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts +25 -1
  179. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.d.ts.map +1 -1
  180. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js +95 -12
  181. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-catchup.js.map +1 -1
  182. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts +83 -0
  183. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.d.ts.map +1 -0
  184. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js +417 -0
  185. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-comparator.js.map +1 -0
  186. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts +73 -0
  187. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.d.ts.map +1 -0
  188. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js +157 -0
  189. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-read-router.js.map +1 -0
  190. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts +1 -1
  191. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.d.ts.map +1 -1
  192. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js +8 -3
  193. package/out/zero-cache/src/services/change-streamer/sqlite-change-log-reader.js.map +1 -1
  194. package/out/zero-cache/src/services/change-streamer/storer.d.ts +63 -1
  195. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  196. package/out/zero-cache/src/services/change-streamer/storer.js +217 -49
  197. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  198. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +8 -1
  199. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  200. package/out/zero-cache/src/services/change-streamer/subscriber.js +10 -1
  201. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  202. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts +29 -0
  203. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.d.ts.map +1 -0
  204. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js +154 -0
  205. package/out/zero-cache/src/services/change-streamer/vfs-watermark-poller.js.map +1 -0
  206. package/out/zero-cache/src/services/http-service.d.ts +1 -0
  207. package/out/zero-cache/src/services/http-service.d.ts.map +1 -1
  208. package/out/zero-cache/src/services/http-service.js +7 -3
  209. package/out/zero-cache/src/services/http-service.js.map +1 -1
  210. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  211. package/out/zero-cache/src/services/litestream/commands.js +16 -7
  212. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  213. package/out/zero-cache/src/services/litestream/config-v5.yml +15 -1
  214. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js +131 -0
  215. package/out/zero-cache/src/services/litestream/litestream-checkpointer.js.map +1 -0
  216. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts +70 -0
  217. package/out/zero-cache/src/services/litestream/litestream-controller.d.ts.map +1 -0
  218. package/out/zero-cache/src/services/litestream/litestream-controller.js +101 -0
  219. package/out/zero-cache/src/services/litestream/litestream-controller.js.map +1 -0
  220. package/out/zero-cache/src/services/litestream/metrics.d.ts +1 -0
  221. package/out/zero-cache/src/services/litestream/metrics.d.ts.map +1 -1
  222. package/out/zero-cache/src/services/litestream/metrics.js +4 -1
  223. package/out/zero-cache/src/services/litestream/metrics.js.map +1 -1
  224. package/out/zero-cache/src/services/mutagen/mutagen.d.ts.map +1 -1
  225. package/out/zero-cache/src/services/mutagen/mutagen.js +1 -1
  226. package/out/zero-cache/src/services/mutagen/mutagen.js.map +1 -1
  227. package/out/zero-cache/src/services/replicator/change-log-db.d.ts +10 -0
  228. package/out/zero-cache/src/services/replicator/change-log-db.d.ts.map +1 -1
  229. package/out/zero-cache/src/services/replicator/change-log-db.js +15 -1
  230. package/out/zero-cache/src/services/replicator/change-log-db.js.map +1 -1
  231. package/out/zero-cache/src/services/replicator/change-processor.d.ts.map +1 -1
  232. package/out/zero-cache/src/services/replicator/change-processor.js +18 -21
  233. package/out/zero-cache/src/services/replicator/change-processor.js.map +1 -1
  234. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts +1 -1
  235. package/out/zero-cache/src/services/replicator/incremental-sync.d.ts.map +1 -1
  236. package/out/zero-cache/src/services/replicator/incremental-sync.js +2 -6
  237. package/out/zero-cache/src/services/replicator/incremental-sync.js.map +1 -1
  238. package/out/zero-cache/src/services/replicator/replicator.d.ts +1 -1
  239. package/out/zero-cache/src/services/replicator/replicator.d.ts.map +1 -1
  240. package/out/zero-cache/src/services/replicator/replicator.js +2 -2
  241. package/out/zero-cache/src/services/replicator/replicator.js.map +1 -1
  242. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts +3 -2
  243. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.d.ts.map +1 -1
  244. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js +8 -6
  245. package/out/zero-cache/src/services/replicator/sqlite-change-log-observability.js.map +1 -1
  246. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts +27 -2
  247. package/out/zero-cache/src/services/replicator/write-worker-client.d.ts.map +1 -1
  248. package/out/zero-cache/src/services/replicator/write-worker-client.js +3 -2
  249. package/out/zero-cache/src/services/replicator/write-worker-client.js.map +1 -1
  250. package/out/zero-cache/src/services/replicator/write-worker.js +24 -5
  251. package/out/zero-cache/src/services/replicator/write-worker.js.map +1 -1
  252. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts +3 -2
  253. package/out/zero-cache/src/services/view-syncer/client-handler.d.ts.map +1 -1
  254. package/out/zero-cache/src/services/view-syncer/client-handler.js +25 -17
  255. package/out/zero-cache/src/services/view-syncer/client-handler.js.map +1 -1
  256. package/out/zero-cache/src/services/view-syncer/cvr-store.d.ts.map +1 -1
  257. package/out/zero-cache/src/services/view-syncer/cvr-store.js +14 -1
  258. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  259. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts +3 -3
  260. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  261. package/out/zero-cache/src/services/view-syncer/view-syncer.js +6 -2
  262. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  263. package/out/zero-cache/src/types/downstream.d.ts +11 -0
  264. package/out/zero-cache/src/types/downstream.d.ts.map +1 -0
  265. package/out/zero-cache/src/types/pg.d.ts +15 -2
  266. package/out/zero-cache/src/types/pg.d.ts.map +1 -1
  267. package/out/zero-cache/src/types/pg.js +21 -8
  268. package/out/zero-cache/src/types/pg.js.map +1 -1
  269. package/out/zero-cache/src/types/poke-chunk.d.ts +18 -0
  270. package/out/zero-cache/src/types/poke-chunk.d.ts.map +1 -0
  271. package/out/zero-cache/src/types/poke-chunk.js +57 -0
  272. package/out/zero-cache/src/types/poke-chunk.js.map +1 -0
  273. package/out/zero-cache/src/types/processes.js +1 -4
  274. package/out/zero-cache/src/types/processes.js.map +1 -1
  275. package/out/zero-cache/src/types/streams.d.ts +4 -9
  276. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  277. package/out/zero-cache/src/types/streams.js.map +1 -1
  278. package/out/zero-cache/src/types/subscription.d.ts +2 -11
  279. package/out/zero-cache/src/types/subscription.d.ts.map +1 -1
  280. package/out/zero-cache/src/types/subscription.js +4 -25
  281. package/out/zero-cache/src/types/subscription.js.map +1 -1
  282. package/out/zero-cache/src/workers/connection.d.ts +22 -4
  283. package/out/zero-cache/src/workers/connection.d.ts.map +1 -1
  284. package/out/zero-cache/src/workers/connection.js +112 -14
  285. package/out/zero-cache/src/workers/connection.js.map +1 -1
  286. package/out/zero-cache/src/workers/replicator.d.ts +1 -0
  287. package/out/zero-cache/src/workers/replicator.d.ts.map +1 -1
  288. package/out/zero-cache/src/workers/replicator.js +4 -2
  289. package/out/zero-cache/src/workers/replicator.js.map +1 -1
  290. package/out/zero-client/src/client/connection-manager.d.ts.map +1 -1
  291. package/out/zero-client/src/client/connection-manager.js +46 -0
  292. package/out/zero-client/src/client/connection-manager.js.map +1 -1
  293. package/out/zero-client/src/client/context.d.ts +15 -0
  294. package/out/zero-client/src/client/context.d.ts.map +1 -1
  295. package/out/zero-client/src/client/context.js +44 -0
  296. package/out/zero-client/src/client/context.js.map +1 -1
  297. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
  298. package/out/zero-client/src/client/inspector/lazy-inspector.js +19 -12
  299. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  300. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  301. package/out/zero-client/src/client/version.js +1 -1
  302. package/out/zero-client/src/client/zero-poke-handler.d.ts +7 -2
  303. package/out/zero-client/src/client/zero-poke-handler.d.ts.map +1 -1
  304. package/out/zero-client/src/client/zero-poke-handler.js +52 -2
  305. package/out/zero-client/src/client/zero-poke-handler.js.map +1 -1
  306. package/out/zero-client/src/client/zero-rep.d.ts.map +1 -1
  307. package/out/zero-client/src/client/zero-rep.js +2 -1
  308. package/out/zero-client/src/client/zero-rep.js.map +1 -1
  309. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  310. package/out/zero-client/src/client/zero.js +98 -25
  311. package/out/zero-client/src/client/zero.js.map +1 -1
  312. package/out/zero-protocol/src/custom-queries.d.ts +1 -1
  313. package/out/zero-protocol/src/down.d.ts +1 -1
  314. package/out/zero-protocol/src/error.d.ts +4 -4
  315. package/out/zero-protocol/src/inspect-down.js +1 -1
  316. package/out/zero-protocol/src/mutate-server.d.ts +2 -2
  317. package/out/zero-protocol/src/poke.d.ts +89 -7
  318. package/out/zero-protocol/src/poke.d.ts.map +1 -1
  319. package/out/zero-protocol/src/poke.js +16 -8
  320. package/out/zero-protocol/src/poke.js.map +1 -1
  321. package/out/zero-protocol/src/protocol-version.d.ts +12 -7
  322. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  323. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  324. package/out/zero-protocol/src/push.d.ts +1 -1
  325. package/out/zero-protocol/src/query-server.d.ts +2 -2
  326. package/out/zql/src/ivm/array-view.d.ts.map +1 -1
  327. package/out/zql/src/ivm/array-view.js +5 -3
  328. package/out/zql/src/ivm/array-view.js.map +1 -1
  329. package/out/zql/src/ivm/data.d.ts.map +1 -1
  330. package/out/zql/src/ivm/data.js +8 -3
  331. package/out/zql/src/ivm/data.js.map +1 -1
  332. package/out/zql/src/ivm/deferred-input.d.ts +44 -0
  333. package/out/zql/src/ivm/deferred-input.d.ts.map +1 -0
  334. package/out/zql/src/ivm/deferred-input.js +88 -0
  335. package/out/zql/src/ivm/deferred-input.js.map +1 -0
  336. package/out/zql/src/ivm/memory-source.d.ts +10 -2
  337. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  338. package/out/zql/src/ivm/memory-source.js +19 -16
  339. package/out/zql/src/ivm/memory-source.js.map +1 -1
  340. package/out/zql/src/ivm/stream.d.ts +0 -1
  341. package/out/zql/src/ivm/stream.d.ts.map +1 -1
  342. package/out/zql/src/ivm/stream.js +1 -7
  343. package/out/zql/src/ivm/stream.js.map +1 -1
  344. package/out/zql/src/ivm/union-fan-in.d.ts +1 -1
  345. package/out/zql/src/ivm/union-fan-in.d.ts.map +1 -1
  346. package/out/zql/src/ivm/union-fan-in.js +11 -2
  347. package/out/zql/src/ivm/union-fan-in.js.map +1 -1
  348. package/out/zql/src/ivm/view-apply-change.js +6 -1
  349. package/out/zql/src/ivm/view-apply-change.js.map +1 -1
  350. package/out/zql/src/query/query-delegate-base.d.ts +6 -0
  351. package/out/zql/src/query/query-delegate-base.d.ts.map +1 -1
  352. package/out/zql/src/query/query-delegate-base.js +73 -10
  353. package/out/zql/src/query/query-delegate-base.js.map +1 -1
  354. package/out/zql/src/query/query-delegate.d.ts +22 -0
  355. package/out/zql/src/query/query-delegate.d.ts.map +1 -1
  356. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  357. package/out/zqlite/src/query-builder.js +2 -1
  358. package/out/zqlite/src/query-builder.js.map +1 -1
  359. package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
  360. package/out/zqlite/src/sqlite-stat-fanout.js +1 -0
  361. package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
  362. package/out/zqlite/src/table-source.d.ts +1 -1
  363. package/out/zqlite/src/table-source.d.ts.map +1 -1
  364. package/out/zqlite/src/table-source.js +3 -2
  365. package/out/zqlite/src/table-source.js.map +1 -1
  366. package/package.json +2 -3
  367. package/out/zero-cache/src/server/backup-watermark-poller.js +0 -31
  368. package/out/zero-cache/src/server/backup-watermark-poller.js.map +0 -1
  369. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts +0 -57
  370. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.d.ts.map +0 -1
  371. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js +0 -137
  372. package/out/zero-cache/src/services/litestream/vfs-watermark-poller.js.map +0 -1
@@ -11,11 +11,11 @@ import { listIndexes, listTables } from "../../../db/lite-tables.js";
11
11
  import { getOrCreateCounter, getOrCreateHistogram } from "../../../observability/metrics.js";
12
12
  import { initReplicationState } from "../../replicator/schema/replication-state.js";
13
13
  import { id } from "../../../types/sql.js";
14
- import { connectPgClient, pgClient } from "../../../types/pg.js";
14
+ import { connectPgClient } from "../../../types/pg.js";
15
15
  import { toStateVersionString } from "./lsn.js";
16
16
  import { READONLY } from "../../../db/mode-enum.js";
17
17
  import { runTx } from "../../../db/run-transaction.js";
18
- import { getPublicationInfo } from "./schema/published.js";
18
+ import { getPublicationInfo, warnForSkippedIndexes } from "./schema/published.js";
19
19
  import { dropShard, getInternalShardConfig, getReplicaState, initReplica, validatePublications } from "./schema/shard.js";
20
20
  import { createLiteIndexStatement, createLiteTableStatement } from "../../../db/create.js";
21
21
  import { ReplicationStatusPublisher } from "../../replicator/replication-status.js";
@@ -27,7 +27,6 @@ import { TransactionPool, importSnapshot } from "../../../db/transaction-pool.js
27
27
  import { CpuProfiler } from "../../../types/profiler.js";
28
28
  import { createReplicaAndSlot } from "./replication-slots.js";
29
29
  import { ensureShardSchema } from "./schema/init.js";
30
- import { resolver } from "@rocicorp/resolver";
31
30
  import { platform, tmpdir } from "node:os";
32
31
  import { join } from "node:path";
33
32
  import { mkdtemp, rm } from "node:fs/promises";
@@ -40,60 +39,66 @@ import { pipeline as pipeline$1 } from "node:stream/promises";
40
39
  */
41
40
  async function initialSync(lc, shard, tx, upstreamURI, syncOptions, context, { backupV5 } = { backupV5: true }) {
42
41
  if (!ALLOWED_APP_ID_CHARACTERS.test(shard.appID)) throw new Error("The App ID may only consist of lower-case letters, numbers, and the underscore character");
43
- const { tableCopyWorkers, profileCopy, textCopy = false, replicationSlotFailover = false, shadow } = syncOptions;
42
+ const { tableCopyWorkers, profileCopy, textCopy = false, replicationSlotFailover = false, installPartialIndexTriggers = true, shadow } = syncOptions;
44
43
  const syncMode = shadow ? "shadow" : "initial";
45
44
  const copyFormat = textCopy ? "text" : "binary";
46
45
  const start = performance.now();
47
46
  let sql;
48
- let replicationSession;
49
47
  const replicaID = Date.now().toString();
50
48
  let slotName;
49
+ let slotSession;
51
50
  const statusPublisher = ReplicationStatusPublisher.forRunningTransaction(tx, shadow ? async () => {} : void 0).publish(lc, "Initializing");
52
- let releaseShadowSnapshot;
53
51
  try {
54
52
  const copyProfiler = profileCopy ? await CpuProfiler.connect() : null;
55
53
  sql = await connectPgClient(lc, upstreamURI, "initial-sync");
56
- replicationSession = shadow ? void 0 : pgClient(lc, upstreamURI, "initial-sync-replication-session", {
57
- ["fetch_types"]: false,
58
- connection: { replication: "database" }
59
- });
60
54
  const pgVersion = await checkUpstreamConfig(sql);
61
- const { publications } = shadow ? await getInternalShardConfig(sql, shard) : await ensurePublishedTables(lc, sql, shard);
55
+ const { publications } = shadow ? await getInternalShardConfig(sql, shard) : await ensurePublishedTables(lc, sql, shard, installPartialIndexTriggers);
62
56
  lc.info?.(`Upstream is setup with publications [${publications}]`);
63
57
  const { database, host } = sql.options;
64
58
  lc.info?.(shadow ? `acquiring exported snapshot on ${database}@${host} (shadow mode)` : `opening replication session to ${database}@${host}`);
65
- let snapshot;
59
+ async function captureSnapshot(snapshot) {
60
+ const published = await runTx(must(sql), async (tx) => {
61
+ await tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshot}'`);
62
+ return getPublicationInfo(tx, publications);
63
+ }, { mode: READONLY });
64
+ warnForSkippedIndexes(lc, published);
65
+ validatePublications(lc, published);
66
+ const { tables, indexes } = published;
67
+ const numTables = tables.length;
68
+ if (platform() === "win32" && tableCopyWorkers < numTables) lc.warn?.(`Increasing the number of copy workers from ${tableCopyWorkers} to ${numTables} to work around a Node/Postgres connection bug`);
69
+ const numWorkers = platform() === "win32" ? numTables : Math.min(tableCopyWorkers, numTables);
70
+ const copyPool = await connectPgClient(lc, upstreamURI, "initial-sync-copy-worker", {
71
+ max: numWorkers,
72
+ ["max_lifetime"]: 7200
73
+ });
74
+ return {
75
+ published,
76
+ tables,
77
+ indexes,
78
+ numTables,
79
+ copyPool,
80
+ copiers: await startTableCopyWorkers(lc, copyPool, snapshot, numWorkers, numTables)
81
+ };
82
+ }
66
83
  let lsn;
84
+ let snapshotResult;
67
85
  if (shadow) {
68
- const acquired = await acquireExportedSnapshotForShadowSync(lc, upstreamURI);
69
- snapshot = acquired.snapshot;
70
- lsn = acquired.lsn;
71
- releaseShadowSnapshot = acquired.release;
86
+ const exported = await acquireExportedSnapshotForShadowSync(lc, upstreamURI, captureSnapshot);
87
+ lsn = exported.lsn;
88
+ snapshotResult = exported.result;
72
89
  } else {
73
- const slot = await createReplicaAndSlot(lc, sql, must(replicationSession), shard, replicaID, replicationSlotFailover && pgVersion >= 17e4, {
90
+ const replication = await createReplicaAndSlot(lc, sql, "initial-sync-replication-session", shard, replicaID, replicationSlotFailover && pgVersion >= 17e4, {
74
91
  backupPath: backupV5 ? replicaID : null,
75
92
  backupV5
76
- });
77
- snapshot = slot.snapshot_name;
78
- lsn = slot.consistent_point;
79
- slotName = slot.slot_name;
93
+ }, captureSnapshot);
94
+ lsn = replication.slot.consistent_point;
95
+ slotName = replication.slot.slot_name;
96
+ snapshotResult = replication.capturedSnapshot;
97
+ slotSession = replication.initialSession;
80
98
  }
99
+ const { published, tables, indexes, numTables, copyPool, copiers } = snapshotResult;
81
100
  const initialVersion = toStateVersionString(lsn);
82
101
  initReplicationState(tx, publications, initialVersion, context);
83
- const published = await runTx(sql, async (tx) => {
84
- await tx.unsafe(`SET TRANSACTION SNAPSHOT '${snapshot}'`);
85
- return getPublicationInfo(tx, publications);
86
- }, { mode: READONLY });
87
- validatePublications(lc, published);
88
- const { tables, indexes } = published;
89
- const numTables = tables.length;
90
- if (platform() === "win32" && tableCopyWorkers < numTables) lc.warn?.(`Increasing the number of copy workers from ${tableCopyWorkers} to ${numTables} to work around a Node/Postgres connection bug`);
91
- const numWorkers = platform() === "win32" ? numTables : Math.min(tableCopyWorkers, numTables);
92
- const copyPool = await connectPgClient(lc, upstreamURI, "initial-sync-copy-worker", {
93
- max: numWorkers,
94
- ["max_lifetime"]: 7200
95
- });
96
- const copiers = startTableCopyWorkers(lc, copyPool, snapshot, numWorkers, numTables);
97
102
  try {
98
103
  createLiteTables(tx, tables, initialVersion);
99
104
  const sampleRate = shadow?.sampleRate;
@@ -157,6 +162,7 @@ async function initialSync(lc, shard, tx, upstreamURI, syncOptions, context, { b
157
162
  });
158
163
  if (slotName && sql) {
159
164
  lc.warn?.(`dropping replication slot ${slotName}`, e);
165
+ slotSession?.destroy();
160
166
  await sql`
161
167
  SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots
162
168
  WHERE slot_name = ${slotName};
@@ -165,8 +171,6 @@ async function initialSync(lc, shard, tx, upstreamURI, syncOptions, context, { b
165
171
  throw await statusPublisher.publishAndThrowError(lc, "Initializing", e);
166
172
  } finally {
167
173
  statusPublisher.stop();
168
- if (releaseShadowSnapshot) await releaseShadowSnapshot().catch((e) => lc.warn?.(`Error releasing shadow snapshot`, e));
169
- if (replicationSession) await replicationSession.end();
170
174
  if (sql) await sql.end();
171
175
  }
172
176
  }
@@ -210,10 +214,10 @@ async function checkUpstreamConfig(sql) {
210
214
  if (version < 15e4) throw new Error(`Must be running Postgres 15 or higher (currently: "${version}")`);
211
215
  return version;
212
216
  }
213
- async function ensurePublishedTables(lc, sql, shard, validate = true) {
217
+ async function ensurePublishedTables(lc, sql, shard, installPartialIndexTriggers, validate = true) {
214
218
  const { database, host } = sql.options;
215
219
  lc.info?.(`Ensuring upstream PUBLICATION on ${database}@${host}`);
216
- await ensureShardSchema(lc, sql, shard);
220
+ await ensureShardSchema(lc, sql, shard, installPartialIndexTriggers);
217
221
  const { publications } = await getInternalShardConfig(sql, shard);
218
222
  if (validate) {
219
223
  let valid = false;
@@ -226,19 +230,20 @@ async function ensurePublishedTables(lc, sql, shard, validate = true) {
226
230
  else valid = true;
227
231
  if (!valid) {
228
232
  await sql.unsafe(dropShard(shard.appID, shard.shardNum));
229
- return ensurePublishedTables(lc, sql, shard, false);
233
+ return ensurePublishedTables(lc, sql, shard, installPartialIndexTriggers, false);
230
234
  }
231
235
  }
232
236
  return { publications };
233
237
  }
234
- function startTableCopyWorkers(lc, db, snapshot, numWorkers, numTables) {
235
- const { init } = importSnapshot(snapshot);
238
+ async function startTableCopyWorkers(lc, db, snapshot, numWorkers, numTables) {
239
+ const { init, imported } = importSnapshot(snapshot);
236
240
  const tableCopiers = new TransactionPool(lc, {
237
241
  mode: READONLY,
238
242
  init,
239
243
  initialWorkers: numWorkers
240
244
  });
241
245
  tableCopiers.run(db);
246
+ for (let i = 0; i < numWorkers; i++) await imported.dequeue();
242
247
  lc.info?.(`Started ${numWorkers} workers to copy ${numTables} tables`);
243
248
  if (parseInt(process.versions.node) < 22) lc.warn?.("\n\n\nOlder versions of Node have a bug that results in an unresponsive\nPostgres connection after running certain combinations of COPY commands.\nIf initial sync hangs, run zero-cache with Node v22+. This has the additional\nbenefit of being consistent with the Node version run in the production container image.\n\n\n");
244
249
  return tableCopiers;
@@ -246,47 +251,20 @@ function startTableCopyWorkers(lc, db, snapshot, numWorkers, numTables) {
246
251
  /**
247
252
  * Shadow-mode alternative to `createReplicationSlot`: opens a dedicated
248
253
  * READ ONLY REPEATABLE READ transaction on a normal connection, exports the
249
- * snapshot and captures the current WAL LSN, then holds the transaction
250
- * open until `release()` is called. The held transaction keeps the snapshot
251
- * importable by the table-copy workers for the duration of the COPY phase.
252
- *
253
- * Idle-in-transaction timeout is disabled locally so the exporter doesn't
254
- * get killed while workers are still importing.
254
+ * snapshot and captures the current WAL LSN. Note that the LSN and snapshot
255
+ * are not guaranteed to be consistent but it this is fine for shadow sync
256
+ * where the LSN is not actually relied on for anything.
255
257
  */
256
- async function acquireExportedSnapshotForShadowSync(lc, upstreamURI) {
257
- const holder = await connectPgClient(lc, upstreamURI, "shadow-initial-sync-snapshot", { max: 1 });
258
- const ready = resolver();
259
- const release = resolver();
260
- const held = holder.begin(READONLY, async (tx) => {
261
- await tx`SET LOCAL idle_in_transaction_session_timeout = 0`.execute();
262
- const [row] = await tx`
258
+ async function acquireExportedSnapshotForShadowSync(lc, upstreamURI, captureSnapshot) {
259
+ return (await connectPgClient(lc, upstreamURI, "shadow-initial-sync-snapshot", { max: 1 })).begin(READONLY, async (tx) => {
260
+ const [{ lsn, snapshot }] = await tx`
263
261
  SELECT pg_export_snapshot() AS snapshot,
264
262
  pg_current_wal_lsn()::text AS lsn`;
265
- ready.resolve(row);
266
- await release.promise;
267
- }).catch((e) => ready.reject(e));
268
- let snapshot;
269
- let lsn;
270
- try {
271
- ({snapshot, lsn} = await ready.promise);
272
- } catch (e) {
273
- await holder.end().catch((err) => lc.warn?.(`Error ending shadow snapshot holder after failure`, err));
274
- throw e;
275
- }
276
- lc.info?.(`Exported snapshot ${snapshot} at LSN ${lsn} (shadow initial sync)`);
277
- return {
278
- snapshot,
279
- lsn,
280
- release: async () => {
281
- release.resolve();
282
- try {
283
- await held;
284
- } catch (e) {
285
- lc.warn?.(`snapshot holder transaction ended with error`, e);
286
- }
287
- await holder.end();
288
- }
289
- };
263
+ return {
264
+ lsn,
265
+ result: await captureSnapshot(snapshot)
266
+ };
267
+ });
290
268
  }
291
269
  function createLiteTables(tx, tables, initialVersion) {
292
270
  const columnMetadata = must(ColumnMetadataStore.getInstance(tx));
@@ -1 +1 @@
1
- {"version":3,"file":"initial-sync.js","names":[],"sources":["../../../../../../../zero-cache/src/services/change-source/pg/initial-sync.ts"],"sourcesContent":["import {mkdtemp, rm} from 'node:fs/promises';\nimport {platform, tmpdir} from 'node:os';\nimport {join} from 'node:path';\nimport {Writable} from 'node:stream';\nimport {pipeline} from 'node:stream/promises';\nimport type {LogContext} from '@rocicorp/logger';\nimport {resolver} from '@rocicorp/resolver';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport type {JSONObject} from '../../../../../shared/src/bigint-json.ts';\nimport {must} from '../../../../../shared/src/must.ts';\nimport {equals} from '../../../../../shared/src/set-utils.ts';\nimport type {DownloadStatus} from '../../../../../zero-events/src/status.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {\n createLiteIndexStatement,\n createLiteTableStatement,\n} from '../../../db/create.ts';\nimport {listIndexes, listTables} from '../../../db/lite-tables.ts';\nimport * as Mode from '../../../db/mode-enum.ts';\nimport {\n BinaryCopyParser,\n hasBinaryDecoder,\n makeBinaryDecoder,\n textCastDecoder,\n} from '../../../db/pg-copy-binary.ts';\nimport {TsvParser} from '../../../db/pg-copy.ts';\nimport {\n mapPostgresToLite,\n mapPostgresToLiteIndex,\n} from '../../../db/pg-to-lite.ts';\nimport {getTypeParsers} from '../../../db/pg-type-parser.ts';\nimport {runTx} from '../../../db/run-transaction.ts';\nimport type {IndexSpec, PublishedTableSpec} from '../../../db/specs.ts';\nimport {importSnapshot, TransactionPool} from '../../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n} from '../../../observability/metrics.ts';\nimport {\n JSON_STRINGIFIED,\n liteValue,\n type LiteValueType,\n} from '../../../types/lite.ts';\nimport {liteTableName} from '../../../types/names.ts';\nimport {PG_15, PG_17} from '../../../types/pg-versions.ts';\nimport {\n connectPgClient,\n pgClient,\n type PostgresDB,\n type PostgresTransaction,\n type PostgresValueType,\n} from '../../../types/pg.ts';\nimport {CpuProfiler} from '../../../types/profiler.ts';\nimport type {ShardConfig} from '../../../types/shards.ts';\nimport {ALLOWED_APP_ID_CHARACTERS} from '../../../types/shards.ts';\nimport {id} from '../../../types/sql.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {ColumnMetadataStore} from '../../replicator/schema/column-metadata.ts';\nimport {initReplicationState} from '../../replicator/schema/replication-state.ts';\nimport {toStateVersionString} from './lsn.ts';\nimport {createReplicaAndSlot} from './replication-slots.ts';\nimport {ensureShardSchema} from './schema/init.ts';\nimport {getPublicationInfo} from './schema/published.ts';\nimport {\n dropShard,\n getInternalShardConfig,\n getReplicaState,\n initReplica,\n validatePublications,\n type ReplicaState,\n} from './schema/shard.ts';\n\nexport type InitialSyncOptions = {\n tableCopyWorkers: number;\n profileCopy?: boolean | undefined;\n textCopy?: boolean | undefined;\n replicationSlotFailover?: boolean | undefined;\n /**\n * When set, run initial sync in \"shadow\" mode for verification: skip all\n * upstream mutations (no replication slot, no addReplica, no dropShard, no\n * slot drop on failure), suppress status events, and optionally sample\n * rows from each table via TABLESAMPLE BERNOULLI + LIMIT. The caller is\n * responsible for providing (and discarding) a throwaway SQLite `tx`.\n */\n shadow?:\n | {\n /** 0 < rate <= 1. When 1, no TABLESAMPLE clause is added. */\n sampleRate: number;\n /**\n * LIMIT N cap appended after TABLESAMPLE. Required: shadow sync is\n * for verification only, so every run must commit to a row budget.\n */\n maxRowsPerTable: number;\n }\n | undefined;\n};\n\nexport type ReplicaOptions = {\n backupV5: boolean;\n};\n\n/** Server context to store with the initial sync metadata for debugging. */\nexport type ServerContext = JSONObject;\n\n/**\n * @returns The {@link ReplicaState} of the initialized replica, or `undefined`\n * for a shadow sync.\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n syncOptions: InitialSyncOptions,\n context: ServerContext,\n {backupV5}: ReplicaOptions = {backupV5: true},\n): Promise<ReplicaState | undefined> {\n if (!ALLOWED_APP_ID_CHARACTERS.test(shard.appID)) {\n throw new Error(\n 'The App ID may only consist of lower-case letters, numbers, and the underscore character',\n );\n }\n const {\n tableCopyWorkers,\n profileCopy,\n textCopy = false,\n replicationSlotFailover = false,\n shadow,\n } = syncOptions;\n const syncMode: InitialSyncMode = shadow ? 'shadow' : 'initial';\n const copyFormat: CopyFormat = textCopy ? 'text' : 'binary';\n const start = performance.now();\n let sql: PostgresDB | undefined;\n let replicationSession: PostgresDB | undefined;\n const replicaID = Date.now().toString();\n let slotName: string | undefined; // undefined === shadow\n const statusPublisher = ReplicationStatusPublisher.forRunningTransaction(\n tx,\n shadow ? async () => {} : undefined,\n ).publish(lc, 'Initializing');\n let releaseShadowSnapshot: (() => Promise<void>) | undefined;\n try {\n const copyProfiler = profileCopy ? await CpuProfiler.connect() : null;\n sql = await connectPgClient(lc, upstreamURI, 'initial-sync');\n // Replication session is only needed to create a replication slot in the\n // real path. In shadow mode we export a snapshot on a normal connection\n // instead, so no replication session is opened.\n replicationSession = shadow\n ? undefined\n : pgClient(lc, upstreamURI, 'initial-sync-replication-session', {\n ['fetch_types']: false, // Necessary for the streaming protocol\n connection: {replication: 'database'}, // https://www.postgresql.org/docs/current/protocol-replication.html\n });\n\n const pgVersion = await checkUpstreamConfig(sql);\n\n // In shadow mode we assume the shard is already initialized and just\n // read back the existing publications. `ensurePublishedTables` would\n // otherwise run DDL and potentially call `dropShard`, which must never\n // happen during a shadow run.\n const {publications} = shadow\n ? await getInternalShardConfig(sql, shard)\n : await ensurePublishedTables(lc, sql, shard);\n lc.info?.(`Upstream is setup with publications [${publications}]`);\n\n const {database, host} = sql.options;\n lc.info?.(\n shadow\n ? `acquiring exported snapshot on ${database}@${host} (shadow mode)`\n : `opening replication session to ${database}@${host}`,\n );\n\n let snapshot: string;\n let lsn: string;\n\n if (shadow) {\n const acquired = await acquireExportedSnapshotForShadowSync(\n lc,\n upstreamURI,\n );\n snapshot = acquired.snapshot;\n lsn = acquired.lsn;\n releaseShadowSnapshot = acquired.release;\n } else {\n const slot = await createReplicaAndSlot(\n lc,\n sql,\n must(replicationSession),\n shard,\n replicaID,\n replicationSlotFailover && pgVersion >= PG_17,\n {\n // When backing up with litestream v5, a unique backup path is\n // required since the LTX format does not tolerate multiple writers\n // (and there are no v3 \"generations\" to workaround it).\n backupPath: backupV5 ? replicaID : null,\n backupV5,\n },\n );\n snapshot = slot.snapshot_name;\n lsn = slot.consistent_point;\n slotName = slot.slot_name;\n }\n\n const initialVersion = toStateVersionString(lsn);\n\n initReplicationState(tx, publications, initialVersion, context);\n\n // Run up to MAX_WORKERS to copy of tables at the replication slot's snapshot.\n // Retrieve the published schema at the consistent_point.\n const published = await runTx(\n sql,\n async tx => {\n await tx.unsafe(/* sql*/ `SET TRANSACTION SNAPSHOT '${snapshot}'`);\n return getPublicationInfo(tx, publications);\n },\n {mode: Mode.READONLY},\n );\n // Note: If this throws, initial-sync is aborted.\n validatePublications(lc, published);\n\n // Now that tables have been validated, kick off the copiers.\n const {tables, indexes} = published;\n const numTables = tables.length;\n if (platform() === 'win32' && tableCopyWorkers < numTables) {\n lc.warn?.(\n `Increasing the number of copy workers from ${tableCopyWorkers} to ` +\n `${numTables} to work around a Node/Postgres connection bug`,\n );\n }\n const numWorkers =\n platform() === 'win32'\n ? numTables\n : Math.min(tableCopyWorkers, numTables);\n\n const copyPool = await connectPgClient(\n lc,\n upstreamURI,\n 'initial-sync-copy-worker',\n {\n max: numWorkers,\n ['max_lifetime']: 120 * 60, // set a long (2h) limit for COPY streaming\n },\n );\n const copiers = startTableCopyWorkers(\n lc,\n copyPool,\n snapshot,\n numWorkers,\n numTables,\n );\n try {\n createLiteTables(tx, tables, initialVersion);\n const sampleRate = shadow?.sampleRate;\n const maxRowsPerTable = shadow?.maxRowsPerTable;\n const downloads = await Promise.all(\n tables.map(spec =>\n copiers.processReadTask((db, lc) =>\n getInitialDownloadState(lc, db, spec, shadow !== undefined),\n ),\n ),\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying ${numTables} upstream tables at version ${initialVersion}`,\n 5000,\n () => ({downloadStatus: downloads.map(({status}) => status)}),\n );\n\n void copyProfiler?.start();\n const copyStart = performance.now();\n const copyResults = await Promise.all(\n downloads.map(table =>\n copiers.processReadTask((db, lc) =>\n copy(\n lc,\n table,\n copyPool,\n db,\n tx,\n textCopy,\n syncMode,\n sampleRate,\n maxRowsPerTable,\n ),\n ),\n ),\n );\n const copyElapsed = performance.now() - copyStart;\n void copyProfiler?.stopAndDispose(lc, 'initial-copy');\n copiers.setDone();\n\n const copySummary = initialSyncCopySummary(copyResults, copyElapsed);\n\n statusPublisher.publish(\n lc,\n 'Indexing',\n `Creating ${indexes.length} indexes`,\n 5000,\n );\n const indexStart = performance.now();\n createLiteIndices(lc, tx, indexes);\n const index = performance.now() - indexStart;\n lc.info?.(`Created indexes (${index.toFixed(3)} ms)`);\n\n if (slotName && replicaID) {\n await initReplica(sql, shard, replicaID, published, context);\n } else {\n assert(shadow, 'expected to be in shadow sync if there is no slotName');\n const rowsByTable = new Map<string, number>();\n for (let i = 0; i < downloads.length; i++) {\n rowsByTable.set(downloads[i].status.table, copyResults[i].rows);\n }\n verifyShadowReplica(lc, tx, published, rowsByTable);\n }\n\n const elapsed = performance.now() - start;\n const copyOtherMs = Math.max(0, elapsed - copySummary.flushMs - index);\n recordInitialSyncRunMetrics(\n {\n durationMs: elapsed,\n rows: copySummary.rows,\n copyBytes: copySummary.copyBytes,\n copyMs: copySummary.copyMs,\n copyOtherMs,\n flushMs: copySummary.flushMs,\n indexMs: index,\n },\n {\n result: 'success',\n syncMode,\n copyFormat,\n },\n );\n lc.info?.(\n `Synced ${copySummary.rows.toLocaleString()} rows of ${numTables} tables in ${publications} up to ${lsn} ` +\n `(flush: ${copySummary.flushMs.toFixed(3)}, index: ${index.toFixed(3)}, total: ${elapsed.toFixed(3)} ms)`,\n {\n syncMode,\n copyFormat,\n publications,\n lsn,\n ...copySummary,\n indexes: indexes.length,\n indexMs: index,\n copyOtherMs,\n totalMs: elapsed,\n },\n );\n\n return slotName && replicaID\n ? must(\n await getReplicaState(sql, shard, replicaID),\n `Missing replica with id ${replicaID}`,\n )\n : undefined; // shadow sync only\n } finally {\n // All meaningful errors are handled at the processReadTask() call site.\n void copyPool.end().catch(e => lc.warn?.(`Error closing copyPool`, e));\n }\n } catch (e) {\n recordInitialSyncRunMetrics(\n {durationMs: performance.now() - start},\n {\n result: 'error',\n syncMode,\n copyFormat,\n },\n );\n if (slotName && sql) {\n // If initial-sync did not succeed, make a best effort to drop the\n // orphaned replication slot to avoid running out of slots in\n // pathological cases that result in repeated failures.\n lc.warn?.(`dropping replication slot ${slotName}`, e);\n await sql`\n SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots\n WHERE slot_name = ${slotName};\n `.catch(e => lc.warn?.(`Unable to drop replication slot ${slotName}`, e));\n }\n throw await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n if (releaseShadowSnapshot) {\n await releaseShadowSnapshot().catch(e =>\n lc.warn?.(`Error releasing shadow snapshot`, e),\n );\n }\n if (replicationSession) {\n await replicationSession.end();\n }\n if (sql) {\n await sql.end();\n }\n }\n}\n\nexport type ShadowSyncOptions = {\n sampleRate: number;\n maxRowsPerTable: number;\n /**\n * Parent directory for the throwaway SQLite replica. Defaults to the OS\n * tmpdir. Primarily for tests that need to isolate the scratch directory.\n */\n parentDir?: string | undefined;\n};\n\n/**\n * Exercises the initial-sync code path against a sample of rows from every\n * published table, writing into a throwaway SQLite database that is deleted\n * when the run ends. Produces zero upstream mutations: no replication slot,\n * no `addReplica`, no `dropShard`, no status events.\n *\n * Intended to be invoked periodically so that if a customer ever needs a\n * full reset, we have recent confidence that `initialSync` still works.\n * The shard must already be initialized upstream.\n */\nexport async function shadowInitialSync(\n lc: LogContext,\n shard: ShardConfig,\n upstreamURI: string,\n shadow: ShadowSyncOptions,\n context: ServerContext,\n syncOptions?: Pick<InitialSyncOptions, 'textCopy'>,\n): Promise<void> {\n const dir = await mkdtemp(\n join(shadow.parentDir ?? tmpdir(), 'zero-shadow-sync-'),\n );\n const dbPath = join(dir, 'shadow-replica.db');\n const db = new Database(lc, dbPath);\n try {\n await initialSync(\n lc,\n shard,\n db,\n upstreamURI,\n {\n // Shadow sync copies small samples, so one worker is plenty —\n // no reason to burn additional upstream connections.\n tableCopyWorkers: 1,\n textCopy: syncOptions?.textCopy,\n shadow,\n },\n context,\n );\n } finally {\n try {\n db.close();\n } catch (e) {\n lc.warn?.(`Error closing shadow replica db`, e);\n }\n await rm(dir, {recursive: true, force: true}).catch(e =>\n lc.warn?.(`Error cleaning up shadow replica dir ${dir}`, e),\n );\n }\n}\n\nasync function checkUpstreamConfig(sql: PostgresDB) {\n const {walLevel, version} = (\n await sql<{walLevel: string; version: number}[]>`\n SELECT current_setting('wal_level') as \"walLevel\", \n current_setting('server_version_num') as \"version\";\n `\n )[0];\n\n if (walLevel !== 'logical') {\n throw new Error(\n `Postgres must be configured with \"wal_level = logical\" (currently: \"${walLevel})`,\n );\n }\n if (version < PG_15) {\n throw new Error(\n `Must be running Postgres 15 or higher (currently: \"${version}\")`,\n );\n }\n return version;\n}\n\nasync function ensurePublishedTables(\n lc: LogContext,\n sql: PostgresDB,\n shard: ShardConfig,\n validate = true,\n): Promise<{publications: string[]}> {\n const {database, host} = sql.options;\n lc.info?.(`Ensuring upstream PUBLICATION on ${database}@${host}`);\n\n // Technically, ensureShardSchema() is already called by\n // initializePostgresChangeSource, so the call here is redundant\n // but harmless. On the contrary, it makes initial sync more\n // self-contained (for test setup), and also plays a role in\n // recovering from corruption (below) after the shard is dropped.\n await ensureShardSchema(lc, sql, shard);\n const {publications} = await getInternalShardConfig(sql, shard);\n\n if (validate) {\n let valid = false;\n const nonInternalPublications = publications.filter(\n p => !p.startsWith('_'),\n );\n const exists = await sql`\n SELECT pubname FROM pg_publication WHERE pubname IN ${sql(publications)}\n `.values();\n if (exists.length !== publications.length) {\n lc.warn?.(\n `some configured publications [${publications}] are missing: ` +\n `[${exists.flat()}]. resyncing`,\n );\n } else if (\n !equals(new Set(shard.publications), new Set(nonInternalPublications))\n ) {\n lc.warn?.(\n `requested publications [${shard.publications}] differ from previous` +\n `publications [${nonInternalPublications}]. resyncing`,\n );\n } else {\n valid = true;\n }\n if (!valid) {\n await sql.unsafe(dropShard(shard.appID, shard.shardNum));\n return ensurePublishedTables(lc, sql, shard, false);\n }\n }\n return {publications};\n}\n\nfunction startTableCopyWorkers(\n lc: LogContext,\n db: PostgresDB,\n snapshot: string,\n numWorkers: number,\n numTables: number,\n): TransactionPool {\n const {init} = importSnapshot(snapshot);\n const tableCopiers = new TransactionPool(lc, {\n mode: Mode.READONLY,\n init,\n initialWorkers: numWorkers,\n });\n tableCopiers.run(db);\n\n lc.info?.(`Started ${numWorkers} workers to copy ${numTables} tables`);\n\n if (parseInt(process.versions.node) < 22) {\n lc.warn?.(\n `\\n\\n\\n` +\n `Older versions of Node have a bug that results in an unresponsive\\n` +\n `Postgres connection after running certain combinations of COPY commands.\\n` +\n `If initial sync hangs, run zero-cache with Node v22+. This has the additional\\n` +\n `benefit of being consistent with the Node version run in the production container image.` +\n `\\n\\n\\n`,\n );\n }\n return tableCopiers;\n}\n\n/**\n * Shadow-mode alternative to `createReplicationSlot`: opens a dedicated\n * READ ONLY REPEATABLE READ transaction on a normal connection, exports the\n * snapshot and captures the current WAL LSN, then holds the transaction\n * open until `release()` is called. The held transaction keeps the snapshot\n * importable by the table-copy workers for the duration of the COPY phase.\n *\n * Idle-in-transaction timeout is disabled locally so the exporter doesn't\n * get killed while workers are still importing.\n */\nasync function acquireExportedSnapshotForShadowSync(\n lc: LogContext,\n upstreamURI: string,\n): Promise<{\n snapshot: string;\n lsn: string;\n release: () => Promise<void>;\n}> {\n const holder = await connectPgClient(\n lc,\n upstreamURI,\n 'shadow-initial-sync-snapshot',\n {\n max: 1,\n },\n );\n const ready = resolver<{snapshot: string; lsn: string}>();\n const release = resolver<void>();\n const held = holder\n .begin(Mode.READONLY, async tx => {\n await tx`SET LOCAL idle_in_transaction_session_timeout = 0`.execute();\n const [row] = await tx<{snapshot: string; lsn: string}[]>`\n SELECT pg_export_snapshot() AS snapshot,\n pg_current_wal_lsn()::text AS lsn`;\n ready.resolve(row);\n await release.promise;\n })\n .catch(e => ready.reject(e));\n\n let snapshot: string;\n let lsn: string;\n try {\n ({snapshot, lsn} = await ready.promise);\n } catch (e) {\n await holder\n .end()\n .catch(err =>\n lc.warn?.(`Error ending shadow snapshot holder after failure`, err),\n );\n throw e;\n }\n lc.info?.(\n `Exported snapshot ${snapshot} at LSN ${lsn} (shadow initial sync)`,\n );\n return {\n snapshot,\n lsn,\n release: async () => {\n release.resolve();\n try {\n await held;\n } catch (e) {\n lc.warn?.(`snapshot holder transaction ended with error`, e);\n }\n await holder.end();\n },\n };\n}\n\nfunction createLiteTables(\n tx: Database,\n tables: PublishedTableSpec[],\n initialVersion: string,\n) {\n // TODO: Figure out how to reuse the ChangeProcessor here to avoid\n // duplicating the ColumnMetadata logic.\n const columnMetadata = must(ColumnMetadataStore.getInstance(tx));\n for (const t of tables) {\n tx.exec(createLiteTableStatement(mapPostgresToLite(t, initialVersion)));\n const tableName = liteTableName(t);\n for (const [colName, colSpec] of Object.entries(t.columns)) {\n columnMetadata.insert(tableName, colName, colSpec);\n }\n }\n}\n\nfunction createLiteIndices(lc: LogContext, tx: Database, indices: IndexSpec[]) {\n for (const [i, index] of indices.entries()) {\n const stmt = createLiteIndexStatement(mapPostgresToLiteIndex(index));\n lc.info?.(`Creating index ${i + 1}/${indices.length}: ${stmt}`);\n const start = performance.now();\n tx.exec(stmt);\n lc.info?.(\n `Created index ${i + 1}/${indices.length} ` +\n `(${(performance.now() - start).toFixed(3)} ms): ${stmt}`,\n );\n }\n}\n\n/**\n * Runs structural assertions over a just-synced replica and throws if any\n * fail. Only called in shadow mode — a successful return means the replica\n * is schema-complete, row-count consistent, and its column metadata is in\n * sync with its lite schema.\n *\n * Note: this intentionally does NOT verify ZQL-queryability. Tables that\n * `computeZqlSpecs` drops (no PK / no all-NOT-NULL unique index, unsupported\n * column types, etc.) are silently skipped in production too — there's\n * nothing shadow-specific about them, so failing here would diverge from\n * prod over an upstream-schema condition prod accepts.\n *\n * Exported for testing.\n */\nexport function verifyShadowReplica(\n lc: LogContext,\n db: Database,\n published: {tables: PublishedTableSpec[]; indexes: IndexSpec[]},\n rowsByTable: ReadonlyMap<string, number>,\n): void {\n const issues: string[] = [];\n let columnsChecked = 0;\n let rowsChecked = 0;\n\n // 1. Schema completeness: every published table exists in the replica\n // with at least the expected column set.\n const liteTables = listTables(db);\n const liteTableByName = new Map(liteTables.map(t => [t.name, t]));\n for (const pt of published.tables) {\n const name = liteTableName(pt);\n const lite = liteTableByName.get(name);\n if (!lite) {\n issues.push(`missing table in replica: ${name}`);\n continue;\n }\n for (const col of Object.keys(pt.columns)) {\n columnsChecked++;\n if (!(col in lite.columns)) {\n issues.push(`column missing in replica table ${name}: ${col}`);\n }\n }\n }\n\n // Every published index exists in the replica.\n const liteIndexNames = new Set(listIndexes(db).map(i => i.name));\n for (const ix of published.indexes) {\n const mapped = mapPostgresToLiteIndex(ix);\n if (!liteIndexNames.has(mapped.name)) {\n issues.push(\n `missing index in replica: ${mapped.name} on ${mapped.tableName}`,\n );\n }\n }\n\n // 2. Row counts: SQLite COUNT(*) matches the in-memory copy counter.\n for (const [table, expected] of rowsByTable) {\n try {\n const [row] = db\n .prepare(`SELECT COUNT(*) as count FROM \"${table}\"`)\n .all<{count: number}>();\n if (row.count !== expected) {\n issues.push(\n `row count mismatch for table ${table}: ` +\n `copy counter reported ${expected}, replica has ${row.count}`,\n );\n } else {\n rowsChecked += row.count;\n }\n } catch (e) {\n issues.push(`could not count rows in table ${table}: ${String(e)}`);\n }\n }\n\n // 3. Column metadata: every published column has a _zero.column_metadata row.\n const meta = must(ColumnMetadataStore.getInstance(db));\n for (const pt of published.tables) {\n const name = liteTableName(pt);\n const rows = meta.getTable(name);\n for (const col of Object.keys(pt.columns)) {\n if (!rows.has(col)) {\n issues.push(`missing column_metadata row for ${name}.${col}`);\n }\n }\n }\n\n if (issues.length) {\n throw new Error(\n `Shadow replica verification failed (${issues.length} issue(s)):\\n` +\n issues.map(i => ` - ${i}`).join('\\n'),\n );\n }\n\n lc.info?.(\n `Shadow replica verification passed: ` +\n `${published.tables.length} tables, ` +\n `${published.indexes.length} indexes, ` +\n `${columnsChecked} columns, ` +\n `${rowsChecked.toLocaleString()} rows`,\n );\n}\n\n// Verified empirically that batches of 50 seem to be the sweet spot,\n// similar to the report in https://sqlite.org/forum/forumpost/8878a512d3652655\n//\n// Exported for testing.\nexport const INSERT_BATCH_SIZE = 50;\n\nconst MB = 1024 * 1024;\nconst MAX_BUFFERED_ROWS = 10_000;\nconst BUFFERED_SIZE_THRESHOLD = 8 * MB;\n\nexport type DownloadStatements = {\n select: string;\n getTotalRows: string;\n getTotalBytes: string;\n};\n\n/**\n * Produces ` TABLESAMPLE BERNOULLI(n)` when `sampleRate` is < 1, else `''`.\n * Row-level Bernoulli sampling is used (rather than SYSTEM) because it\n * produces a more uniform sample and, unlike SYSTEM, still returns rows\n * for small tables at low rates.\n */\nfunction tableSampleClause(sampleRate: number | undefined): string {\n if (sampleRate === undefined || sampleRate >= 1) {\n return '';\n }\n // Round away float noise (e.g. 0.3 * 100 = 30.000000000000004) while still\n // preserving sub-integer rates like 0.001 (= 0.1%).\n const pct = parseFloat((sampleRate * 100).toFixed(6));\n return /*sql*/ ` TABLESAMPLE BERNOULLI(${pct})`;\n}\n\nfunction limitClause(maxRowsPerTable: number | undefined): string {\n return maxRowsPerTable !== undefined\n ? /*sql*/ ` LIMIT ${maxRowsPerTable}`\n : '';\n}\n\n/**\n * Returns the SELECT column expressions for binary COPY, casting columns\n * without a known binary decoder to `::text`.\n */\nexport function makeBinarySelectExprs(\n table: PublishedTableSpec,\n cols: string[],\n): string[] {\n return cols.map(col => {\n const spec = table.columns[col];\n return hasBinaryDecoder(spec) ? id(col) : `${id(col)}::text`;\n });\n}\n\nexport function makeDownloadStatements(\n table: PublishedTableSpec,\n cols: string[],\n sampleRate?: number | undefined,\n maxRowsPerTable?: number | undefined,\n selectExprs?: string[] | undefined,\n): DownloadStatements {\n const filterConditions = Object.values(table.publications)\n .map(({rowFilter}) => rowFilter)\n .filter(f => !!f); // remove nulls\n const where =\n filterConditions.length === 0\n ? ''\n : /*sql*/ `WHERE ${filterConditions.join(' OR ')}`;\n const sample = tableSampleClause(sampleRate);\n const limit = limitClause(maxRowsPerTable);\n const fromTable = /*sql*/ `FROM ${id(table.schema)}.${id(table.name)}${sample} ${where}`;\n const select = /*sql*/ `SELECT ${(selectExprs ?? cols.map(id)).join(',')} ${fromTable}${limit}`;\n if (limit) {\n // With LIMIT, wrap counts/sums in a subquery so they reflect the\n // capped rowset rather than the full (sampled) table.\n const bytesExpr = cols\n .map(col => `COALESCE(pg_column_size(${id(col)}), 0)`)\n .join(' + ');\n return {\n select,\n getTotalRows: /*sql*/ `SELECT COUNT(*)::bigint AS \"totalRows\" FROM (SELECT 1 AS _ ${fromTable}${limit}) s`,\n getTotalBytes: /*sql*/ `SELECT COALESCE(SUM(b), 0)::bigint AS \"totalBytes\" FROM (SELECT (${bytesExpr}) AS b ${fromTable}${limit}) s`,\n };\n }\n const totalBytes = `(${cols.map(col => `SUM(COALESCE(pg_column_size(${id(col)}), 0))`).join(' + ')})`;\n return {\n select,\n getTotalRows: /*sql*/ `SELECT COUNT(*) AS \"totalRows\" ${fromTable}`,\n getTotalBytes: /*sql*/ `SELECT ${totalBytes} AS \"totalBytes\" ${fromTable}`,\n };\n}\n\ntype DownloadState = {\n spec: PublishedTableSpec;\n status: DownloadStatus;\n};\n\ntype CopyFormat = 'binary' | 'text';\ntype InitialSyncMode = 'initial' | 'shadow';\n\ntype InitialSyncMetricAttrs = {\n syncMode: InitialSyncMode;\n copyFormat: CopyFormat;\n};\n\ntype InitialSyncRunMetricAttrs = {\n syncMode: InitialSyncMode;\n copyFormat: CopyFormat;\n result: 'success' | 'error';\n};\n\ntype CopyResult = {\n schema: string;\n table: string;\n replicaTable: string;\n syncMode: InitialSyncMode;\n copyFormat: CopyFormat;\n columnCount: number;\n rows: number;\n estimatedRows: number;\n estimatedBytes: number | undefined;\n flushMs: number;\n elapsedMs: number;\n sourceWaitMs: number;\n processingMs: number;\n copyBytes: number;\n};\n\nconst INITIAL_SYNC_DURATION_HISTOGRAM_BOUNDARIES_S = [\n 1, 2, 5, 10, 30, 60, 120, 300, 600, 1200, 2400, 3600, 7200,\n];\nexport const COPY_METRIC_BATCH_BYTES = 8 * 1024 * 1024;\nconst SLOW_COPY_FLUSH_MS = 10_000;\n\n// change-streamer imports this module before startOtelAuto() runs, so create\n// instruments lazily to avoid binding them to OTel's no-op meter provider.\nfunction initialSyncRuns() {\n return getOrCreateCounter(\n 'replication',\n 'initial_sync_runs',\n 'Initial sync runs, labeled by result.',\n );\n}\n\nfunction initialSyncDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_duration',\n 'Wall-clock duration of an initial sync run, labeled by result.',\n );\n}\n\nfunction initialSyncCopyDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_copy_duration',\n 'Wall-clock duration of the COPY phase for a successful initial sync run.',\n );\n}\n\nfunction initialSyncCopyOtherDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_copy_other_duration',\n 'Initial sync total duration excluding SQLite flush and index time for a successful run.',\n );\n}\n\nfunction initialSyncFlushDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_flush_duration',\n 'Total SQLite flush time for a successful initial sync run.',\n );\n}\n\nfunction initialSyncIndexDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_index_duration',\n 'SQLite index creation time for a successful initial sync run.',\n );\n}\n\nfunction initialSyncRows() {\n return getOrCreateCounter(\n 'replication',\n 'initial_sync_rows',\n 'Rows copied during successful initial sync runs.',\n );\n}\n\nfunction initialSyncCopyStream() {\n return getOrCreateCounter('replication', 'initial_sync_copy_stream', {\n description:\n 'PostgreSQL COPY stream bytes processed during initial sync, including in-progress and failed runs.',\n unit: 'bytes',\n });\n}\n\nfunction initialSyncCompletedCopyStream() {\n return getOrCreateCounter(\n 'replication',\n 'initial_sync_completed_copy_stream',\n {\n description:\n 'PostgreSQL COPY stream bytes processed during successful initial sync runs.',\n unit: 'bytes',\n },\n );\n}\n\nfunction initialSyncCopyChunks() {\n return getOrCreateCounter(\n 'replication',\n 'initial_sync_copy_chunks',\n 'PostgreSQL COPY stream chunks processed during initial sync.',\n );\n}\n\nexport function createCopyMetricBatcher(\n record: (bytes: number, chunks: number) => void,\n) {\n let bytes = 0;\n let chunks = 0;\n\n function flush() {\n if (chunks === 0) {\n return;\n }\n record(bytes, chunks);\n bytes = 0;\n chunks = 0;\n }\n\n return {\n add(chunkBytes: number) {\n bytes += chunkBytes;\n chunks++;\n if (bytes >= COPY_METRIC_BATCH_BYTES) {\n flush();\n }\n },\n flush,\n };\n}\n\nexport function initialSyncCopyMetrics(attrs: InitialSyncMetricAttrs) {\n const labels = initialSyncMetricAttrs(attrs);\n const copyStreamMetric = initialSyncCopyStream();\n const copyChunksMetric = initialSyncCopyChunks();\n return createCopyMetricBatcher((bytes, chunks) => {\n copyStreamMetric.add(bytes, labels);\n copyChunksMetric.add(chunks, labels);\n });\n}\n\nfunction initialSyncDurationHistogram(name: string, description: string) {\n return getOrCreateHistogram('replication', name, {\n description,\n unit: 's',\n bucketBoundaries: INITIAL_SYNC_DURATION_HISTOGRAM_BOUNDARIES_S,\n });\n}\n\nfunction initialSyncMetricAttrs(attrs: InitialSyncMetricAttrs) {\n return {\n sync_mode: attrs.syncMode,\n copy_format: attrs.copyFormat,\n };\n}\n\nfunction initialSyncRunMetricAttrs(attrs: InitialSyncRunMetricAttrs) {\n return {\n ...initialSyncMetricAttrs(attrs),\n result: attrs.result,\n };\n}\n\nfunction recordInitialSyncRunMetrics(\n stats: {\n durationMs: number;\n rows?: number | undefined;\n copyBytes?: number | undefined;\n copyMs?: number | undefined;\n copyOtherMs?: number | undefined;\n flushMs?: number | undefined;\n indexMs?: number | undefined;\n },\n attrs: InitialSyncRunMetricAttrs,\n) {\n const labels = initialSyncRunMetricAttrs(attrs);\n initialSyncRuns().add(1, labels);\n initialSyncDuration().recordMs(stats.durationMs, labels);\n if (attrs.result === 'success') {\n if (stats.copyMs !== undefined) {\n initialSyncCopyDuration().recordMs(stats.copyMs, labels);\n }\n if (stats.copyOtherMs !== undefined) {\n initialSyncCopyOtherDuration().recordMs(stats.copyOtherMs, labels);\n }\n if (stats.flushMs !== undefined) {\n initialSyncFlushDuration().recordMs(stats.flushMs, labels);\n }\n if (stats.indexMs !== undefined) {\n initialSyncIndexDuration().recordMs(stats.indexMs, labels);\n }\n if (stats.rows !== undefined && stats.rows > 0) {\n initialSyncRows().add(stats.rows, labels);\n }\n if (stats.copyBytes !== undefined && stats.copyBytes > 0) {\n initialSyncCompletedCopyStream().add(stats.copyBytes, labels);\n }\n }\n}\n\nfunction initialSyncCopySummary(\n results: readonly CopyResult[],\n copyMs: number,\n) {\n const totals = results.reduce(\n (acc, curr) => {\n acc.rows += curr.rows;\n acc.flushMs += curr.flushMs;\n acc.copyBytes += curr.copyBytes;\n return acc;\n },\n {\n rows: 0,\n flushMs: 0,\n copyBytes: 0,\n },\n );\n return {\n tables: results.length,\n rows: totals.rows,\n copyMs,\n flushMs: totals.flushMs,\n copyBytes: totals.copyBytes,\n };\n}\n\nfunction logSlowCopyFlush(\n lc: LogContext,\n details: {\n schema: string;\n table: string;\n replicaTable: string;\n syncMode: InitialSyncMode;\n copyFormat: CopyFormat;\n elapsedMs: number;\n flushedRows: number;\n flushedBytes: number;\n rows: number;\n copyBytes: number;\n },\n) {\n if (details.elapsedMs < SLOW_COPY_FLUSH_MS || details.flushedRows === 0) {\n return;\n }\n lc.info?.('initial-sync table copy slow flush', details);\n}\n\n// Exported for testing.\nexport async function getInitialDownloadState(\n lc: LogContext,\n sql: PostgresDB,\n spec: PublishedTableSpec,\n skipTotals: boolean,\n): Promise<DownloadState> {\n const start = performance.now();\n const table = liteTableName(spec);\n const columns = Object.keys(spec.columns);\n if (skipTotals) {\n // Shadow sync suppresses status events, so the pg_class\n // estimates would be computed and thrown away.\n return {\n spec,\n status: {table, columns, rows: 0, totalRows: 0, totalBytes: 0},\n };\n }\n // Use pg_class estimates instead of expensive COUNT(*) and\n // SUM(pg_column_size(...)) full table scans. The exact values are only\n // used for progress reporting, so estimates are sufficient.\n const qualifiedName = `${id(spec.schema)}.${id(spec.name)}`;\n const estimateResult = await sql<\n {totalRows: number; totalBytes: number}[]\n >`SELECT GREATEST(reltuples, 0)::float8 AS \"totalRows\",\n pg_table_size(oid)::float8 AS \"totalBytes\"\n FROM pg_class\n WHERE oid = ${qualifiedName}::regclass`;\n\n const {totalRows, totalBytes} = estimateResult[0] ?? {\n totalRows: 0,\n totalBytes: 0,\n };\n\n const state: DownloadState = {\n spec,\n status: {\n table,\n columns,\n rows: 0,\n totalRows,\n totalBytes,\n },\n };\n const elapsed = (performance.now() - start).toFixed(3);\n lc.info?.(`Computed initial download state for ${table} (${elapsed} ms)`, {\n state: state.status,\n });\n return state;\n}\n\nfunction copy(\n lc: LogContext,\n {spec: table, status}: DownloadState,\n dbClient: PostgresDB,\n from: PostgresTransaction,\n to: Database,\n textCopy: boolean,\n syncMode: InitialSyncMode,\n sampleRate?: number | undefined,\n maxRowsPerTable?: number | undefined,\n): Promise<CopyResult> {\n if (textCopy) {\n return copyText(\n lc,\n table,\n status,\n dbClient,\n from,\n to,\n syncMode,\n sampleRate,\n maxRowsPerTable,\n );\n }\n return copyBinary(\n lc,\n table,\n status,\n from,\n to,\n syncMode,\n sampleRate,\n maxRowsPerTable,\n );\n}\n\nasync function copyBinary(\n lc: LogContext,\n table: PublishedTableSpec,\n status: DownloadStatus,\n from: PostgresTransaction,\n to: Database,\n syncMode: InitialSyncMode,\n sampleRate?: number | undefined,\n maxRowsPerTable?: number | undefined,\n): Promise<CopyResult> {\n const start = performance.now();\n const copyFormat: CopyFormat = 'binary';\n const copyMetrics = initialSyncCopyMetrics({syncMode, copyFormat});\n let flushMs = 0;\n let copyBytes = 0;\n let processingMs = 0;\n\n const tableName = liteTableName(table);\n const orderedColumns = Object.entries(table.columns);\n\n const columnNames = orderedColumns.map(([c]) => c);\n const columnSpecs = orderedColumns.map(([_name, spec]) => spec);\n const insertColumnList = columnNames.map(c => id(c)).join(',');\n\n const valuesSql =\n columnNames.length > 0 ? `(${'?,'.repeat(columnNames.length - 1)}?)` : '()';\n const insertSql = /*sql*/ `\n INSERT INTO \"${tableName}\" (${insertColumnList}) VALUES ${valuesSql}`;\n const insertStmt = to.prepare(insertSql);\n const insertBatchStmt = to.prepare(\n insertSql + `,${valuesSql}`.repeat(INSERT_BATCH_SIZE - 1),\n );\n\n // Build SELECT with ::text casts for columns without a known binary decoder.\n const select = makeDownloadStatements(\n table,\n columnNames,\n sampleRate,\n maxRowsPerTable,\n makeBinarySelectExprs(table, columnNames),\n ).select;\n\n const decoders = orderedColumns.map(([, spec]) =>\n hasBinaryDecoder(spec) ? makeBinaryDecoder(spec) : textCastDecoder,\n );\n\n const valuesPerRow = columnSpecs.length;\n const valuesPerBatch = valuesPerRow * INSERT_BATCH_SIZE;\n\n const pendingValues: LiteValueType[] = Array.from({\n length: MAX_BUFFERED_ROWS * valuesPerRow,\n });\n let pendingRows = 0;\n let pendingSize = 0;\n\n function flush() {\n const flushStart = performance.now();\n const flushedRows = pendingRows;\n const flushedSize = pendingSize;\n\n let l = 0;\n for (; pendingRows > INSERT_BATCH_SIZE; pendingRows -= INSERT_BATCH_SIZE) {\n insertBatchStmt.run(pendingValues.slice(l, (l += valuesPerBatch)));\n }\n for (; pendingRows > 0; pendingRows--) {\n insertStmt.run(pendingValues.slice(l, (l += valuesPerRow)));\n }\n const flushedValues = flushedRows * valuesPerRow;\n for (let i = 0; i < flushedValues; i++) {\n pendingValues[i] = undefined as unknown as LiteValueType;\n }\n pendingSize = 0;\n status.rows += flushedRows;\n\n const elapsed = performance.now() - flushStart;\n flushMs += elapsed;\n lc.debug?.(\n `flushed ${flushedRows} ${tableName} rows (${flushedSize} bytes) in ${elapsed.toFixed(3)} ms`,\n );\n logSlowCopyFlush(lc, {\n schema: table.schema,\n table: table.name,\n replicaTable: tableName,\n syncMode,\n copyFormat,\n elapsedMs: elapsed,\n flushedRows,\n flushedBytes: flushedSize,\n rows: status.rows,\n copyBytes,\n });\n }\n\n const binaryParser = new BinaryCopyParser();\n let col = 0;\n\n lc.info?.(`Starting binary copy stream of ${tableName}:`, select);\n\n const copyStreamStart = performance.now();\n await pipeline(\n await from\n .unsafe(`COPY (${select}) TO STDOUT WITH (FORMAT binary)`)\n .readable(),\n new Writable({\n highWaterMark: BUFFERED_SIZE_THRESHOLD,\n\n write(\n chunk: Buffer,\n _encoding: string,\n callback: (error?: Error) => void,\n ) {\n const processingStart = performance.now();\n try {\n copyBytes += chunk.length;\n copyMetrics.add(chunk.length);\n for (const fieldBuf of binaryParser.parse(chunk)) {\n const fieldSize = fieldBuf === null ? 4 : fieldBuf.length;\n pendingSize += fieldSize;\n pendingValues[pendingRows * valuesPerRow + col] =\n fieldBuf === null ? null : decoders[col](fieldBuf);\n\n if (++col === decoders.length) {\n col = 0;\n if (\n ++pendingRows >= MAX_BUFFERED_ROWS - valuesPerRow ||\n pendingSize >= BUFFERED_SIZE_THRESHOLD\n ) {\n flush();\n }\n }\n }\n processingMs += performance.now() - processingStart;\n callback();\n } catch (e) {\n processingMs += performance.now() - processingStart;\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n },\n\n final: (callback: (error?: Error) => void) => {\n const processingStart = performance.now();\n try {\n copyMetrics.flush();\n flush();\n processingMs += performance.now() - processingStart;\n callback();\n } catch (e) {\n processingMs += performance.now() - processingStart;\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n },\n\n destroy(error, callback) {\n copyMetrics.flush();\n callback(error);\n },\n }),\n );\n\n const sourceWaitMs = Math.max(\n 0,\n performance.now() - copyStreamStart - processingMs,\n );\n const elapsed = performance.now() - start;\n const result = {\n schema: table.schema,\n table: table.name,\n replicaTable: tableName,\n syncMode,\n copyFormat,\n columnCount: columnNames.length,\n rows: status.rows,\n estimatedRows: status.totalRows,\n estimatedBytes: status.totalBytes,\n flushMs,\n elapsedMs: elapsed,\n sourceWaitMs,\n processingMs,\n copyBytes,\n } satisfies CopyResult;\n\n lc.info?.(\n `Finished copying ${status.rows} rows into ${tableName} ` +\n `(flush: ${flushMs.toFixed(3)} ms) (total: ${elapsed.toFixed(3)} ms) `,\n result,\n );\n return result;\n}\n\nasync function copyText(\n lc: LogContext,\n table: PublishedTableSpec,\n status: DownloadStatus,\n dbClient: PostgresDB,\n from: PostgresTransaction,\n to: Database,\n syncMode: InitialSyncMode,\n sampleRate?: number | undefined,\n maxRowsPerTable?: number | undefined,\n): Promise<CopyResult> {\n const start = performance.now();\n const copyFormat: CopyFormat = 'text';\n const copyMetrics = initialSyncCopyMetrics({syncMode, copyFormat});\n let flushMs = 0;\n let copyBytes = 0;\n let processingMs = 0;\n\n const tableName = liteTableName(table);\n const orderedColumns = Object.entries(table.columns);\n\n const columnNames = orderedColumns.map(([c]) => c);\n const columnSpecs = orderedColumns.map(([_name, spec]) => spec);\n const insertColumnList = columnNames.map(c => id(c)).join(',');\n\n const valuesSql =\n columnNames.length > 0 ? `(${'?,'.repeat(columnNames.length - 1)}?)` : '()';\n const insertSql = /*sql*/ `\n INSERT INTO \"${tableName}\" (${insertColumnList}) VALUES ${valuesSql}`;\n const insertStmt = to.prepare(insertSql);\n const insertBatchStmt = to.prepare(\n insertSql + `,${valuesSql}`.repeat(INSERT_BATCH_SIZE - 1),\n );\n\n const {select} = makeDownloadStatements(\n table,\n columnNames,\n sampleRate,\n maxRowsPerTable,\n );\n const valuesPerRow = columnSpecs.length;\n const valuesPerBatch = valuesPerRow * INSERT_BATCH_SIZE;\n\n const pendingValues: LiteValueType[] = Array.from({\n length: MAX_BUFFERED_ROWS * valuesPerRow,\n });\n let pendingRows = 0;\n let pendingSize = 0;\n\n function flush() {\n const flushStart = performance.now();\n const flushedRows = pendingRows;\n const flushedSize = pendingSize;\n\n let l = 0;\n for (; pendingRows > INSERT_BATCH_SIZE; pendingRows -= INSERT_BATCH_SIZE) {\n insertBatchStmt.run(pendingValues.slice(l, (l += valuesPerBatch)));\n }\n for (; pendingRows > 0; pendingRows--) {\n insertStmt.run(pendingValues.slice(l, (l += valuesPerRow)));\n }\n const flushedValues = flushedRows * valuesPerRow;\n for (let i = 0; i < flushedValues; i++) {\n pendingValues[i] = undefined as unknown as LiteValueType;\n }\n pendingSize = 0;\n status.rows += flushedRows;\n\n const elapsed = performance.now() - flushStart;\n flushMs += elapsed;\n lc.debug?.(\n `flushed ${flushedRows} ${tableName} rows (${flushedSize} bytes) in ${elapsed.toFixed(3)} ms`,\n );\n logSlowCopyFlush(lc, {\n schema: table.schema,\n table: table.name,\n replicaTable: tableName,\n syncMode,\n copyFormat,\n elapsedMs: elapsed,\n flushedRows,\n flushedBytes: flushedSize,\n rows: status.rows,\n copyBytes,\n });\n }\n\n lc.info?.(`Starting text copy stream of ${tableName}:`, select);\n const pgParsers = await getTypeParsers(dbClient, {returnJsonAsString: true});\n const parsers = columnSpecs.map(c => {\n const pgParse = pgParsers.getTypeParser(c.typeOID);\n return (val: string) =>\n liteValue(\n pgParse(val) as PostgresValueType,\n c.dataType,\n JSON_STRINGIFIED,\n );\n });\n\n const tsvParser = new TsvParser();\n let col = 0;\n\n const copyStreamStart = performance.now();\n await pipeline(\n await from.unsafe(`COPY (${select}) TO STDOUT`).readable(),\n new Writable({\n highWaterMark: BUFFERED_SIZE_THRESHOLD,\n\n write(\n chunk: Buffer,\n _encoding: string,\n callback: (error?: Error) => void,\n ) {\n const processingStart = performance.now();\n try {\n copyBytes += chunk.length;\n copyMetrics.add(chunk.length);\n for (const text of tsvParser.parse(chunk)) {\n const fieldSize = text === null ? 4 : text.length;\n pendingSize += fieldSize;\n pendingValues[pendingRows * valuesPerRow + col] =\n text === null ? null : parsers[col](text);\n\n if (++col === parsers.length) {\n col = 0;\n if (\n ++pendingRows >= MAX_BUFFERED_ROWS - valuesPerRow ||\n pendingSize >= BUFFERED_SIZE_THRESHOLD\n ) {\n flush();\n }\n }\n }\n processingMs += performance.now() - processingStart;\n callback();\n } catch (e) {\n processingMs += performance.now() - processingStart;\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n },\n\n final: (callback: (error?: Error) => void) => {\n const processingStart = performance.now();\n try {\n copyMetrics.flush();\n flush();\n processingMs += performance.now() - processingStart;\n callback();\n } catch (e) {\n processingMs += performance.now() - processingStart;\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n },\n\n destroy(error, callback) {\n copyMetrics.flush();\n callback(error);\n },\n }),\n );\n\n const sourceWaitMs = Math.max(\n 0,\n performance.now() - copyStreamStart - processingMs,\n );\n const elapsed = performance.now() - start;\n const result = {\n schema: table.schema,\n table: table.name,\n replicaTable: tableName,\n syncMode,\n copyFormat,\n columnCount: columnNames.length,\n rows: status.rows,\n estimatedRows: status.totalRows,\n estimatedBytes: status.totalBytes,\n flushMs,\n elapsedMs: elapsed,\n sourceWaitMs,\n processingMs,\n copyBytes,\n } satisfies CopyResult;\n lc.info?.(\n `Finished copying ${status.rows} rows into ${tableName} ` +\n `(flush: ${flushMs.toFixed(3)} ms) (total: ${elapsed.toFixed(3)} ms) `,\n result,\n );\n return result;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4GA,eAAsB,YACpB,IACA,OACA,IACA,aACA,aACA,SACA,EAAC,aAA4B,EAAC,UAAU,KAAI,GACT;CACnC,IAAI,CAAC,0BAA0B,KAAK,MAAM,KAAK,GAC7C,MAAM,IAAI,MACR,0FACF;CAEF,MAAM,EACJ,kBACA,aACA,WAAW,OACX,0BAA0B,OAC1B,WACE;CACJ,MAAM,WAA4B,SAAS,WAAW;CACtD,MAAM,aAAyB,WAAW,SAAS;CACnD,MAAM,QAAQ,YAAY,IAAI;CAC9B,IAAI;CACJ,IAAI;CACJ,MAAM,YAAY,KAAK,IAAI,EAAE,SAAS;CACtC,IAAI;CACJ,MAAM,kBAAkB,2BAA2B,sBACjD,IACA,SAAS,YAAY,CAAC,IAAI,KAAA,CAC5B,EAAE,QAAQ,IAAI,cAAc;CAC5B,IAAI;CACJ,IAAI;EACF,MAAM,eAAe,cAAc,MAAM,YAAY,QAAQ,IAAI;EACjE,MAAM,MAAM,gBAAgB,IAAI,aAAa,cAAc;EAI3D,qBAAqB,SACjB,KAAA,IACA,SAAS,IAAI,aAAa,oCAAoC;IAC3D,gBAAgB;GACjB,YAAY,EAAC,aAAa,WAAU;EACtC,CAAC;EAEL,MAAM,YAAY,MAAM,oBAAoB,GAAG;EAM/C,MAAM,EAAC,iBAAgB,SACnB,MAAM,uBAAuB,KAAK,KAAK,IACvC,MAAM,sBAAsB,IAAI,KAAK,KAAK;EAC9C,GAAG,OAAO,wCAAwC,aAAa,EAAE;EAEjE,MAAM,EAAC,UAAU,SAAQ,IAAI;EAC7B,GAAG,OACD,SACI,kCAAkC,SAAS,GAAG,KAAK,kBACnD,kCAAkC,SAAS,GAAG,MACpD;EAEA,IAAI;EACJ,IAAI;EAEJ,IAAI,QAAQ;GACV,MAAM,WAAW,MAAM,qCACrB,IACA,WACF;GACA,WAAW,SAAS;GACpB,MAAM,SAAS;GACf,wBAAwB,SAAS;EACnC,OAAO;GACL,MAAM,OAAO,MAAM,qBACjB,IACA,KACA,KAAK,kBAAkB,GACvB,OACA,WACA,2BAA2B,aAAA,MAC3B;IAIE,YAAY,WAAW,YAAY;IACnC;GACF,CACF;GACA,WAAW,KAAK;GAChB,MAAM,KAAK;GACX,WAAW,KAAK;EAClB;EAEA,MAAM,iBAAiB,qBAAqB,GAAG;EAE/C,qBAAqB,IAAI,cAAc,gBAAgB,OAAO;EAI9D,MAAM,YAAY,MAAM,MACtB,KACA,OAAM,OAAM;GACV,MAAM,GAAG,OAAgB,6BAA6B,SAAS,EAAE;GACjE,OAAO,mBAAmB,IAAI,YAAY;EAC5C,GACA,EAAC,MAAM,SAAa,CACtB;EAEA,qBAAqB,IAAI,SAAS;EAGlC,MAAM,EAAC,QAAQ,YAAW;EAC1B,MAAM,YAAY,OAAO;EACzB,IAAI,SAAS,MAAM,WAAW,mBAAmB,WAC/C,GAAG,OACD,8CAA8C,iBAAiB,MAC1D,UAAU,+CACjB;EAEF,MAAM,aACJ,SAAS,MAAM,UACX,YACA,KAAK,IAAI,kBAAkB,SAAS;EAE1C,MAAM,WAAW,MAAM,gBACrB,IACA,aACA,4BACA;GACE,KAAK;IACJ,iBAAiB;EACpB,CACF;EACA,MAAM,UAAU,sBACd,IACA,UACA,UACA,YACA,SACF;EACA,IAAI;GACF,iBAAiB,IAAI,QAAQ,cAAc;GAC3C,MAAM,aAAa,QAAQ;GAC3B,MAAM,kBAAkB,QAAQ;GAChC,MAAM,YAAY,MAAM,QAAQ,IAC9B,OAAO,KAAI,SACT,QAAQ,iBAAiB,IAAI,OAC3B,wBAAwB,IAAI,IAAI,MAAM,WAAW,KAAA,CAAS,CAC5D,CACF,CACF;GACA,gBAAgB,QACd,IACA,gBACA,WAAW,UAAU,8BAA8B,kBACnD,YACO,EAAC,gBAAgB,UAAU,KAAK,EAAC,aAAY,MAAM,EAAC,EAC7D;GAEA,cAAmB,MAAM;GACzB,MAAM,YAAY,YAAY,IAAI;GAClC,MAAM,cAAc,MAAM,QAAQ,IAChC,UAAU,KAAI,UACZ,QAAQ,iBAAiB,IAAI,OAC3B,KACE,IACA,OACA,UACA,IACA,IACA,UACA,UACA,YACA,eACF,CACF,CACF,CACF;GACA,MAAM,cAAc,YAAY,IAAI,IAAI;GACxC,cAAmB,eAAe,IAAI,cAAc;GACpD,QAAQ,QAAQ;GAEhB,MAAM,cAAc,uBAAuB,aAAa,WAAW;GAEnE,gBAAgB,QACd,IACA,YACA,YAAY,QAAQ,OAAO,WAC3B,GACF;GACA,MAAM,aAAa,YAAY,IAAI;GACnC,kBAAkB,IAAI,IAAI,OAAO;GACjC,MAAM,QAAQ,YAAY,IAAI,IAAI;GAClC,GAAG,OAAO,oBAAoB,MAAM,QAAQ,CAAC,EAAE,KAAK;GAEpD,IAAI,YAAY,WACd,MAAM,YAAY,KAAK,OAAO,WAAW,WAAW,OAAO;QACtD;IACL,OAAO,QAAQ,uDAAuD;IACtE,MAAM,8BAAc,IAAI,IAAoB;IAC5C,KAAK,IAAI,IAAI,GAAG,IAAI,UAAU,QAAQ,KACpC,YAAY,IAAI,UAAU,GAAG,OAAO,OAAO,YAAY,GAAG,IAAI;IAEhE,oBAAoB,IAAI,IAAI,WAAW,WAAW;GACpD;GAEA,MAAM,UAAU,YAAY,IAAI,IAAI;GACpC,MAAM,cAAc,KAAK,IAAI,GAAG,UAAU,YAAY,UAAU,KAAK;GACrE,4BACE;IACE,YAAY;IACZ,MAAM,YAAY;IAClB,WAAW,YAAY;IACvB,QAAQ,YAAY;IACpB;IACA,SAAS,YAAY;IACrB,SAAS;GACX,GACA;IACE,QAAQ;IACR;IACA;GACF,CACF;GACA,GAAG,OACD,UAAU,YAAY,KAAK,eAAe,EAAE,WAAW,UAAU,aAAa,aAAa,SAAS,IAAI,WAC3F,YAAY,QAAQ,QAAQ,CAAC,EAAE,WAAW,MAAM,QAAQ,CAAC,EAAE,WAAW,QAAQ,QAAQ,CAAC,EAAE,OACtG;IACE;IACA;IACA;IACA;IACA,GAAG;IACH,SAAS,QAAQ;IACjB,SAAS;IACT;IACA,SAAS;GACX,CACF;GAEA,OAAO,YAAY,YACf,KACE,MAAM,gBAAgB,KAAK,OAAO,SAAS,GAC3C,2BAA2B,WAC7B,IACA,KAAA;EACN,UAAU;GAER,SAAc,IAAI,EAAE,OAAM,MAAK,GAAG,OAAO,0BAA0B,CAAC,CAAC;EACvE;CACF,SAAS,GAAG;EACV,4BACE,EAAC,YAAY,YAAY,IAAI,IAAI,MAAK,GACtC;GACE,QAAQ;GACR;GACA;EACF,CACF;EACA,IAAI,YAAY,KAAK;GAInB,GAAG,OAAO,6BAA6B,YAAY,CAAC;GACpD,MAAM,GAAG;;8BAEe,SAAS;QAC/B,OAAM,MAAK,GAAG,OAAO,mCAAmC,YAAY,CAAC,CAAC;EAC1E;EACA,MAAM,MAAM,gBAAgB,qBAAqB,IAAI,gBAAgB,CAAC;CACxE,UAAU;EACR,gBAAgB,KAAK;EACrB,IAAI,uBACF,MAAM,sBAAsB,EAAE,OAAM,MAClC,GAAG,OAAO,mCAAmC,CAAC,CAChD;EAEF,IAAI,oBACF,MAAM,mBAAmB,IAAI;EAE/B,IAAI,KACF,MAAM,IAAI,IAAI;CAElB;AACF;;;;;;;;;;;AAsBA,eAAsB,kBACpB,IACA,OACA,aACA,QACA,SACA,aACe;CACf,MAAM,MAAM,MAAM,QAChB,KAAK,OAAO,aAAa,OAAO,GAAG,mBAAmB,CACxD;CAEA,MAAM,KAAK,IAAI,SAAS,IADT,KAAK,KAAK,mBACG,CAAM;CAClC,IAAI;EACF,MAAM,YACJ,IACA,OACA,IACA,aACA;GAGE,kBAAkB;GAClB,UAAU,aAAa;GACvB;EACF,GACA,OACF;CACF,UAAU;EACR,IAAI;GACF,GAAG,MAAM;EACX,SAAS,GAAG;GACV,GAAG,OAAO,mCAAmC,CAAC;EAChD;EACA,MAAM,GAAG,KAAK;GAAC,WAAW;GAAM,OAAO;EAAI,CAAC,EAAE,OAAM,MAClD,GAAG,OAAO,wCAAwC,OAAO,CAAC,CAC5D;CACF;AACF;AAEA,eAAe,oBAAoB,KAAiB;CAClD,MAAM,EAAC,UAAU,aACf,MAAM,GAA0C;;;KAIhD;CAEF,IAAI,aAAa,WACf,MAAM,IAAI,MACR,uEAAuE,SAAS,EAClF;CAEF,IAAI,UAAA,MACF,MAAM,IAAI,MACR,sDAAsD,QAAQ,GAChE;CAEF,OAAO;AACT;AAEA,eAAe,sBACb,IACA,KACA,OACA,WAAW,MACwB;CACnC,MAAM,EAAC,UAAU,SAAQ,IAAI;CAC7B,GAAG,OAAO,oCAAoC,SAAS,GAAG,MAAM;CAOhE,MAAM,kBAAkB,IAAI,KAAK,KAAK;CACtC,MAAM,EAAC,iBAAgB,MAAM,uBAAuB,KAAK,KAAK;CAE9D,IAAI,UAAU;EACZ,IAAI,QAAQ;EACZ,MAAM,0BAA0B,aAAa,QAC3C,MAAK,CAAC,EAAE,WAAW,GAAG,CACxB;EACA,MAAM,SAAS,MAAM,GAAG;4DACgC,IAAI,YAAY,EAAE;QACtE,OAAO;EACX,IAAI,OAAO,WAAW,aAAa,QACjC,GAAG,OACD,iCAAiC,aAAa,kBACxC,OAAO,KAAK,EAAE,aACtB;OACK,IACL,CAAC,OAAO,IAAI,IAAI,MAAM,YAAY,GAAG,IAAI,IAAI,uBAAuB,CAAC,GAErE,GAAG,OACD,2BAA2B,MAAM,aAAa,sCAC3B,wBAAwB,aAC7C;OAEA,QAAQ;EAEV,IAAI,CAAC,OAAO;GACV,MAAM,IAAI,OAAO,UAAU,MAAM,OAAO,MAAM,QAAQ,CAAC;GACvD,OAAO,sBAAsB,IAAI,KAAK,OAAO,KAAK;EACpD;CACF;CACA,OAAO,EAAC,aAAY;AACtB;AAEA,SAAS,sBACP,IACA,IACA,UACA,YACA,WACiB;CACjB,MAAM,EAAC,SAAQ,eAAe,QAAQ;CACtC,MAAM,eAAe,IAAI,gBAAgB,IAAI;EAC3C,MAAM;EACN;EACA,gBAAgB;CAClB,CAAC;CACD,aAAa,IAAI,EAAE;CAEnB,GAAG,OAAO,WAAW,WAAW,mBAAmB,UAAU,QAAQ;CAErE,IAAI,SAAS,QAAQ,SAAS,IAAI,IAAI,IACpC,GAAG,OACD,kUAMF;CAEF,OAAO;AACT;;;;;;;;;;;AAYA,eAAe,qCACb,IACA,aAKC;CACD,MAAM,SAAS,MAAM,gBACnB,IACA,aACA,gCACA,EACE,KAAK,EACP,CACF;CACA,MAAM,QAAQ,SAA0C;CACxD,MAAM,UAAU,SAAe;CAC/B,MAAM,OAAO,OACV,MAAM,UAAe,OAAM,OAAM;EAChC,MAAM,EAAE,oDAAoD,QAAQ;EACpE,MAAM,CAAC,OAAO,MAAM,EAAqC;;;EAGzD,MAAM,QAAQ,GAAG;EACjB,MAAM,QAAQ;CAChB,CAAC,EACA,OAAM,MAAK,MAAM,OAAO,CAAC,CAAC;CAE7B,IAAI;CACJ,IAAI;CACJ,IAAI;EACF,CAAC,CAAC,UAAU,OAAO,MAAM,MAAM;CACjC,SAAS,GAAG;EACV,MAAM,OACH,IAAI,EACJ,OAAM,QACL,GAAG,OAAO,qDAAqD,GAAG,CACpE;EACF,MAAM;CACR;CACA,GAAG,OACD,qBAAqB,SAAS,UAAU,IAAI,uBAC9C;CACA,OAAO;EACL;EACA;EACA,SAAS,YAAY;GACnB,QAAQ,QAAQ;GAChB,IAAI;IACF,MAAM;GACR,SAAS,GAAG;IACV,GAAG,OAAO,gDAAgD,CAAC;GAC7D;GACA,MAAM,OAAO,IAAI;EACnB;CACF;AACF;AAEA,SAAS,iBACP,IACA,QACA,gBACA;CAGA,MAAM,iBAAiB,KAAK,oBAAoB,YAAY,EAAE,CAAC;CAC/D,KAAK,MAAM,KAAK,QAAQ;EACtB,GAAG,KAAK,yBAAyB,kBAAkB,GAAG,cAAc,CAAC,CAAC;EACtE,MAAM,YAAY,cAAc,CAAC;EACjC,KAAK,MAAM,CAAC,SAAS,YAAY,OAAO,QAAQ,EAAE,OAAO,GACvD,eAAe,OAAO,WAAW,SAAS,OAAO;CAErD;AACF;AAEA,SAAS,kBAAkB,IAAgB,IAAc,SAAsB;CAC7E,KAAK,MAAM,CAAC,GAAG,UAAU,QAAQ,QAAQ,GAAG;EAC1C,MAAM,OAAO,yBAAyB,uBAAuB,KAAK,CAAC;EACnE,GAAG,OAAO,kBAAkB,IAAI,EAAE,GAAG,QAAQ,OAAO,IAAI,MAAM;EAC9D,MAAM,QAAQ,YAAY,IAAI;EAC9B,GAAG,KAAK,IAAI;EACZ,GAAG,OACD,iBAAiB,IAAI,EAAE,GAAG,QAAQ,OAAO,KAClC,YAAY,IAAI,IAAI,OAAO,QAAQ,CAAC,EAAE,QAAQ,MACvD;CACF;AACF;;;;;;;;;;;;;;;AAgBA,SAAgB,oBACd,IACA,IACA,WACA,aACM;CACN,MAAM,SAAmB,CAAC;CAC1B,IAAI,iBAAiB;CACrB,IAAI,cAAc;CAIlB,MAAM,aAAa,WAAW,EAAE;CAChC,MAAM,kBAAkB,IAAI,IAAI,WAAW,KAAI,MAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;CAChE,KAAK,MAAM,MAAM,UAAU,QAAQ;EACjC,MAAM,OAAO,cAAc,EAAE;EAC7B,MAAM,OAAO,gBAAgB,IAAI,IAAI;EACrC,IAAI,CAAC,MAAM;GACT,OAAO,KAAK,6BAA6B,MAAM;GAC/C;EACF;EACA,KAAK,MAAM,OAAO,OAAO,KAAK,GAAG,OAAO,GAAG;GACzC;GACA,IAAI,EAAE,OAAO,KAAK,UAChB,OAAO,KAAK,mCAAmC,KAAK,IAAI,KAAK;EAEjE;CACF;CAGA,MAAM,iBAAiB,IAAI,IAAI,YAAY,EAAE,EAAE,KAAI,MAAK,EAAE,IAAI,CAAC;CAC/D,KAAK,MAAM,MAAM,UAAU,SAAS;EAClC,MAAM,SAAS,uBAAuB,EAAE;EACxC,IAAI,CAAC,eAAe,IAAI,OAAO,IAAI,GACjC,OAAO,KACL,6BAA6B,OAAO,KAAK,MAAM,OAAO,WACxD;CAEJ;CAGA,KAAK,MAAM,CAAC,OAAO,aAAa,aAC9B,IAAI;EACF,MAAM,CAAC,OAAO,GACX,QAAQ,kCAAkC,MAAM,EAAE,EAClD,IAAqB;EACxB,IAAI,IAAI,UAAU,UAChB,OAAO,KACL,gCAAgC,MAAM,0BACX,SAAS,gBAAgB,IAAI,OAC1D;OAEA,eAAe,IAAI;CAEvB,SAAS,GAAG;EACV,OAAO,KAAK,iCAAiC,MAAM,IAAI,OAAO,CAAC,GAAG;CACpE;CAIF,MAAM,OAAO,KAAK,oBAAoB,YAAY,EAAE,CAAC;CACrD,KAAK,MAAM,MAAM,UAAU,QAAQ;EACjC,MAAM,OAAO,cAAc,EAAE;EAC7B,MAAM,OAAO,KAAK,SAAS,IAAI;EAC/B,KAAK,MAAM,OAAO,OAAO,KAAK,GAAG,OAAO,GACtC,IAAI,CAAC,KAAK,IAAI,GAAG,GACf,OAAO,KAAK,mCAAmC,KAAK,GAAG,KAAK;CAGlE;CAEA,IAAI,OAAO,QACT,MAAM,IAAI,MACR,uCAAuC,OAAO,OAAO,iBACnD,OAAO,KAAI,MAAK,OAAO,GAAG,EAAE,KAAK,IAAI,CACzC;CAGF,GAAG,OACD,uCACK,UAAU,OAAO,OAAO,WACxB,UAAU,QAAQ,OAAO,YACzB,eAAe,YACf,YAAY,eAAe,EAAE,MACpC;AACF;AAQA,IAAM,KAAK,OAAO;AAClB,IAAM,oBAAoB;AAC1B,IAAM,0BAA0B,IAAI;;;;;;;AAcpC,SAAS,kBAAkB,YAAwC;CACjE,IAAI,eAAe,KAAA,KAAa,cAAc,GAC5C,OAAO;CAKT,OAAe,0BADH,YAAY,aAAa,KAAK,QAAQ,CAAC,CACV,EAAI;AAC/C;AAEA,SAAS,YAAY,iBAA6C;CAChE,OAAO,oBAAoB,KAAA,IACf,UAAU,oBAClB;AACN;;;;;AAMA,SAAgB,sBACd,OACA,MACU;CACV,OAAO,KAAK,KAAI,QAAO;EACrB,MAAM,OAAO,MAAM,QAAQ;EAC3B,OAAO,iBAAiB,IAAI,IAAI,GAAG,GAAG,IAAI,GAAG,GAAG,GAAG,EAAE;CACvD,CAAC;AACH;AAEA,SAAgB,uBACd,OACA,MACA,YACA,iBACA,aACoB;CACpB,MAAM,mBAAmB,OAAO,OAAO,MAAM,YAAY,EACtD,KAAK,EAAC,gBAAe,SAAS,EAC9B,QAAO,MAAK,CAAC,CAAC,CAAC;CAClB,MAAM,QACJ,iBAAiB,WAAW,IACxB,KACQ,SAAS,iBAAiB,KAAK,MAAM;CACnD,MAAM,SAAS,kBAAkB,UAAU;CAC3C,MAAM,QAAQ,YAAY,eAAe;CACzC,MAAM,YAAoB,QAAQ,GAAG,MAAM,MAAM,EAAE,GAAG,GAAG,MAAM,IAAI,IAAI,OAAO,GAAG;CACjF,MAAM,SAAiB,WAAW,eAAe,KAAK,IAAI,EAAE,GAAG,KAAK,GAAG,EAAE,GAAG,YAAY;CACxF,IAAI,OAAO;EAGT,MAAM,YAAY,KACf,KAAI,QAAO,2BAA2B,GAAG,GAAG,EAAE,MAAM,EACpD,KAAK,KAAK;EACb,OAAO;GACL;GACA,cAAsB,8DAA8D,YAAY,MAAM;GACtG,eAAuB,oEAAoE,UAAU,SAAS,YAAY,MAAM;EAClI;CACF;CACA,MAAM,aAAa,IAAI,KAAK,KAAI,QAAO,+BAA+B,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,KAAK,EAAE;CACnG,OAAO;EACL;EACA,cAAsB,kCAAkC;EACxD,eAAuB,UAAU,WAAW,mBAAmB;CACjE;AACF;AAsCA,IAAM,+CAA+C;CACnD;CAAG;CAAG;CAAG;CAAI;CAAI;CAAI;CAAK;CAAK;CAAK;CAAM;CAAM;CAAM;AACxD;AAEA,IAAM,qBAAqB;AAI3B,SAAS,kBAAkB;CACzB,OAAO,mBACL,eACA,qBACA,uCACF;AACF;AAEA,SAAS,sBAAsB;CAC7B,OAAO,6BACL,yBACA,gEACF;AACF;AAEA,SAAS,0BAA0B;CACjC,OAAO,6BACL,8BACA,0EACF;AACF;AAEA,SAAS,+BAA+B;CACtC,OAAO,6BACL,oCACA,yFACF;AACF;AAEA,SAAS,2BAA2B;CAClC,OAAO,6BACL,+BACA,4DACF;AACF;AAEA,SAAS,2BAA2B;CAClC,OAAO,6BACL,+BACA,+DACF;AACF;AAEA,SAAS,kBAAkB;CACzB,OAAO,mBACL,eACA,qBACA,kDACF;AACF;AAEA,SAAS,wBAAwB;CAC/B,OAAO,mBAAmB,eAAe,4BAA4B;EACnE,aACE;EACF,MAAM;CACR,CAAC;AACH;AAEA,SAAS,iCAAiC;CACxC,OAAO,mBACL,eACA,sCACA;EACE,aACE;EACF,MAAM;CACR,CACF;AACF;AAEA,SAAS,wBAAwB;CAC/B,OAAO,mBACL,eACA,4BACA,8DACF;AACF;AAEA,SAAgB,wBACd,QACA;CACA,IAAI,QAAQ;CACZ,IAAI,SAAS;CAEb,SAAS,QAAQ;EACf,IAAI,WAAW,GACb;EAEF,OAAO,OAAO,MAAM;EACpB,QAAQ;EACR,SAAS;CACX;CAEA,OAAO;EACL,IAAI,YAAoB;GACtB,SAAS;GACT;GACA,IAAI,SAAA,SACF,MAAM;EAEV;EACA;CACF;AACF;AAEA,SAAgB,uBAAuB,OAA+B;CACpE,MAAM,SAAS,uBAAuB,KAAK;CAC3C,MAAM,mBAAmB,sBAAsB;CAC/C,MAAM,mBAAmB,sBAAsB;CAC/C,OAAO,yBAAyB,OAAO,WAAW;EAChD,iBAAiB,IAAI,OAAO,MAAM;EAClC,iBAAiB,IAAI,QAAQ,MAAM;CACrC,CAAC;AACH;AAEA,SAAS,6BAA6B,MAAc,aAAqB;CACvE,OAAO,qBAAqB,eAAe,MAAM;EAC/C;EACA,MAAM;EACN,kBAAkB;CACpB,CAAC;AACH;AAEA,SAAS,uBAAuB,OAA+B;CAC7D,OAAO;EACL,WAAW,MAAM;EACjB,aAAa,MAAM;CACrB;AACF;AAEA,SAAS,0BAA0B,OAAkC;CACnE,OAAO;EACL,GAAG,uBAAuB,KAAK;EAC/B,QAAQ,MAAM;CAChB;AACF;AAEA,SAAS,4BACP,OASA,OACA;CACA,MAAM,SAAS,0BAA0B,KAAK;CAC9C,gBAAgB,EAAE,IAAI,GAAG,MAAM;CAC/B,oBAAoB,EAAE,SAAS,MAAM,YAAY,MAAM;CACvD,IAAI,MAAM,WAAW,WAAW;EAC9B,IAAI,MAAM,WAAW,KAAA,GACnB,wBAAwB,EAAE,SAAS,MAAM,QAAQ,MAAM;EAEzD,IAAI,MAAM,gBAAgB,KAAA,GACxB,6BAA6B,EAAE,SAAS,MAAM,aAAa,MAAM;EAEnE,IAAI,MAAM,YAAY,KAAA,GACpB,yBAAyB,EAAE,SAAS,MAAM,SAAS,MAAM;EAE3D,IAAI,MAAM,YAAY,KAAA,GACpB,yBAAyB,EAAE,SAAS,MAAM,SAAS,MAAM;EAE3D,IAAI,MAAM,SAAS,KAAA,KAAa,MAAM,OAAO,GAC3C,gBAAgB,EAAE,IAAI,MAAM,MAAM,MAAM;EAE1C,IAAI,MAAM,cAAc,KAAA,KAAa,MAAM,YAAY,GACrD,+BAA+B,EAAE,IAAI,MAAM,WAAW,MAAM;CAEhE;AACF;AAEA,SAAS,uBACP,SACA,QACA;CACA,MAAM,SAAS,QAAQ,QACpB,KAAK,SAAS;EACb,IAAI,QAAQ,KAAK;EACjB,IAAI,WAAW,KAAK;EACpB,IAAI,aAAa,KAAK;EACtB,OAAO;CACT,GACA;EACE,MAAM;EACN,SAAS;EACT,WAAW;CACb,CACF;CACA,OAAO;EACL,QAAQ,QAAQ;EAChB,MAAM,OAAO;EACb;EACA,SAAS,OAAO;EAChB,WAAW,OAAO;CACpB;AACF;AAEA,SAAS,iBACP,IACA,SAYA;CACA,IAAI,QAAQ,YAAY,sBAAsB,QAAQ,gBAAgB,GACpE;CAEF,GAAG,OAAO,sCAAsC,OAAO;AACzD;AAGA,eAAsB,wBACpB,IACA,KACA,MACA,YACwB;CACxB,MAAM,QAAQ,YAAY,IAAI;CAC9B,MAAM,QAAQ,cAAc,IAAI;CAChC,MAAM,UAAU,OAAO,KAAK,KAAK,OAAO;CACxC,IAAI,YAGF,OAAO;EACL;EACA,QAAQ;GAAC;GAAO;GAAS,MAAM;GAAG,WAAW;GAAG,YAAY;EAAC;CAC/D;CAaF,MAAM,EAAC,WAAW,gBAAc,MAPH,GAE5B;;;kBAGe,GANS,GAAG,KAAK,MAAM,EAAE,GAAG,GAAG,KAAK,IAAI,IAM1B,aAEiB,MAAM;EACnD,WAAW;EACX,YAAY;CACd;CAEA,MAAM,QAAuB;EAC3B;EACA,QAAQ;GACN;GACA;GACA,MAAM;GACN;GACA;EACF;CACF;CACA,MAAM,WAAW,YAAY,IAAI,IAAI,OAAO,QAAQ,CAAC;CACrD,GAAG,OAAO,uCAAuC,MAAM,IAAI,QAAQ,OAAO,EACxE,OAAO,MAAM,OACf,CAAC;CACD,OAAO;AACT;AAEA,SAAS,KACP,IACA,EAAC,MAAM,OAAO,UACd,UACA,MACA,IACA,UACA,UACA,YACA,iBACqB;CACrB,IAAI,UACF,OAAO,SACL,IACA,OACA,QACA,UACA,MACA,IACA,UACA,YACA,eACF;CAEF,OAAO,WACL,IACA,OACA,QACA,MACA,IACA,UACA,YACA,eACF;AACF;AAEA,eAAe,WACb,IACA,OACA,QACA,MACA,IACA,UACA,YACA,iBACqB;CACrB,MAAM,QAAQ,YAAY,IAAI;CAC9B,MAAM,aAAyB;CAC/B,MAAM,cAAc,uBAAuB;EAAC;EAAU;CAAU,CAAC;CACjE,IAAI,UAAU;CACd,IAAI,YAAY;CAChB,IAAI,eAAe;CAEnB,MAAM,YAAY,cAAc,KAAK;CACrC,MAAM,iBAAiB,OAAO,QAAQ,MAAM,OAAO;CAEnD,MAAM,cAAc,eAAe,KAAK,CAAC,OAAO,CAAC;CACjD,MAAM,cAAc,eAAe,KAAK,CAAC,OAAO,UAAU,IAAI;CAC9D,MAAM,mBAAmB,YAAY,KAAI,MAAK,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG;CAE7D,MAAM,YACJ,YAAY,SAAS,IAAI,IAAI,KAAK,OAAO,YAAY,SAAS,CAAC,EAAE,MAAM;CACzE,MAAM,YAAoB;mBACT,UAAU,KAAK,iBAAiB,WAAW;CAC5D,MAAM,aAAa,GAAG,QAAQ,SAAS;CACvC,MAAM,kBAAkB,GAAG,QACzB,YAAY,IAAI,YAAY,OAAA,EAA4B,CAC1D;CAGA,MAAM,SAAS,uBACb,OACA,aACA,YACA,iBACA,sBAAsB,OAAO,WAAW,CAC1C,EAAE;CAEF,MAAM,WAAW,eAAe,KAAK,GAAG,UACtC,iBAAiB,IAAI,IAAI,kBAAkB,IAAI,IAAI,eACrD;CAEA,MAAM,eAAe,YAAY;CACjC,MAAM,iBAAiB,eAAA;CAEvB,MAAM,gBAAiC,MAAM,KAAK,EAChD,QAAQ,oBAAoB,aAC9B,CAAC;CACD,IAAI,cAAc;CAClB,IAAI,cAAc;CAElB,SAAS,QAAQ;EACf,MAAM,aAAa,YAAY,IAAI;EACnC,MAAM,cAAc;EACpB,MAAM,cAAc;EAEpB,IAAI,IAAI;EACR,OAAO,cAAA,IAAiC,eAAA,IACtC,gBAAgB,IAAI,cAAc,MAAM,GAAI,KAAK,cAAe,CAAC;EAEnE,OAAO,cAAc,GAAG,eACtB,WAAW,IAAI,cAAc,MAAM,GAAI,KAAK,YAAa,CAAC;EAE5D,MAAM,gBAAgB,cAAc;EACpC,KAAK,IAAI,IAAI,GAAG,IAAI,eAAe,KACjC,cAAc,KAAK,KAAA;EAErB,cAAc;EACd,OAAO,QAAQ;EAEf,MAAM,UAAU,YAAY,IAAI,IAAI;EACpC,WAAW;EACX,GAAG,QACD,WAAW,YAAY,GAAG,UAAU,SAAS,YAAY,aAAa,QAAQ,QAAQ,CAAC,EAAE,IAC3F;EACA,iBAAiB,IAAI;GACnB,QAAQ,MAAM;GACd,OAAO,MAAM;GACb,cAAc;GACd;GACA;GACA,WAAW;GACX;GACA,cAAc;GACd,MAAM,OAAO;GACb;EACF,CAAC;CACH;CAEA,MAAM,eAAe,IAAI,iBAAiB;CAC1C,IAAI,MAAM;CAEV,GAAG,OAAO,kCAAkC,UAAU,IAAI,MAAM;CAEhE,MAAM,kBAAkB,YAAY,IAAI;CACxC,MAAM,WACJ,MAAM,KACH,OAAO,SAAS,OAAO,iCAAiC,EACxD,SAAS,GACZ,IAAI,SAAS;EACX,eAAe;EAEf,MACE,OACA,WACA,UACA;GACA,MAAM,kBAAkB,YAAY,IAAI;GACxC,IAAI;IACF,aAAa,MAAM;IACnB,YAAY,IAAI,MAAM,MAAM;IAC5B,KAAK,MAAM,YAAY,aAAa,MAAM,KAAK,GAAG;KAChD,MAAM,YAAY,aAAa,OAAO,IAAI,SAAS;KACnD,eAAe;KACf,cAAc,cAAc,eAAe,OACzC,aAAa,OAAO,OAAO,SAAS,KAAK,QAAQ;KAEnD,IAAI,EAAE,QAAQ,SAAS,QAAQ;MAC7B,MAAM;MACN,IACE,EAAE,eAAe,oBAAoB,gBACrC,eAAe,yBAEf,MAAM;KAEV;IACF;IACA,gBAAgB,YAAY,IAAI,IAAI;IACpC,SAAS;GACX,SAAS,GAAG;IACV,gBAAgB,YAAY,IAAI,IAAI;IACpC,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;GACxD;EACF;EAEA,QAAQ,aAAsC;GAC5C,MAAM,kBAAkB,YAAY,IAAI;GACxC,IAAI;IACF,YAAY,MAAM;IAClB,MAAM;IACN,gBAAgB,YAAY,IAAI,IAAI;IACpC,SAAS;GACX,SAAS,GAAG;IACV,gBAAgB,YAAY,IAAI,IAAI;IACpC,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;GACxD;EACF;EAEA,QAAQ,OAAO,UAAU;GACvB,YAAY,MAAM;GAClB,SAAS,KAAK;EAChB;CACF,CAAC,CACH;CAEA,MAAM,eAAe,KAAK,IACxB,GACA,YAAY,IAAI,IAAI,kBAAkB,YACxC;CACA,MAAM,UAAU,YAAY,IAAI,IAAI;CACpC,MAAM,SAAS;EACb,QAAQ,MAAM;EACd,OAAO,MAAM;EACb,cAAc;EACd;EACA;EACA,aAAa,YAAY;EACzB,MAAM,OAAO;EACb,eAAe,OAAO;EACtB,gBAAgB,OAAO;EACvB;EACA,WAAW;EACX;EACA;EACA;CACF;CAEA,GAAG,OACD,oBAAoB,OAAO,KAAK,aAAa,UAAU,WAC1C,QAAQ,QAAQ,CAAC,EAAE,eAAe,QAAQ,QAAQ,CAAC,EAAE,QAClE,MACF;CACA,OAAO;AACT;AAEA,eAAe,SACb,IACA,OACA,QACA,UACA,MACA,IACA,UACA,YACA,iBACqB;CACrB,MAAM,QAAQ,YAAY,IAAI;CAC9B,MAAM,aAAyB;CAC/B,MAAM,cAAc,uBAAuB;EAAC;EAAU;CAAU,CAAC;CACjE,IAAI,UAAU;CACd,IAAI,YAAY;CAChB,IAAI,eAAe;CAEnB,MAAM,YAAY,cAAc,KAAK;CACrC,MAAM,iBAAiB,OAAO,QAAQ,MAAM,OAAO;CAEnD,MAAM,cAAc,eAAe,KAAK,CAAC,OAAO,CAAC;CACjD,MAAM,cAAc,eAAe,KAAK,CAAC,OAAO,UAAU,IAAI;CAC9D,MAAM,mBAAmB,YAAY,KAAI,MAAK,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG;CAE7D,MAAM,YACJ,YAAY,SAAS,IAAI,IAAI,KAAK,OAAO,YAAY,SAAS,CAAC,EAAE,MAAM;CACzE,MAAM,YAAoB;mBACT,UAAU,KAAK,iBAAiB,WAAW;CAC5D,MAAM,aAAa,GAAG,QAAQ,SAAS;CACvC,MAAM,kBAAkB,GAAG,QACzB,YAAY,IAAI,YAAY,OAAA,EAA4B,CAC1D;CAEA,MAAM,EAAC,WAAU,uBACf,OACA,aACA,YACA,eACF;CACA,MAAM,eAAe,YAAY;CACjC,MAAM,iBAAiB,eAAA;CAEvB,MAAM,gBAAiC,MAAM,KAAK,EAChD,QAAQ,oBAAoB,aAC9B,CAAC;CACD,IAAI,cAAc;CAClB,IAAI,cAAc;CAElB,SAAS,QAAQ;EACf,MAAM,aAAa,YAAY,IAAI;EACnC,MAAM,cAAc;EACpB,MAAM,cAAc;EAEpB,IAAI,IAAI;EACR,OAAO,cAAA,IAAiC,eAAA,IACtC,gBAAgB,IAAI,cAAc,MAAM,GAAI,KAAK,cAAe,CAAC;EAEnE,OAAO,cAAc,GAAG,eACtB,WAAW,IAAI,cAAc,MAAM,GAAI,KAAK,YAAa,CAAC;EAE5D,MAAM,gBAAgB,cAAc;EACpC,KAAK,IAAI,IAAI,GAAG,IAAI,eAAe,KACjC,cAAc,KAAK,KAAA;EAErB,cAAc;EACd,OAAO,QAAQ;EAEf,MAAM,UAAU,YAAY,IAAI,IAAI;EACpC,WAAW;EACX,GAAG,QACD,WAAW,YAAY,GAAG,UAAU,SAAS,YAAY,aAAa,QAAQ,QAAQ,CAAC,EAAE,IAC3F;EACA,iBAAiB,IAAI;GACnB,QAAQ,MAAM;GACd,OAAO,MAAM;GACb,cAAc;GACd;GACA;GACA,WAAW;GACX;GACA,cAAc;GACd,MAAM,OAAO;GACb;EACF,CAAC;CACH;CAEA,GAAG,OAAO,gCAAgC,UAAU,IAAI,MAAM;CAC9D,MAAM,YAAY,MAAM,eAAe,UAAU,EAAC,oBAAoB,KAAI,CAAC;CAC3E,MAAM,UAAU,YAAY,KAAI,MAAK;EACnC,MAAM,UAAU,UAAU,cAAc,EAAE,OAAO;EACjD,QAAQ,QACN,UACE,QAAQ,GAAG,GACX,EAAE,UAAA,GAEJ;CACJ,CAAC;CAED,MAAM,YAAY,IAAI,UAAU;CAChC,IAAI,MAAM;CAEV,MAAM,kBAAkB,YAAY,IAAI;CACxC,MAAM,WACJ,MAAM,KAAK,OAAO,SAAS,OAAO,YAAY,EAAE,SAAS,GACzD,IAAI,SAAS;EACX,eAAe;EAEf,MACE,OACA,WACA,UACA;GACA,MAAM,kBAAkB,YAAY,IAAI;GACxC,IAAI;IACF,aAAa,MAAM;IACnB,YAAY,IAAI,MAAM,MAAM;IAC5B,KAAK,MAAM,QAAQ,UAAU,MAAM,KAAK,GAAG;KACzC,MAAM,YAAY,SAAS,OAAO,IAAI,KAAK;KAC3C,eAAe;KACf,cAAc,cAAc,eAAe,OACzC,SAAS,OAAO,OAAO,QAAQ,KAAK,IAAI;KAE1C,IAAI,EAAE,QAAQ,QAAQ,QAAQ;MAC5B,MAAM;MACN,IACE,EAAE,eAAe,oBAAoB,gBACrC,eAAe,yBAEf,MAAM;KAEV;IACF;IACA,gBAAgB,YAAY,IAAI,IAAI;IACpC,SAAS;GACX,SAAS,GAAG;IACV,gBAAgB,YAAY,IAAI,IAAI;IACpC,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;GACxD;EACF;EAEA,QAAQ,aAAsC;GAC5C,MAAM,kBAAkB,YAAY,IAAI;GACxC,IAAI;IACF,YAAY,MAAM;IAClB,MAAM;IACN,gBAAgB,YAAY,IAAI,IAAI;IACpC,SAAS;GACX,SAAS,GAAG;IACV,gBAAgB,YAAY,IAAI,IAAI;IACpC,SAAS,aAAa,QAAQ,IAAI,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC;GACxD;EACF;EAEA,QAAQ,OAAO,UAAU;GACvB,YAAY,MAAM;GAClB,SAAS,KAAK;EAChB;CACF,CAAC,CACH;CAEA,MAAM,eAAe,KAAK,IACxB,GACA,YAAY,IAAI,IAAI,kBAAkB,YACxC;CACA,MAAM,UAAU,YAAY,IAAI,IAAI;CACpC,MAAM,SAAS;EACb,QAAQ,MAAM;EACd,OAAO,MAAM;EACb,cAAc;EACd;EACA;EACA,aAAa,YAAY;EACzB,MAAM,OAAO;EACb,eAAe,OAAO;EACtB,gBAAgB,OAAO;EACvB;EACA,WAAW;EACX;EACA;EACA;CACF;CACA,GAAG,OACD,oBAAoB,OAAO,KAAK,aAAa,UAAU,WAC1C,QAAQ,QAAQ,CAAC,EAAE,eAAe,QAAQ,QAAQ,CAAC,EAAE,QAClE,MACF;CACA,OAAO;AACT"}
1
+ {"version":3,"file":"initial-sync.js","names":[],"sources":["../../../../../../../zero-cache/src/services/change-source/pg/initial-sync.ts"],"sourcesContent":["import {mkdtemp, rm} from 'node:fs/promises';\nimport {platform, tmpdir} from 'node:os';\nimport {join} from 'node:path';\nimport {Writable, type Readable} from 'node:stream';\nimport {pipeline} from 'node:stream/promises';\nimport type {LogContext} from '@rocicorp/logger';\nimport {assert} from '../../../../../shared/src/asserts.ts';\nimport type {JSONObject} from '../../../../../shared/src/bigint-json.ts';\nimport {must} from '../../../../../shared/src/must.ts';\nimport {equals} from '../../../../../shared/src/set-utils.ts';\nimport type {DownloadStatus} from '../../../../../zero-events/src/status.ts';\nimport {Database} from '../../../../../zqlite/src/db.ts';\nimport {\n createLiteIndexStatement,\n createLiteTableStatement,\n} from '../../../db/create.ts';\nimport {listIndexes, listTables} from '../../../db/lite-tables.ts';\nimport * as Mode from '../../../db/mode-enum.ts';\nimport {\n BinaryCopyParser,\n hasBinaryDecoder,\n makeBinaryDecoder,\n textCastDecoder,\n} from '../../../db/pg-copy-binary.ts';\nimport {TsvParser} from '../../../db/pg-copy.ts';\nimport {\n mapPostgresToLite,\n mapPostgresToLiteIndex,\n} from '../../../db/pg-to-lite.ts';\nimport {getTypeParsers} from '../../../db/pg-type-parser.ts';\nimport {runTx} from '../../../db/run-transaction.ts';\nimport type {IndexSpec, PublishedTableSpec} from '../../../db/specs.ts';\nimport {importSnapshot, TransactionPool} from '../../../db/transaction-pool.ts';\nimport {\n getOrCreateCounter,\n getOrCreateHistogram,\n} from '../../../observability/metrics.ts';\nimport {\n JSON_STRINGIFIED,\n liteValue,\n type LiteValueType,\n} from '../../../types/lite.ts';\nimport {liteTableName} from '../../../types/names.ts';\nimport {PG_15, PG_17} from '../../../types/pg-versions.ts';\nimport {\n connectPgClient,\n type PostgresDB,\n type PostgresTransaction,\n type PostgresValueType,\n} from '../../../types/pg.ts';\nimport {CpuProfiler} from '../../../types/profiler.ts';\nimport type {ShardConfig} from '../../../types/shards.ts';\nimport {ALLOWED_APP_ID_CHARACTERS} from '../../../types/shards.ts';\nimport {id} from '../../../types/sql.ts';\nimport {ReplicationStatusPublisher} from '../../replicator/replication-status.ts';\nimport {ColumnMetadataStore} from '../../replicator/schema/column-metadata.ts';\nimport {initReplicationState} from '../../replicator/schema/replication-state.ts';\nimport {toStateVersionString} from './lsn.ts';\nimport {createReplicaAndSlot} from './replication-slots.ts';\nimport {ensureShardSchema} from './schema/init.ts';\nimport {getPublicationInfo, warnForSkippedIndexes} from './schema/published.ts';\nimport {\n dropShard,\n getInternalShardConfig,\n getReplicaState,\n initReplica,\n validatePublications,\n type ReplicaState,\n} from './schema/shard.ts';\n\nexport type InitialSyncOptions = {\n tableCopyWorkers: number;\n profileCopy?: boolean | undefined;\n textCopy?: boolean | undefined;\n replicationSlotFailover?: boolean | undefined;\n installPartialIndexTriggers?: boolean | undefined;\n /**\n * When set, run initial sync in \"shadow\" mode for verification: skip all\n * upstream mutations (no replication slot, no addReplica, no dropShard, no\n * slot drop on failure), suppress status events, and optionally sample\n * rows from each table via TABLESAMPLE BERNOULLI + LIMIT. The caller is\n * responsible for providing (and discarding) a throwaway SQLite `tx`.\n */\n shadow?:\n | {\n /** 0 < rate <= 1. When 1, no TABLESAMPLE clause is added. */\n sampleRate: number;\n /**\n * LIMIT N cap appended after TABLESAMPLE. Required: shadow sync is\n * for verification only, so every run must commit to a row budget.\n */\n maxRowsPerTable: number;\n }\n | undefined;\n};\n\nexport type ReplicaOptions = {\n backupV5: boolean;\n};\n\n/** Server context to store with the initial sync metadata for debugging. */\nexport type ServerContext = JSONObject;\n\n/**\n * @returns The {@link ReplicaState} of the initialized replica, or `undefined`\n * for a shadow sync.\n */\nexport async function initialSync(\n lc: LogContext,\n shard: ShardConfig,\n tx: Database,\n upstreamURI: string,\n syncOptions: InitialSyncOptions,\n context: ServerContext,\n {backupV5}: ReplicaOptions = {backupV5: true},\n): Promise<ReplicaState | undefined> {\n if (!ALLOWED_APP_ID_CHARACTERS.test(shard.appID)) {\n throw new Error(\n 'The App ID may only consist of lower-case letters, numbers, and the underscore character',\n );\n }\n const {\n tableCopyWorkers,\n profileCopy,\n textCopy = false,\n replicationSlotFailover = false,\n installPartialIndexTriggers = true,\n shadow,\n } = syncOptions;\n const syncMode: InitialSyncMode = shadow ? 'shadow' : 'initial';\n const copyFormat: CopyFormat = textCopy ? 'text' : 'binary';\n const start = performance.now();\n let sql: PostgresDB | undefined;\n const replicaID = Date.now().toString();\n let slotName: string | undefined; // undefined === shadow\n let slotSession: Readable | undefined; // undefined === shadow\n const statusPublisher = ReplicationStatusPublisher.forRunningTransaction(\n tx,\n shadow ? async () => {} : undefined,\n ).publish(lc, 'Initializing');\n try {\n const copyProfiler = profileCopy ? await CpuProfiler.connect() : null;\n sql = await connectPgClient(lc, upstreamURI, 'initial-sync');\n const pgVersion = await checkUpstreamConfig(sql);\n\n // In shadow mode we assume the shard is already initialized and just\n // read back the existing publications. `ensurePublishedTables` would\n // otherwise run DDL and potentially call `dropShard`, which must never\n // happen during a shadow run.\n const {publications} = shadow\n ? await getInternalShardConfig(sql, shard)\n : await ensurePublishedTables(\n lc,\n sql,\n shard,\n installPartialIndexTriggers,\n );\n lc.info?.(`Upstream is setup with publications [${publications}]`);\n\n const {database, host} = sql.options;\n lc.info?.(\n shadow\n ? `acquiring exported snapshot on ${database}@${host} (shadow mode)`\n : `opening replication session to ${database}@${host}`,\n );\n\n // Captures the necessary information from the consistent snapshot\n // created for the new replication slot (or a suitable equivalent\n // for shadow sync).\n async function captureSnapshot(snapshot: string) {\n // Run up to MAX_WORKERS to copy of tables at the replication slot's snapshot.\n // Retrieve the published schema at the consistent_point.\n const published = await runTx(\n must(sql),\n async tx => {\n await tx.unsafe(/* sql*/ `SET TRANSACTION SNAPSHOT '${snapshot}'`);\n return getPublicationInfo(tx, publications);\n },\n {mode: Mode.READONLY},\n );\n warnForSkippedIndexes(lc, published);\n // Note: If this throws, initial-sync is aborted.\n validatePublications(lc, published);\n\n // Now that tables have been validated, kick off the copiers.\n const {tables, indexes} = published;\n const numTables = tables.length;\n if (platform() === 'win32' && tableCopyWorkers < numTables) {\n lc.warn?.(\n `Increasing the number of copy workers from ${tableCopyWorkers} to ` +\n `${numTables} to work around a Node/Postgres connection bug`,\n );\n }\n const numWorkers =\n platform() === 'win32'\n ? numTables\n : Math.min(tableCopyWorkers, numTables);\n\n const copyPool = await connectPgClient(\n lc,\n upstreamURI,\n 'initial-sync-copy-worker',\n {\n max: numWorkers,\n ['max_lifetime']: 120 * 60, // set a long (2h) limit for COPY streaming\n },\n );\n const copiers = await startTableCopyWorkers(\n lc,\n copyPool,\n snapshot,\n numWorkers,\n numTables,\n );\n return {\n published,\n tables,\n indexes,\n numTables,\n copyPool,\n copiers,\n };\n }\n\n let lsn: string;\n let snapshotResult: Awaited<ReturnType<typeof captureSnapshot>>;\n\n if (shadow) {\n const exported = await acquireExportedSnapshotForShadowSync(\n lc,\n upstreamURI,\n captureSnapshot,\n );\n lsn = exported.lsn;\n snapshotResult = exported.result;\n } else {\n const replication = await createReplicaAndSlot(\n lc,\n sql,\n 'initial-sync-replication-session',\n shard,\n replicaID,\n replicationSlotFailover && pgVersion >= PG_17,\n {\n // When backing up with litestream v5, a unique backup path is\n // required since the LTX format does not tolerate multiple writers\n // (and there are no v3 \"generations\" to workaround it).\n backupPath: backupV5 ? replicaID : null,\n backupV5,\n },\n captureSnapshot,\n );\n lsn = replication.slot.consistent_point;\n slotName = replication.slot.slot_name;\n snapshotResult = replication.capturedSnapshot;\n slotSession = replication.initialSession;\n }\n\n const {published, tables, indexes, numTables, copyPool, copiers} =\n snapshotResult;\n\n const initialVersion = toStateVersionString(lsn);\n initReplicationState(tx, publications, initialVersion, context);\n try {\n createLiteTables(tx, tables, initialVersion);\n const sampleRate = shadow?.sampleRate;\n const maxRowsPerTable = shadow?.maxRowsPerTable;\n const downloads = await Promise.all(\n tables.map(spec =>\n copiers.processReadTask((db, lc) =>\n getInitialDownloadState(lc, db, spec, shadow !== undefined),\n ),\n ),\n );\n statusPublisher.publish(\n lc,\n 'Initializing',\n `Copying ${numTables} upstream tables at version ${initialVersion}`,\n 5000,\n () => ({downloadStatus: downloads.map(({status}) => status)}),\n );\n\n void copyProfiler?.start();\n const copyStart = performance.now();\n const copyResults = await Promise.all(\n downloads.map(table =>\n copiers.processReadTask((db, lc) =>\n copy(\n lc,\n table,\n copyPool,\n db,\n tx,\n textCopy,\n syncMode,\n sampleRate,\n maxRowsPerTable,\n ),\n ),\n ),\n );\n const copyElapsed = performance.now() - copyStart;\n void copyProfiler?.stopAndDispose(lc, 'initial-copy');\n copiers.setDone();\n\n const copySummary = initialSyncCopySummary(copyResults, copyElapsed);\n\n statusPublisher.publish(\n lc,\n 'Indexing',\n `Creating ${indexes.length} indexes`,\n 5000,\n );\n const indexStart = performance.now();\n createLiteIndices(lc, tx, indexes);\n const index = performance.now() - indexStart;\n lc.info?.(`Created indexes (${index.toFixed(3)} ms)`);\n\n if (slotName && replicaID) {\n await initReplica(sql, shard, replicaID, published, context);\n } else {\n assert(shadow, 'expected to be in shadow sync if there is no slotName');\n const rowsByTable = new Map<string, number>();\n for (let i = 0; i < downloads.length; i++) {\n rowsByTable.set(downloads[i].status.table, copyResults[i].rows);\n }\n verifyShadowReplica(lc, tx, published, rowsByTable);\n }\n\n const elapsed = performance.now() - start;\n const copyOtherMs = Math.max(0, elapsed - copySummary.flushMs - index);\n recordInitialSyncRunMetrics(\n {\n durationMs: elapsed,\n rows: copySummary.rows,\n copyBytes: copySummary.copyBytes,\n copyMs: copySummary.copyMs,\n copyOtherMs,\n flushMs: copySummary.flushMs,\n indexMs: index,\n },\n {\n result: 'success',\n syncMode,\n copyFormat,\n },\n );\n lc.info?.(\n `Synced ${copySummary.rows.toLocaleString()} rows of ${numTables} tables in ${publications} up to ${lsn} ` +\n `(flush: ${copySummary.flushMs.toFixed(3)}, index: ${index.toFixed(3)}, total: ${elapsed.toFixed(3)} ms)`,\n {\n syncMode,\n copyFormat,\n publications,\n lsn,\n ...copySummary,\n indexes: indexes.length,\n indexMs: index,\n copyOtherMs,\n totalMs: elapsed,\n },\n );\n\n return slotName && replicaID\n ? must(\n await getReplicaState(sql, shard, replicaID),\n `Missing replica with id ${replicaID}`,\n )\n : undefined; // shadow sync only\n } finally {\n // All meaningful errors are handled at the processReadTask() call site.\n void copyPool.end().catch(e => lc.warn?.(`Error closing copyPool`, e));\n }\n } catch (e) {\n recordInitialSyncRunMetrics(\n {durationMs: performance.now() - start},\n {\n result: 'error',\n syncMode,\n copyFormat,\n },\n );\n if (slotName && sql) {\n // If initial-sync did not succeed, make a best effort to drop the\n // orphaned replication slot to avoid running out of slots in\n // pathological cases that result in repeated failures.\n lc.warn?.(`dropping replication slot ${slotName}`, e);\n // release any initial replication session so the slot can be dropped\n slotSession?.destroy();\n await sql`\n SELECT pg_drop_replication_slot(slot_name) FROM pg_replication_slots\n WHERE slot_name = ${slotName};\n `.catch(e => lc.warn?.(`Unable to drop replication slot ${slotName}`, e));\n }\n throw await statusPublisher.publishAndThrowError(lc, 'Initializing', e);\n } finally {\n statusPublisher.stop();\n if (sql) {\n await sql.end();\n }\n }\n}\n\nexport type ShadowSyncOptions = {\n sampleRate: number;\n maxRowsPerTable: number;\n /**\n * Parent directory for the throwaway SQLite replica. Defaults to the OS\n * tmpdir. Primarily for tests that need to isolate the scratch directory.\n */\n parentDir?: string | undefined;\n};\n\n/**\n * Exercises the initial-sync code path against a sample of rows from every\n * published table, writing into a throwaway SQLite database that is deleted\n * when the run ends. Produces zero upstream mutations: no replication slot,\n * no `addReplica`, no `dropShard`, no status events.\n *\n * Intended to be invoked periodically so that if a customer ever needs a\n * full reset, we have recent confidence that `initialSync` still works.\n * The shard must already be initialized upstream.\n */\nexport async function shadowInitialSync(\n lc: LogContext,\n shard: ShardConfig,\n upstreamURI: string,\n shadow: ShadowSyncOptions,\n context: ServerContext,\n syncOptions?: Pick<InitialSyncOptions, 'textCopy'>,\n): Promise<void> {\n const dir = await mkdtemp(\n join(shadow.parentDir ?? tmpdir(), 'zero-shadow-sync-'),\n );\n const dbPath = join(dir, 'shadow-replica.db');\n const db = new Database(lc, dbPath);\n try {\n await initialSync(\n lc,\n shard,\n db,\n upstreamURI,\n {\n // Shadow sync copies small samples, so one worker is plenty —\n // no reason to burn additional upstream connections.\n tableCopyWorkers: 1,\n textCopy: syncOptions?.textCopy,\n shadow,\n },\n context,\n );\n } finally {\n try {\n db.close();\n } catch (e) {\n lc.warn?.(`Error closing shadow replica db`, e);\n }\n await rm(dir, {recursive: true, force: true}).catch(e =>\n lc.warn?.(`Error cleaning up shadow replica dir ${dir}`, e),\n );\n }\n}\n\nasync function checkUpstreamConfig(sql: PostgresDB) {\n const {walLevel, version} = (\n await sql<{walLevel: string; version: number}[]>`\n SELECT current_setting('wal_level') as \"walLevel\", \n current_setting('server_version_num') as \"version\";\n `\n )[0];\n\n if (walLevel !== 'logical') {\n throw new Error(\n `Postgres must be configured with \"wal_level = logical\" (currently: \"${walLevel})`,\n );\n }\n if (version < PG_15) {\n throw new Error(\n `Must be running Postgres 15 or higher (currently: \"${version}\")`,\n );\n }\n return version;\n}\n\nasync function ensurePublishedTables(\n lc: LogContext,\n sql: PostgresDB,\n shard: ShardConfig,\n installPartialIndexTriggers: boolean,\n validate = true,\n): Promise<{publications: string[]}> {\n const {database, host} = sql.options;\n lc.info?.(`Ensuring upstream PUBLICATION on ${database}@${host}`);\n\n // Technically, ensureShardSchema() is already called by\n // initializePostgresChangeSource, so the call here is redundant\n // but harmless. On the contrary, it makes initial sync more\n // self-contained (for test setup), and also plays a role in\n // recovering from corruption (below) after the shard is dropped.\n await ensureShardSchema(lc, sql, shard, installPartialIndexTriggers);\n const {publications} = await getInternalShardConfig(sql, shard);\n\n if (validate) {\n let valid = false;\n const nonInternalPublications = publications.filter(\n p => !p.startsWith('_'),\n );\n const exists = await sql`\n SELECT pubname FROM pg_publication WHERE pubname IN ${sql(publications)}\n `.values();\n if (exists.length !== publications.length) {\n lc.warn?.(\n `some configured publications [${publications}] are missing: ` +\n `[${exists.flat()}]. resyncing`,\n );\n } else if (\n !equals(new Set(shard.publications), new Set(nonInternalPublications))\n ) {\n lc.warn?.(\n `requested publications [${shard.publications}] differ from previous` +\n `publications [${nonInternalPublications}]. resyncing`,\n );\n } else {\n valid = true;\n }\n if (!valid) {\n await sql.unsafe(dropShard(shard.appID, shard.shardNum));\n return ensurePublishedTables(\n lc,\n sql,\n shard,\n installPartialIndexTriggers,\n false,\n );\n }\n }\n return {publications};\n}\n\nasync function startTableCopyWorkers(\n lc: LogContext,\n db: PostgresDB,\n snapshot: string,\n numWorkers: number,\n numTables: number,\n): Promise<TransactionPool> {\n const {init, imported} = importSnapshot(snapshot);\n const tableCopiers = new TransactionPool(lc, {\n mode: Mode.READONLY,\n init,\n initialWorkers: numWorkers,\n });\n tableCopiers.run(db);\n\n // Wait for all workers to run the importSnapshot() init task before\n // proceeding (to allow the snapshot to be released).\n for (let i = 0; i < numWorkers; i++) {\n await imported.dequeue();\n }\n\n lc.info?.(`Started ${numWorkers} workers to copy ${numTables} tables`);\n\n if (parseInt(process.versions.node) < 22) {\n lc.warn?.(\n `\\n\\n\\n` +\n `Older versions of Node have a bug that results in an unresponsive\\n` +\n `Postgres connection after running certain combinations of COPY commands.\\n` +\n `If initial sync hangs, run zero-cache with Node v22+. This has the additional\\n` +\n `benefit of being consistent with the Node version run in the production container image.` +\n `\\n\\n\\n`,\n );\n }\n return tableCopiers;\n}\n\n/**\n * Shadow-mode alternative to `createReplicationSlot`: opens a dedicated\n * READ ONLY REPEATABLE READ transaction on a normal connection, exports the\n * snapshot and captures the current WAL LSN. Note that the LSN and snapshot\n * are not guaranteed to be consistent but it this is fine for shadow sync\n * where the LSN is not actually relied on for anything.\n */\nasync function acquireExportedSnapshotForShadowSync<T>(\n lc: LogContext,\n upstreamURI: string,\n captureSnapshot: (snapshot: string) => Promise<T>,\n): Promise<{\n lsn: string;\n result: T;\n}> {\n const holder = await connectPgClient(\n lc,\n upstreamURI,\n 'shadow-initial-sync-snapshot',\n {max: 1},\n );\n return holder.begin(Mode.READONLY, async tx => {\n const [{lsn, snapshot}] = await tx<{snapshot: string; lsn: string}[]>`\n SELECT pg_export_snapshot() AS snapshot,\n pg_current_wal_lsn()::text AS lsn`;\n const result = await captureSnapshot(snapshot);\n return {lsn, result};\n });\n}\n\nfunction createLiteTables(\n tx: Database,\n tables: PublishedTableSpec[],\n initialVersion: string,\n) {\n // TODO: Figure out how to reuse the ChangeProcessor here to avoid\n // duplicating the ColumnMetadata logic.\n const columnMetadata = must(ColumnMetadataStore.getInstance(tx));\n for (const t of tables) {\n tx.exec(createLiteTableStatement(mapPostgresToLite(t, initialVersion)));\n const tableName = liteTableName(t);\n for (const [colName, colSpec] of Object.entries(t.columns)) {\n columnMetadata.insert(tableName, colName, colSpec);\n }\n }\n}\n\nfunction createLiteIndices(lc: LogContext, tx: Database, indices: IndexSpec[]) {\n for (const [i, index] of indices.entries()) {\n const stmt = createLiteIndexStatement(mapPostgresToLiteIndex(index));\n lc.info?.(`Creating index ${i + 1}/${indices.length}: ${stmt}`);\n const start = performance.now();\n tx.exec(stmt);\n lc.info?.(\n `Created index ${i + 1}/${indices.length} ` +\n `(${(performance.now() - start).toFixed(3)} ms): ${stmt}`,\n );\n }\n}\n\n/**\n * Runs structural assertions over a just-synced replica and throws if any\n * fail. Only called in shadow mode — a successful return means the replica\n * is schema-complete, row-count consistent, and its column metadata is in\n * sync with its lite schema.\n *\n * Note: this intentionally does NOT verify ZQL-queryability. Tables that\n * `computeZqlSpecs` drops (no PK / no all-NOT-NULL unique index, unsupported\n * column types, etc.) are silently skipped in production too — there's\n * nothing shadow-specific about them, so failing here would diverge from\n * prod over an upstream-schema condition prod accepts.\n *\n * Exported for testing.\n */\nexport function verifyShadowReplica(\n lc: LogContext,\n db: Database,\n published: {tables: PublishedTableSpec[]; indexes: IndexSpec[]},\n rowsByTable: ReadonlyMap<string, number>,\n): void {\n const issues: string[] = [];\n let columnsChecked = 0;\n let rowsChecked = 0;\n\n // 1. Schema completeness: every published table exists in the replica\n // with at least the expected column set.\n const liteTables = listTables(db);\n const liteTableByName = new Map(liteTables.map(t => [t.name, t]));\n for (const pt of published.tables) {\n const name = liteTableName(pt);\n const lite = liteTableByName.get(name);\n if (!lite) {\n issues.push(`missing table in replica: ${name}`);\n continue;\n }\n for (const col of Object.keys(pt.columns)) {\n columnsChecked++;\n if (!(col in lite.columns)) {\n issues.push(`column missing in replica table ${name}: ${col}`);\n }\n }\n }\n\n // Every published index exists in the replica.\n const liteIndexNames = new Set(listIndexes(db).map(i => i.name));\n for (const ix of published.indexes) {\n const mapped = mapPostgresToLiteIndex(ix);\n if (!liteIndexNames.has(mapped.name)) {\n issues.push(\n `missing index in replica: ${mapped.name} on ${mapped.tableName}`,\n );\n }\n }\n\n // 2. Row counts: SQLite COUNT(*) matches the in-memory copy counter.\n for (const [table, expected] of rowsByTable) {\n try {\n const [row] = db\n .prepare(`SELECT COUNT(*) as count FROM \"${table}\"`)\n .all<{count: number}>();\n if (row.count !== expected) {\n issues.push(\n `row count mismatch for table ${table}: ` +\n `copy counter reported ${expected}, replica has ${row.count}`,\n );\n } else {\n rowsChecked += row.count;\n }\n } catch (e) {\n issues.push(`could not count rows in table ${table}: ${String(e)}`);\n }\n }\n\n // 3. Column metadata: every published column has a _zero.column_metadata row.\n const meta = must(ColumnMetadataStore.getInstance(db));\n for (const pt of published.tables) {\n const name = liteTableName(pt);\n const rows = meta.getTable(name);\n for (const col of Object.keys(pt.columns)) {\n if (!rows.has(col)) {\n issues.push(`missing column_metadata row for ${name}.${col}`);\n }\n }\n }\n\n if (issues.length) {\n throw new Error(\n `Shadow replica verification failed (${issues.length} issue(s)):\\n` +\n issues.map(i => ` - ${i}`).join('\\n'),\n );\n }\n\n lc.info?.(\n `Shadow replica verification passed: ` +\n `${published.tables.length} tables, ` +\n `${published.indexes.length} indexes, ` +\n `${columnsChecked} columns, ` +\n `${rowsChecked.toLocaleString()} rows`,\n );\n}\n\n// Verified empirically that batches of 50 seem to be the sweet spot,\n// similar to the report in https://sqlite.org/forum/forumpost/8878a512d3652655\n//\n// Exported for testing.\nexport const INSERT_BATCH_SIZE = 50;\n\nconst MB = 1024 * 1024;\nconst MAX_BUFFERED_ROWS = 10_000;\nconst BUFFERED_SIZE_THRESHOLD = 8 * MB;\n\nexport type DownloadStatements = {\n select: string;\n getTotalRows: string;\n getTotalBytes: string;\n};\n\n/**\n * Produces ` TABLESAMPLE BERNOULLI(n)` when `sampleRate` is < 1, else `''`.\n * Row-level Bernoulli sampling is used (rather than SYSTEM) because it\n * produces a more uniform sample and, unlike SYSTEM, still returns rows\n * for small tables at low rates.\n */\nfunction tableSampleClause(sampleRate: number | undefined): string {\n if (sampleRate === undefined || sampleRate >= 1) {\n return '';\n }\n // Round away float noise (e.g. 0.3 * 100 = 30.000000000000004) while still\n // preserving sub-integer rates like 0.001 (= 0.1%).\n const pct = parseFloat((sampleRate * 100).toFixed(6));\n return /*sql*/ ` TABLESAMPLE BERNOULLI(${pct})`;\n}\n\nfunction limitClause(maxRowsPerTable: number | undefined): string {\n return maxRowsPerTable !== undefined\n ? /*sql*/ ` LIMIT ${maxRowsPerTable}`\n : '';\n}\n\n/**\n * Returns the SELECT column expressions for binary COPY, casting columns\n * without a known binary decoder to `::text`.\n */\nexport function makeBinarySelectExprs(\n table: PublishedTableSpec,\n cols: string[],\n): string[] {\n return cols.map(col => {\n const spec = table.columns[col];\n return hasBinaryDecoder(spec) ? id(col) : `${id(col)}::text`;\n });\n}\n\nexport function makeDownloadStatements(\n table: PublishedTableSpec,\n cols: string[],\n sampleRate?: number | undefined,\n maxRowsPerTable?: number | undefined,\n selectExprs?: string[] | undefined,\n): DownloadStatements {\n const filterConditions = Object.values(table.publications)\n .map(({rowFilter}) => rowFilter)\n .filter(f => !!f); // remove nulls\n const where =\n filterConditions.length === 0\n ? ''\n : /*sql*/ `WHERE ${filterConditions.join(' OR ')}`;\n const sample = tableSampleClause(sampleRate);\n const limit = limitClause(maxRowsPerTable);\n const fromTable = /*sql*/ `FROM ${id(table.schema)}.${id(table.name)}${sample} ${where}`;\n const select = /*sql*/ `SELECT ${(selectExprs ?? cols.map(id)).join(',')} ${fromTable}${limit}`;\n if (limit) {\n // With LIMIT, wrap counts/sums in a subquery so they reflect the\n // capped rowset rather than the full (sampled) table.\n const bytesExpr = cols\n .map(col => `COALESCE(pg_column_size(${id(col)}), 0)`)\n .join(' + ');\n return {\n select,\n getTotalRows: /*sql*/ `SELECT COUNT(*)::bigint AS \"totalRows\" FROM (SELECT 1 AS _ ${fromTable}${limit}) s`,\n getTotalBytes: /*sql*/ `SELECT COALESCE(SUM(b), 0)::bigint AS \"totalBytes\" FROM (SELECT (${bytesExpr}) AS b ${fromTable}${limit}) s`,\n };\n }\n const totalBytes = `(${cols.map(col => `SUM(COALESCE(pg_column_size(${id(col)}), 0))`).join(' + ')})`;\n return {\n select,\n getTotalRows: /*sql*/ `SELECT COUNT(*) AS \"totalRows\" ${fromTable}`,\n getTotalBytes: /*sql*/ `SELECT ${totalBytes} AS \"totalBytes\" ${fromTable}`,\n };\n}\n\ntype DownloadState = {\n spec: PublishedTableSpec;\n status: DownloadStatus;\n};\n\ntype CopyFormat = 'binary' | 'text';\ntype InitialSyncMode = 'initial' | 'shadow';\n\ntype InitialSyncMetricAttrs = {\n syncMode: InitialSyncMode;\n copyFormat: CopyFormat;\n};\n\ntype InitialSyncRunMetricAttrs = {\n syncMode: InitialSyncMode;\n copyFormat: CopyFormat;\n result: 'success' | 'error';\n};\n\ntype CopyResult = {\n schema: string;\n table: string;\n replicaTable: string;\n syncMode: InitialSyncMode;\n copyFormat: CopyFormat;\n columnCount: number;\n rows: number;\n estimatedRows: number;\n estimatedBytes: number | undefined;\n flushMs: number;\n elapsedMs: number;\n sourceWaitMs: number;\n processingMs: number;\n copyBytes: number;\n};\n\nconst INITIAL_SYNC_DURATION_HISTOGRAM_BOUNDARIES_S = [\n 1, 2, 5, 10, 30, 60, 120, 300, 600, 1200, 2400, 3600, 7200,\n];\nexport const COPY_METRIC_BATCH_BYTES = 8 * 1024 * 1024;\nconst SLOW_COPY_FLUSH_MS = 10_000;\n\n// change-streamer imports this module before startOtelAuto() runs, so create\n// instruments lazily to avoid binding them to OTel's no-op meter provider.\nfunction initialSyncRuns() {\n return getOrCreateCounter(\n 'replication',\n 'initial_sync_runs',\n 'Initial sync runs, labeled by result.',\n );\n}\n\nfunction initialSyncDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_duration',\n 'Wall-clock duration of an initial sync run, labeled by result.',\n );\n}\n\nfunction initialSyncCopyDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_copy_duration',\n 'Wall-clock duration of the COPY phase for a successful initial sync run.',\n );\n}\n\nfunction initialSyncCopyOtherDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_copy_other_duration',\n 'Initial sync total duration excluding SQLite flush and index time for a successful run.',\n );\n}\n\nfunction initialSyncFlushDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_flush_duration',\n 'Total SQLite flush time for a successful initial sync run.',\n );\n}\n\nfunction initialSyncIndexDuration() {\n return initialSyncDurationHistogram(\n 'initial_sync_index_duration',\n 'SQLite index creation time for a successful initial sync run.',\n );\n}\n\nfunction initialSyncRows() {\n return getOrCreateCounter(\n 'replication',\n 'initial_sync_rows',\n 'Rows copied during successful initial sync runs.',\n );\n}\n\nfunction initialSyncCopyStream() {\n return getOrCreateCounter('replication', 'initial_sync_copy_stream', {\n description:\n 'PostgreSQL COPY stream bytes processed during initial sync, including in-progress and failed runs.',\n unit: 'bytes',\n });\n}\n\nfunction initialSyncCompletedCopyStream() {\n return getOrCreateCounter(\n 'replication',\n 'initial_sync_completed_copy_stream',\n {\n description:\n 'PostgreSQL COPY stream bytes processed during successful initial sync runs.',\n unit: 'bytes',\n },\n );\n}\n\nfunction initialSyncCopyChunks() {\n return getOrCreateCounter(\n 'replication',\n 'initial_sync_copy_chunks',\n 'PostgreSQL COPY stream chunks processed during initial sync.',\n );\n}\n\nexport function createCopyMetricBatcher(\n record: (bytes: number, chunks: number) => void,\n) {\n let bytes = 0;\n let chunks = 0;\n\n function flush() {\n if (chunks === 0) {\n return;\n }\n record(bytes, chunks);\n bytes = 0;\n chunks = 0;\n }\n\n return {\n add(chunkBytes: number) {\n bytes += chunkBytes;\n chunks++;\n if (bytes >= COPY_METRIC_BATCH_BYTES) {\n flush();\n }\n },\n flush,\n };\n}\n\nexport function initialSyncCopyMetrics(attrs: InitialSyncMetricAttrs) {\n const labels = initialSyncMetricAttrs(attrs);\n const copyStreamMetric = initialSyncCopyStream();\n const copyChunksMetric = initialSyncCopyChunks();\n return createCopyMetricBatcher((bytes, chunks) => {\n copyStreamMetric.add(bytes, labels);\n copyChunksMetric.add(chunks, labels);\n });\n}\n\nfunction initialSyncDurationHistogram(name: string, description: string) {\n return getOrCreateHistogram('replication', name, {\n description,\n unit: 's',\n bucketBoundaries: INITIAL_SYNC_DURATION_HISTOGRAM_BOUNDARIES_S,\n });\n}\n\nfunction initialSyncMetricAttrs(attrs: InitialSyncMetricAttrs) {\n return {\n sync_mode: attrs.syncMode,\n copy_format: attrs.copyFormat,\n };\n}\n\nfunction initialSyncRunMetricAttrs(attrs: InitialSyncRunMetricAttrs) {\n return {\n ...initialSyncMetricAttrs(attrs),\n result: attrs.result,\n };\n}\n\nfunction recordInitialSyncRunMetrics(\n stats: {\n durationMs: number;\n rows?: number | undefined;\n copyBytes?: number | undefined;\n copyMs?: number | undefined;\n copyOtherMs?: number | undefined;\n flushMs?: number | undefined;\n indexMs?: number | undefined;\n },\n attrs: InitialSyncRunMetricAttrs,\n) {\n const labels = initialSyncRunMetricAttrs(attrs);\n initialSyncRuns().add(1, labels);\n initialSyncDuration().recordMs(stats.durationMs, labels);\n if (attrs.result === 'success') {\n if (stats.copyMs !== undefined) {\n initialSyncCopyDuration().recordMs(stats.copyMs, labels);\n }\n if (stats.copyOtherMs !== undefined) {\n initialSyncCopyOtherDuration().recordMs(stats.copyOtherMs, labels);\n }\n if (stats.flushMs !== undefined) {\n initialSyncFlushDuration().recordMs(stats.flushMs, labels);\n }\n if (stats.indexMs !== undefined) {\n initialSyncIndexDuration().recordMs(stats.indexMs, labels);\n }\n if (stats.rows !== undefined && stats.rows > 0) {\n initialSyncRows().add(stats.rows, labels);\n }\n if (stats.copyBytes !== undefined && stats.copyBytes > 0) {\n initialSyncCompletedCopyStream().add(stats.copyBytes, labels);\n }\n }\n}\n\nfunction initialSyncCopySummary(\n results: readonly CopyResult[],\n copyMs: number,\n) {\n const totals = results.reduce(\n (acc, curr) => {\n acc.rows += curr.rows;\n acc.flushMs += curr.flushMs;\n acc.copyBytes += curr.copyBytes;\n return acc;\n },\n {\n rows: 0,\n flushMs: 0,\n copyBytes: 0,\n },\n );\n return {\n tables: results.length,\n rows: totals.rows,\n copyMs,\n flushMs: totals.flushMs,\n copyBytes: totals.copyBytes,\n };\n}\n\nfunction logSlowCopyFlush(\n lc: LogContext,\n details: {\n schema: string;\n table: string;\n replicaTable: string;\n syncMode: InitialSyncMode;\n copyFormat: CopyFormat;\n elapsedMs: number;\n flushedRows: number;\n flushedBytes: number;\n rows: number;\n copyBytes: number;\n },\n) {\n if (details.elapsedMs < SLOW_COPY_FLUSH_MS || details.flushedRows === 0) {\n return;\n }\n lc.info?.('initial-sync table copy slow flush', details);\n}\n\n// Exported for testing.\nexport async function getInitialDownloadState(\n lc: LogContext,\n sql: PostgresDB,\n spec: PublishedTableSpec,\n skipTotals: boolean,\n): Promise<DownloadState> {\n const start = performance.now();\n const table = liteTableName(spec);\n const columns = Object.keys(spec.columns);\n if (skipTotals) {\n // Shadow sync suppresses status events, so the pg_class\n // estimates would be computed and thrown away.\n return {\n spec,\n status: {table, columns, rows: 0, totalRows: 0, totalBytes: 0},\n };\n }\n // Use pg_class estimates instead of expensive COUNT(*) and\n // SUM(pg_column_size(...)) full table scans. The exact values are only\n // used for progress reporting, so estimates are sufficient.\n const qualifiedName = `${id(spec.schema)}.${id(spec.name)}`;\n const estimateResult = await sql<\n {totalRows: number; totalBytes: number}[]\n >`SELECT GREATEST(reltuples, 0)::float8 AS \"totalRows\",\n pg_table_size(oid)::float8 AS \"totalBytes\"\n FROM pg_class\n WHERE oid = ${qualifiedName}::regclass`;\n\n const {totalRows, totalBytes} = estimateResult[0] ?? {\n totalRows: 0,\n totalBytes: 0,\n };\n\n const state: DownloadState = {\n spec,\n status: {\n table,\n columns,\n rows: 0,\n totalRows,\n totalBytes,\n },\n };\n const elapsed = (performance.now() - start).toFixed(3);\n lc.info?.(`Computed initial download state for ${table} (${elapsed} ms)`, {\n state: state.status,\n });\n return state;\n}\n\nfunction copy(\n lc: LogContext,\n {spec: table, status}: DownloadState,\n dbClient: PostgresDB,\n from: PostgresTransaction,\n to: Database,\n textCopy: boolean,\n syncMode: InitialSyncMode,\n sampleRate?: number | undefined,\n maxRowsPerTable?: number | undefined,\n): Promise<CopyResult> {\n if (textCopy) {\n return copyText(\n lc,\n table,\n status,\n dbClient,\n from,\n to,\n syncMode,\n sampleRate,\n maxRowsPerTable,\n );\n }\n return copyBinary(\n lc,\n table,\n status,\n from,\n to,\n syncMode,\n sampleRate,\n maxRowsPerTable,\n );\n}\n\nasync function copyBinary(\n lc: LogContext,\n table: PublishedTableSpec,\n status: DownloadStatus,\n from: PostgresTransaction,\n to: Database,\n syncMode: InitialSyncMode,\n sampleRate?: number | undefined,\n maxRowsPerTable?: number | undefined,\n): Promise<CopyResult> {\n const start = performance.now();\n const copyFormat: CopyFormat = 'binary';\n const copyMetrics = initialSyncCopyMetrics({syncMode, copyFormat});\n let flushMs = 0;\n let copyBytes = 0;\n let processingMs = 0;\n\n const tableName = liteTableName(table);\n const orderedColumns = Object.entries(table.columns);\n\n const columnNames = orderedColumns.map(([c]) => c);\n const columnSpecs = orderedColumns.map(([_name, spec]) => spec);\n const insertColumnList = columnNames.map(c => id(c)).join(',');\n\n const valuesSql =\n columnNames.length > 0 ? `(${'?,'.repeat(columnNames.length - 1)}?)` : '()';\n const insertSql = /*sql*/ `\n INSERT INTO \"${tableName}\" (${insertColumnList}) VALUES ${valuesSql}`;\n const insertStmt = to.prepare(insertSql);\n const insertBatchStmt = to.prepare(\n insertSql + `,${valuesSql}`.repeat(INSERT_BATCH_SIZE - 1),\n );\n\n // Build SELECT with ::text casts for columns without a known binary decoder.\n const select = makeDownloadStatements(\n table,\n columnNames,\n sampleRate,\n maxRowsPerTable,\n makeBinarySelectExprs(table, columnNames),\n ).select;\n\n const decoders = orderedColumns.map(([, spec]) =>\n hasBinaryDecoder(spec) ? makeBinaryDecoder(spec) : textCastDecoder,\n );\n\n const valuesPerRow = columnSpecs.length;\n const valuesPerBatch = valuesPerRow * INSERT_BATCH_SIZE;\n\n const pendingValues: LiteValueType[] = Array.from({\n length: MAX_BUFFERED_ROWS * valuesPerRow,\n });\n let pendingRows = 0;\n let pendingSize = 0;\n\n function flush() {\n const flushStart = performance.now();\n const flushedRows = pendingRows;\n const flushedSize = pendingSize;\n\n let l = 0;\n for (; pendingRows > INSERT_BATCH_SIZE; pendingRows -= INSERT_BATCH_SIZE) {\n insertBatchStmt.run(pendingValues.slice(l, (l += valuesPerBatch)));\n }\n for (; pendingRows > 0; pendingRows--) {\n insertStmt.run(pendingValues.slice(l, (l += valuesPerRow)));\n }\n const flushedValues = flushedRows * valuesPerRow;\n for (let i = 0; i < flushedValues; i++) {\n pendingValues[i] = undefined as unknown as LiteValueType;\n }\n pendingSize = 0;\n status.rows += flushedRows;\n\n const elapsed = performance.now() - flushStart;\n flushMs += elapsed;\n lc.debug?.(\n `flushed ${flushedRows} ${tableName} rows (${flushedSize} bytes) in ${elapsed.toFixed(3)} ms`,\n );\n logSlowCopyFlush(lc, {\n schema: table.schema,\n table: table.name,\n replicaTable: tableName,\n syncMode,\n copyFormat,\n elapsedMs: elapsed,\n flushedRows,\n flushedBytes: flushedSize,\n rows: status.rows,\n copyBytes,\n });\n }\n\n const binaryParser = new BinaryCopyParser();\n let col = 0;\n\n lc.info?.(`Starting binary copy stream of ${tableName}:`, select);\n\n const copyStreamStart = performance.now();\n await pipeline(\n await from\n .unsafe(`COPY (${select}) TO STDOUT WITH (FORMAT binary)`)\n .readable(),\n new Writable({\n highWaterMark: BUFFERED_SIZE_THRESHOLD,\n\n write(\n chunk: Buffer,\n _encoding: string,\n callback: (error?: Error) => void,\n ) {\n const processingStart = performance.now();\n try {\n copyBytes += chunk.length;\n copyMetrics.add(chunk.length);\n for (const fieldBuf of binaryParser.parse(chunk)) {\n const fieldSize = fieldBuf === null ? 4 : fieldBuf.length;\n pendingSize += fieldSize;\n pendingValues[pendingRows * valuesPerRow + col] =\n fieldBuf === null ? null : decoders[col](fieldBuf);\n\n if (++col === decoders.length) {\n col = 0;\n if (\n ++pendingRows >= MAX_BUFFERED_ROWS - valuesPerRow ||\n pendingSize >= BUFFERED_SIZE_THRESHOLD\n ) {\n flush();\n }\n }\n }\n processingMs += performance.now() - processingStart;\n callback();\n } catch (e) {\n processingMs += performance.now() - processingStart;\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n },\n\n final: (callback: (error?: Error) => void) => {\n const processingStart = performance.now();\n try {\n copyMetrics.flush();\n flush();\n processingMs += performance.now() - processingStart;\n callback();\n } catch (e) {\n processingMs += performance.now() - processingStart;\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n },\n\n destroy(error, callback) {\n copyMetrics.flush();\n callback(error);\n },\n }),\n );\n\n const sourceWaitMs = Math.max(\n 0,\n performance.now() - copyStreamStart - processingMs,\n );\n const elapsed = performance.now() - start;\n const result = {\n schema: table.schema,\n table: table.name,\n replicaTable: tableName,\n syncMode,\n copyFormat,\n columnCount: columnNames.length,\n rows: status.rows,\n estimatedRows: status.totalRows,\n estimatedBytes: status.totalBytes,\n flushMs,\n elapsedMs: elapsed,\n sourceWaitMs,\n processingMs,\n copyBytes,\n } satisfies CopyResult;\n\n lc.info?.(\n `Finished copying ${status.rows} rows into ${tableName} ` +\n `(flush: ${flushMs.toFixed(3)} ms) (total: ${elapsed.toFixed(3)} ms) `,\n result,\n );\n return result;\n}\n\nasync function copyText(\n lc: LogContext,\n table: PublishedTableSpec,\n status: DownloadStatus,\n dbClient: PostgresDB,\n from: PostgresTransaction,\n to: Database,\n syncMode: InitialSyncMode,\n sampleRate?: number | undefined,\n maxRowsPerTable?: number | undefined,\n): Promise<CopyResult> {\n const start = performance.now();\n const copyFormat: CopyFormat = 'text';\n const copyMetrics = initialSyncCopyMetrics({syncMode, copyFormat});\n let flushMs = 0;\n let copyBytes = 0;\n let processingMs = 0;\n\n const tableName = liteTableName(table);\n const orderedColumns = Object.entries(table.columns);\n\n const columnNames = orderedColumns.map(([c]) => c);\n const columnSpecs = orderedColumns.map(([_name, spec]) => spec);\n const insertColumnList = columnNames.map(c => id(c)).join(',');\n\n const valuesSql =\n columnNames.length > 0 ? `(${'?,'.repeat(columnNames.length - 1)}?)` : '()';\n const insertSql = /*sql*/ `\n INSERT INTO \"${tableName}\" (${insertColumnList}) VALUES ${valuesSql}`;\n const insertStmt = to.prepare(insertSql);\n const insertBatchStmt = to.prepare(\n insertSql + `,${valuesSql}`.repeat(INSERT_BATCH_SIZE - 1),\n );\n\n const {select} = makeDownloadStatements(\n table,\n columnNames,\n sampleRate,\n maxRowsPerTable,\n );\n const valuesPerRow = columnSpecs.length;\n const valuesPerBatch = valuesPerRow * INSERT_BATCH_SIZE;\n\n const pendingValues: LiteValueType[] = Array.from({\n length: MAX_BUFFERED_ROWS * valuesPerRow,\n });\n let pendingRows = 0;\n let pendingSize = 0;\n\n function flush() {\n const flushStart = performance.now();\n const flushedRows = pendingRows;\n const flushedSize = pendingSize;\n\n let l = 0;\n for (; pendingRows > INSERT_BATCH_SIZE; pendingRows -= INSERT_BATCH_SIZE) {\n insertBatchStmt.run(pendingValues.slice(l, (l += valuesPerBatch)));\n }\n for (; pendingRows > 0; pendingRows--) {\n insertStmt.run(pendingValues.slice(l, (l += valuesPerRow)));\n }\n const flushedValues = flushedRows * valuesPerRow;\n for (let i = 0; i < flushedValues; i++) {\n pendingValues[i] = undefined as unknown as LiteValueType;\n }\n pendingSize = 0;\n status.rows += flushedRows;\n\n const elapsed = performance.now() - flushStart;\n flushMs += elapsed;\n lc.debug?.(\n `flushed ${flushedRows} ${tableName} rows (${flushedSize} bytes) in ${elapsed.toFixed(3)} ms`,\n );\n logSlowCopyFlush(lc, {\n schema: table.schema,\n table: table.name,\n replicaTable: tableName,\n syncMode,\n copyFormat,\n elapsedMs: elapsed,\n flushedRows,\n flushedBytes: flushedSize,\n rows: status.rows,\n copyBytes,\n });\n }\n\n lc.info?.(`Starting text copy stream of ${tableName}:`, select);\n const pgParsers = await getTypeParsers(dbClient, {returnJsonAsString: true});\n const parsers = columnSpecs.map(c => {\n const pgParse = pgParsers.getTypeParser(c.typeOID);\n return (val: string) =>\n liteValue(\n pgParse(val) as PostgresValueType,\n c.dataType,\n JSON_STRINGIFIED,\n );\n });\n\n const tsvParser = new TsvParser();\n let col = 0;\n\n const copyStreamStart = performance.now();\n await pipeline(\n await from.unsafe(`COPY (${select}) TO STDOUT`).readable(),\n new Writable({\n highWaterMark: BUFFERED_SIZE_THRESHOLD,\n\n write(\n chunk: Buffer,\n _encoding: string,\n callback: (error?: Error) => void,\n ) {\n const processingStart = performance.now();\n try {\n copyBytes += chunk.length;\n copyMetrics.add(chunk.length);\n for (const text of tsvParser.parse(chunk)) {\n const fieldSize = text === null ? 4 : text.length;\n pendingSize += fieldSize;\n pendingValues[pendingRows * valuesPerRow + col] =\n text === null ? null : parsers[col](text);\n\n if (++col === parsers.length) {\n col = 0;\n if (\n ++pendingRows >= MAX_BUFFERED_ROWS - valuesPerRow ||\n pendingSize >= BUFFERED_SIZE_THRESHOLD\n ) {\n flush();\n }\n }\n }\n processingMs += performance.now() - processingStart;\n callback();\n } catch (e) {\n processingMs += performance.now() - processingStart;\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n },\n\n final: (callback: (error?: Error) => void) => {\n const processingStart = performance.now();\n try {\n copyMetrics.flush();\n flush();\n processingMs += performance.now() - processingStart;\n callback();\n } catch (e) {\n processingMs += performance.now() - processingStart;\n callback(e instanceof Error ? e : new Error(String(e)));\n }\n },\n\n destroy(error, callback) {\n copyMetrics.flush();\n callback(error);\n },\n }),\n );\n\n const sourceWaitMs = Math.max(\n 0,\n performance.now() - copyStreamStart - processingMs,\n );\n const elapsed = performance.now() - start;\n const result = {\n schema: table.schema,\n table: table.name,\n replicaTable: tableName,\n syncMode,\n copyFormat,\n columnCount: columnNames.length,\n rows: status.rows,\n estimatedRows: status.totalRows,\n estimatedBytes: status.totalBytes,\n flushMs,\n elapsedMs: elapsed,\n sourceWaitMs,\n processingMs,\n copyBytes,\n } satisfies CopyResult;\n lc.info?.(\n `Finished copying ${status.rows} rows into ${tableName} ` +\n `(flush: ${flushMs.toFixed(3)} ms) (total: ${elapsed.toFixed(3)} ms) `,\n result,\n );\n return result;\n}\n"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { LogContext } from '@rocicorp/logger';
2
+ import postgres from 'postgres';
2
3
  import { type PostgresDB } from '../../../../types/pg.ts';
3
4
  import { type Sink, type Source } from '../../../../types/streams.ts';
4
5
  import type { Message } from './pgoutput.types.ts';
@@ -12,6 +13,7 @@ export type StreamMessage = [
12
13
  type SourceWithPendingQueue<T> = Source<T> & {
13
14
  queued: number;
14
15
  };
16
+ export declare function createReplicationSessionFor(db: PostgresDB, applicationName: string): postgres.Sql<{}>;
15
17
  export declare function subscribe(lc: LogContext, db: PostgresDB, slot: string, publications: string[], lsn: bigint, retriesIfReplicationSlotActive?: number, applicationName?: string, inboundTimeoutOverrideMs?: number | undefined): Promise<{
16
18
  messages: SourceWithPendingQueue<StreamMessage>;
17
19
  acks: Sink<bigint>;
@@ -80,5 +82,23 @@ export declare function evaluateInboundLiveness({ sinceLastReceived, inboundTime
80
82
  resetWindow: boolean;
81
83
  timedOut: boolean;
82
84
  };
85
+ /**
86
+ * Starts a "dummy" replication session for the specified `slot` which:
87
+ * * does not consume any records
88
+ * * keeps the session alive by sending status messages at the starting LSN
89
+ *
90
+ * This essentially reserves the replication slot for this process by marking
91
+ * it as "active" in pg_replication_slots, allowing replication slot cleanup
92
+ * logic to distinguish a new-but-initializing replication slot from one that
93
+ * has been abandoned / orphaned.
94
+ *
95
+ * When initialization (e.g. initial-sync) has finished, the server should take
96
+ * over the replication slot using pg_terminate_backend, which will close the
97
+ * session.
98
+ *
99
+ * Note that caller should not close the supplied `session`, as that will
100
+ * also terminate the dummy replication session.
101
+ */
102
+ export declare function keepSlotActiveUntilTakenOver(lc: LogContext, session: postgres.Sql, slot: string, dummyPublication: string, lsn: bigint): Promise<import("stream").Readable>;
83
103
  export {};
84
104
  //# sourceMappingURL=stream.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/logical-replication/stream.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAKjD,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAO,KAAK,IAAI,EAAE,KAAK,MAAM,EAAC,MAAM,8BAA8B,CAAC;AAK1E,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAC;AAIjD,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM;IACX,OAAO,GAAG;QAAC,GAAG,EAAE,WAAW,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAC;CACrD,CAAC;AAKF,KAAK,sBAAsB,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,SAAS,CAC7B,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EAAE,EACtB,GAAG,EAAE,MAAM,EACX,8BAA8B,SAA6C,EAC3E,eAAe,SAAoB,EACnC,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,GAC5C,OAAO,CAAC;IACT,QAAQ,EAAE,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAChD,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;CACpB,CAAC,CAyJD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,sBAAsB,CACpC,kBAAkB,EAAE,MAAM,EAC1B,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,GAC5C;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB,CA6BA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,iBAAiB,EACjB,gBAAgB,EAChB,MAAM,GACP,EAAE;IACD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAC,CAK5C"}
1
+ {"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../../../../../../../../zero-cache/src/services/change-source/pg/logical-replication/stream.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AAEjD,OAAO,QAA2C,MAAM,UAAU,CAAC;AAGnE,OAAO,EAAC,KAAK,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAExD,OAAO,EAAO,KAAK,IAAI,EAAE,KAAK,MAAM,EAAC,MAAM,8BAA8B,CAAC;AAK1E,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,qBAAqB,CAAC;AAIjD,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM;IACX,OAAO,GAAG;QAAC,GAAG,EAAE,WAAW,CAAC;QAAC,aAAa,EAAE,OAAO,CAAA;KAAC;CACrD,CAAC;AAKF,KAAK,sBAAsB,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,2BAA2B,CACzC,EAAE,EAAE,UAAU,EACd,eAAe,EAAE,MAAM,oBAqBxB;AAED,wBAAsB,SAAS,CAC7B,EAAE,EAAE,UAAU,EACd,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EAAE,EACtB,GAAG,EAAE,MAAM,EACX,8BAA8B,SAA6C,EAC3E,eAAe,SAAoB,EACnC,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,GAC5C,OAAO,CAAC;IACT,QAAQ,EAAE,sBAAsB,CAAC,aAAa,CAAC,CAAC;IAChD,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;CACpB,CAAC,CAuID;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,sBAAsB,CACpC,kBAAkB,EAAE,MAAM,EAC1B,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,GAC5C;IACD,OAAO,EAAE,OAAO,CAAC;IACjB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB,CA6BA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,iBAAiB,EACjB,gBAAgB,EAChB,MAAM,GACP,EAAE;IACD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAC,CAK5C;AA8DD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,UAAU,EACd,OAAO,EAAE,QAAQ,CAAC,GAAG,EACrB,IAAI,EAAE,MAAM,EACZ,gBAAgB,EAAE,MAAM,EACxB,GAAG,EAAE,MAAM,sCA8BZ"}